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