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