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