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