1 /* $OpenBSD: admin.c,v 1.46 2007/01/25 18:56:33 otto Exp $ */ 2 /* 3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> 4 * Copyright (c) 2005 Joris Vink <joris@openbsd.org> 5 * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include "includes.h" 21 22 #include "cvs.h" 23 #include "log.h" 24 #include "remote.h" 25 26 #define ADM_EFLAG 0x01 27 28 void cvs_admin_local(struct cvs_file *); 29 30 struct cvs_cmd cvs_cmd_admin = { 31 CVS_OP_ADMIN, 0, "admin", 32 { "adm", "rcs" }, 33 "Administrative front-end for RCS", 34 "[-ILqU] [-A oldfile] [-a users] [-b branch]\n" 35 "[-c string] [-e [users]] [-k mode] [-l [rev]] [-m rev:msg]\n" 36 "[-N tag[:rev]] [-n tag[:rev]] [-o rev] [-s state[:rev]]" 37 "[-t file | str]\n" 38 "[-u [rev]] file ...", 39 "A:a:b::c:e::Ik:l::Lm:N:n:o:qs:t:Uu::", 40 NULL, 41 cvs_admin 42 }; 43 44 static int runflags = 0; 45 static int lkmode = RCS_LOCK_INVAL; 46 static char *alist, *comment, *elist, *logmsg, *logstr; 47 static char *oldfilename, *orange, *state, *statestr; 48 static RCSNUM *logrev; 49 50 int 51 cvs_admin(int argc, char **argv) 52 { 53 int ch; 54 int flags; 55 struct cvs_recursion cr; 56 57 flags = CR_RECURSE_DIRS; 58 59 alist = comment = elist = logmsg = logstr = NULL; 60 oldfilename = orange = state = statestr = NULL; 61 62 while ((ch = getopt(argc, argv, cvs_cmd_admin.cmd_opts)) != -1) { 63 switch (ch) { 64 case 'A': 65 oldfilename = optarg; 66 break; 67 case 'a': 68 alist = optarg; 69 break; 70 case 'b': 71 break; 72 case 'c': 73 comment = optarg; 74 break; 75 case 'e': 76 elist = optarg; 77 runflags |= ADM_EFLAG; 78 break; 79 case 'I': 80 break; 81 case 'k': 82 break; 83 case 'L': 84 if (lkmode == RCS_LOCK_LOOSE) { 85 cvs_log(LP_ERR, "-L and -U are incompatible"); 86 fatal("%s", cvs_cmd_admin.cmd_synopsis); 87 } 88 lkmode = RCS_LOCK_STRICT; 89 break; 90 case 'l': 91 break; 92 case 'm': 93 logstr = optarg; 94 break; 95 case 'N': 96 break; 97 case 'n': 98 break; 99 case 'o': 100 orange = optarg; 101 break; 102 case 'q': 103 verbosity = 0; 104 break; 105 case 's': 106 statestr = optarg; 107 break; 108 case 't': 109 break; 110 case 'U': 111 if (lkmode == RCS_LOCK_STRICT) { 112 cvs_log(LP_ERR, "-U and -L are incompatible"); 113 fatal("%s", cvs_cmd_admin.cmd_synopsis); 114 } 115 lkmode = RCS_LOCK_LOOSE; 116 break; 117 case 'u': 118 break; 119 default: 120 fatal("%s", cvs_cmd_admin.cmd_synopsis); 121 } 122 } 123 124 argc -= optind; 125 argv += optind; 126 127 if (argc == 0) 128 fatal("%s", cvs_cmd_admin.cmd_synopsis); 129 130 cr.enterdir = NULL; 131 cr.leavedir = NULL; 132 133 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 134 cvs_client_connect_to_server(); 135 cr.fileproc = cvs_client_sendfile; 136 137 if (oldfilename != NULL) 138 cvs_client_send_request("Argument -A%s", oldfilename); 139 140 if (alist != NULL) 141 cvs_client_send_request("Argument -a%s", alist); 142 143 if (comment != NULL) 144 cvs_client_send_request("Argument -c%s", comment); 145 146 if (runflags & ADM_EFLAG) 147 cvs_client_send_request("Argument -e%s", 148 (elist != NULL) ? elist : ""); 149 150 if (lkmode == RCS_LOCK_STRICT) 151 cvs_client_send_request("Argument -L"); 152 else if (lkmode == RCS_LOCK_LOOSE) 153 cvs_client_send_request("Argument -U"); 154 155 if (logstr != NULL) 156 cvs_client_send_request("Argument -m%s", logstr); 157 158 if (orange != NULL) 159 cvs_client_send_request("Argument -o%s", orange); 160 161 if (statestr != NULL) 162 cvs_client_send_request("Argument -s%s", statestr); 163 164 if (verbosity == 0) 165 cvs_client_send_request("Argument -q"); 166 167 } else { 168 flags |= CR_REPO; 169 cr.fileproc = cvs_admin_local; 170 } 171 172 cr.flags = flags; 173 174 cvs_file_run(argc, argv, &cr); 175 176 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 177 cvs_client_send_files(argv, argc); 178 cvs_client_senddir("."); 179 cvs_client_send_request("admin"); 180 cvs_client_get_responses(); 181 } 182 183 return (0); 184 } 185 186 void 187 cvs_admin_local(struct cvs_file *cf) 188 { 189 int i; 190 191 cvs_log(LP_TRACE, "cvs_admin_local(%s)", cf->file_path); 192 193 cvs_file_classify(cf, NULL, 0); 194 195 if (cf->file_type == CVS_DIR) { 196 if (verbosity > 1) 197 cvs_log(LP_NOTICE, "Administrating %s", cf->file_name); 198 return; 199 } 200 201 if (cf->file_status == FILE_UNKNOWN) 202 return; 203 else if (cf->file_status == FILE_ADDED) { 204 cvs_log(LP_ERR, "cannot admin newly added file `%s'", 205 cf->file_name); 206 return; 207 } 208 209 if (verbosity > 0) 210 cvs_printf("RCS file: %s\n", cf->file_rcs->rf_path); 211 212 if (oldfilename != NULL) { 213 struct cvs_file *ocf; 214 struct rcs_access *acp; 215 int ofd; 216 char *d, *f, fpath[MAXPATHLEN], repo[MAXPATHLEN]; 217 218 219 if ((f = basename(oldfilename)) == NULL) 220 fatal("cvs_admin_local: basename failed"); 221 if ((d = dirname(oldfilename)) == NULL) 222 fatal("cvs_admin_local: dirname failed"); 223 224 cvs_get_repository_path(d, repo, MAXPATHLEN); 225 226 if (cvs_path_cat(repo, f, fpath, MAXPATHLEN) >= MAXPATHLEN) 227 fatal("cvs_admin_local: truncation"); 228 229 if (strlcat(fpath, RCS_FILE_EXT, MAXPATHLEN) >= MAXPATHLEN) 230 fatal("cvs_admin_local: truncation"); 231 232 if ((ofd = open(fpath, O_RDONLY)) == -1) 233 fatal("cvs_admin_local: open: `%s': %s", fpath, 234 strerror(errno)); 235 236 /* XXX: S_ISREG() check instead of blindly using CVS_FILE? */ 237 ocf = cvs_file_get_cf(d, f, ofd, CVS_FILE); 238 239 ocf->file_rcs = rcs_open(fpath, ofd, RCS_READ, 0444); 240 if (ocf->file_rcs == NULL) 241 fatal("cvs_admin_local: rcs_open failed"); 242 243 TAILQ_FOREACH(acp, &(ocf->file_rcs->rf_access), ra_list) 244 rcs_access_add(cf->file_rcs, acp->ra_name); 245 246 (void)close(ofd); 247 248 cvs_file_free(ocf); 249 } 250 251 if (alist != NULL) { 252 struct cvs_argvector *aargv; 253 254 aargv = cvs_strsplit(alist, ","); 255 for (i = 0; aargv->argv[i] != NULL; i++) 256 rcs_access_add(cf->file_rcs, aargv->argv[i]); 257 258 cvs_argv_destroy(aargv); 259 } 260 261 if (comment != NULL) 262 rcs_comment_set(cf->file_rcs, comment); 263 264 if (elist != NULL) { 265 struct cvs_argvector *eargv; 266 267 eargv = cvs_strsplit(elist, ","); 268 for (i = 0; eargv->argv[i] != NULL; i++) 269 rcs_access_remove(cf->file_rcs, eargv->argv[i]); 270 271 cvs_argv_destroy(eargv); 272 } else if (runflags & ADM_EFLAG) { 273 struct rcs_access *rap; 274 275 while (!TAILQ_EMPTY(&(cf->file_rcs->rf_access))) { 276 rap = TAILQ_FIRST(&(cf->file_rcs->rf_access)); 277 TAILQ_REMOVE(&(cf->file_rcs->rf_access), rap, ra_list); 278 xfree(rap->ra_name); 279 xfree(rap); 280 } 281 /* no synced anymore */ 282 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 283 } 284 285 if (logstr != NULL) { 286 if ((logmsg = strchr(logstr, ':')) == NULL) { 287 cvs_log(LP_ERR, "missing log message"); 288 return; 289 } 290 291 *logmsg++ = '\0'; 292 if ((logrev = rcsnum_parse(logstr)) == NULL) { 293 cvs_log(LP_ERR, "`%s' bad revision number", logstr); 294 return; 295 } 296 297 if (rcs_rev_setlog(cf->file_rcs, logrev, logmsg) < 0) { 298 cvs_log(LP_ERR, "failed to set logmsg for `%s' to `%s'", 299 logstr, logmsg); 300 rcsnum_free(logrev); 301 return; 302 } 303 304 rcsnum_free(logrev); 305 } 306 307 if (orange != NULL) { 308 struct rcs_delta *rdp, *nrdp; 309 char b[16]; 310 311 cvs_revision_select(cf->file_rcs, orange); 312 for (rdp = TAILQ_FIRST(&(cf->file_rcs->rf_delta)); 313 rdp != NULL; rdp = nrdp) { 314 nrdp = TAILQ_NEXT(rdp, rd_list); 315 316 /* 317 * Delete selected revisions. 318 */ 319 if (rdp->rd_flags & RCS_RD_SELECT) { 320 rcsnum_tostr(rdp->rd_num, b, sizeof(b)); 321 if (verbosity > 0) 322 cvs_printf("deleting revision %s\n", b); 323 324 (void)rcs_rev_remove(cf->file_rcs, rdp->rd_num); 325 } 326 } 327 } 328 329 if (statestr != NULL) { 330 struct cvs_argvector *sargv; 331 332 sargv = cvs_strsplit(statestr, ":"); 333 if (sargv->argv[1] != NULL) { 334 state = xstrdup(sargv->argv[0]); 335 336 if ((logrev = rcsnum_parse(sargv->argv[1])) == NULL) { 337 cvs_log(LP_ERR, "`%s' bad revision number", statestr); 338 cvs_argv_destroy(sargv); 339 xfree(state); 340 return; 341 } 342 } else { 343 state = xstrdup(statestr); 344 logrev = rcsnum_alloc(); 345 rcsnum_cpy(cf->file_rcs->rf_head, logrev, 0); 346 } 347 348 if (rcs_state_check(state) < 0) { 349 cvs_log(LP_ERR, "invalid state `%s'", state); 350 cvs_argv_destroy(sargv); 351 rcsnum_free(logrev); 352 xfree(state); 353 return; 354 } 355 356 (void)rcs_state_set(cf->file_rcs, logrev, state); 357 358 cvs_argv_destroy(sargv); 359 rcsnum_free(logrev); 360 xfree(state); 361 } 362 363 if (lkmode != RCS_LOCK_INVAL) 364 (void)rcs_lock_setmode(cf->file_rcs, lkmode); 365 366 rcs_write(cf->file_rcs); 367 368 if (verbosity > 0) 369 cvs_printf("done\n"); 370 } 371