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