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