1 /* $OpenBSD: admin.c,v 1.63 2008/06/20 16:32:06 tobias 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, 2007 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 <sys/param.h> 21 #include <sys/dirent.h> 22 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <libgen.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "cvs.h" 30 #include "remote.h" 31 32 #define ADM_EFLAG 0x01 33 34 void cvs_admin_local(struct cvs_file *); 35 36 struct cvs_cmd cvs_cmd_admin = { 37 CVS_OP_ADMIN, CVS_USE_WDIR | CVS_LOCK_REPO, "admin", 38 { "adm", "rcs" }, 39 "Administrative front-end for RCS", 40 "[-ILqU] [-A oldfile] [-a users] [-b branch]\n" 41 "[-c string] [-e [users]] [-k mode] [-l [rev]] [-m rev:msg]\n" 42 "[-N tag[:rev]] [-n tag[:rev]] [-o rev] [-s state[:rev]]" 43 "[-t file | str]\n" 44 "[-u [rev]] file ...", 45 "A:a:b::c:e::Ik:l::Lm:N:n:o:qs:t:Uu::", 46 NULL, 47 cvs_admin 48 }; 49 50 static int runflags = 0; 51 static int lkmode = RCS_LOCK_INVAL; 52 static char *alist, *comment, *elist, *logmsg, *logstr, *koptstr; 53 static char *oldfilename, *orange, *state, *statestr; 54 static RCSNUM *logrev; 55 56 int 57 cvs_admin(int argc, char **argv) 58 { 59 int ch; 60 int flags; 61 struct cvs_recursion cr; 62 63 flags = CR_RECURSE_DIRS; 64 65 alist = comment = elist = logmsg = logstr = NULL; 66 oldfilename = orange = state = statestr = NULL; 67 68 while ((ch = getopt(argc, argv, cvs_cmd_admin.cmd_opts)) != -1) { 69 switch (ch) { 70 case 'A': 71 oldfilename = optarg; 72 break; 73 case 'a': 74 alist = optarg; 75 break; 76 case 'b': 77 break; 78 case 'c': 79 comment = optarg; 80 break; 81 case 'e': 82 elist = optarg; 83 runflags |= ADM_EFLAG; 84 break; 85 case 'I': 86 break; 87 case 'k': 88 koptstr = optarg; 89 kflag = rcs_kflag_get(koptstr); 90 if (RCS_KWEXP_INVAL(kflag)) { 91 cvs_log(LP_ERR, 92 "invalid RCS keyword expansion mode"); 93 fatal("%s", cvs_cmd_admin.cmd_synopsis); 94 } 95 break; 96 case 'L': 97 if (lkmode == RCS_LOCK_LOOSE) { 98 cvs_log(LP_ERR, "-L and -U are incompatible"); 99 fatal("%s", cvs_cmd_admin.cmd_synopsis); 100 } 101 lkmode = RCS_LOCK_STRICT; 102 break; 103 case 'l': 104 break; 105 case 'm': 106 logstr = optarg; 107 break; 108 case 'N': 109 break; 110 case 'n': 111 break; 112 case 'o': 113 orange = optarg; 114 break; 115 case 'q': 116 verbosity = 0; 117 break; 118 case 's': 119 statestr = optarg; 120 break; 121 case 't': 122 break; 123 case 'U': 124 if (lkmode == RCS_LOCK_STRICT) { 125 cvs_log(LP_ERR, "-U and -L are incompatible"); 126 fatal("%s", cvs_cmd_admin.cmd_synopsis); 127 } 128 lkmode = RCS_LOCK_LOOSE; 129 break; 130 case 'u': 131 break; 132 default: 133 fatal("%s", cvs_cmd_admin.cmd_synopsis); 134 } 135 } 136 137 argc -= optind; 138 argv += optind; 139 140 if (argc == 0) 141 fatal("%s", cvs_cmd_admin.cmd_synopsis); 142 143 cr.enterdir = NULL; 144 cr.leavedir = NULL; 145 146 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 147 cvs_client_connect_to_server(); 148 cr.fileproc = cvs_client_sendfile; 149 150 if (oldfilename != NULL) 151 cvs_client_send_request("Argument -A%s", oldfilename); 152 153 if (alist != NULL) 154 cvs_client_send_request("Argument -a%s", alist); 155 156 if (comment != NULL) 157 cvs_client_send_request("Argument -c%s", comment); 158 159 if (runflags & ADM_EFLAG) 160 cvs_client_send_request("Argument -e%s", 161 (elist != NULL) ? elist : ""); 162 163 if (koptstr != NULL) 164 cvs_client_send_request("Argument -k%s", koptstr); 165 166 if (lkmode == RCS_LOCK_STRICT) 167 cvs_client_send_request("Argument -L"); 168 else if (lkmode == RCS_LOCK_LOOSE) 169 cvs_client_send_request("Argument -U"); 170 171 if (logstr != NULL) 172 cvs_client_send_logmsg(logstr); 173 174 if (orange != NULL) 175 cvs_client_send_request("Argument -o%s", orange); 176 177 if (statestr != NULL) 178 cvs_client_send_request("Argument -s%s", statestr); 179 180 if (verbosity == 0) 181 cvs_client_send_request("Argument -q"); 182 183 } else { 184 flags |= CR_REPO; 185 cr.fileproc = cvs_admin_local; 186 } 187 188 cr.flags = flags; 189 190 cvs_file_run(argc, argv, &cr); 191 192 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 193 cvs_client_send_files(argv, argc); 194 cvs_client_senddir("."); 195 cvs_client_send_request("admin"); 196 cvs_client_get_responses(); 197 } 198 199 return (0); 200 } 201 202 void 203 cvs_admin_local(struct cvs_file *cf) 204 { 205 int i; 206 207 cvs_log(LP_TRACE, "cvs_admin_local(%s)", cf->file_path); 208 209 cvs_file_classify(cf, cvs_directory_tag); 210 211 if (cf->file_type == CVS_DIR) { 212 if (verbosity > 1) 213 cvs_log(LP_NOTICE, "Administrating %s", cf->file_name); 214 return; 215 } 216 217 if (cf->file_ent == NULL) 218 return; 219 else if (cf->file_status == FILE_ADDED) { 220 cvs_log(LP_ERR, "cannot admin newly added file `%s'", 221 cf->file_name); 222 return; 223 } 224 225 if (cf->file_rcs == NULL) { 226 cvs_log(LP_ERR, "lost RCS file for `%s'", cf->file_path); 227 return; 228 } 229 230 if (verbosity > 0) 231 cvs_printf("RCS file: %s\n", cf->file_rcs->rf_path); 232 233 if (oldfilename != NULL) { 234 struct cvs_file *ocf; 235 struct rcs_access *acp; 236 int ofd; 237 char *d, *f, fpath[MAXPATHLEN], repo[MAXPATHLEN]; 238 239 240 if ((f = basename(oldfilename)) == NULL) 241 fatal("cvs_admin_local: basename failed"); 242 if ((d = dirname(oldfilename)) == NULL) 243 fatal("cvs_admin_local: dirname failed"); 244 245 cvs_get_repository_path(d, repo, MAXPATHLEN); 246 247 (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", repo, f); 248 249 if (strlcat(fpath, RCS_FILE_EXT, MAXPATHLEN) >= MAXPATHLEN) 250 fatal("cvs_admin_local: truncation"); 251 252 if ((ofd = open(fpath, O_RDONLY)) == -1) 253 fatal("cvs_admin_local: open: `%s': %s", fpath, 254 strerror(errno)); 255 256 /* XXX: S_ISREG() check instead of blindly using CVS_FILE? */ 257 ocf = cvs_file_get_cf(d, f, oldfilename, ofd, CVS_FILE, 0); 258 259 ocf->file_rcs = rcs_open(fpath, ofd, RCS_READ, 0444); 260 if (ocf->file_rcs == NULL) 261 fatal("cvs_admin_local: rcs_open failed"); 262 263 TAILQ_FOREACH(acp, &(ocf->file_rcs->rf_access), ra_list) 264 rcs_access_add(cf->file_rcs, acp->ra_name); 265 266 cvs_file_free(ocf); 267 } 268 269 if (alist != NULL) { 270 struct cvs_argvector *aargv; 271 272 aargv = cvs_strsplit(alist, ","); 273 for (i = 0; aargv->argv[i] != NULL; i++) 274 rcs_access_add(cf->file_rcs, aargv->argv[i]); 275 276 cvs_argv_destroy(aargv); 277 } 278 279 if (comment != NULL) 280 rcs_comment_set(cf->file_rcs, comment); 281 282 if (elist != NULL) { 283 struct cvs_argvector *eargv; 284 285 eargv = cvs_strsplit(elist, ","); 286 for (i = 0; eargv->argv[i] != NULL; i++) 287 rcs_access_remove(cf->file_rcs, eargv->argv[i]); 288 289 cvs_argv_destroy(eargv); 290 } else if (runflags & ADM_EFLAG) { 291 struct rcs_access *rap; 292 293 while (!TAILQ_EMPTY(&(cf->file_rcs->rf_access))) { 294 rap = TAILQ_FIRST(&(cf->file_rcs->rf_access)); 295 TAILQ_REMOVE(&(cf->file_rcs->rf_access), rap, ra_list); 296 xfree(rap->ra_name); 297 xfree(rap); 298 } 299 /* no synced anymore */ 300 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 301 } 302 303 /* Default `-kv' is accepted here. */ 304 if (kflag) { 305 if (cf->file_rcs->rf_expand == NULL || 306 strcmp(cf->file_rcs->rf_expand, koptstr) != 0) 307 rcs_kwexp_set(cf->file_rcs, kflag); 308 } 309 310 if (logstr != NULL) { 311 if ((logmsg = strchr(logstr, ':')) == NULL) { 312 cvs_log(LP_ERR, "missing log message"); 313 return; 314 } 315 316 *logmsg++ = '\0'; 317 if ((logrev = rcsnum_parse(logstr)) == NULL) { 318 cvs_log(LP_ERR, "`%s' bad revision number", logstr); 319 return; 320 } 321 322 if (rcs_rev_setlog(cf->file_rcs, logrev, logmsg) < 0) { 323 cvs_log(LP_ERR, "failed to set logmsg for `%s' to `%s'", 324 logstr, logmsg); 325 rcsnum_free(logrev); 326 return; 327 } 328 329 rcsnum_free(logrev); 330 } 331 332 if (orange != NULL) { 333 struct rcs_delta *rdp, *nrdp; 334 char b[CVS_REV_BUFSZ]; 335 336 cvs_revision_select(cf->file_rcs, orange); 337 for (rdp = TAILQ_FIRST(&(cf->file_rcs->rf_delta)); 338 rdp != NULL; rdp = nrdp) { 339 nrdp = TAILQ_NEXT(rdp, rd_list); 340 341 /* 342 * Delete selected revisions. 343 */ 344 if (rdp->rd_flags & RCS_RD_SELECT) { 345 rcsnum_tostr(rdp->rd_num, b, sizeof(b)); 346 if (verbosity > 0) 347 cvs_printf("deleting revision %s\n", b); 348 349 (void)rcs_rev_remove(cf->file_rcs, rdp->rd_num); 350 } 351 } 352 } 353 354 if (statestr != NULL) { 355 struct cvs_argvector *sargv; 356 357 sargv = cvs_strsplit(statestr, ":"); 358 if (sargv->argv[1] != NULL) { 359 state = xstrdup(sargv->argv[0]); 360 361 if ((logrev = rcsnum_parse(sargv->argv[1])) == NULL) { 362 cvs_log(LP_ERR, "`%s' bad revision number", statestr); 363 cvs_argv_destroy(sargv); 364 xfree(state); 365 return; 366 } 367 } else if (cf->file_rcs->rf_head != NULL) { 368 state = xstrdup(statestr); 369 logrev = rcsnum_alloc(); 370 rcsnum_cpy(cf->file_rcs->rf_head, logrev, 0); 371 } else { 372 cvs_log(LP_ERR, "head revision missing"); 373 cvs_argv_destroy(sargv); 374 return; 375 } 376 377 if (rcs_state_check(state) < 0) { 378 cvs_log(LP_ERR, "invalid state `%s'", state); 379 cvs_argv_destroy(sargv); 380 rcsnum_free(logrev); 381 xfree(state); 382 return; 383 } 384 385 (void)rcs_state_set(cf->file_rcs, logrev, state); 386 387 cvs_argv_destroy(sargv); 388 rcsnum_free(logrev); 389 xfree(state); 390 } 391 392 if (lkmode != RCS_LOCK_INVAL) 393 (void)rcs_lock_setmode(cf->file_rcs, lkmode); 394 395 rcs_write(cf->file_rcs); 396 397 if (verbosity > 0) 398 cvs_printf("done\n"); 399 } 400