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