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