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