xref: /openbsd/usr.bin/rdist/rdist.c (revision d7259957)
1 /*	$OpenBSD: rdist.c,v 1.33 2022/12/04 23:50:49 cheloha 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 "gram.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
addhostlist(char * name,struct namelist ** hostlist)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
main(int argc,char ** argv,char ** envp)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 		default:
252 			usage();
253 		}
254 
255 	if (debug) {
256 		printf("%s\n", getversion());
257 		msgprconfig();
258 	}
259 
260 	if (nflag && IS_ON(options, DO_VERIFY))
261 		fatalerr(
262 		 "The -n flag and \"verify\" mode may not both be used.");
263 
264 	if (path_remsh == NULL) {
265 		if ((cp = getenv("RSH")) != NULL && *cp != '\0')
266 			path_remsh = cp;
267 		else
268 			path_remsh = _PATH_RSH;
269 	}
270 
271 	/*
272 	 * Don't fork children for nflag
273 	 */
274 	if (nflag)
275 		do_fork = 0;
276 
277 	if (cmdargs)
278 		docmdargs(realargc - optind, &realargv[optind]);
279 	else {
280 		if (fin == NULL)
281 			fin = opendist(distfile);
282 		(void) yyparse();
283 		/*
284 		 * Need to keep stdin open for child processing later
285 		 */
286 		if (fin != stdin)
287 			(void) fclose(fin);
288 		if (nerrs == 0)
289 			docmds(hostlist, realargc-optind, &realargv[optind]);
290 	}
291 
292 	exit(nerrs != 0);
293 }
294 
295 /*
296  * Open a distfile
297  */
298 FILE *
opendist(char * distfile)299 opendist(char *distfile)
300 {
301 	char *file = NULL;
302 	FILE *fp;
303 
304 	if (distfile == NULL) {
305 		if (access("distfile", R_OK) == 0)
306 			file = "distfile";
307 		else if (access("Distfile", R_OK) == 0)
308 			file = "Distfile";
309 	} else {
310 		/*
311 		 * Try to test to see if file is readable before running m4.
312 		 */
313 		if (access(distfile, R_OK) != 0)
314 			fatalerr("%s: Cannot access file: %s.",
315 				 distfile, SYSERR);
316 		file = distfile;
317 	}
318 
319 	if (file == NULL)
320 		fatalerr("No distfile found.");
321 
322 	fp = fopen(file, "r");
323 
324 	if (fp == NULL)
325 		fatalerr("%s: open failed: %s.", file, SYSERR);
326 
327 	return(fp);
328 }
329 
330 /*
331  * Print usage message and exit.
332  */
333 static void
usage(void)334 usage(void)
335 {
336 	extern char *__progname;
337 
338 	(void) fprintf(stderr,
339 		"usage: %s [-DFnV] [-A num] [-a num] [-c mini_distfile]"
340 		" [-d var=value]\n"
341 		"\t[-f distfile] [-L remote_logopts] [-l local_logopts]"
342 		" [-M maxproc]\n"
343 		"\t[-m host] [-o distopts] [-P rsh-path] [-p rdistd-path]"
344 		" [-t timeout]\n"
345 		"\t[name ...]\n", __progname);
346 
347 	exit(1);
348 }
349 
350 /*
351  * rcp like interface for distributing files.
352  */
353 void
docmdargs(int nargs,char ** args)354 docmdargs(int nargs, char **args)
355 {
356 	struct namelist *nl, *prev;
357 	char *cp;
358 	struct namelist *files, *hosts;
359 	struct subcmd *scmds;
360 	char *dest;
361 	static struct namelist tnl;
362 	int i;
363 
364 	if (nargs < 2)
365 		usage();
366 
367 	prev = NULL;
368 	files = NULL;
369 	for (i = 0; i < nargs - 1; i++) {
370 		nl = makenl(args[i]);
371 		if (prev == NULL)
372 			files = prev = nl;
373 		else {
374 			prev->n_next = nl;
375 			prev = nl;
376 		}
377 	}
378 
379 	cp = args[i];
380 	if ((dest = strchr(cp, ':')) != NULL)
381 		*dest++ = '\0';
382 	tnl.n_name = cp;
383 	tnl.n_regex = NULL;
384 	tnl.n_next = NULL;
385 	hosts = expand(&tnl, E_ALL);
386 	if (nerrs)
387 		exit(1);
388 
389 	if (dest == NULL || *dest == '\0')
390 		scmds = NULL;
391 	else {
392 		scmds = makesubcmd(INSTALL);
393 		scmds->sc_options = options;
394 		scmds->sc_name = dest;
395 	}
396 
397 	debugmsg(DM_MISC, "docmdargs()\nfiles = %s", getnlstr(files));
398 	debugmsg(DM_MISC, "host = %s", getnlstr(hosts));
399 
400 	insert(NULL, files, hosts, scmds);
401 	docmds(NULL, 0, NULL);
402 }
403 
404 /*
405  * Get a list of NAME blocks (mostly for debugging).
406  */
407 char *
getnlstr(struct namelist * nl)408 getnlstr(struct namelist *nl)
409 {
410 	static char buf[16384];
411 	size_t len = 0;
412 
413 	(void) snprintf(buf, sizeof(buf), "(");
414 
415 	while (nl != NULL) {
416 		if (nl->n_name == NULL)
417 			continue;
418 		len += strlen(nl->n_name) + 2;
419 		if (len >= sizeof(buf)) {
420 			(void) strlcpy(buf,
421 				       "getnlstr() Buffer not large enough",
422 				       sizeof(buf));
423 			return(buf);
424 		}
425 		(void) strlcat(buf, " ", sizeof(buf));
426 		(void) strlcat(buf, nl->n_name, sizeof(buf));
427 		nl = nl->n_next;
428 	}
429 
430 	(void) strlcat(buf, " )", sizeof(buf));
431 
432 	return(buf);
433 }
434