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