xref: /openbsd-src/usr.bin/rdist/rdist.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: rdist.c,v 1.8 2001/07/09 07:04:51 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1983 Regents of the University of California.
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  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 #if 0
38 static char RCSid[] =
39 "$From: rdist.c,v 6.65 1995/12/12 00:20:39 mcooper Exp $";
40 #else
41 static char RCSid[] =
42 "$OpenBSD: rdist.c,v 1.8 2001/07/09 07:04:51 deraadt Exp $";
43 #endif
44 
45 static char sccsid[] = "@(#)main.c	5.1 (Berkeley) 6/6/85";
46 
47 static char copyright[] =
48 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
49  All rights reserved.\n";
50 #endif /* not lint */
51 
52 
53 #include "defs.h"
54 #include "y.tab.h"
55 #include <netdb.h>
56 #include <sys/ioctl.h>
57 
58 /*
59  * Remote distribution program.
60  */
61 
62 #ifdef __STDC__
63 void		docmdargs(int, char **);
64 void		usage(void);
65 #else
66 void		docmdargs();
67 void		usage();
68 #endif
69 
70 char   	       *distfile = NULL;		/* Name of distfile to use */
71 int     	maxchildren = MAXCHILDREN;	/* Max no of concurrent PIDs */
72 int		nflag = 0;			/* Say without doing */
73 long		min_freespace = 0;		/* Min filesys free space */
74 long		min_freefiles = 0;		/* Min filesys free # files */
75 FILE   	       *fin = NULL;			/* Input file pointer */
76 struct group   *gr = NULL;			/* Static area for getgrent */
77 char		localmsglist[] = "stdout=all:notify=all:syslog=nerror,ferror";
78 char   	       *remotemsglist = NULL;
79 char		optchars[] = "A:a:bcd:DFf:hil:L:M:m:NnOo:p:P:qRrst:Vvwxy";
80 FILE   	       *opendist();
81 char	       *path_rdistd = _PATH_RDISTD;
82 char	       *path_remsh = NULL;
83 
84 /*
85  * Add a hostname to the host list
86  */
87 static void addhostlist(name, hostlist)
88 	char *name;
89 	struct namelist **hostlist;
90 {
91 	register struct namelist *ptr, *new;
92 
93 	if (!name || !hostlist)
94 		return;
95 
96 	new = (struct namelist *) xmalloc(sizeof(struct namelist));
97 	new->n_name = xstrdup(name);
98 	new->n_next = NULL;
99 
100 	if (*hostlist) {
101 		for (ptr = *hostlist; ptr && ptr->n_next; ptr = ptr->n_next)
102 			;
103 		ptr->n_next = new;
104 	} else
105 		*hostlist = new;
106 }
107 
108 int
109 main(argc, argv, envp)
110 	int argc;
111 	char *argv[];
112 	char **envp;
113 {
114 	struct namelist *hostlist = NULL;
115 	register int x;
116 	register char *cp;
117 	int cmdargs = 0;
118 	int c;
119 
120 	/*
121 	 * We initialize progname here instead of init() because
122 	 * things in msgparseopts() need progname set.
123 	 */
124 	setprogname(argv);
125 
126 	if ((cp = msgparseopts(localmsglist, TRUE))) {
127 		error("Bad builtin log option (%s): %s.",
128 		      localmsglist, cp);
129 		usage();
130 	}
131 
132 	if (init(argc, argv, envp) < 0)
133 		exit(1);
134 
135 	/*
136 	 * Be backwards compatible.
137 	 */
138 	for (x = 1; x <= argc && argv[x]; x++) {
139 		if (strcmp(argv[x], "-Server") != 0)
140 			continue;
141 #if	defined(_PATH_OLDRDIST)
142 		message(MT_SYSLOG,
143 			"Old rdist (-Server) requested; running %s",
144 			_PATH_OLDRDIST);
145 		(void) execl(_PATH_OLDRDIST, xbasename(_PATH_OLDRDIST),
146 			     "-Server", (char *)NULL);
147 		fatalerr("Exec old rdist failed: %s: %s.",
148 			 _PATH_OLDRDIST, SYSERR);
149 #else	/* !_PATH_OLDRDIST */
150 		fatalerr("Old rdist not available.");
151 #endif	/* _PATH_OLDRDIST */
152 		exit(1);
153 	}
154 
155 #if	defined(DIRECT_RCMD)
156 	if (becomeuser() != 0)
157 		exit(1);
158 #else	/* !DIRECT_RCMD */
159 	/*
160 	 * Perform check to make sure we are not incorrectly installed
161 	 * setuid to root or anybody else.
162 	 */
163 	if (getuid() != geteuid())
164 		fatalerr("This version of rdist should not be installed setuid.");
165 #endif	/* DIRECT_RCMD */
166 
167 	while ((c = getopt(argc, argv, optchars)) != -1)
168 		switch (c) {
169 		case 'l':
170 			if ((cp = msgparseopts(optarg, TRUE))) {
171 				error("Bad log option \"%s\": %s.", optarg,cp);
172 				usage();
173 			}
174 			break;
175 
176 		case 'L':
177 			remotemsglist = xstrdup(optarg);
178 			break;
179 
180 		case 'A':
181 		case 'a':
182 		case 'M':
183 		case 't':
184 			if (!isdigit(*optarg)) {
185 				error("\"%s\" is not a number.", optarg);
186 				usage();
187 			}
188 			if (c == 'a')
189 				min_freespace = atoi(optarg);
190 			else if (c == 'A')
191 				min_freefiles = atoi(optarg);
192 			else if (c == 'M')
193 				maxchildren = atoi(optarg);
194 			else if (c == 't')
195 				rtimeout = atoi(optarg);
196 			break;
197 
198 		case 'F':
199 			do_fork = FALSE;
200 			break;
201 
202 		case 'f':
203 			distfile = xstrdup(optarg);
204 			if (distfile[0] == '-' && distfile[1] == CNULL)
205 				fin = stdin;
206 			break;
207 
208 		case 'm':
209 			addhostlist(optarg, &hostlist);
210 			break;
211 
212 		case 'd':
213 			define(optarg);
214 			break;
215 
216 		case 'D':
217 			debug = DM_ALL;
218 			if ((cp = msgparseopts("stdout=all,debug", TRUE))) {
219 				error("Enable debug messages failed: %s.", cp);
220 				usage();
221 			}
222 			break;
223 
224 		case 'c':
225 			cmdargs++;
226 			break;
227 
228 		case 'n':
229 			nflag++;
230 			break;
231 
232 		case 'V':
233 			printf("%s\n", getversion());
234 			exit(0);
235 
236 		case 'o':
237 			if (parsedistopts(optarg, &options, TRUE)) {
238 				error("Bad dist option string \"%s\".",
239 				      optarg);
240 				usage();
241 			}
242 			break;
243 
244 		case 'p':
245 			if (!optarg) {
246 				error("No path specified to \"-p\".");
247 				usage();
248 			}
249 			path_rdistd = xstrdup(optarg);
250 			break;
251 
252 		case 'P':
253 			if (!optarg) {
254 				error("No path specified to \"-P\".");
255 				usage();
256 			}
257 			if ((cp = searchpath(optarg)))
258 				path_remsh = xstrdup(cp);
259 			else {
260 				error("No component of path \"%s\" exists.",
261 				      optarg);
262 				usage();
263 			}
264 			break;
265 
266 			/*
267 			 * These options are obsoleted by -o.  They are
268 			 * provided only for backwards compatibility
269 			 */
270 		case 'v':	FLAG_ON(options, DO_VERIFY);		break;
271 		case 'N':	FLAG_ON(options, DO_CHKNFS);		break;
272 		case 'O':	FLAG_ON(options, DO_CHKREADONLY);	break;
273 		case 'q':	FLAG_ON(options, DO_QUIET);		break;
274 		case 'b':	FLAG_ON(options, DO_COMPARE);		break;
275 		case 'r':	FLAG_ON(options, DO_NODESCEND);		break;
276 		case 'R':	FLAG_ON(options, DO_REMOVE);		break;
277 		case 's':	FLAG_ON(options, DO_SAVETARGETS);	break;
278 		case 'w':	FLAG_ON(options, DO_WHOLE);		break;
279 		case 'y':	FLAG_ON(options, DO_YOUNGER);		break;
280 		case 'h':	FLAG_ON(options, DO_FOLLOW);		break;
281 		case 'i':	FLAG_ON(options, DO_IGNLNKS);		break;
282 		case 'x':	FLAG_ON(options, DO_NOEXEC);		break;
283 
284 		case '?':
285 		default:
286 			usage();
287 		}
288 
289 	if (debug) {
290 		printf("%s\n", getversion());
291 		msgprconfig();
292 	}
293 
294 	if (nflag && IS_ON(options, DO_VERIFY))
295 		fatalerr(
296 		 "The -n flag and \"verify\" mode may not both be used.");
297 
298 	if (path_remsh == NULL)
299 		path_remsh = getenv("RSH");
300 
301 	/*
302 	 * Don't fork children for nflag
303 	 */
304 	if (nflag)
305 		do_fork = 0;
306 
307 	if (cmdargs)
308 		docmdargs(realargc - optind, &realargv[optind]);
309 	else {
310 		if (fin == NULL)
311 			fin = opendist(distfile);
312 		(void) yyparse();
313 		/*
314 		 * Need to keep stdin open for child processing later
315 		 */
316 		if (fin != stdin)
317 			(void) fclose(fin);
318 		if (nerrs == 0)
319 			docmds(hostlist, realargc-optind, &realargv[optind]);
320 	}
321 
322 	exit(nerrs != 0);
323 }
324 
325 /*
326  * Open a distfile
327  */
328 FILE *opendist(distfile)
329 	char *distfile;
330 {
331 	char *file = NULL;
332 	FILE *fp;
333 
334 	if (distfile == NULL) {
335 		if (access("distfile", R_OK) == 0)
336 			file = "distfile";
337 		else if (access("Distfile", R_OK) == 0)
338 			file = "Distfile";
339 	} else {
340 		/*
341 		 * Try to test to see if file is readable before running m4.
342 		 */
343 		if (access(distfile, R_OK) != 0)
344 			fatalerr("%s: Cannot access file: %s.",
345 				 distfile, SYSERR);
346 		file = distfile;
347 	}
348 
349 	if (file == NULL)
350 		fatalerr("No distfile found.");
351 
352 	fp = fopen(file, "r");
353 
354 	if (fp == NULL)
355 		fatalerr("%s: open failed: %s.", file, SYSERR);
356 
357 	return(fp);
358 }
359 
360 /*
361  * Print usage message and exit.
362  */
363 void
364 usage()
365 {
366 	char *sopts = "cDFnv";
367 
368 	(void) fprintf(stderr,
369 		      "Usage: %s [-%s] [-A <num>] [-a <num>] [-d var=value]\n",
370 		       progname, sopts);
371 	(void) fprintf(stderr,
372        "\t[-f distfile] [-l <msgopt>] [-L <msgopt>] [-M <maxproc>]\n");
373 	(void) fprintf(stderr,
374        "\t[-m host] [-o <distopts>] [-p <rdistd-cmd>] [-P <rsh-path>]\n");
375 	(void) fprintf(stderr,
376        "\t[-t <timeout>] [target ...]\n");
377 
378 	(void) fprintf(stderr,
379 		      "OR:    %s [-%s] -c source [...] machine[:dest]\n",
380 		       progname, sopts);
381 
382 	(void) fprintf(stderr, "OR:    %s -V\n", progname);
383 
384 	(void) fprintf(stderr, "\nThe values for <distopts> are:\n\t%s\n",
385 		       getdistoptlist());
386 
387 	msgprusage();
388 
389 	exit(1);
390 }
391 
392 /*
393  * rcp like interface for distributing files.
394  */
395 void
396 docmdargs(nargs, args)
397 	int nargs;
398 	char *args[];
399 {
400 	register struct namelist *nl, *prev;
401 	register char *cp;
402 	struct namelist *files, *hosts;
403 	struct subcmd *cmds;
404 	char *dest;
405 	static struct namelist tnl = { NULL, NULL };
406 	int i;
407 
408 	if (nargs < 2)
409 		usage();
410 
411 	prev = NULL;
412 	files = NULL;
413 	for (i = 0; i < nargs - 1; i++) {
414 		nl = makenl(args[i]);
415 		if (prev == NULL)
416 			files = prev = nl;
417 		else {
418 			prev->n_next = nl;
419 			prev = nl;
420 		}
421 	}
422 
423 	cp = args[i];
424 	if ((dest = strchr(cp, ':')) != NULL)
425 		*dest++ = '\0';
426 	tnl.n_name = cp;
427 	hosts = expand(&tnl, E_ALL);
428 	if (nerrs)
429 		exit(1);
430 
431 	if (dest == NULL || *dest == '\0')
432 		cmds = NULL;
433 	else {
434 		cmds = makesubcmd(INSTALL);
435 		cmds->sc_options = options;
436 		cmds->sc_name = dest;
437 	}
438 
439 	debugmsg(DM_MISC, "docmdargs()\nfiles = %s", getnlstr(files));
440 	debugmsg(DM_MISC, "host = %s", getnlstr(hosts));
441 
442 	insert(NULL, files, hosts, cmds);
443 	docmds(0, NULL, 0, (char **)NULL);
444 }
445 
446 /*
447  * Get a list of NAME blocks (mostly for debugging).
448  */
449 extern char *getnlstr(nl)
450 	register struct namelist *nl;
451 {
452 	static char buf[16384];
453 	register int count = 0, len = 0;
454 
455 	(void) sprintf(buf, "(");
456 
457 	while (nl != NULL) {
458 		if (nl->n_name == NULL)
459 			continue;
460 		len += strlen(nl->n_name) + 2;
461 		if (len >= sizeof(buf)) {
462 			(void) strcpy(buf,
463 				      "getnlstr() Buffer not large enough");
464 			return(buf);
465 		}
466 		++count;
467 		(void) strcat(buf, " ");
468 		(void) strcat(buf, nl->n_name);
469 		nl = nl->n_next;
470 	}
471 
472 	(void) strcat(buf, " )");
473 
474 	return(buf);
475 }
476