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