1*431378d1Snaddy /* $OpenBSD: admin.c,v 1.69 2020/10/19 19:51:20 naddy Exp $ */
2a30554baSxsa /*
3a30554baSxsa * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4a30554baSxsa * Copyright (c) 2005 Joris Vink <joris@openbsd.org>
5dcb4505bSxsa * Copyright (c) 2006, 2007 Xavier Santolaria <xsa@openbsd.org>
6a30554baSxsa *
7a30554baSxsa * Permission to use, copy, modify, and distribute this software for any
8a30554baSxsa * purpose with or without fee is hereby granted, provided that the above
9a30554baSxsa * copyright notice and this permission notice appear in all copies.
10a30554baSxsa *
11a30554baSxsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12a30554baSxsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a30554baSxsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14a30554baSxsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a30554baSxsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a30554baSxsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a30554baSxsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18a30554baSxsa */
19a30554baSxsa
20b9fc9a72Sderaadt #include <sys/types.h>
211f8531bdSotto #include <sys/dirent.h>
221f8531bdSotto
231f8531bdSotto #include <errno.h>
241f8531bdSotto #include <fcntl.h>
251f8531bdSotto #include <libgen.h>
26397ddb8aSnicm #include <stdlib.h>
271f8531bdSotto #include <string.h>
281f8531bdSotto #include <unistd.h>
29a30554baSxsa
30a30554baSxsa #include "cvs.h"
31a30554baSxsa #include "remote.h"
32a30554baSxsa
33a30554baSxsa #define ADM_EFLAG 0x01
34a30554baSxsa
35a30554baSxsa void cvs_admin_local(struct cvs_file *);
36a30554baSxsa
37a30554baSxsa struct cvs_cmd cvs_cmd_admin = {
38fb3beb6cSjoris CVS_OP_ADMIN, CVS_USE_WDIR | CVS_LOCK_REPO, "admin",
39a30554baSxsa { "adm", "rcs" },
40a30554baSxsa "Administrative front-end for RCS",
41607fa2aeSxsa "[-ILqU] [-A oldfile] [-a users] [-b branch]\n"
42a30554baSxsa "[-c string] [-e [users]] [-k mode] [-l [rev]] [-m rev:msg]\n"
43a30554baSxsa "[-N tag[:rev]] [-n tag[:rev]] [-o rev] [-s state[:rev]]"
44a30554baSxsa "[-t file | str]\n"
45a30554baSxsa "[-u [rev]] file ...",
46a30554baSxsa "A:a:b::c:e::Ik:l::Lm:N:n:o:qs:t:Uu::",
47a30554baSxsa NULL,
48a30554baSxsa cvs_admin
49a30554baSxsa };
50a30554baSxsa
51a30554baSxsa static int runflags = 0;
52a30554baSxsa static int lkmode = RCS_LOCK_INVAL;
53dcb4505bSxsa static char *alist, *comment, *elist, *logmsg, *logstr, *koptstr;
54911a7d5cStobias static char *oldfilename, *orange, *state, *staterevstr;
55a30554baSxsa
56a30554baSxsa int
cvs_admin(int argc,char ** argv)57a30554baSxsa cvs_admin(int argc, char **argv)
58a30554baSxsa {
59a30554baSxsa int ch;
60a30554baSxsa int flags;
61911a7d5cStobias char *statestr;
62a30554baSxsa struct cvs_recursion cr;
63a30554baSxsa
64a30554baSxsa flags = CR_RECURSE_DIRS;
65a30554baSxsa
66607fa2aeSxsa alist = comment = elist = logmsg = logstr = NULL;
67607fa2aeSxsa oldfilename = orange = state = statestr = NULL;
6827109178Sxsa
69a30554baSxsa while ((ch = getopt(argc, argv, cvs_cmd_admin.cmd_opts)) != -1) {
70a30554baSxsa switch (ch) {
71a30554baSxsa case 'A':
72607fa2aeSxsa oldfilename = optarg;
73a30554baSxsa break;
74a30554baSxsa case 'a':
750a78a75fSxsa alist = optarg;
76a30554baSxsa break;
77a30554baSxsa case 'b':
78a30554baSxsa break;
79a30554baSxsa case 'c':
80a30554baSxsa comment = optarg;
81a30554baSxsa break;
82a30554baSxsa case 'e':
83a30554baSxsa elist = optarg;
84a30554baSxsa runflags |= ADM_EFLAG;
85a30554baSxsa break;
86a30554baSxsa case 'I':
87a30554baSxsa break;
88a30554baSxsa case 'k':
89dcb4505bSxsa koptstr = optarg;
90dcb4505bSxsa kflag = rcs_kflag_get(koptstr);
91dcb4505bSxsa if (RCS_KWEXP_INVAL(kflag)) {
92dcb4505bSxsa cvs_log(LP_ERR,
930bc1d395Stobias "invalid RCS keyword expansion mode");
94dcb4505bSxsa fatal("%s", cvs_cmd_admin.cmd_synopsis);
95dcb4505bSxsa }
96a30554baSxsa break;
97a30554baSxsa case 'L':
98a30554baSxsa if (lkmode == RCS_LOCK_LOOSE) {
99a30554baSxsa cvs_log(LP_ERR, "-L and -U are incompatible");
100a30554baSxsa fatal("%s", cvs_cmd_admin.cmd_synopsis);
101a30554baSxsa }
102a30554baSxsa lkmode = RCS_LOCK_STRICT;
103a30554baSxsa break;
104a30554baSxsa case 'l':
105a30554baSxsa break;
106a30554baSxsa case 'm':
10727109178Sxsa logstr = optarg;
108a30554baSxsa break;
109a30554baSxsa case 'N':
110a30554baSxsa break;
111a30554baSxsa case 'n':
112a30554baSxsa break;
113a30554baSxsa case 'o':
1140a78a75fSxsa orange = optarg;
115a30554baSxsa break;
116a30554baSxsa case 'q':
117a30554baSxsa verbosity = 0;
118a30554baSxsa break;
119a30554baSxsa case 's':
1208b6645caSxsa statestr = optarg;
121a30554baSxsa break;
122a30554baSxsa case 't':
123a30554baSxsa break;
124a30554baSxsa case 'U':
125a30554baSxsa if (lkmode == RCS_LOCK_STRICT) {
126a30554baSxsa cvs_log(LP_ERR, "-U and -L are incompatible");
127a30554baSxsa fatal("%s", cvs_cmd_admin.cmd_synopsis);
128a30554baSxsa }
129a30554baSxsa lkmode = RCS_LOCK_LOOSE;
130a30554baSxsa break;
131a30554baSxsa case 'u':
132a30554baSxsa break;
133a30554baSxsa default:
134a30554baSxsa fatal("%s", cvs_cmd_admin.cmd_synopsis);
135a30554baSxsa }
136a30554baSxsa }
137a30554baSxsa
138a30554baSxsa argc -= optind;
139a30554baSxsa argv += optind;
140a30554baSxsa
141fc5a22f5Sxsa if (argc == 0)
142a30554baSxsa fatal("%s", cvs_cmd_admin.cmd_synopsis);
143a30554baSxsa
144a30554baSxsa cr.enterdir = NULL;
145a30554baSxsa cr.leavedir = NULL;
146a30554baSxsa
1474dcde513Sjoris if (cvsroot_is_remote()) {
14880f6ca9bSjoris cvs_client_connect_to_server();
149a30554baSxsa cr.fileproc = cvs_client_sendfile;
150a30554baSxsa
151607fa2aeSxsa if (oldfilename != NULL)
152607fa2aeSxsa cvs_client_send_request("Argument -A%s", oldfilename);
153607fa2aeSxsa
1540a78a75fSxsa if (alist != NULL)
1550a78a75fSxsa cvs_client_send_request("Argument -a%s", alist);
1560a78a75fSxsa
157a30554baSxsa if (comment != NULL)
158a30554baSxsa cvs_client_send_request("Argument -c%s", comment);
159a30554baSxsa
1600a78a75fSxsa if (runflags & ADM_EFLAG)
1610a78a75fSxsa cvs_client_send_request("Argument -e%s",
1620a78a75fSxsa (elist != NULL) ? elist : "");
1630a78a75fSxsa
164dcb4505bSxsa if (koptstr != NULL)
165dcb4505bSxsa cvs_client_send_request("Argument -k%s", koptstr);
166dcb4505bSxsa
167a30554baSxsa if (lkmode == RCS_LOCK_STRICT)
168a30554baSxsa cvs_client_send_request("Argument -L");
169a30554baSxsa else if (lkmode == RCS_LOCK_LOOSE)
170a30554baSxsa cvs_client_send_request("Argument -U");
171a30554baSxsa
17242cb4ab1Sxsa if (logstr != NULL)
173dd06abc6Stobias cvs_client_send_logmsg(logstr);
17442cb4ab1Sxsa
1750a78a75fSxsa if (orange != NULL)
1760a78a75fSxsa cvs_client_send_request("Argument -o%s", orange);
1770a78a75fSxsa
1788b6645caSxsa if (statestr != NULL)
1798b6645caSxsa cvs_client_send_request("Argument -s%s", statestr);
1808b6645caSxsa
181a30554baSxsa if (verbosity == 0)
182a30554baSxsa cvs_client_send_request("Argument -q");
183a30554baSxsa
184a30554baSxsa } else {
185911a7d5cStobias if (statestr != NULL) {
186911a7d5cStobias if ((staterevstr = strchr(statestr, ':')) != NULL)
187911a7d5cStobias *staterevstr++ = '\0';
188911a7d5cStobias state = statestr;
189911a7d5cStobias if (rcs_state_check(state) < 0) {
190911a7d5cStobias cvs_log(LP_ERR, "invalid state `%s'", state);
191911a7d5cStobias state = NULL;
192911a7d5cStobias }
193911a7d5cStobias }
194911a7d5cStobias
195214edc83Sxsa flags |= CR_REPO;
196a30554baSxsa cr.fileproc = cvs_admin_local;
197a30554baSxsa }
198a30554baSxsa
199a30554baSxsa cr.flags = flags;
200a30554baSxsa
201a30554baSxsa cvs_file_run(argc, argv, &cr);
202a30554baSxsa
2034dcde513Sjoris if (cvsroot_is_remote()) {
204a30554baSxsa cvs_client_send_files(argv, argc);
205a30554baSxsa cvs_client_senddir(".");
206a30554baSxsa cvs_client_send_request("admin");
207a30554baSxsa cvs_client_get_responses();
208a30554baSxsa }
209a30554baSxsa
210a30554baSxsa return (0);
211a30554baSxsa }
212a30554baSxsa
213a30554baSxsa void
cvs_admin_local(struct cvs_file * cf)214a30554baSxsa cvs_admin_local(struct cvs_file *cf)
215a30554baSxsa {
216a30554baSxsa int i;
217911a7d5cStobias RCSNUM *rev;
218a30554baSxsa
219a30554baSxsa cvs_log(LP_TRACE, "cvs_admin_local(%s)", cf->file_path);
220a30554baSxsa
22151ef6581Sjoris cvs_file_classify(cf, cvs_directory_tag);
222214edc83Sxsa
223a30554baSxsa if (cf->file_type == CVS_DIR) {
224a30554baSxsa if (verbosity > 1)
225a30554baSxsa cvs_log(LP_NOTICE, "Administrating %s", cf->file_name);
226a30554baSxsa return;
227a30554baSxsa }
228a30554baSxsa
2296d2ee454Stobias if (cf->file_ent == NULL)
230a30554baSxsa return;
231a30554baSxsa else if (cf->file_status == FILE_ADDED) {
232a30554baSxsa cvs_log(LP_ERR, "cannot admin newly added file `%s'",
233a30554baSxsa cf->file_name);
234a30554baSxsa return;
235a30554baSxsa }
236a30554baSxsa
237518886fbStobias if (cf->file_rcs == NULL) {
238518886fbStobias cvs_log(LP_ERR, "lost RCS file for `%s'", cf->file_path);
239518886fbStobias return;
240518886fbStobias }
241518886fbStobias
242a30554baSxsa if (verbosity > 0)
243214edc83Sxsa cvs_printf("RCS file: %s\n", cf->file_rcs->rf_path);
244a30554baSxsa
245607fa2aeSxsa if (oldfilename != NULL) {
246607fa2aeSxsa struct cvs_file *ocf;
247607fa2aeSxsa struct rcs_access *acp;
248607fa2aeSxsa int ofd;
249*431378d1Snaddy char *d, dbuf[PATH_MAX], *f, fbuf[PATH_MAX];
250*431378d1Snaddy char fpath[PATH_MAX], repo[PATH_MAX];
251607fa2aeSxsa
252*431378d1Snaddy if (strlcpy(fbuf, oldfilename, sizeof(fbuf)) >= sizeof(fbuf))
253*431378d1Snaddy fatal("cvs_admin_local: truncation");
254*431378d1Snaddy if ((f = basename(fbuf)) == NULL)
255607fa2aeSxsa fatal("cvs_admin_local: basename failed");
256*431378d1Snaddy
257*431378d1Snaddy if (strlcpy(dbuf, oldfilename, sizeof(dbuf)) >= sizeof(dbuf))
258*431378d1Snaddy fatal("cvs_admin_local: truncation");
259*431378d1Snaddy if ((d = dirname(dbuf)) == NULL)
260607fa2aeSxsa fatal("cvs_admin_local: dirname failed");
261607fa2aeSxsa
262b9fc9a72Sderaadt cvs_get_repository_path(d, repo, PATH_MAX);
263607fa2aeSxsa
264b9fc9a72Sderaadt (void)xsnprintf(fpath, PATH_MAX, "%s/%s", repo, f);
265607fa2aeSxsa
266b9fc9a72Sderaadt if (strlcat(fpath, RCS_FILE_EXT, PATH_MAX) >= PATH_MAX)
267607fa2aeSxsa fatal("cvs_admin_local: truncation");
268607fa2aeSxsa
269607fa2aeSxsa if ((ofd = open(fpath, O_RDONLY)) == -1)
270607fa2aeSxsa fatal("cvs_admin_local: open: `%s': %s", fpath,
271607fa2aeSxsa strerror(errno));
272607fa2aeSxsa
273607fa2aeSxsa /* XXX: S_ISREG() check instead of blindly using CVS_FILE? */
274a4a7c2faSjoris ocf = cvs_file_get_cf(d, f, oldfilename, ofd, CVS_FILE, 0);
275607fa2aeSxsa
276607fa2aeSxsa ocf->file_rcs = rcs_open(fpath, ofd, RCS_READ, 0444);
277607fa2aeSxsa if (ocf->file_rcs == NULL)
278607fa2aeSxsa fatal("cvs_admin_local: rcs_open failed");
279607fa2aeSxsa
280607fa2aeSxsa TAILQ_FOREACH(acp, &(ocf->file_rcs->rf_access), ra_list)
281607fa2aeSxsa rcs_access_add(cf->file_rcs, acp->ra_name);
282607fa2aeSxsa
283607fa2aeSxsa cvs_file_free(ocf);
284607fa2aeSxsa }
285607fa2aeSxsa
2860a78a75fSxsa if (alist != NULL) {
2870a78a75fSxsa struct cvs_argvector *aargv;
2880a78a75fSxsa
2890a78a75fSxsa aargv = cvs_strsplit(alist, ",");
2900a78a75fSxsa for (i = 0; aargv->argv[i] != NULL; i++)
2910a78a75fSxsa rcs_access_add(cf->file_rcs, aargv->argv[i]);
2920a78a75fSxsa
2930a78a75fSxsa cvs_argv_destroy(aargv);
2940a78a75fSxsa }
2950a78a75fSxsa
296a30554baSxsa if (comment != NULL)
297a30554baSxsa rcs_comment_set(cf->file_rcs, comment);
298a30554baSxsa
299a30554baSxsa if (elist != NULL) {
300a30554baSxsa struct cvs_argvector *eargv;
301a30554baSxsa
302a30554baSxsa eargv = cvs_strsplit(elist, ",");
303a30554baSxsa for (i = 0; eargv->argv[i] != NULL; i++)
304a30554baSxsa rcs_access_remove(cf->file_rcs, eargv->argv[i]);
305a30554baSxsa
306a30554baSxsa cvs_argv_destroy(eargv);
307a30554baSxsa } else if (runflags & ADM_EFLAG) {
308a30554baSxsa struct rcs_access *rap;
309a30554baSxsa
310a30554baSxsa while (!TAILQ_EMPTY(&(cf->file_rcs->rf_access))) {
311a30554baSxsa rap = TAILQ_FIRST(&(cf->file_rcs->rf_access));
312a30554baSxsa TAILQ_REMOVE(&(cf->file_rcs->rf_access), rap, ra_list);
313397ddb8aSnicm free(rap->ra_name);
314397ddb8aSnicm free(rap);
315a30554baSxsa }
316a30554baSxsa /* no synced anymore */
317a30554baSxsa cf->file_rcs->rf_flags &= ~RCS_SYNCED;
318a30554baSxsa }
319a30554baSxsa
320dcb4505bSxsa /* Default `-kv' is accepted here. */
32137fdff3fStobias if (kflag) {
322dcb4505bSxsa if (cf->file_rcs->rf_expand == NULL ||
323dcb4505bSxsa strcmp(cf->file_rcs->rf_expand, koptstr) != 0)
324dcb4505bSxsa rcs_kwexp_set(cf->file_rcs, kflag);
325dcb4505bSxsa }
326dcb4505bSxsa
32727109178Sxsa if (logstr != NULL) {
32827109178Sxsa if ((logmsg = strchr(logstr, ':')) == NULL) {
32927109178Sxsa cvs_log(LP_ERR, "missing log message");
33027109178Sxsa return;
33127109178Sxsa }
33227109178Sxsa
33327109178Sxsa *logmsg++ = '\0';
334911a7d5cStobias if ((rev = rcsnum_parse(logstr)) == NULL) {
33527109178Sxsa cvs_log(LP_ERR, "`%s' bad revision number", logstr);
33627109178Sxsa return;
33727109178Sxsa }
33827109178Sxsa
339911a7d5cStobias if (rcs_rev_setlog(cf->file_rcs, rev, logmsg) < 0) {
34027109178Sxsa cvs_log(LP_ERR, "failed to set logmsg for `%s' to `%s'",
34127109178Sxsa logstr, logmsg);
34253ce2177Sfcambus free(rev);
34327109178Sxsa return;
34427109178Sxsa }
34527109178Sxsa
34653ce2177Sfcambus free(rev);
34727109178Sxsa }
34827109178Sxsa
3490a78a75fSxsa if (orange != NULL) {
3500a78a75fSxsa struct rcs_delta *rdp, *nrdp;
3510a7da307Sxsa char b[CVS_REV_BUFSZ];
3520a78a75fSxsa
3530a78a75fSxsa cvs_revision_select(cf->file_rcs, orange);
3540a78a75fSxsa for (rdp = TAILQ_FIRST(&(cf->file_rcs->rf_delta));
3550a78a75fSxsa rdp != NULL; rdp = nrdp) {
3560a78a75fSxsa nrdp = TAILQ_NEXT(rdp, rd_list);
3570a78a75fSxsa
3580a78a75fSxsa /*
3590a78a75fSxsa * Delete selected revisions.
3600a78a75fSxsa */
3610a78a75fSxsa if (rdp->rd_flags & RCS_RD_SELECT) {
3620a78a75fSxsa rcsnum_tostr(rdp->rd_num, b, sizeof(b));
3630a78a75fSxsa if (verbosity > 0)
3640a78a75fSxsa cvs_printf("deleting revision %s\n", b);
3650a78a75fSxsa
3660a78a75fSxsa (void)rcs_rev_remove(cf->file_rcs, rdp->rd_num);
3670a78a75fSxsa }
3680a78a75fSxsa }
3690a78a75fSxsa }
3700a78a75fSxsa
371911a7d5cStobias if (state != NULL) {
372911a7d5cStobias if (staterevstr != NULL) {
373911a7d5cStobias if ((rev = rcsnum_parse(staterevstr)) == NULL) {
374911a7d5cStobias cvs_log(LP_ERR, "`%s' bad revision number",
375911a7d5cStobias staterevstr);
3768b6645caSxsa return;
3778b6645caSxsa }
378e28eda4eStobias } else if (cf->file_rcs->rf_head != NULL) {
379911a7d5cStobias rev = rcsnum_alloc();
380911a7d5cStobias rcsnum_cpy(cf->file_rcs->rf_head, rev, 0);
381e28eda4eStobias } else {
382e28eda4eStobias cvs_log(LP_ERR, "head revision missing");
383e28eda4eStobias return;
3848b6645caSxsa }
3858b6645caSxsa
386911a7d5cStobias (void)rcs_state_set(cf->file_rcs, rev, state);
3878b6645caSxsa
38853ce2177Sfcambus free(rev);
3898b6645caSxsa }
3908b6645caSxsa
391a30554baSxsa if (lkmode != RCS_LOCK_INVAL)
392a30554baSxsa (void)rcs_lock_setmode(cf->file_rcs, lkmode);
393a30554baSxsa
3943e180cccSxsa rcs_write(cf->file_rcs);
3953e180cccSxsa
396a30554baSxsa if (verbosity > 0)
397a30554baSxsa cvs_printf("done\n");
398a30554baSxsa }
399