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