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