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