1 /* $OpenBSD: rdist.c,v 1.17 2003/06/03 02:56:15 millert 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 "defs.h" 33 #include "y.tab.h" 34 35 #ifndef lint 36 #if 0 37 static char RCSid[] __attribute__((__unused__)) = 38 "$From: rdist.c,v 1.6 2001/03/12 18:16:36 kim Exp $"; 39 #else 40 static char RCSid[] __attribute__((__unused__)) = 41 "$OpenBSD: rdist.c,v 1.17 2003/06/03 02:56:15 millert Exp $"; 42 #endif 43 44 static char sccsid[] __attribute__((__unused__)) = 45 "@(#)main.c 5.1 (Berkeley) 6/6/85"; 46 47 static char copyright[] __attribute__((__unused__)) = 48 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 49 All rights reserved.\n"; 50 #endif /* not lint */ 51 52 53 #include <netdb.h> 54 #include <sys/ioctl.h> 55 56 /* 57 * Remote distribution program. 58 */ 59 60 char *distfile = NULL; /* Name of distfile to use */ 61 int maxchildren = MAXCHILDREN; /* Max no of concurrent PIDs */ 62 int nflag = 0; /* Say without doing */ 63 long min_freespace = 0; /* Min filesys free space */ 64 long min_freefiles = 0; /* Min filesys free # files */ 65 FILE *fin = NULL; /* Input file pointer */ 66 char localmsglist[] = "stdout=all:notify=all:syslog=nerror,ferror"; 67 char *remotemsglist = NULL; 68 char optchars[] = "A:a:bcd:DFf:hil:L:M:m:NnOo:p:P:qRrst:Vvwxy"; 69 char *path_rdistd = _PATH_RDISTD; 70 char *path_remsh = NULL; 71 72 static void addhostlist(char *, struct namelist **); 73 static void usage(void); 74 int main(int, char **, char **); 75 76 /* 77 * Add a hostname to the host list 78 */ 79 static void 80 addhostlist(char *name, struct namelist **hostlist) 81 { 82 struct namelist *ptr, *new; 83 84 if (!name || !hostlist) 85 return; 86 87 new = (struct namelist *) xmalloc(sizeof(struct namelist)); 88 new->n_name = xstrdup(name); 89 new->n_regex = NULL; 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 100 int 101 main(int argc, char **argv, char **envp) 102 { 103 extern char *__progname; 104 struct namelist *hostlist = NULL; 105 int x; 106 char *cp; 107 int cmdargs = 0; 108 int c; 109 110 progname = __progname; 111 112 if ((cp = msgparseopts(localmsglist, TRUE)) != NULL) { 113 error("Bad builtin log option (%s): %s.", 114 localmsglist, cp); 115 usage(); 116 } 117 118 if ((cp = getenv("RDIST_OPTIONS")) != NULL) 119 if (parsedistopts(cp, &options, TRUE)) { 120 error("Bad dist option environment string \"%s\".", 121 cp); 122 exit(1); 123 } 124 125 if (init(argc, argv, envp) < 0) 126 exit(1); 127 128 /* 129 * Be backwards compatible. 130 */ 131 for (x = 1; x <= argc && argv[x]; x++) { 132 if (strcmp(argv[x], "-Server") != 0) 133 continue; 134 #if defined(_PATH_OLDRDIST) 135 message(MT_SYSLOG, 136 "Old rdist (-Server) requested; running %s", 137 _PATH_OLDRDIST); 138 (void) execl(_PATH_OLDRDIST, xbasename(_PATH_OLDRDIST), 139 "-Server", (char *)NULL); 140 fatalerr("Exec old rdist failed: %s: %s.", 141 _PATH_OLDRDIST, SYSERR); 142 #else /* !_PATH_OLDRDIST */ 143 fatalerr("Old rdist not available."); 144 #endif /* _PATH_OLDRDIST */ 145 exit(1); 146 } 147 148 #if defined(DIRECT_RCMD) 149 if (becomeuser() != 0) 150 exit(1); 151 #else /* !DIRECT_RCMD */ 152 /* 153 * Perform check to make sure we are not incorrectly installed 154 * setuid to root or anybody else. 155 */ 156 if (getuid() != geteuid()) 157 fatalerr("This version of rdist should not be installed setuid."); 158 #endif /* DIRECT_RCMD */ 159 160 while ((c = getopt(argc, argv, optchars)) != -1) 161 switch (c) { 162 case 'l': 163 if ((cp = msgparseopts(optarg, TRUE)) != NULL) { 164 error("Bad log option \"%s\": %s.", optarg,cp); 165 usage(); 166 } 167 break; 168 169 case 'L': 170 remotemsglist = xstrdup(optarg); 171 break; 172 173 case 'A': 174 case 'a': 175 case 'M': 176 case 't': 177 if (!isdigit((unsigned char)*optarg)) { 178 error("\"%s\" is not a number.", optarg); 179 usage(); 180 } 181 if (c == 'a') 182 min_freespace = atoi(optarg); 183 else if (c == 'A') 184 min_freefiles = atoi(optarg); 185 else if (c == 'M') 186 maxchildren = atoi(optarg); 187 else if (c == 't') 188 rtimeout = atoi(optarg); 189 break; 190 191 case 'F': 192 do_fork = FALSE; 193 break; 194 195 case 'f': 196 distfile = xstrdup(optarg); 197 if (distfile[0] == '-' && distfile[1] == CNULL) 198 fin = stdin; 199 break; 200 201 case 'm': 202 addhostlist(optarg, &hostlist); 203 break; 204 205 case 'd': 206 define(optarg); 207 break; 208 209 case 'D': 210 debug = DM_ALL; 211 if ((cp = msgparseopts("stdout=all,debug", 212 TRUE)) != NULL) { 213 error("Enable debug messages failed: %s.", cp); 214 usage(); 215 } 216 break; 217 218 case 'c': 219 cmdargs++; 220 break; 221 222 case 'n': 223 nflag++; 224 break; 225 226 case 'V': 227 printf("%s\n", getversion()); 228 exit(0); 229 230 case 'o': 231 if (parsedistopts(optarg, &options, TRUE)) { 232 error("Bad dist option string \"%s\".", 233 optarg); 234 usage(); 235 } 236 break; 237 238 case 'p': 239 if (!optarg) { 240 error("No path specified to \"-p\"."); 241 usage(); 242 } 243 path_rdistd = xstrdup(optarg); 244 break; 245 246 case 'P': 247 if (!optarg) { 248 error("No path specified to \"-P\"."); 249 usage(); 250 } 251 if ((cp = searchpath(optarg)) != NULL) 252 path_remsh = xstrdup(cp); 253 else { 254 error("No component of path \"%s\" exists.", 255 optarg); 256 usage(); 257 } 258 break; 259 260 /* 261 * These options are obsoleted by -o. They are 262 * provided only for backwards compatibility 263 */ 264 case 'v': FLAG_ON(options, DO_VERIFY); break; 265 case 'N': FLAG_ON(options, DO_CHKNFS); break; 266 case 'O': FLAG_ON(options, DO_CHKREADONLY); break; 267 case 'q': FLAG_ON(options, DO_QUIET); break; 268 case 'b': FLAG_ON(options, DO_COMPARE); break; 269 case 'r': FLAG_ON(options, DO_NODESCEND); break; 270 case 'R': FLAG_ON(options, DO_REMOVE); break; 271 case 's': FLAG_ON(options, DO_SAVETARGETS); break; 272 case 'w': FLAG_ON(options, DO_WHOLE); break; 273 case 'y': FLAG_ON(options, DO_YOUNGER); break; 274 case 'h': FLAG_ON(options, DO_FOLLOW); break; 275 case 'i': FLAG_ON(options, DO_IGNLNKS); break; 276 case 'x': FLAG_ON(options, DO_NOEXEC); break; 277 278 case '?': 279 default: 280 usage(); 281 } 282 283 if (debug) { 284 printf("%s\n", getversion()); 285 msgprconfig(); 286 } 287 288 if (nflag && IS_ON(options, DO_VERIFY)) 289 fatalerr( 290 "The -n flag and \"verify\" mode may not both be used."); 291 292 if (path_remsh == NULL) { 293 if ((cp = getenv("RSH")) != NULL && *cp != '\0') 294 path_remsh = cp; 295 else 296 path_remsh = _PATH_REMSH; 297 } 298 299 /* 300 * Don't fork children for nflag 301 */ 302 if (nflag) 303 do_fork = 0; 304 305 if (cmdargs) 306 docmdargs(realargc - optind, &realargv[optind]); 307 else { 308 if (fin == NULL) 309 fin = opendist(distfile); 310 (void) yyparse(); 311 /* 312 * Need to keep stdin open for child processing later 313 */ 314 if (fin != stdin) 315 (void) fclose(fin); 316 if (nerrs == 0) 317 docmds(hostlist, realargc-optind, &realargv[optind]); 318 } 319 320 exit(nerrs != 0); 321 } 322 323 /* 324 * Open a distfile 325 */ 326 FILE * 327 opendist(char *distfile) 328 { 329 char *file = NULL; 330 FILE *fp; 331 332 if (distfile == NULL) { 333 if (access("distfile", R_OK) == 0) 334 file = "distfile"; 335 else if (access("Distfile", R_OK) == 0) 336 file = "Distfile"; 337 } else { 338 /* 339 * Try to test to see if file is readable before running m4. 340 */ 341 if (access(distfile, R_OK) != 0) 342 fatalerr("%s: Cannot access file: %s.", 343 distfile, SYSERR); 344 file = distfile; 345 } 346 347 if (file == NULL) 348 fatalerr("No distfile found."); 349 350 fp = fopen(file, "r"); 351 352 if (fp == NULL) 353 fatalerr("%s: open failed: %s.", file, SYSERR); 354 355 return(fp); 356 } 357 358 /* 359 * Print usage message and exit. 360 */ 361 static void 362 usage(void) 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 */ 393 void 394 docmdargs(int nargs, char **args) 395 { 396 struct namelist *nl, *prev; 397 char *cp; 398 struct namelist *files, *hosts; 399 struct subcmd *cmds; 400 char *dest; 401 static struct namelist tnl; 402 int i; 403 404 if (nargs < 2) 405 usage(); 406 407 prev = NULL; 408 files = NULL; 409 for (i = 0; i < nargs - 1; i++) { 410 nl = makenl(args[i]); 411 if (prev == NULL) 412 files = prev = nl; 413 else { 414 prev->n_next = nl; 415 prev = nl; 416 } 417 } 418 419 cp = args[i]; 420 if ((dest = strchr(cp, ':')) != NULL) 421 *dest++ = '\0'; 422 tnl.n_name = cp; 423 tnl.n_regex = NULL; 424 tnl.n_next = NULL; 425 hosts = expand(&tnl, E_ALL); 426 if (nerrs) 427 exit(1); 428 429 if (dest == NULL || *dest == '\0') 430 cmds = NULL; 431 else { 432 cmds = makesubcmd(INSTALL); 433 cmds->sc_options = options; 434 cmds->sc_name = dest; 435 } 436 437 debugmsg(DM_MISC, "docmdargs()\nfiles = %s", getnlstr(files)); 438 debugmsg(DM_MISC, "host = %s", getnlstr(hosts)); 439 440 insert(NULL, files, hosts, cmds); 441 docmds(NULL, 0, NULL); 442 } 443 444 /* 445 * Get a list of NAME blocks (mostly for debugging). 446 */ 447 char * 448 getnlstr(struct namelist *nl) 449 { 450 static char buf[16384]; 451 size_t len = 0; 452 453 (void) snprintf(buf, sizeof(buf), "("); 454 455 while (nl != NULL) { 456 if (nl->n_name == NULL) 457 continue; 458 len += strlen(nl->n_name) + 2; 459 if (len >= sizeof(buf)) { 460 (void) strlcpy(buf, 461 "getnlstr() Buffer not large enough", 462 sizeof(buf)); 463 return(buf); 464 } 465 (void) strlcat(buf, " ", sizeof(buf)); 466 (void) strlcat(buf, nl->n_name, sizeof(buf)); 467 nl = nl->n_next; 468 } 469 470 (void) strlcat(buf, " )", sizeof(buf)); 471 472 return(buf); 473 } 474