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