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