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