1 /* $OpenBSD: rdist.c,v 1.33 2022/12/04 23:50:49 cheloha 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 "gram.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 default: 252 usage(); 253 } 254 255 if (debug) { 256 printf("%s\n", getversion()); 257 msgprconfig(); 258 } 259 260 if (nflag && IS_ON(options, DO_VERIFY)) 261 fatalerr( 262 "The -n flag and \"verify\" mode may not both be used."); 263 264 if (path_remsh == NULL) { 265 if ((cp = getenv("RSH")) != NULL && *cp != '\0') 266 path_remsh = cp; 267 else 268 path_remsh = _PATH_RSH; 269 } 270 271 /* 272 * Don't fork children for nflag 273 */ 274 if (nflag) 275 do_fork = 0; 276 277 if (cmdargs) 278 docmdargs(realargc - optind, &realargv[optind]); 279 else { 280 if (fin == NULL) 281 fin = opendist(distfile); 282 (void) yyparse(); 283 /* 284 * Need to keep stdin open for child processing later 285 */ 286 if (fin != stdin) 287 (void) fclose(fin); 288 if (nerrs == 0) 289 docmds(hostlist, realargc-optind, &realargv[optind]); 290 } 291 292 exit(nerrs != 0); 293 } 294 295 /* 296 * Open a distfile 297 */ 298 FILE * 299 opendist(char *distfile) 300 { 301 char *file = NULL; 302 FILE *fp; 303 304 if (distfile == NULL) { 305 if (access("distfile", R_OK) == 0) 306 file = "distfile"; 307 else if (access("Distfile", R_OK) == 0) 308 file = "Distfile"; 309 } else { 310 /* 311 * Try to test to see if file is readable before running m4. 312 */ 313 if (access(distfile, R_OK) != 0) 314 fatalerr("%s: Cannot access file: %s.", 315 distfile, SYSERR); 316 file = distfile; 317 } 318 319 if (file == NULL) 320 fatalerr("No distfile found."); 321 322 fp = fopen(file, "r"); 323 324 if (fp == NULL) 325 fatalerr("%s: open failed: %s.", file, SYSERR); 326 327 return(fp); 328 } 329 330 /* 331 * Print usage message and exit. 332 */ 333 static void 334 usage(void) 335 { 336 extern char *__progname; 337 338 (void) fprintf(stderr, 339 "usage: %s [-DFnV] [-A num] [-a num] [-c mini_distfile]" 340 " [-d var=value]\n" 341 "\t[-f distfile] [-L remote_logopts] [-l local_logopts]" 342 " [-M maxproc]\n" 343 "\t[-m host] [-o distopts] [-P rsh-path] [-p rdistd-path]" 344 " [-t timeout]\n" 345 "\t[name ...]\n", __progname); 346 347 exit(1); 348 } 349 350 /* 351 * rcp like interface for distributing files. 352 */ 353 void 354 docmdargs(int nargs, char **args) 355 { 356 struct namelist *nl, *prev; 357 char *cp; 358 struct namelist *files, *hosts; 359 struct subcmd *scmds; 360 char *dest; 361 static struct namelist tnl; 362 int i; 363 364 if (nargs < 2) 365 usage(); 366 367 prev = NULL; 368 files = NULL; 369 for (i = 0; i < nargs - 1; i++) { 370 nl = makenl(args[i]); 371 if (prev == NULL) 372 files = prev = nl; 373 else { 374 prev->n_next = nl; 375 prev = nl; 376 } 377 } 378 379 cp = args[i]; 380 if ((dest = strchr(cp, ':')) != NULL) 381 *dest++ = '\0'; 382 tnl.n_name = cp; 383 tnl.n_regex = NULL; 384 tnl.n_next = NULL; 385 hosts = expand(&tnl, E_ALL); 386 if (nerrs) 387 exit(1); 388 389 if (dest == NULL || *dest == '\0') 390 scmds = NULL; 391 else { 392 scmds = makesubcmd(INSTALL); 393 scmds->sc_options = options; 394 scmds->sc_name = dest; 395 } 396 397 debugmsg(DM_MISC, "docmdargs()\nfiles = %s", getnlstr(files)); 398 debugmsg(DM_MISC, "host = %s", getnlstr(hosts)); 399 400 insert(NULL, files, hosts, scmds); 401 docmds(NULL, 0, NULL); 402 } 403 404 /* 405 * Get a list of NAME blocks (mostly for debugging). 406 */ 407 char * 408 getnlstr(struct namelist *nl) 409 { 410 static char buf[16384]; 411 size_t len = 0; 412 413 (void) snprintf(buf, sizeof(buf), "("); 414 415 while (nl != NULL) { 416 if (nl->n_name == NULL) 417 continue; 418 len += strlen(nl->n_name) + 2; 419 if (len >= sizeof(buf)) { 420 (void) strlcpy(buf, 421 "getnlstr() Buffer not large enough", 422 sizeof(buf)); 423 return(buf); 424 } 425 (void) strlcat(buf, " ", sizeof(buf)); 426 (void) strlcat(buf, nl->n_name, sizeof(buf)); 427 nl = nl->n_next; 428 } 429 430 (void) strlcat(buf, " )", sizeof(buf)); 431 432 return(buf); 433 } 434