Annotation of src/usr.bin/cvs/admin.c, Revision 1.18
1.18 ! joris 1: /* $OpenBSD: admin.c,v 1.17 2005/05/31 08:58:47 xsa Exp $ */
1.1 joris 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4: * Copyright (c) 2005 Joris Vink <joris@openbsd.org>
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: *
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. The name of the author may not be used to endorse or promote products
14: * derived from this software without specific prior written permission.
15: *
16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26: */
27:
28: #include <sys/types.h>
29: #include <sys/stat.h>
30:
31: #include <errno.h>
1.17 xsa 32: #include <fcntl.h>
1.1 joris 33: #include <stdio.h>
34: #include <stdlib.h>
1.17 xsa 35: #include <string.h>
1.1 joris 36: #include <unistd.h>
37:
38: #include "cvs.h"
39: #include "log.h"
40: #include "proto.h"
41:
42:
43: #define LOCK_SET 0x01
44: #define LOCK_REMOVE 0x02
45:
46: #define FLAG_BRANCH 0x01
47: #define FLAG_DELUSER 0x02
48: #define FLAG_INTERACTIVE 0x04
49: #define FLAG_QUIET 0x08
50:
1.15 jfb 51: static int cvs_admin_init (struct cvs_cmd *, int, char **, int *);
52: static int cvs_admin_pre_exec (struct cvsroot *);
1.16 jfb 53: static int cvs_admin_remote (CVSFILE *, void *);
54: static int cvs_admin_local (CVSFILE *, void *);
1.15 jfb 55:
56: struct cvs_cmd cvs_cmd_admin = {
57: CVS_OP_ADMIN, CVS_REQ_ADMIN, "admin",
58: { "adm", "rcs" },
59: "Administrative front-end for RCS",
60: "",
61: "a:A:b::c:e::Ik:l::Lm:n:N:o:qs:t:u::U",
62: NULL,
63: CF_SORT | CF_IGNORE | CF_RECURSE,
64: cvs_admin_init,
65: cvs_admin_pre_exec,
1.16 jfb 66: cvs_admin_remote,
67: cvs_admin_local,
1.15 jfb 68: NULL,
69: NULL,
1.7 joris 70: CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2
71: };
72:
73: static char *q, *Ntag, *ntag, *comment, *replace_msg;
74: static char *alist, *subst, *lockrev_arg, *unlockrev_arg;
75: static char *state, *userfile, *branch_arg, *elist, *range;
1.16 jfb 76: static int runflags, kflag, lockrev, lkmode;
77:
78: /* flag as invalid */
79: static int kflag = RCS_KWEXP_ERR;
80: static int lkmode = RCS_LOCK_INVAL;
1.7 joris 81:
1.15 jfb 82: static int
83: cvs_admin_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
1.1 joris 84: {
1.7 joris 85: int ch;
1.1 joris 86: RCSNUM *rcs;
87:
1.16 jfb 88: runflags = lockrev = 0;
1.5 joris 89: Ntag = ntag = comment = replace_msg = NULL;
90: state = alist = subst = elist = lockrev_arg = NULL;
91: range = userfile = branch_arg = unlockrev_arg = NULL;
1.1 joris 92:
93: /* option-o-rama ! */
1.15 jfb 94: while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
1.1 joris 95: switch (ch) {
96: case 'a':
97: alist = optarg;
98: break;
99: case 'A':
100: userfile = optarg;
101: break;
102: case 'b':
103: runflags |= FLAG_BRANCH;
104: if (optarg)
105: branch_arg = optarg;
106: break;
107: case 'c':
108: comment = optarg;
109: break;
110: case 'e':
111: runflags |= FLAG_DELUSER;
112: if (optarg)
113: elist = optarg;
114: break;
115: case 'I':
116: runflags |= FLAG_INTERACTIVE;
117: break;
118: case 'k':
119: subst = optarg;
1.3 joris 120: kflag = rcs_kflag_get(subst);
121: if (RCS_KWEXP_INVAL(kflag)) {
122: cvs_log(LP_ERR,
123: "invalid RCS keyword expansion mode");
124: rcs_kflag_usage();
1.11 joris 125: return (CVS_EX_USAGE);
1.3 joris 126: }
1.1 joris 127: break;
128: case 'l':
129: lockrev |= LOCK_SET;
130: if (optarg)
131: lockrev_arg = optarg;
132: break;
133: case 'L':
1.16 jfb 134: lkmode = RCS_LOCK_STRICT;
1.1 joris 135: break;
136: case 'm':
137: replace_msg = optarg;
138: break;
139: case 'n':
1.5 joris 140: ntag = optarg;
1.1 joris 141: break;
142: case 'N':
1.5 joris 143: Ntag = optarg;
1.1 joris 144: break;
145: case 'o':
1.5 joris 146: range = optarg;
1.1 joris 147: break;
148: case 'q':
149: runflags |= FLAG_QUIET;
150: break;
151: case 's':
1.5 joris 152: state = optarg;
1.1 joris 153: break;
154: case 't':
155: break;
156: case 'u':
157: lockrev |= LOCK_REMOVE;
158: if (optarg)
159: unlockrev_arg = optarg;
160: break;
161: case 'U':
1.16 jfb 162: if (lkmode != RCS_LOCK_INVAL) {
163: cvs_log(LP_ERR, "-L and -U are incompatible");
164: return (CVS_EX_USAGE);
165: }
166: lkmode = RCS_LOCK_LOOSE;
1.1 joris 167: break;
168: default:
1.11 joris 169: return (CVS_EX_USAGE);
1.1 joris 170: }
171: }
172:
173: argc -= optind;
174: argv += optind;
175:
176: if (lockrev_arg != NULL) {
177: if ((rcs = rcsnum_parse(lockrev_arg)) == NULL) {
178: cvs_log(LP_ERR, "%s is not a numeric branch",
179: lockrev_arg);
1.11 joris 180: return (CVS_EX_USAGE);
1.1 joris 181: }
182: rcsnum_free(rcs);
183: }
184:
185: if (unlockrev_arg != NULL) {
186: if ((rcs = rcsnum_parse(unlockrev_arg)) == NULL) {
187: cvs_log(LP_ERR, "%s is not a numeric branch",
188: unlockrev_arg);
1.11 joris 189: return (CVS_EX_USAGE);
1.1 joris 190: }
191: rcsnum_free(rcs);
192: }
193:
194: if (replace_msg != NULL) {
195: if ((q = strchr(replace_msg, ':')) == NULL) {
196: cvs_log(LP_ERR, "invalid option for -m");
1.11 joris 197: return (CVS_EX_USAGE);
1.1 joris 198: }
199: *q = '\0';
200: if ((rcs = rcsnum_parse(replace_msg)) == NULL) {
201: cvs_log(LP_ERR, "%s is not a numeric revision",
202: replace_msg);
1.11 joris 203: return (CVS_EX_USAGE);
1.1 joris 204: }
1.2 joris 205: rcsnum_free(rcs);
1.1 joris 206: *q = ':';
207: }
208:
1.7 joris 209: *arg = optind;
210: return (0);
211: }
1.1 joris 212:
1.15 jfb 213: static int
214: cvs_admin_pre_exec(struct cvsroot *root)
1.7 joris 215: {
1.16 jfb 216: if (root->cr_method == CVS_METHOD_LOCAL)
217: return (0);
218:
1.18 ! joris 219: if ((alist != NULL) && ((cvs_sendarg(root, "-a", 0) < 0) ||
1.7 joris 220: (cvs_sendarg(root, alist, 0) < 0)))
1.11 joris 221: return (CVS_EX_PROTO);
1.1 joris 222:
1.7 joris 223: if ((userfile != NULL) && ((cvs_sendarg(root, "-A", 0) < 0) ||
224: (cvs_sendarg(root, userfile, 0) < 0)))
1.11 joris 225: return (CVS_EX_PROTO);
1.1 joris 226:
1.7 joris 227: if (runflags & FLAG_BRANCH) {
228: if (cvs_sendarg(root, "-b", 0) < 0)
1.11 joris 229: return (CVS_EX_PROTO);
1.7 joris 230: if ((branch_arg != NULL) &&
231: (cvs_sendarg(root, branch_arg, 0) < 0))
1.11 joris 232: return (CVS_EX_PROTO);
1.7 joris 233: }
1.1 joris 234:
1.7 joris 235: if ((comment != NULL) && ((cvs_sendarg(root, "-c", 0) < 0) ||
236: (cvs_sendarg(root, comment, 0) < 0)))
1.11 joris 237: return (CVS_EX_PROTO);
1.1 joris 238:
1.7 joris 239: if (runflags & FLAG_DELUSER) {
240: if (cvs_sendarg(root, "-e", 0) < 0)
1.11 joris 241: return (CVS_EX_PROTO);
1.7 joris 242: if ((elist != NULL) &&
243: (cvs_sendarg(root, elist, 0) < 0))
1.11 joris 244: return (CVS_EX_PROTO);
1.7 joris 245: }
1.1 joris 246:
1.7 joris 247: if (runflags & FLAG_INTERACTIVE) {
248: if (cvs_sendarg(root, "-I", 0) < 0)
1.11 joris 249: return (CVS_EX_PROTO);
1.7 joris 250: }
1.1 joris 251:
1.7 joris 252: if ((subst != NULL) && ((cvs_sendarg(root, "-k", 0) < 0) ||
253: (cvs_sendarg(root, subst, 0) < 0)))
1.11 joris 254: return (CVS_EX_PROTO);
1.5 joris 255:
1.7 joris 256: if (lockrev & LOCK_SET) {
257: if (cvs_sendarg(root, "-l", 0) < 0)
1.11 joris 258: return (CVS_EX_PROTO);
1.7 joris 259: if ((lockrev_arg != NULL) &&
260: (cvs_sendarg(root, lockrev_arg, 0) < 0))
1.11 joris 261: return (CVS_EX_PROTO);
1.7 joris 262: }
1.5 joris 263:
1.16 jfb 264: if ((lkmode == RCS_LOCK_STRICT) && (cvs_sendarg(root, "-L", 0) < 0))
265: return (CVS_EX_PROTO);
266: else if ((lkmode == RCS_LOCK_LOOSE) && (cvs_sendarg(root, "-U", 0) < 0))
1.11 joris 267: return (CVS_EX_PROTO);
1.5 joris 268:
1.7 joris 269: if ((replace_msg != NULL) && ((cvs_sendarg(root, "-m", 0) < 0)
270: || (cvs_sendarg(root, replace_msg, 0) < 0)))
1.11 joris 271: return (CVS_EX_PROTO);
1.5 joris 272:
1.7 joris 273: if ((ntag != NULL) && ((cvs_sendarg(root, "-n", 0) < 0) ||
274: (cvs_sendarg(root, ntag, 0) < 0)))
1.11 joris 275: return (CVS_EX_PROTO);
1.1 joris 276:
1.7 joris 277: if ((Ntag != NULL) && ((cvs_sendarg(root, "-N", 0) < 0) ||
278: (cvs_sendarg(root, Ntag, 0) < 0)))
1.11 joris 279: return (CVS_EX_PROTO);
1.1 joris 280:
1.7 joris 281: if ((range != NULL) && ((cvs_sendarg(root, "-o", 0) < 0) ||
282: (cvs_sendarg(root, range, 0) < 0)))
1.11 joris 283: return (CVS_EX_PROTO);
1.1 joris 284:
1.7 joris 285: if ((state != NULL) && ((cvs_sendarg(root, "-s", 0) < 0) ||
286: (cvs_sendarg(root, state, 0) < 0)))
1.11 joris 287: return (CVS_EX_PROTO);
1.1 joris 288:
1.7 joris 289: if (lockrev & LOCK_REMOVE) {
290: if (cvs_sendarg(root, "-u", 0) < 0)
1.11 joris 291: return (CVS_EX_PROTO);
1.7 joris 292: if ((unlockrev_arg != NULL) &&
293: (cvs_sendarg(root, unlockrev_arg, 0) < 0))
1.11 joris 294: return (CVS_EX_PROTO);
1.1 joris 295: }
296:
297: return (0);
298: }
299:
300: /*
1.16 jfb 301: * cvs_admin_remote()
1.1 joris 302: *
303: * Perform admin commands on each file.
304: */
1.15 jfb 305: static int
1.16 jfb 306: cvs_admin_remote(CVSFILE *cf, void *arg)
1.1 joris 307: {
1.16 jfb 308: int ret;
309: char *repo, fpath[MAXPATHLEN];
1.1 joris 310: struct cvsroot *root;
311:
312: ret = 0;
1.16 jfb 313: root = CVS_DIR_ROOT(cf);
314: repo = CVS_DIR_REPO(cf);
1.14 joris 315:
1.16 jfb 316: if (cf->cf_type == DT_DIR) {
317: if (cf->cf_cvstat == CVS_FST_UNKNOWN)
318: ret = cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
319: cf->cf_name);
320: else
321: ret = cvs_senddir(root, cf);
322: if (ret == -1)
323: ret = CVS_EX_PROTO;
1.1 joris 324:
325: return (ret);
326: }
327:
1.16 jfb 328: cvs_file_getpath(cf, fpath, sizeof(fpath));
1.1 joris 329:
1.16 jfb 330: if (cvs_sendentry(root, cf) < 0)
331: return (CVS_EX_PROTO);
332:
333: switch (cf->cf_cvstat) {
334: case CVS_FST_UNKNOWN:
335: ret = cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name);
336: break;
337: case CVS_FST_UPTODATE:
338: ret = cvs_sendreq(root, CVS_REQ_UNCHANGED, cf->cf_name);
339: break;
340: case CVS_FST_MODIFIED:
341: ret = cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name);
342: if (ret == 0)
343: ret = cvs_sendfile(root, fpath);
344: default:
345: break;
346: }
1.1 joris 347:
1.16 jfb 348: return (ret);
349: }
1.14 joris 350:
1.16 jfb 351: /*
352: * cvs_admin_local()
353: *
354: * Perform administrative operations on a local RCS file.
355: */
356: static int
357: cvs_admin_local(CVSFILE *cf, void *arg)
358: {
359: int ret, len;
360: char *repo, fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
361: RCSFILE *rf;
362: struct cvsroot *root;
1.1 joris 363:
1.16 jfb 364: if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
365: cvs_log(LP_WARN, "I know nothing about %s", fpath);
366: return (0);
367: }
1.1 joris 368:
1.16 jfb 369: cvs_file_getpath(cf, fpath, sizeof(fpath));
1.1 joris 370:
1.16 jfb 371: len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
372: root->cr_dir, repo, cf->cf_name, RCS_FILE_EXT);
373: if (len == -1 || len >= (int)sizeof(rcspath)) {
374: errno = ENAMETOOLONG;
375: cvs_log(LP_ERRNO, "%s", rcspath);
376: return (-1);
1.1 joris 377: }
378:
1.16 jfb 379: rf = rcs_open(rcspath, RCS_RDWR);
380: if (rf == NULL)
381: return (CVS_EX_DATA);
382:
383: if (!RCS_KWEXP_INVAL(kflag))
384: ret = rcs_kwexp_set(rf, kflag);
385: if (lkmode != RCS_LOCK_INVAL)
386: ret = rcs_lock_setmode(rf, lkmode);
387:
388: rcs_close(rf);
389:
390: return (0);
1.1 joris 391: }