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