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.8 (Berkeley) 03/01/91"; 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 void cleanup(), lostconn(); 21 22 /* 23 * Do the commands in cmds (initialized by yyparse). 24 */ 25 docmds(dhosts, argc, argv) 26 char **dhosts; 27 int argc; 28 char **argv; 29 { 30 register struct cmd *c; 31 register struct namelist *f; 32 register char **cpp; 33 extern struct cmd *cmds; 34 35 signal(SIGHUP, cleanup); 36 signal(SIGINT, cleanup); 37 signal(SIGQUIT, cleanup); 38 signal(SIGTERM, cleanup); 39 40 for (c = cmds; c != NULL; c = c->c_next) { 41 if (dhosts != NULL && *dhosts != NULL) { 42 for (cpp = dhosts; *cpp; cpp++) 43 if (strcmp(c->c_name, *cpp) == 0) 44 goto fndhost; 45 continue; 46 } 47 fndhost: 48 if (argc) { 49 for (cpp = argv; *cpp; cpp++) { 50 if (c->c_label != NULL && 51 strcmp(c->c_label, *cpp) == 0) { 52 cpp = NULL; 53 goto found; 54 } 55 for (f = c->c_files; f != NULL; f = f->n_next) 56 if (strcmp(f->n_name, *cpp) == 0) 57 goto found; 58 } 59 continue; 60 } else 61 cpp = NULL; 62 found: 63 switch (c->c_type) { 64 case ARROW: 65 doarrow(cpp, c->c_files, c->c_name, c->c_cmds); 66 break; 67 case DCOLON: 68 dodcolon(cpp, c->c_files, c->c_name, c->c_cmds); 69 break; 70 default: 71 fatal("illegal command type %d\n", c->c_type); 72 } 73 } 74 closeconn(); 75 } 76 77 /* 78 * Process commands for sending files to other machines. 79 */ 80 doarrow(filev, files, rhost, cmds) 81 char **filev; 82 struct namelist *files; 83 char *rhost; 84 struct subcmd *cmds; 85 { 86 register struct namelist *f; 87 register struct subcmd *sc; 88 register char **cpp; 89 int n, ddir, opts = options; 90 91 if (debug) 92 printf("doarrow(%x, %s, %x)\n", files, rhost, cmds); 93 94 if (files == NULL) { 95 error("no files to be updated\n"); 96 return; 97 } 98 99 subcmds = cmds; 100 ddir = files->n_next != NULL; /* destination is a directory */ 101 if (nflag) 102 printf("updating host %s\n", rhost); 103 else { 104 if (setjmp(env)) 105 goto done; 106 signal(SIGPIPE, lostconn); 107 if (!makeconn(rhost)) 108 return; 109 if ((lfp = fopen(tempfile, "w")) == NULL) { 110 fatal("cannot open %s\n", tempfile); 111 exit(1); 112 } 113 } 114 for (f = files; f != NULL; f = f->n_next) { 115 if (filev) { 116 for (cpp = filev; *cpp; cpp++) 117 if (strcmp(f->n_name, *cpp) == 0) 118 goto found; 119 if (!nflag) 120 (void) fclose(lfp); 121 continue; 122 } 123 found: 124 n = 0; 125 for (sc = cmds; sc != NULL; sc = sc->sc_next) { 126 if (sc->sc_type != INSTALL) 127 continue; 128 n++; 129 install(f->n_name, sc->sc_name, 130 sc->sc_name == NULL ? 0 : ddir, sc->sc_options); 131 opts = sc->sc_options; 132 } 133 if (n == 0) 134 install(f->n_name, NULL, 0, options); 135 } 136 done: 137 if (!nflag) { 138 (void) signal(SIGPIPE, cleanup); 139 (void) fclose(lfp); 140 lfp = NULL; 141 } 142 for (sc = cmds; sc != NULL; sc = sc->sc_next) 143 if (sc->sc_type == NOTIFY) 144 notify(tempfile, rhost, sc->sc_args, 0); 145 if (!nflag) { 146 (void) unlink(tempfile); 147 for (; ihead != NULL; ihead = ihead->nextp) { 148 free(ihead); 149 if ((opts & IGNLNKS) || ihead->count == 0) 150 continue; 151 log(lfp, "%s: Warning: missing links\n", 152 ihead->pathname); 153 } 154 } 155 } 156 157 /* 158 * Create a connection to the rdist server on the machine rhost. 159 */ 160 makeconn(rhost) 161 char *rhost; 162 { 163 register char *ruser, *cp; 164 static char *cur_host = NULL; 165 static int port = -1; 166 char tuser[20]; 167 int n; 168 extern char user[]; 169 extern int userid; 170 171 if (debug) 172 printf("makeconn(%s)\n", rhost); 173 174 if (cur_host != NULL && rem >= 0) { 175 if (strcmp(cur_host, rhost) == 0) 176 return(1); 177 closeconn(); 178 } 179 cur_host = rhost; 180 cp = index(rhost, '@'); 181 if (cp != NULL) { 182 char c = *cp; 183 184 *cp = '\0'; 185 strncpy(tuser, rhost, sizeof(tuser)-1); 186 *cp = c; 187 rhost = cp + 1; 188 ruser = tuser; 189 if (*ruser == '\0') 190 ruser = user; 191 else if (!okname(ruser)) 192 return(0); 193 } else 194 ruser = user; 195 if (!qflag) 196 printf("updating host %s\n", rhost); 197 (void) sprintf(buf, "%s -Server%s", _PATH_RDIST, qflag ? " -q" : ""); 198 if (port < 0) { 199 struct servent *sp; 200 201 if ((sp = getservbyname("shell", "tcp")) == NULL) 202 fatal("shell/tcp: unknown service"); 203 port = sp->s_port; 204 } 205 206 if (debug) { 207 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser); 208 printf("buf = %s\n", buf); 209 } 210 211 fflush(stdout); 212 setreuid(userid, 0); 213 rem = rcmd(&rhost, port, user, ruser, buf, 0); 214 setreuid(0, userid); 215 if (rem < 0) 216 return(0); 217 cp = buf; 218 if (read(rem, cp, 1) != 1) 219 lostconn(); 220 if (*cp == 'V') { 221 do { 222 if (read(rem, cp, 1) != 1) 223 lostconn(); 224 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 225 *--cp = '\0'; 226 cp = buf; 227 n = 0; 228 while (*cp >= '0' && *cp <= '9') 229 n = (n * 10) + (*cp++ - '0'); 230 if (*cp == '\0' && n == VERSION) 231 return(1); 232 error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n); 233 } else 234 error("connection failed: version numbers don't match\n"); 235 closeconn(); 236 return(0); 237 } 238 239 /* 240 * Signal end of previous connection. 241 */ 242 closeconn() 243 { 244 if (debug) 245 printf("closeconn()\n"); 246 247 if (rem >= 0) { 248 (void) write(rem, "\2\n", 2); 249 (void) close(rem); 250 rem = -1; 251 } 252 } 253 254 void 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