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