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