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
cvs_admin(int argc,char ** argv)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
cvs_admin_local(struct cvs_file * cf)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