1 /* $OpenBSD: docmd.c,v 1.17 2003/06/03 02:56:14 millert 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 #ifndef lint 35 #if 0 36 static char RCSid[] __attribute__((__unused__)) = 37 "$From: docmd.c,v 1.8 2001/03/12 18:42:23 kim Exp $"; 38 #else 39 static char RCSid[] __attribute__((__unused__)) = 40 "$OpenBSD: docmd.c,v 1.17 2003/06/03 02:56:14 millert Exp $"; 41 #endif 42 43 static char sccsid[] __attribute__((__unused__)) = 44 "@(#)docmd.c 5.1 (Berkeley) 6/6/85"; 45 46 static char copyright[] __attribute__((__unused__)) = 47 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 48 All rights reserved.\n"; 49 #endif /* not lint */ 50 51 /* 52 * Functions for rdist that do command (cmd) related activities. 53 */ 54 55 #include <sys/socket.h> 56 #include <netdb.h> 57 58 struct subcmd *subcmds; /* list of sub-commands for 59 current cmd */ 60 struct namelist *filelist; /* list of source files */ 61 extern struct cmd *cmds; /* Initialized by yyparse() */ 62 time_t lastmod; /* Last modify time */ 63 64 extern char target[BUFSIZ]; 65 extern char *ptarget; 66 extern int activechildren; 67 extern int maxchildren; 68 extern int amchild; 69 extern char *path_rdistd; 70 71 static void closeconn(void); 72 static void notify(char *, struct namelist *, time_t); 73 static void checkcmd(struct cmd *); 74 static void markfailed(struct cmd *, struct cmd *); 75 static int remotecmd(char *, char *, char *, char *); 76 static int makeconn(char *); 77 static void doarrow(struct cmd *, char **); 78 static void rcmptime(struct stat *, struct subcmd *, char **); 79 static void cmptime(char *, struct subcmd *, char **); 80 static void dodcolon(struct cmd *, char **); 81 static void docmdhost(struct cmd *, char **); 82 static void docmd(struct cmd *, int, char **); 83 84 /* 85 * Signal end of connection. 86 */ 87 static void 88 closeconn(void) 89 { 90 debugmsg(DM_CALL, "closeconn() called\n"); 91 92 if (rem_w >= 0) { 93 /* We don't care if the connection is still good or not */ 94 signal(SIGPIPE, SIG_IGN); 95 96 (void) sendcmd(C_FERRMSG, NULL); 97 (void) close(rem_w); 98 (void) close(rem_r); /* This can't hurt */ 99 rem_w = -1; 100 rem_r = -1; 101 } 102 } 103 104 /* 105 * Notify the list of people the changes that were made. 106 * rhost == NULL if we are mailing a list of changes compared to at time 107 * stamp file. 108 */ 109 static void 110 notify(char *rhost, struct namelist *to, time_t lmod) 111 { 112 int fd; 113 size_t len; 114 FILE *pf; 115 struct stat stb; 116 static char buf[BUFSIZ]; 117 extern char *locuser; 118 char *file, *user; 119 120 if (IS_ON(options, DO_VERIFY) || to == NULL) 121 return; 122 123 if ((file = getnotifyfile()) == NULL) 124 return; 125 126 if (!IS_ON(options, DO_QUIET)) { 127 message(MT_INFO, "notify %s%s %s", 128 (rhost) ? "@" : "", 129 (rhost) ? rhost : "", getnlstr(to)); 130 } 131 132 if (nflag) 133 return; 134 135 debugmsg(DM_MISC, "notify() temp file = '%s'", file); 136 137 if ((fd = open(file, O_RDONLY)) < 0) { 138 error("%s: open for reading failed: %s", file, SYSERR); 139 return; 140 } 141 if (fstat(fd, &stb) < 0) { 142 error("%s: fstat failed: %s", file, SYSERR); 143 (void) close(fd); 144 return; 145 } 146 if (stb.st_size == 0) { 147 (void) close(fd); 148 return; 149 } 150 /* 151 * Create a pipe to mailling program. 152 * Set IFS to avoid possible security problem with users 153 * setting "IFS=/". 154 */ 155 (void) snprintf(buf, sizeof(buf), "IFS=\" \t\"; export IFS; %s -oi -t", 156 _PATH_SENDMAIL); 157 pf = popen(buf, "w"); 158 if (pf == NULL) { 159 error("notify: \"%s\" failed\n", _PATH_SENDMAIL); 160 (void) unlink(file); 161 (void) close(fd); 162 return; 163 } 164 /* 165 * Output the proper header information. 166 */ 167 (void) fprintf(pf, "From: rdist (Remote distribution program)\n"); 168 (void) fprintf(pf, "To:"); 169 if (!any('@', to->n_name) && rhost != NULL) 170 (void) fprintf(pf, " %s@%s", to->n_name, rhost); 171 else 172 (void) fprintf(pf, " %s", to->n_name); 173 to = to->n_next; 174 while (to != NULL) { 175 if (!any('@', to->n_name) && rhost != NULL) 176 (void) fprintf(pf, ", %s@%s", to->n_name, rhost); 177 else 178 (void) fprintf(pf, ", %s", to->n_name); 179 to = to->n_next; 180 } 181 (void) putc('\n', pf); 182 183 if ((user = getlogin()) == NULL) 184 user = locuser; 185 186 if (rhost != NULL) 187 (void) fprintf(pf, 188 "Subject: files updated by %s from %s to %s\n", 189 locuser, host, rhost); 190 else 191 (void) fprintf(pf, "Subject: files updated after %s\n", 192 ctime(&lmod)); 193 (void) putc('\n', pf); 194 (void) putc('\n', pf); 195 (void) fprintf(pf, "Options: %s\n\n", getondistoptlist(options)); 196 197 while ((len = read(fd, buf, sizeof(buf))) != (size_t)-1) 198 (void) fwrite(buf, 1, len, pf); 199 200 (void) pclose(pf); 201 (void) close(fd); 202 (void) unlink(file); 203 } 204 205 /* 206 * XXX Hack for NFS. If a hostname from the distfile 207 * ends with a '+', then the normal restriction of 208 * skipping files that are on an NFS filesystem is 209 * bypassed. We always strip '+' to be consistent. 210 */ 211 static void 212 checkcmd(struct cmd *cmd) 213 { 214 int l; 215 216 if (!cmd || !(cmd->c_name)) { 217 debugmsg(DM_MISC, "checkcmd() NULL cmd parameter"); 218 return; 219 } 220 221 l = strlen(cmd->c_name); 222 if (l <= 0) 223 return; 224 if (cmd->c_name[l-1] == '+') { 225 cmd->c_flags |= CMD_NOCHKNFS; 226 cmd->c_name[l-1] = CNULL; 227 } 228 } 229 230 /* 231 * Mark all other entries for this command (cmd) 232 * as assigned. 233 */ 234 void 235 markassigned(struct cmd *cmd, struct cmd *cmdlist) 236 { 237 struct cmd *pcmd; 238 239 for (pcmd = cmdlist; pcmd; pcmd = pcmd->c_next) { 240 checkcmd(pcmd); 241 if (pcmd->c_type == cmd->c_type && 242 strcmp(pcmd->c_name, cmd->c_name)==0) 243 pcmd->c_flags |= CMD_ASSIGNED; 244 } 245 } 246 247 /* 248 * Mark the command "cmd" as failed for all commands in list cmdlist. 249 */ 250 static void 251 markfailed(struct cmd *cmd, struct cmd *cmdlist) 252 { 253 struct cmd *pc; 254 255 if (!cmd) { 256 debugmsg(DM_MISC, "markfailed() NULL cmd parameter"); 257 return; 258 } 259 260 checkcmd(cmd); 261 cmd->c_flags |= CMD_CONNFAILED; 262 for (pc = cmdlist; pc; pc = pc->c_next) { 263 checkcmd(pc); 264 if (pc->c_type == cmd->c_type && 265 strcmp(pc->c_name, cmd->c_name)==0) 266 pc->c_flags |= CMD_CONNFAILED; 267 } 268 } 269 270 static int 271 remotecmd(char *rhost, char *luser, char *ruser, char *cmd) 272 { 273 int desc; 274 #if defined(DIRECT_RCMD) 275 static int port = -1; 276 #endif /* DIRECT_RCMD */ 277 278 debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser); 279 debugmsg(DM_MISC, "Remote command = '%s'\n", cmd); 280 281 (void) fflush(stdout); 282 (void) fflush(stderr); 283 (void) signal(SIGALRM, sighandler); 284 (void) alarm(RTIMEOUT); 285 286 #if defined(DIRECT_RCMD) 287 (void) signal(SIGPIPE, sighandler); 288 289 if (port < 0) { 290 struct servent *sp; 291 292 if ((sp = getservbyname("shell", "tcp")) == NULL) 293 fatalerr("shell/tcp: unknown service"); 294 port = sp->s_port; 295 } 296 297 if (becomeroot() != 0) 298 exit(1); 299 desc = rcmd(&rhost, port, luser, ruser, cmd, 0); 300 if (becomeuser() != 0) 301 exit(1); 302 #else /* !DIRECT_RCMD */ 303 debugmsg(DM_MISC, "Remote shell command = '%s'\n", 304 path_remsh ? path_remsh : "default"); 305 (void) signal(SIGPIPE, SIG_IGN); 306 desc = rcmdsh(&rhost, -1, luser, ruser, cmd, path_remsh); 307 if (desc > 0) 308 (void) signal(SIGPIPE, sighandler); 309 #endif /* DIRECT_RCMD */ 310 311 (void) alarm(0); 312 313 return(desc); 314 } 315 316 /* 317 * Create a connection to the rdist server on the machine rhost. 318 * Return 0 if the connection fails or 1 if it succeeds. 319 */ 320 static int 321 makeconn(char *rhost) 322 { 323 char *ruser, *cp; 324 static char *cur_host = NULL; 325 extern char *locuser; 326 extern long min_freefiles, min_freespace; 327 extern char *remotemsglist; 328 char tuser[BUFSIZ], buf[BUFSIZ]; 329 u_char respbuff[BUFSIZ]; 330 int n; 331 332 debugmsg(DM_CALL, "makeconn(%s)", rhost); 333 334 /* 335 * See if we're already connected to this host 336 */ 337 if (cur_host != NULL && rem_w >= 0) { 338 if (strcmp(cur_host, rhost) == 0) 339 return(1); 340 closeconn(); 341 } 342 343 /* 344 * Determine remote user and current host names 345 */ 346 cur_host = rhost; 347 cp = strchr(rhost, '@'); 348 349 if (cp != NULL) { 350 char c = *cp; 351 352 *cp = CNULL; 353 (void) strlcpy((char *)tuser, rhost, sizeof(tuser)); 354 *cp = c; 355 rhost = cp + 1; 356 ruser = tuser; 357 if (*ruser == CNULL) 358 ruser = locuser; 359 else if (!okname(ruser)) 360 return(0); 361 } else 362 ruser = locuser; 363 364 if (!IS_ON(options, DO_QUIET)) 365 message(MT_VERBOSE, "updating host %s", rhost); 366 367 (void) snprintf(buf, sizeof(buf), "%.*s -S", 368 (int)(sizeof(buf)-5), path_rdistd); 369 370 if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0) 371 return(0); 372 373 /* 374 * First thing received should be S_VERSION 375 */ 376 respbuff[0] = '\0'; 377 n = remline(respbuff, sizeof(respbuff), TRUE); 378 if (n <= 0 || respbuff[0] != S_VERSION) { 379 if (n > 0) 380 error("Unexpected input from server: \"%s\".", respbuff); 381 else 382 error("No input from server."); 383 closeconn(); 384 return(0); 385 } 386 387 /* 388 * For future compatibility we check to see if the server 389 * sent it's version number to us. If it did, we use it, 390 * otherwise, we send our version number to the server and let 391 * it decide if it can handle our protocol version. 392 */ 393 if (respbuff[1] == CNULL) { 394 /* 395 * The server wants us to send it our version number 396 */ 397 (void) sendcmd(S_VERSION, "%d", VERSION); 398 if (response() < 0) 399 return(0); 400 } else { 401 /* 402 * The server sent it's version number to us 403 */ 404 proto_version = atoi(&respbuff[1]); 405 if (proto_version != VERSION) { 406 fatalerr( 407 "Server version (%d) is not the same as local version (%d).", 408 proto_version, VERSION); 409 return(0); 410 } 411 } 412 413 /* 414 * Send config commands 415 */ 416 if (host[0]) { 417 (void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host); 418 if (response() < 0) 419 return(0); 420 } 421 if (min_freespace) { 422 (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREESPACE, 423 min_freespace); 424 if (response() < 0) 425 return(0); 426 } 427 if (min_freefiles) { 428 (void) sendcmd(C_SETCONFIG, "%c%d", SC_FREEFILES, 429 min_freefiles); 430 if (response() < 0) 431 return(0); 432 } 433 if (remotemsglist) { 434 (void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist); 435 if (response() < 0) 436 return(0); 437 } 438 if (strcmp(defowner, "bin") != 0) { 439 (void) sendcmd(C_SETCONFIG, "%c%s", SC_DEFOWNER, defowner); 440 if (response() < 0) 441 return(0); 442 } 443 if (strcmp(defgroup, "bin") != 0) { 444 (void) sendcmd(C_SETCONFIG, "%c%s", SC_DEFGROUP, defgroup); 445 if (response() < 0) 446 return(0); 447 } 448 449 return(1); 450 } 451 452 /* 453 * Process commands for sending files to other machines. 454 */ 455 static void 456 doarrow(struct cmd *cmd, char **filev) 457 { 458 struct namelist *f; 459 struct subcmd *sc; 460 char **cpp; 461 int n, ddir, destdir; 462 volatile opt_t opts = options; 463 struct namelist *files; 464 struct subcmd *sbcmds; 465 char *rhost; 466 volatile int didupdate = 0; 467 468 if (setjmp_ok) { 469 error("reentrant call to doarrow"); 470 abort(); 471 } 472 473 if (!cmd) { 474 debugmsg(DM_MISC, "doarrow() NULL cmd parameter"); 475 return; 476 } 477 478 files = cmd->c_files; 479 sbcmds = cmd->c_cmds; 480 rhost = cmd->c_name; 481 482 if (files == NULL) { 483 error("No files to be updated on %s for target \"%s\"", 484 rhost, cmd->c_label); 485 return; 486 } 487 488 debugmsg(DM_CALL, "doarrow(%x, %s, %x) start", 489 files, A(rhost), sbcmds); 490 491 if (nflag) 492 (void) printf("updating host %s\n", rhost); 493 else { 494 if (cmd->c_flags & CMD_CONNFAILED) { 495 debugmsg(DM_MISC, 496 "makeconn %s failed before; skipping\n", 497 rhost); 498 return; 499 } 500 501 if (setjmp(finish_jmpbuf)) { 502 setjmp_ok = FALSE; 503 debugmsg(DM_MISC, "setjmp to finish_jmpbuf"); 504 markfailed(cmd, cmds); 505 return; 506 } 507 setjmp_ok = TRUE; 508 509 if (!makeconn(rhost)) { 510 setjmp_ok = FALSE; 511 markfailed(cmd, cmds); 512 return; 513 } 514 } 515 516 subcmds = sbcmds; 517 filelist = files; 518 519 n = 0; 520 for (sc = sbcmds; sc != NULL; sc = sc->sc_next) { 521 if (sc->sc_type != INSTALL) 522 continue; 523 n++; 524 /* 525 * destination is a directory if one of the following is true: 526 * a) more than one name specified on left side of -> directive 527 * b) basename of destination in "install" directive is "." 528 * (e.g. install /tmp/.;) 529 * c) name on left side of -> directive is a directory on local system. 530 * 531 * We need 2 destdir flags (destdir and ddir) because single directory 532 * source is handled differently. In this case, ddir is 0 (which 533 * tells install() not to send DIRTARGET directive to remote rdistd) 534 * and destdir is 1 (which tells remfilename() how to build the FILE 535 * variables correctly). In every other case, destdir and ddir will 536 * have the same value. 537 */ 538 ddir = files->n_next != NULL; /* destination is a directory */ 539 if (!ddir) { 540 struct stat s; 541 int isadir = 0; 542 543 if (lstat(files->n_name, &s) == 0) 544 isadir = S_ISDIR(s.st_mode); 545 if (!isadir && sc->sc_name && *sc->sc_name) 546 ddir = !strcmp(xbasename(sc->sc_name),"."); 547 destdir = isadir | ddir; 548 } else 549 destdir = ddir; 550 551 debugmsg(DM_MISC, 552 "Debug files->n_next= %d, destdir=%d, ddir=%d", 553 files->n_next, destdir, ddir); 554 555 if (!sc->sc_name || !*sc->sc_name) { 556 destdir = 0; 557 ddir = 0; 558 } 559 560 debugmsg(DM_MISC, 561 "Debug sc->sc_name=%x, destdir=%d, ddir=%d", 562 sc->sc_name, destdir, ddir); 563 564 for (f = files; f != NULL; f = f->n_next) { 565 if (filev) { 566 for (cpp = filev; *cpp; cpp++) 567 if (strcmp(f->n_name, *cpp) == 0) 568 goto found; 569 continue; 570 } 571 found: 572 if (install(f->n_name, sc->sc_name, ddir, destdir, 573 sc->sc_options) > 0) 574 ++didupdate; 575 opts = sc->sc_options; 576 } 577 578 } /* end loop for each INSTALL command */ 579 580 /* if no INSTALL commands present, do default install */ 581 if (!n) { 582 for (f = files; f != NULL; f = f->n_next) { 583 if (filev) { 584 for (cpp = filev; *cpp; cpp++) 585 if (strcmp(f->n_name, *cpp) == 0) 586 goto found2; 587 continue; 588 } 589 found2: 590 /* ddir & destdir set to zero for default install */ 591 if (install(f->n_name, NULL, 0, 0, options) > 0) 592 ++didupdate; 593 } 594 } 595 596 /* 597 * Run any commands for the entire cmd 598 */ 599 if (didupdate > 0) { 600 runcmdspecial(cmd, opts); 601 didupdate = 0; 602 } 603 604 if (!nflag) 605 (void) signal(SIGPIPE, cleanup); 606 607 for (sc = sbcmds; sc != NULL; sc = sc->sc_next) 608 if (sc->sc_type == NOTIFY) 609 notify(rhost, sc->sc_args, (time_t) 0); 610 611 if (!nflag) { 612 struct linkbuf *nextl, *l; 613 614 for (l = ihead; l != NULL; freelinkinfo(l), l = nextl) { 615 nextl = l->nextp; 616 if (contimedout || IS_ON(opts, DO_IGNLNKS) || 617 l->count == 0) 618 continue; 619 message(MT_WARNING, "%s: Warning: %d %s link%s", 620 l->pathname, abs(l->count), 621 (l->count > 0) ? "missing" : "extra", 622 (l->count == 1) ? "" : "s"); 623 } 624 ihead = NULL; 625 } 626 setjmp_ok = FALSE; 627 } 628 629 int 630 okname(char *name) 631 { 632 char *cp = name; 633 int c, isbad; 634 635 for (isbad = FALSE; *cp && !isbad; ++cp) { 636 c = *cp; 637 if (c & 0200) 638 isbad = TRUE; 639 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 640 isbad = TRUE; 641 } 642 643 if (isbad) { 644 error("Invalid user name \"%s\"\n", name); 645 return(0); 646 } 647 return(1); 648 } 649 650 static void 651 rcmptime(struct stat *st, struct subcmd *sbcmds, char **env) 652 { 653 DIR *d; 654 DIRENTRY *dp; 655 char *cp; 656 char *optarget; 657 int len; 658 659 debugmsg(DM_CALL, "rcmptime(%x) start", st); 660 661 if ((d = opendir((char *) target)) == NULL) { 662 error("%s: open directory failed: %s", target, SYSERR); 663 return; 664 } 665 optarget = ptarget; 666 len = ptarget - target; 667 while ((dp = readdir(d)) != NULL) { 668 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 669 continue; 670 if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) { 671 error("%s/%s: Name too long\n", target, dp->d_name); 672 continue; 673 } 674 ptarget = optarget; 675 *ptarget++ = '/'; 676 cp = dp->d_name; 677 while ((*ptarget++ = *cp++) != '\0') 678 ; 679 ptarget--; 680 cmptime(target, sbcmds, env); 681 } 682 (void) closedir((DIR *) d); 683 ptarget = optarget; 684 *ptarget = '\0'; 685 } 686 687 /* 688 * Compare the mtime of file to the list of time stamps. 689 */ 690 static void 691 cmptime(char *name, struct subcmd *sbcmds, char **env) 692 { 693 struct subcmd *sc; 694 struct stat stb; 695 696 debugmsg(DM_CALL, "cmptime(%s)", name); 697 698 if (except(name)) 699 return; 700 701 if (nflag) { 702 (void) printf("comparing dates: %s\n", name); 703 return; 704 } 705 706 /* 707 * first time cmptime() is called? 708 */ 709 if (ptarget == NULL) { 710 if (exptilde(target, name, sizeof(target)) == NULL) 711 return; 712 ptarget = name = target; 713 while (*ptarget) 714 ptarget++; 715 } 716 if (access(name, R_OK) < 0 || stat(name, &stb) < 0) { 717 error("%s: cannot access file: %s", name, SYSERR); 718 return; 719 } 720 721 if (S_ISDIR(stb.st_mode)) { 722 rcmptime(&stb, sbcmds, env); 723 return; 724 } else if (!S_ISREG(stb.st_mode)) { 725 error("%s: not a plain file", name); 726 return; 727 } 728 729 if (stb.st_mtime > lastmod) { 730 message(MT_INFO, "%s: file is newer", name); 731 for (sc = sbcmds; sc != NULL; sc = sc->sc_next) { 732 char buf[BUFSIZ]; 733 if (sc->sc_type != SPECIAL) 734 continue; 735 if (sc->sc_args != NULL && !inlist(sc->sc_args, name)) 736 continue; 737 (void) snprintf(buf, sizeof(buf), "%s=%s;%s", 738 E_LOCFILE, name, sc->sc_name); 739 message(MT_CHANGE, "special \"%s\"", buf); 740 if (*env) { 741 size_t len = strlen(*env) + strlen(name) + 2; 742 *env = (char *) xrealloc(*env, len); 743 (void) strlcat(*env, name, len); 744 (void) strlcat(*env, ":", len); 745 } 746 if (IS_ON(options, DO_VERIFY)) 747 continue; 748 749 runcommand(buf); 750 } 751 } 752 } 753 754 /* 755 * Process commands for comparing files to time stamp files. 756 */ 757 static void 758 dodcolon(struct cmd *cmd, char **filev) 759 { 760 struct subcmd *sc; 761 struct namelist *f; 762 char *cp, **cpp; 763 struct stat stb; 764 struct namelist *files = cmd->c_files; 765 struct subcmd *sbcmds = cmd->c_cmds; 766 char *env, *stamp = cmd->c_name; 767 768 debugmsg(DM_CALL, "dodcolon()"); 769 770 if (files == NULL) { 771 error("No files to be updated for target \"%s\"", 772 cmd->c_label); 773 return; 774 } 775 if (stat(stamp, &stb) < 0) { 776 error("%s: stat failed: %s", stamp, SYSERR); 777 return; 778 } 779 780 debugmsg(DM_MISC, "%s: mtime %d\n", stamp, stb.st_mtime); 781 782 env = NULL; 783 for (sc = sbcmds; sc != NULL; sc = sc->sc_next) { 784 if (sc->sc_type == CMDSPECIAL) { 785 env = (char *) xmalloc(sizeof(E_FILES) + 3); 786 (void) snprintf(env, sizeof(E_FILES) + 3, 787 "%s='", E_FILES); 788 break; 789 } 790 } 791 792 subcmds = sbcmds; 793 filelist = files; 794 795 lastmod = stb.st_mtime; 796 if (!nflag && !IS_ON(options, DO_VERIFY)) 797 /* 798 * Set atime and mtime to current time 799 */ 800 (void) setfiletime(stamp, (time_t) 0, (time_t) 0); 801 802 for (f = files; f != NULL; f = f->n_next) { 803 if (filev) { 804 for (cpp = filev; *cpp; cpp++) 805 if (strcmp(f->n_name, *cpp) == 0) 806 goto found; 807 continue; 808 } 809 found: 810 ptarget = NULL; 811 cmptime(f->n_name, sbcmds, &env); 812 } 813 814 for (sc = sbcmds; sc != NULL; sc = sc->sc_next) { 815 if (sc->sc_type == NOTIFY) 816 notify(NULL, sc->sc_args, (time_t)lastmod); 817 else if (sc->sc_type == CMDSPECIAL && env) { 818 size_t len = strlen(env); 819 if (env[len - 1] == ':') 820 env[--len] = CNULL; 821 len += 2 + strlen(sc->sc_name) + 1; 822 env = xrealloc(env, len); 823 (void) strlcat(env, "';", len); 824 (void) strlcat(env, sc->sc_name, len); 825 message(MT_CHANGE, "cmdspecial \"%s\"", env); 826 if (!nflag && IS_OFF(options, DO_VERIFY)) 827 runcommand(env); 828 (void) free(env); 829 env = NULL; /* so cmdspecial is only called once */ 830 } 831 } 832 if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile())) 833 (void) unlink(cp); 834 } 835 836 /* 837 * Return TRUE if file is in the exception list. 838 */ 839 int 840 except(char *file) 841 { 842 struct subcmd *sc; 843 struct namelist *nl; 844 845 debugmsg(DM_CALL, "except(%s)", file); 846 847 for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 848 if (sc->sc_type == EXCEPT) { 849 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) 850 if (strcmp(file, nl->n_name) == 0) 851 return(1); 852 continue; 853 } 854 if (sc->sc_type == PATTERN) { 855 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) { 856 char ebuf[BUFSIZ]; 857 int ecode = 0; 858 859 /* allocate and compile n_regex as needed */ 860 if (nl->n_regex == NULL) { 861 nl->n_regex = (regex_t *) 862 xmalloc(sizeof(regex_t)); 863 ecode = regcomp(nl->n_regex, nl->n_name, 864 REG_NOSUB); 865 } 866 if (ecode == 0) { 867 ecode = regexec(nl->n_regex, file, 0, 868 NULL, 0); 869 } 870 switch (ecode) { 871 case REG_NOMATCH: 872 break; 873 case 0: 874 return(1); /* match! */ 875 default: 876 regerror(ecode, nl->n_regex, ebuf, 877 sizeof(ebuf)); 878 error("Regex error \"%s\" for \"%s\".", 879 ebuf, nl->n_name); 880 return(0); 881 } 882 } 883 } 884 } 885 return(0); 886 } 887 888 /* 889 * Do a specific command for a specific host 890 */ 891 static void 892 docmdhost(struct cmd *cmd, char **filev) 893 { 894 checkcmd(cmd); 895 896 /* 897 * If we're multi-threaded and we're the parent, spawn a 898 * new child process. 899 */ 900 if (do_fork && !amchild) { 901 pid_t pid; 902 903 /* 904 * If we're at maxchildren, wait for number of active 905 * children to fall below max number of children. 906 */ 907 while (activechildren >= maxchildren) 908 waitup(); 909 910 pid = spawn(cmd, cmds); 911 if (pid == 0) 912 /* Child */ 913 amchild = 1; 914 else 915 /* Parent */ 916 return; 917 } 918 919 /* 920 * Disable NFS checks 921 */ 922 if (cmd->c_flags & CMD_NOCHKNFS) 923 FLAG_OFF(options, DO_CHKNFS); 924 925 if (!nflag) { 926 currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>"; 927 #if defined(SETARGS) || defined(HAVE_SETPROCTITLE) 928 setproctitle("update %s", currenthost); 929 #endif /* SETARGS || HAVE_SETPROCTITLE */ 930 } 931 932 switch (cmd->c_type) { 933 case ARROW: 934 doarrow(cmd, filev); 935 break; 936 case DCOLON: 937 dodcolon(cmd, filev); 938 break; 939 default: 940 fatalerr("illegal command type %d", cmd->c_type); 941 } 942 } 943 944 /* 945 * Do a specific command (cmd) 946 */ 947 static void 948 docmd(struct cmd *cmd, int argc, char **argv) 949 { 950 struct namelist *f; 951 int i; 952 953 if (argc) { 954 for (i = 0; i < argc; i++) { 955 if (cmd->c_label != NULL && 956 strcmp(cmd->c_label, argv[i]) == 0) { 957 docmdhost(cmd, NULL); 958 return; 959 } 960 for (f = cmd->c_files; f != NULL; f = f->n_next) 961 if (strcmp(f->n_name, argv[i]) == 0) { 962 docmdhost(cmd, &argv[i]); 963 return; 964 } 965 } 966 } else 967 docmdhost(cmd, NULL); 968 } 969 970 /* 971 * 972 * Multiple hosts are updated at once via a "ring" of at most 973 * maxchildren rdist processes. The parent rdist fork()'s a child 974 * for a given host. That child will update the given target files 975 * and then continue scanning through the remaining targets looking 976 * for more work for a given host. Meanwhile, the parent gets the 977 * next target command and makes sure that it hasn't encountered 978 * that host yet since the children are responsible for everything 979 * for that host. If no children have done this host, then check 980 * to see if the number of active proc's is less than maxchildren. 981 * If so, then spawn a new child for that host. Otherwise, wait 982 * for a child to finish. 983 * 984 */ 985 986 /* 987 * Do the commands in cmds (initialized by yyparse). 988 */ 989 void 990 docmds(struct namelist *hostlist, int argc, char **argv) 991 { 992 struct cmd *c; 993 char *cp; 994 int i; 995 996 (void) signal(SIGHUP, sighandler); 997 (void) signal(SIGINT, sighandler); 998 (void) signal(SIGQUIT, sighandler); 999 (void) signal(SIGTERM, sighandler); 1000 1001 if (!nflag) 1002 mysetlinebuf(stdout); /* Make output (mostly) clean */ 1003 1004 #if defined(USE_STATDB) 1005 if (!nflag && (dostatdb || juststatdb)) { 1006 extern long reccount; 1007 message(MT_INFO, "Making stat database [%s] ... \n", 1008 gettimestr()); 1009 if (mkstatdb() < 0) 1010 error("Warning: Make stat database failed."); 1011 message(MT_INFO, 1012 "Stat database created: %d files stored [%s].\n", 1013 reccount, gettimestr()); 1014 if (juststatdb) 1015 return; 1016 } 1017 #endif /* USE_STATDB */ 1018 1019 /* 1020 * Print errors for any command line targets we didn't find. 1021 * If any errors are found, return to main() which will then exit. 1022 */ 1023 for (i = 0; i < argc; i++) { 1024 int found; 1025 1026 for (found = FALSE, c = cmds; c != NULL; c = c->c_next) { 1027 if (c->c_label && argv[i] && 1028 strcmp(c->c_label, argv[i]) == 0) { 1029 found = TRUE; 1030 break; 1031 } 1032 } 1033 if (!found) 1034 error("Label \"%s\" is not defined in the distfile.", 1035 argv[i]); 1036 } 1037 if (nerrs) 1038 return; 1039 1040 /* 1041 * Main command loop. Loop through all the commands. 1042 */ 1043 for (c = cmds; c != NULL; c = c->c_next) { 1044 checkcmd(c); 1045 if (do_fork) { 1046 /* 1047 * Let the children take care of their assigned host 1048 */ 1049 if (amchild) { 1050 if (strcmp(c->c_name, currenthost) != 0) 1051 continue; 1052 } else if (c->c_flags & CMD_ASSIGNED) { 1053 /* This cmd has been previously assigned */ 1054 debugmsg(DM_MISC, "prev assigned: %s\n", 1055 c->c_name); 1056 continue; 1057 } 1058 } 1059 1060 if (hostlist) { 1061 /* Do specific hosts as specified on command line */ 1062 struct namelist *nlptr; 1063 1064 for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next) 1065 /* 1066 * Try an exact match and then a match 1067 * without '@' (if present). 1068 */ 1069 if ((strcmp(c->c_name, nlptr->n_name) == 0) || 1070 ((cp = strchr(c->c_name, '@')) && 1071 strcmp(++cp, nlptr->n_name) == 0)) 1072 docmd(c, argc, argv); 1073 continue; 1074 } else 1075 /* Do all of the command */ 1076 docmd(c, argc, argv); 1077 } 1078 1079 if (do_fork) { 1080 /* 1081 * We're multi-threaded, so do appropriate shutdown 1082 * actions based on whether we're the parent or a child. 1083 */ 1084 if (amchild) { 1085 if (!IS_ON(options, DO_QUIET)) 1086 message(MT_VERBOSE, "updating of %s finished", 1087 currenthost); 1088 closeconn(); 1089 cleanup(0); 1090 exit(nerrs); 1091 } 1092 1093 /* 1094 * Wait for all remaining active children to finish 1095 */ 1096 while (activechildren > 0) { 1097 debugmsg(DM_MISC, 1098 "Waiting for %d children to finish.\n", 1099 activechildren); 1100 waitup(); 1101 } 1102 } else if (!nflag) { 1103 /* 1104 * We're single-threaded so close down current connection 1105 */ 1106 closeconn(); 1107 cleanup(0); 1108 } 1109 } 1110