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