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