1 /* $NetBSD: docmd.c,v 1.10 1996/08/13 03:24:18 explorer Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)docmd.c 8.1 (Berkeley) 6/9/93"; 39 #else 40 static char *rcsid = "$NetBSD: docmd.c,v 1.10 1996/08/13 03:24:18 explorer Exp $"; 41 #endif 42 #endif /* not lint */ 43 44 #include "defs.h" 45 #include <setjmp.h> 46 #include <netdb.h> 47 #include <regex.h> 48 49 FILE *lfp; /* log file for recording files updated */ 50 struct subcmd *subcmds; /* list of sub-commands for current cmd */ 51 jmp_buf env; 52 53 static int makeconn __P((char *)); 54 static int okname __P((char *)); 55 static void closeconn __P((void)); 56 static void cmptime __P((char *)); 57 static void doarrow __P((char **, 58 struct namelist *, char *, struct subcmd *)); 59 static void dodcolon __P((char **, 60 struct namelist *, char *, struct subcmd *)); 61 static void notify __P((char *, char *, struct namelist *, time_t)); 62 static void rcmptime __P((struct stat *)); 63 64 /* 65 * Do the commands in cmds (initialized by yyparse). 66 */ 67 void 68 docmds(dhosts, argc, argv) 69 char **dhosts; 70 int argc; 71 char **argv; 72 { 73 register struct cmd *c; 74 register struct namelist *f; 75 register char **cpp; 76 extern struct cmd *cmds; 77 78 signal(SIGHUP, cleanup); 79 signal(SIGINT, cleanup); 80 signal(SIGQUIT, cleanup); 81 signal(SIGTERM, cleanup); 82 83 for (c = cmds; c != NULL; c = c->c_next) { 84 if (dhosts != NULL && *dhosts != NULL) { 85 for (cpp = dhosts; *cpp; cpp++) 86 if (strcmp(c->c_name, *cpp) == 0) 87 goto fndhost; 88 continue; 89 } 90 fndhost: 91 if (argc) { 92 for (cpp = argv; *cpp; cpp++) { 93 if (c->c_label != NULL && 94 strcmp(c->c_label, *cpp) == 0) { 95 cpp = NULL; 96 goto found; 97 } 98 for (f = c->c_files; f != NULL; f = f->n_next) 99 if (strcmp(f->n_name, *cpp) == 0) 100 goto found; 101 } 102 continue; 103 } else 104 cpp = NULL; 105 found: 106 switch (c->c_type) { 107 case ARROW: 108 doarrow(cpp, c->c_files, c->c_name, c->c_cmds); 109 break; 110 case DCOLON: 111 dodcolon(cpp, c->c_files, c->c_name, c->c_cmds); 112 break; 113 default: 114 fatal("illegal command type %d\n", c->c_type); 115 } 116 } 117 closeconn(); 118 } 119 120 /* 121 * Process commands for sending files to other machines. 122 */ 123 static void 124 doarrow(filev, files, rhost, cmds) 125 char **filev; 126 struct namelist *files; 127 char *rhost; 128 struct subcmd *cmds; 129 { 130 register struct namelist *f; 131 register struct subcmd *sc; 132 register char **cpp; 133 int n, ddir, opts = options; 134 135 if (debug) 136 printf("doarrow(%x, %s, %x)\n", files, rhost, cmds); 137 138 if (files == NULL) { 139 error("no files to be updated\n"); 140 return; 141 } 142 143 subcmds = cmds; 144 ddir = files->n_next != NULL; /* destination is a directory */ 145 if (nflag) 146 printf("updating host %s\n", rhost); 147 else { 148 if (setjmp(env)) 149 goto done; 150 signal(SIGPIPE, lostconn); 151 if (!makeconn(rhost)) 152 return; 153 if ((lfp = fopen(tempfile, "w")) == NULL) { 154 fatal("cannot open %s\n", tempfile); 155 exit(1); 156 } 157 } 158 for (f = files; f != NULL; f = f->n_next) { 159 if (filev) { 160 for (cpp = filev; *cpp; cpp++) 161 if (strcmp(f->n_name, *cpp) == 0) 162 goto found; 163 if (!nflag) 164 (void) fclose(lfp); 165 continue; 166 } 167 found: 168 n = 0; 169 for (sc = cmds; sc != NULL; sc = sc->sc_next) { 170 if (sc->sc_type != INSTALL) 171 continue; 172 n++; 173 install(f->n_name, sc->sc_name, 174 sc->sc_name == NULL ? 0 : ddir, sc->sc_options); 175 opts = sc->sc_options; 176 } 177 if (n == 0) 178 install(f->n_name, NULL, 0, options); 179 } 180 done: 181 if (!nflag) { 182 (void) signal(SIGPIPE, cleanup); 183 (void) fclose(lfp); 184 lfp = NULL; 185 } 186 for (sc = cmds; sc != NULL; sc = sc->sc_next) 187 if (sc->sc_type == NOTIFY) 188 notify(tempfile, rhost, sc->sc_args, 0); 189 if (!nflag) { 190 (void) unlink(tempfile); 191 for (; ihead != NULL; ihead = ihead->nextp) { 192 free(ihead); 193 if ((opts & IGNLNKS) || ihead->count == 0) 194 continue; 195 log(lfp, "%s: Warning: missing links\n", 196 ihead->pathname); 197 } 198 } 199 } 200 201 /* 202 * Create a connection to the rdist server on the machine rhost. 203 */ 204 static int 205 makeconn(rhost) 206 char *rhost; 207 { 208 register char *ruser, *cp; 209 static char *cur_host = NULL; 210 static int port = -1; 211 char tuser[20]; 212 int n; 213 extern char user[]; 214 extern int userid; 215 216 if (debug) 217 printf("makeconn(%s)\n", rhost); 218 219 if (cur_host != NULL && rem >= 0) { 220 if (strcmp(cur_host, rhost) == 0) 221 return(1); 222 closeconn(); 223 } 224 cur_host = rhost; 225 cp = index(rhost, '@'); 226 if (cp != NULL) { 227 char c = *cp; 228 229 *cp = '\0'; 230 strncpy(tuser, rhost, sizeof(tuser)-1); 231 *cp = c; 232 rhost = cp + 1; 233 ruser = tuser; 234 if (*ruser == '\0') 235 ruser = user; 236 else if (!okname(ruser)) 237 return(0); 238 } else 239 ruser = user; 240 if (!qflag) 241 printf("updating host %s\n", rhost); 242 (void) snprintf(buf, sizeof(buf), "%s -Server%s", _PATH_RDIST, 243 qflag ? " -q" : ""); 244 if (port < 0) { 245 struct servent *sp; 246 247 if ((sp = getservbyname("shell", "tcp")) == NULL) 248 fatal("shell/tcp: unknown service"); 249 port = sp->s_port; 250 } 251 252 if (debug) { 253 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser); 254 printf("buf = %s\n", buf); 255 } 256 257 fflush(stdout); 258 seteuid(0); 259 rem = rcmd(&rhost, port, user, ruser, buf, 0); 260 seteuid(userid); 261 if (rem < 0) 262 return(0); 263 cp = buf; 264 if (read(rem, cp, 1) != 1) 265 lostconn(0); 266 if (*cp == 'V') { 267 do { 268 if (read(rem, cp, 1) != 1) 269 lostconn(0); 270 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 271 *--cp = '\0'; 272 cp = buf; 273 n = 0; 274 while (*cp >= '0' && *cp <= '9') 275 n = (n * 10) + (*cp++ - '0'); 276 if (*cp == '\0' && n == VERSION) 277 return(1); 278 error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n); 279 } else { 280 error("connection failed: version numbers don't match\n"); 281 error("got unexpected input:"); 282 do { 283 error("%c", *cp); 284 } while (*cp != '\n' && read(rem, cp, 1) == 1); 285 } 286 closeconn(); 287 return(0); 288 } 289 290 /* 291 * Signal end of previous connection. 292 */ 293 static void 294 closeconn() 295 { 296 if (debug) 297 printf("closeconn()\n"); 298 299 if (rem >= 0) { 300 (void) write(rem, "\2\n", 2); 301 (void) close(rem); 302 rem = -1; 303 } 304 } 305 306 void 307 lostconn(signo) 308 int signo; 309 { 310 if (iamremote) 311 cleanup(0); 312 log(lfp, "rdist: lost connection\n"); 313 longjmp(env, 1); 314 } 315 316 static int 317 okname(name) 318 register char *name; 319 { 320 register char *cp = name; 321 register int c; 322 323 do { 324 c = *cp; 325 if (c & 0200) 326 goto bad; 327 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') 328 goto bad; 329 cp++; 330 } while (*cp); 331 return(1); 332 bad: 333 error("invalid user name %s\n", name); 334 return(0); 335 } 336 337 time_t lastmod; 338 FILE *tfp; 339 extern char target[], *tp; 340 341 /* 342 * Process commands for comparing files to time stamp files. 343 */ 344 static void 345 dodcolon(filev, files, stamp, cmds) 346 char **filev; 347 struct namelist *files; 348 char *stamp; 349 struct subcmd *cmds; 350 { 351 register struct subcmd *sc; 352 register struct namelist *f; 353 register char **cpp; 354 struct timeval tv[2]; 355 struct timezone tz; 356 struct stat stb; 357 358 if (debug) 359 printf("dodcolon()\n"); 360 361 if (files == NULL) { 362 error("no files to be updated\n"); 363 return; 364 } 365 if (stat(stamp, &stb) < 0) { 366 error("%s: %s\n", stamp, strerror(errno)); 367 return; 368 } 369 if (debug) 370 printf("%s: %ld\n", stamp, stb.st_mtime); 371 372 subcmds = cmds; 373 lastmod = stb.st_mtime; 374 if (nflag || (options & VERIFY)) 375 tfp = NULL; 376 else { 377 if ((tfp = fopen(tempfile, "w")) == NULL) { 378 error("%s: %s\n", stamp, strerror(errno)); 379 return; 380 } 381 (void) gettimeofday(&tv[0], &tz); 382 tv[1] = tv[0]; 383 (void) utimes(stamp, tv); 384 } 385 386 for (f = files; f != NULL; f = f->n_next) { 387 if (filev) { 388 for (cpp = filev; *cpp; cpp++) 389 if (strcmp(f->n_name, *cpp) == 0) 390 goto found; 391 continue; 392 } 393 found: 394 tp = NULL; 395 cmptime(f->n_name); 396 } 397 398 if (tfp != NULL) 399 (void) fclose(tfp); 400 for (sc = cmds; sc != NULL; sc = sc->sc_next) 401 if (sc->sc_type == NOTIFY) 402 notify(tempfile, NULL, sc->sc_args, lastmod); 403 if (!nflag && !(options & VERIFY)) 404 (void) unlink(tempfile); 405 } 406 407 /* 408 * Compare the mtime of file to the list of time stamps. 409 */ 410 static void 411 cmptime(name) 412 char *name; 413 { 414 struct stat stb; 415 416 if (debug) 417 printf("cmptime(%s)\n", name); 418 419 if (except(name)) 420 return; 421 422 if (nflag) { 423 printf("comparing dates: %s\n", name); 424 return; 425 } 426 427 /* 428 * first time cmptime() is called? 429 */ 430 if (tp == NULL) { 431 if (exptilde(target, name) == NULL) 432 return; 433 tp = name = target; 434 while (*tp) 435 tp++; 436 } 437 if (access(name, 4) < 0 || stat(name, &stb) < 0) { 438 error("%s: %s\n", name, strerror(errno)); 439 return; 440 } 441 442 switch (stb.st_mode & S_IFMT) { 443 case S_IFREG: 444 break; 445 446 case S_IFDIR: 447 rcmptime(&stb); 448 return; 449 450 default: 451 error("%s: not a plain file\n", name); 452 return; 453 } 454 455 if (stb.st_mtime > lastmod) 456 log(tfp, "new: %s\n", name); 457 } 458 459 static void 460 rcmptime(st) 461 struct stat *st; 462 { 463 register DIR *d; 464 register struct direct *dp; 465 register char *cp; 466 char *otp; 467 int len; 468 469 if (debug) 470 printf("rcmptime(%x)\n", st); 471 472 if ((d = opendir(target)) == NULL) { 473 error("%s: %s\n", target, strerror(errno)); 474 return; 475 } 476 otp = tp; 477 len = tp - target; 478 while (dp = readdir(d)) { 479 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 480 continue; 481 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 482 error("%s/%s: Name too long\n", target, dp->d_name); 483 continue; 484 } 485 tp = otp; 486 *tp++ = '/'; 487 cp = dp->d_name; 488 while (*tp++ = *cp++) 489 ; 490 tp--; 491 cmptime(target); 492 } 493 closedir(d); 494 tp = otp; 495 *tp = '\0'; 496 } 497 498 /* 499 * Notify the list of people the changes that were made. 500 * rhost == NULL if we are mailing a list of changes compared to at time 501 * stamp file. 502 */ 503 static void 504 notify(file, rhost, to, lmod) 505 char *file, *rhost; 506 register struct namelist *to; 507 time_t lmod; 508 { 509 register int fd, len; 510 struct stat stb; 511 FILE *pf; 512 513 if ((options & VERIFY) || to == NULL) 514 return; 515 if (!qflag) { 516 printf("notify "); 517 if (rhost) 518 printf("@%s ", rhost); 519 prnames(to); 520 } 521 if (nflag) 522 return; 523 524 if ((fd = open(file, 0)) < 0) { 525 error("%s: %s\n", file, strerror(errno)); 526 return; 527 } 528 if (fstat(fd, &stb) < 0) { 529 error("%s: %s\n", file, strerror(errno)); 530 (void) close(fd); 531 return; 532 } 533 if (stb.st_size == 0) { 534 (void) close(fd); 535 return; 536 } 537 /* 538 * Create a pipe to mailling program. 539 */ 540 (void)snprintf(buf, sizeof(buf), "%s -oi -t", _PATH_SENDMAIL); 541 pf = popen(buf, "w"); 542 if (pf == NULL) { 543 error("notify: \"%s\" failed\n", _PATH_SENDMAIL); 544 (void) close(fd); 545 return; 546 } 547 /* 548 * Output the proper header information. 549 */ 550 fprintf(pf, "From: rdist (Remote distribution program)\n"); 551 fprintf(pf, "To:"); 552 if (!any('@', to->n_name) && rhost != NULL) 553 fprintf(pf, " %s@%s", to->n_name, rhost); 554 else 555 fprintf(pf, " %s", to->n_name); 556 to = to->n_next; 557 while (to != NULL) { 558 if (!any('@', to->n_name) && rhost != NULL) 559 fprintf(pf, ", %s@%s", to->n_name, rhost); 560 else 561 fprintf(pf, ", %s", to->n_name); 562 to = to->n_next; 563 } 564 putc('\n', pf); 565 if (rhost != NULL) 566 fprintf(pf, "Subject: files updated by rdist from %s to %s\n", 567 host, rhost); 568 else 569 fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod)); 570 putc('\n', pf); 571 572 while ((len = read(fd, buf, BUFSIZ)) > 0) 573 (void) fwrite(buf, 1, len, pf); 574 (void) close(fd); 575 (void) pclose(pf); 576 } 577 578 /* 579 * Return true if name is in the list. 580 */ 581 int 582 inlist(list, file) 583 struct namelist *list; 584 char *file; 585 { 586 register struct namelist *nl; 587 588 for (nl = list; nl != NULL; nl = nl->n_next) 589 if (!strcmp(file, nl->n_name)) 590 return(1); 591 return(0); 592 } 593 594 /* 595 * Return TRUE if file is in the exception list. 596 */ 597 int 598 except(file) 599 char *file; 600 { 601 register struct subcmd *sc; 602 register struct namelist *nl; 603 int err; 604 regex_t s; 605 606 if (debug) 607 printf("except(%s)\n", file); 608 609 for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 610 if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN) 611 continue; 612 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) { 613 if (sc->sc_type == EXCEPT) { 614 if (!strcmp(file, nl->n_name)) 615 return(1); 616 continue; 617 } 618 if ((err = regcomp(&s, nl->n_name, 0)) != 0) { 619 char ebuf[BUFSIZ]; 620 (void) regerror(err, &s, ebuf, sizeof(ebuf)); 621 error("%s: %s\n", nl->n_name, ebuf); 622 } 623 if (regexec(&s, file, 0, NULL, 0) == 0) { 624 regfree(&s); 625 return(1); 626 } 627 regfree(&s); 628 } 629 } 630 return(0); 631 } 632 633 char * 634 colon(cp) 635 register char *cp; 636 { 637 638 while (*cp) { 639 if (*cp == ':') 640 return(cp); 641 if (*cp == '/') 642 return(0); 643 cp++; 644 } 645 return(0); 646 } 647