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