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