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