1 /* $OpenBSD: client.c,v 1.12 1999/03/05 01:36:12 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char RCSid[] = 39 "$From: client.c,v 6.80 1996/02/28 20:34:27 mcooper Exp $"; 40 #else 41 static char RCSid[] = 42 "$OpenBSD: client.c,v 1.12 1999/03/05 01:36:12 millert Exp $"; 43 #endif 44 45 static char sccsid[] = "@(#)client.c"; 46 47 static char copyright[] = 48 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 49 All rights reserved.\n"; 50 #endif /* not lint */ 51 52 /* 53 * Routines used in client mode to communicate with remove server. 54 */ 55 56 #include "defs.h" 57 #include "y.tab.h" 58 59 /* 60 * Update status 61 */ 62 #define US_NOTHING 0 /* No update needed */ 63 #define US_NOENT 1 /* Entry does not exist */ 64 #define US_OUTDATE 2 /* Entry is out of date */ 65 #define US_DOCOMP 3 /* Do a binary comparison */ 66 #define US_MODE 4 /* Modes of file differ */ 67 68 struct linkbuf *ihead = NULL; /* list of files with more than one link */ 69 char buf[BUFSIZ]; /* general purpose buffer */ 70 u_char respbuff[BUFSIZ]; /* Response buffer */ 71 char target[BUFSIZ]; /* target/source directory name */ 72 char source[BUFSIZ]; /* source directory name */ 73 char *ptarget; /* pointer to end of target name */ 74 char *Tdest; /* pointer to last T dest*/ 75 struct namelist *updfilelist = NULL; /* List of updated files */ 76 77 static int sendit(); 78 79 /* 80 * return remote file pathname (relative from target) 81 */ 82 char *remfilename(src, dest, path, rname, destdir) 83 char *src, *dest, *path, *rname; 84 int destdir; 85 { 86 extern struct namelist *filelist; 87 register char *lname, *cp; 88 static char buff[BUFSIZ]; 89 int srclen, pathlen; 90 char *p; 91 92 93 debugmsg(DM_MISC, 94 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n", 95 A(src), A(dest), A(path), A(rname), destdir); 96 97 if (!dest) { 98 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path); 99 return(path); 100 } 101 102 if (!destdir) { 103 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest); 104 return(dest); 105 } 106 107 buff[0] = CNULL; 108 lname = buff; 109 if (path && *path) { 110 cp = strrchr(path, '/'); 111 if (cp == NULL) 112 (void) sprintf(buff, "%s/%s", dest, path); 113 else { 114 srclen = strlen(src); 115 pathlen = strlen(path); 116 if (srclen >= pathlen) 117 cp++; /* xbasename(path) */ 118 else { 119 if (filelist && filelist->n_next == NULL) 120 /* path relative to src */ 121 cp = path + srclen; 122 else { 123 if ((p = strrchr(src, '/'))) 124 cp = path + srclen - strlen(p); 125 else 126 cp = path; 127 } 128 } 129 if ((*cp != '/') && *cp) 130 (void) sprintf(buff, "%s/%s", dest, cp); 131 else 132 (void) sprintf(buff, "%s%s", dest, cp); 133 } 134 } else 135 strcpy(lname, dest); 136 137 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname); 138 139 return(lname); 140 } 141 142 /* 143 * Return true if name is in the list. 144 */ 145 int inlist(list, file) 146 struct namelist *list; 147 char *file; 148 { 149 register struct namelist *nl; 150 151 for (nl = list; nl != NULL; nl = nl->n_next) 152 if (strcmp(file, nl->n_name) == 0) 153 return(1); 154 return(0); 155 } 156 157 /* 158 * Run any special commands for this file 159 */ 160 static void runspecial(starget, opts, rname, destdir) 161 char *starget; 162 opt_t opts; 163 char *rname; 164 int destdir; 165 { 166 register struct subcmd *sc; 167 extern struct subcmd *subcmds; 168 char *rfile; 169 170 rfile = remfilename(source, Tdest, target, rname, destdir); 171 172 for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 173 if (sc->sc_type != SPECIAL) 174 continue; 175 if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) 176 continue; 177 message(MT_CHANGE, "special \"%s\"", sc->sc_name); 178 if (IS_ON(opts, DO_VERIFY)) 179 continue; 180 (void) sendcmd(C_SPECIAL, 181 "%s=%s;%s=%s;%s=%s;export %s %s %s;%s", 182 E_LOCFILE, starget, 183 E_REMFILE, rfile, 184 E_BASEFILE, xbasename(rfile), 185 E_LOCFILE, E_REMFILE, E_BASEFILE, 186 sc->sc_name); 187 while (response() > 0) 188 ; 189 } 190 } 191 192 /* 193 * If we're doing a target with a "cmdspecial" in it, then 194 * save the name of the file being updated for use with "cmdspecial". 195 */ 196 static void addcmdspecialfile(starget, rname, destdir) 197 char *starget; 198 char *rname; 199 int destdir; 200 { 201 char *rfile; 202 struct namelist *new; 203 register struct subcmd *sc; 204 extern struct subcmd *subcmds; 205 int isokay = 0; 206 207 rfile = remfilename(source, Tdest, target, rname, destdir); 208 209 for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) { 210 if (sc->sc_type != CMDSPECIAL) 211 continue; 212 if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) 213 continue; 214 isokay = TRUE; 215 } 216 217 if (isokay) { 218 new = (struct namelist *) xmalloc(sizeof(struct namelist)); 219 new->n_name = xstrdup(rfile); 220 new->n_next = updfilelist; 221 updfilelist = new; 222 } 223 } 224 225 /* 226 * Free the file list 227 */ 228 static void freecmdspecialfiles() 229 { 230 register struct namelist *ptr, *save; 231 232 for (ptr = updfilelist; ptr; ) { 233 if (ptr->n_name) (void) free(ptr->n_name); 234 save = ptr->n_next; 235 (void) free(ptr); 236 if (save) 237 ptr = save->n_next; 238 else 239 ptr = NULL; 240 } 241 updfilelist = NULL; 242 } 243 244 /* 245 * Run commands for an entire cmd 246 */ 247 extern void runcmdspecial(cmd, filev, opts) 248 struct cmd *cmd; 249 char **filev; 250 opt_t opts; 251 { 252 register struct subcmd *sc; 253 register struct namelist *f; 254 int first = TRUE; 255 256 for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) { 257 if (sc->sc_type != CMDSPECIAL) 258 continue; 259 message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name); 260 if (IS_ON(opts, DO_VERIFY)) 261 continue; 262 /* Send all the file names */ 263 for (f = updfilelist; f != NULL; f = f->n_next) { 264 if (first) { 265 (void) sendcmd(C_CMDSPECIAL, NULL); 266 if (response() < 0) 267 return; 268 first = FALSE; 269 } 270 (void) sendcmd(RC_FILE, f->n_name); 271 if (response() < 0) 272 return; 273 } 274 if (first) { 275 (void) sendcmd(C_CMDSPECIAL, NULL); 276 if (response() < 0) 277 return; 278 first = FALSE; 279 } 280 /* Send command to run and wait for it to complete */ 281 (void) sendcmd(RC_COMMAND, sc->sc_name); 282 while (response() > 0) 283 ; 284 first = TRUE; /* Reset in case there are more CMDSPECIAL's */ 285 } 286 freecmdspecialfiles(); 287 } 288 289 /* 290 * For security, reject filenames that contains a newline 291 */ 292 int checkfilename(name) 293 char *name; 294 { 295 register char *cp; 296 297 if (strchr(name, '\n')) { 298 for (cp = name; *cp; cp++) 299 if (*cp == '\n') 300 *cp = '?'; 301 message(MT_NERROR, 302 "Refuse to handle filename containing newline: %s", 303 name); 304 return(-1); 305 } 306 307 return(0); 308 } 309 310 void freelinkinfo(lp) 311 struct linkbuf *lp; 312 { 313 if (lp->pathname) 314 free(lp->pathname); 315 if (lp->src) 316 free(lp->src); 317 if (lp->target) 318 free(lp->target); 319 free(lp); 320 } 321 322 /* 323 * Save and retrieve hard link info 324 */ 325 static struct linkbuf *linkinfo(statp) 326 struct stat *statp; 327 { 328 struct linkbuf *lp; 329 330 /* XXX - linear search doesn't scale with many links */ 331 for (lp = ihead; lp != NULL; lp = lp->nextp) 332 if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) { 333 lp->count--; 334 return(lp); 335 } 336 337 lp = (struct linkbuf *) xmalloc(sizeof(*lp)); 338 lp->nextp = ihead; 339 ihead = lp; 340 lp->inum = statp->st_ino; 341 lp->devnum = statp->st_dev; 342 lp->count = statp->st_nlink - 1; 343 lp->pathname = xstrdup(target); 344 lp->src = xstrdup(source); 345 if (Tdest) 346 lp->target = xstrdup(Tdest); 347 else 348 lp->target = NULL; 349 350 return(NULL); 351 } 352 353 /* 354 * Send a hardlink 355 */ 356 static int sendhardlink(opts, lp, rname, destdir) 357 opt_t opts; 358 struct linkbuf *lp; 359 char *rname; 360 int destdir; 361 { 362 static char buff[MAXPATHLEN]; 363 char *lname; /* name of file to link to */ 364 365 debugmsg(DM_MISC, 366 "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n", 367 rname, lp->pathname ? lp->pathname : "(null)", lp->src 368 ? lp->src : "(null)", lp->target ? lp->target : "(null)"); 369 370 if (lp->target == NULL) 371 (void) sendcmd(C_RECVHARDLINK, "%o %s %s", 372 opts, lp->pathname, rname); 373 else { 374 lname = buff; 375 strcpy(lname, remfilename(lp->src, lp->target, 376 lp->pathname, rname, 377 destdir)); 378 debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname); 379 (void) sendcmd(C_RECVHARDLINK, "%o %s %s", 380 opts, lname, rname); 381 } 382 383 return(response()); 384 } 385 386 /* 387 * Send a file 388 */ 389 static int sendfile(rname, opts, stb, user, group, destdir) 390 char *rname; 391 opt_t opts; 392 struct stat *stb; 393 char *user, *group; 394 int destdir; 395 { 396 int goterr, f; 397 off_t i; 398 399 if (stb->st_nlink > 1) { 400 struct linkbuf *lp; 401 402 if ((lp = linkinfo(stb)) != NULL) 403 return(sendhardlink(opts, lp, rname, destdir)); 404 } 405 406 if ((f = open(target, O_RDONLY)) < 0) { 407 error("%s: open for read failed: %s", target, SYSERR); 408 return(-1); 409 } 410 411 /* 412 * Send file info 413 */ 414 (void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s", 415 opts, stb->st_mode & 07777, 416 (long) stb->st_size, 417 stb->st_mtime, stb->st_atime, 418 user, group, rname); 419 if (response() < 0) { 420 (void) close(f); 421 return(-1); 422 } 423 424 debugmsg(DM_MISC, "Send file '%s' %d bytes\n", 425 rname, (long) stb->st_size); 426 427 /* 428 * Set remote time out alarm handler. 429 */ 430 (void) signal(SIGALRM, sighandler); 431 432 /* 433 * Actually transfer the file 434 */ 435 goterr = 0; 436 for (i = 0; i < stb->st_size; i += BUFSIZ) { 437 int amt = BUFSIZ; 438 439 (void) alarm(rtimeout); 440 if (i + amt > stb->st_size) 441 amt = stb->st_size - i; 442 if (read(f, buf, amt) != amt) { 443 error("%s: File changed size", target); 444 err(); 445 ++goterr; 446 /* 447 * XXX - We have to keep going because the 448 * server expects to receive a fixed number 449 * of bytes that we specified as the file size. 450 * We need Out Of Band communication to handle 451 * this situation gracefully. 452 */ 453 } 454 if (xwrite(rem_w, buf, amt) < 0) { 455 error("%s: Error writing to client: %s", 456 target, SYSERR); 457 err(); 458 ++goterr; 459 break; 460 } 461 (void) alarm(0); 462 } 463 464 (void) alarm(0); /* Insure alarm is off */ 465 (void) close(f); 466 467 debugmsg(DM_MISC, "Send file '%s' %s.\n", 468 (goterr) ? "failed" : "complete", rname); 469 470 /* 471 * Check for errors and end send 472 */ 473 if (goterr) 474 return(-1); 475 else { 476 ack(); 477 f = response(); 478 if (f < 0) 479 return(-1); 480 else if (f == 0 && IS_ON(opts, DO_COMPARE)) 481 return(0); 482 483 runspecial(target, opts, rname, destdir); 484 addcmdspecialfile(target, rname, destdir); 485 486 return(0); 487 } 488 } 489 490 /* 491 * Check for files on the machine being updated that are not on the master 492 * machine and remove them. 493 * 494 * Return < 0 on error. 495 * Return 0 if nothing happened. 496 * Return > 0 if anything is updated. 497 */ 498 static int rmchk(opts) 499 opt_t opts; 500 { 501 register u_char *s; 502 struct stat stb; 503 int didupdate = 0; 504 int n; 505 506 debugmsg(DM_CALL, "rmchk()\n"); 507 508 /* 509 * Tell the remote to clean the files from the last directory sent. 510 */ 511 (void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY)); 512 if (response() < 0) 513 return(-1); 514 515 for ( ; ; ) { 516 n = remline(s = respbuff, sizeof(respbuff), TRUE); 517 if (n <= 0) { 518 error("rmchk: unexpected control record"); 519 return(didupdate); 520 } 521 522 switch (*s++) { 523 case CC_QUERY: /* Query if file should be removed */ 524 /* 525 * Return the following codes to remove query. 526 * CC_NO -- file exists - DON'T remove. 527 * CC_YES -- file doesn't exist - REMOVE. 528 */ 529 (void) sprintf(ptarget, "%s%s", 530 (ptarget[-1] == '/' ? "" : "/"), s); 531 debugmsg(DM_MISC, "check %s\n", target); 532 if (except(target)) 533 (void) sendcmd(CC_NO, NULL); 534 else if (lstat(target, &stb) < 0) { 535 if (sendcmd(CC_YES, NULL) == 0) 536 didupdate = 1; 537 } else 538 (void) sendcmd(CC_NO, NULL); 539 break; 540 541 case CC_END: 542 *ptarget = CNULL; 543 ack(); 544 return(didupdate); 545 546 case C_LOGMSG: 547 if (n > 0) 548 message(MT_INFO, "%s", s); 549 break; 550 551 case C_NOTEMSG: 552 if (n > 0) 553 message(MT_NOTICE, "%s", s); 554 break; 555 /* Goto top of loop */ 556 557 case C_ERRMSG: 558 message(MT_NERROR, "%s", s); 559 return(didupdate); 560 561 case C_FERRMSG: 562 message(MT_FERROR, "%s", s); 563 finish(); 564 565 default: 566 error("rmchk: unexpected response '%s'", respbuff); 567 err(); 568 } 569 } 570 /*NOTREACHED*/ 571 } 572 573 /* 574 * Send a directory 575 * 576 * Return < 0 on error. 577 * Return 0 if nothing happened. 578 * Return > 0 if anything is updated. 579 */ 580 static int senddir(rname, opts, stb, user, group, destdir) 581 char *rname; 582 opt_t opts; 583 struct stat *stb; 584 char *user, *group; 585 int destdir; 586 { 587 DIRENTRY *dp; 588 DIR *d; 589 char *optarget, *cp; 590 int len; 591 int didupdate = 0; 592 593 /* 594 * Don't descend into directory 595 */ 596 if (IS_ON(opts, DO_NODESCEND)) 597 return(0); 598 599 if ((d = opendir(target)) == NULL) { 600 error("%s: opendir failed: %s", target, SYSERR); 601 return(-1); 602 } 603 604 /* 605 * Send recvdir command in recvit() format. 606 */ 607 (void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s", 608 opts, stb->st_mode & 07777, user, group, rname); 609 if (response() < 0) 610 return(-1); 611 612 if (IS_ON(opts, DO_REMOVE)) 613 if (rmchk(opts) > 0) 614 ++didupdate; 615 616 optarget = ptarget; 617 len = ptarget - target; 618 while ((dp = readdir(d))) { 619 if (!strcmp(dp->d_name, ".") || 620 !strcmp(dp->d_name, "..")) 621 continue; 622 if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) { 623 error("%s/%s: Name too long", target, 624 dp->d_name); 625 continue; 626 } 627 ptarget = optarget; 628 if (ptarget[-1] != '/') 629 *ptarget++ = '/'; 630 cp = dp->d_name; 631 while ((*ptarget++ = *cp++)) 632 ; 633 ptarget--; 634 if (sendit(dp->d_name, opts, destdir) > 0) 635 didupdate = 1; 636 } 637 (void) closedir(d); 638 639 (void) sendcmd(C_END, NULL); 640 (void) response(); 641 642 ptarget = optarget; 643 *ptarget = CNULL; 644 645 return(didupdate); 646 } 647 648 /* 649 * Send a link 650 */ 651 static int sendlink(rname, opts, stb, user, group, destdir) 652 char *rname; 653 opt_t opts; 654 struct stat *stb; 655 char *user; 656 char *group; 657 int destdir; 658 { 659 int f, n; 660 static char tbuf[BUFSIZ]; 661 char lbuf[MAXPATHLEN]; 662 u_char *s; 663 664 debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir); 665 666 if (stb->st_nlink > 1) { 667 struct linkbuf *lp; 668 669 if ((lp = linkinfo(stb)) != NULL) 670 return(sendhardlink(opts, lp, rname, destdir)); 671 } 672 673 /* 674 * Gather and send basic link info 675 */ 676 (void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s", 677 opts, stb->st_mode & 07777, 678 (long) stb->st_size, 679 stb->st_mtime, stb->st_atime, 680 user, group, rname); 681 if (response() < 0) 682 return(-1); 683 684 /* 685 * Gather and send additional link info 686 */ 687 if ((n = readlink(target, lbuf, sizeof(lbuf)-1)) != -1) 688 lbuf[n] = '\0'; 689 else { 690 error("%s: readlink failed", target); 691 err(); 692 } 693 (void) sprintf(tbuf, "%.*s", (int) stb->st_size, lbuf); 694 (void) sendcmd(C_NONE, "%s\n", tbuf); 695 696 if (n != stb->st_size) { 697 error("%s: file changed size", target); 698 err(); 699 } else 700 ack(); 701 702 /* 703 * Check response 704 */ 705 f = response(); 706 if (f < 0) 707 return(-1); 708 else if (f == 0 && IS_ON(opts, DO_COMPARE)) 709 return(0); 710 711 /* 712 * Read and process responses from server. 713 * The server may send multiple messages regarding 714 * file deletes if the remote target is a directory. 715 */ 716 for (;;) { 717 n = remline(s = respbuff, sizeof(respbuff), TRUE); 718 if (n == -1) /* normal EOF */ 719 return(0); 720 if (n == 0) { 721 error("expected control record"); 722 continue; 723 } 724 725 switch (*s++) { 726 case C_END: /* End of send operation */ 727 *ptarget = CNULL; 728 ack(); 729 runspecial(target, opts, rname, destdir); 730 addcmdspecialfile(target, rname, destdir); 731 return(0); 732 733 case C_LOGMSG: 734 if (n > 0) 735 message(MT_INFO, "%s", s); 736 break; 737 738 case C_NOTEMSG: 739 if (n > 0) 740 message(MT_NOTICE, "%s", s); 741 break; 742 /* Goto top of loop */ 743 744 case C_ERRMSG: 745 message(MT_NERROR, "%s", s); 746 return(-1); 747 748 case C_FERRMSG: 749 message(MT_FERROR, "%s", s); 750 finish(); 751 752 default: 753 error("install link: unexpected response '%s'", 754 respbuff); 755 err(); 756 } 757 } 758 /*NOTREACHED*/ 759 } 760 761 /* 762 * Check to see if file needs to be updated on the remote machine. 763 * Returns: 764 * US_NOTHING - no update 765 * US_NOENT - remote doesn't exist 766 * US_OUTDATE - out of date 767 * US_DOCOMP - comparing binaries to determine if out of date 768 * US_MODE - File modes do not match 769 */ 770 static int update(rname, opts, statp) 771 char *rname; 772 opt_t opts; 773 struct stat *statp; 774 { 775 register off_t size; 776 register time_t mtime; 777 unsigned short lmode; 778 unsigned short rmode; 779 char *owner = NULL, *group = NULL; 780 int done, n; 781 u_char *cp; 782 783 debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp); 784 785 if (IS_ON(opts, DO_NOEXEC)) 786 if (isexec(target, statp)) { 787 debugmsg(DM_MISC, "%s is an executable\n", target); 788 return(US_NOTHING); 789 } 790 791 /* 792 * Check to see if the file exists on the remote machine. 793 */ 794 (void) sendcmd(C_QUERY, "%s", rname); 795 796 for (done = 0; !done;) { 797 n = remline(cp = respbuff, sizeof(respbuff), TRUE); 798 if (n <= 0) { 799 error("update: unexpected control record in response to query"); 800 return(US_NOTHING); 801 } 802 803 switch (*cp++) { 804 case QC_ONNFS: /* Resides on a NFS */ 805 debugmsg(DM_MISC, 806 "update: %s is on a NFS. Skipping...\n", 807 rname); 808 return(US_NOTHING); 809 810 case QC_SYM: /* Is a symbolic link */ 811 debugmsg(DM_MISC, 812 "update: %s is a symlink. Skipping...\n", 813 rname); 814 return(US_NOTHING); 815 816 case QC_ONRO: /* Resides on a Read-Only fs */ 817 debugmsg(DM_MISC, 818 "update: %s is on a RO fs. Skipping...\n", 819 rname); 820 return(US_NOTHING); 821 822 case QC_YES: 823 done = 1; 824 break; 825 826 case QC_NO: /* file doesn't exist so install it */ 827 return(US_NOENT); 828 829 case C_ERRMSG: 830 if (cp) 831 message(MT_NERROR, "%s", cp); 832 return(US_NOTHING); 833 834 case C_FERRMSG: 835 if (cp) 836 message(MT_FERROR, "%s", cp); 837 finish(); 838 839 case C_NOTEMSG: 840 if (cp) 841 message(MT_NOTICE, "%s", cp); 842 break; 843 /* Goto top of loop */ 844 845 default: 846 error("update: unexpected response to query '%s'", cp); 847 return(US_NOTHING); 848 } 849 } 850 851 /* 852 * Target exists, but no other info passed 853 */ 854 if (n <= 1 || !S_ISREG(statp->st_mode)) 855 return(US_OUTDATE); 856 857 if (IS_ON(opts, DO_COMPARE)) 858 return(US_DOCOMP); 859 860 /* 861 * Parse size 862 */ 863 size = strtol(cp, (char **)&cp, 10); 864 if (*cp++ != ' ') { 865 error("update: size not delimited"); 866 return(US_NOTHING); 867 } 868 869 /* 870 * Parse mtime 871 */ 872 mtime = strtol(cp, (char **)&cp, 10); 873 if (*cp++ != ' ') { 874 error("update: mtime not delimited"); 875 return(US_NOTHING); 876 } 877 878 /* 879 * Parse remote file mode 880 */ 881 rmode = strtol(cp, (char **)&cp, 8); 882 if (cp && *cp) 883 ++cp; 884 885 /* 886 * Be backwards compatible 887 */ 888 if (cp && *cp != CNULL) { 889 /* 890 * Parse remote file owner 891 */ 892 owner = strtok((char *)cp, " "); 893 if (owner == NULL) { 894 error("update: owner not delimited"); 895 return(US_NOTHING); 896 } 897 898 /* 899 * Parse remote file group 900 */ 901 group = strtok(NULL, " "); 902 if (group == NULL) { 903 error("update: group not delimited"); 904 return(US_NOTHING); 905 } 906 } 907 908 /* 909 * File needs to be updated? 910 */ 911 lmode = statp->st_mode & 07777; 912 913 debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n", 914 rname, lmode, rmode); 915 debugmsg(DM_MISC, "update(%s,) size %d mtime %d owner '%s' grp '%s'\n", 916 rname, (int) size, mtime, owner, group); 917 918 if (statp->st_mtime != mtime) { 919 if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) { 920 message(MT_WARNING, 921 "%s: Warning: remote copy is newer", 922 target); 923 return(US_NOTHING); 924 } 925 return(US_OUTDATE); 926 } 927 928 /* 929 * If the mode of a file does not match the local mode, the 930 * whole file is updated. This is done both to insure that 931 * a bogus version of the file has not been installed and to 932 * avoid having to handle weird cases of chmod'ing symlinks 933 * and such. 934 */ 935 if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) { 936 debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n", 937 lmode, rmode); 938 return(US_OUTDATE); 939 } 940 941 if (statp->st_size != size) { 942 debugmsg(DM_MISC, "size does not match (%d != %d).\n", 943 (int) statp->st_size, size); 944 return(US_OUTDATE); 945 } 946 947 /* 948 * Check ownership 949 */ 950 if (!IS_ON(opts, DO_NOCHKOWNER) && owner) { 951 if (!IS_ON(opts, DO_NUMCHKOWNER)) { 952 /* Check by string compare */ 953 if (strcmp(owner, getusername(statp->st_uid, 954 target, opts)) != 0) { 955 debugmsg(DM_MISC, 956 "owner does not match (%s != %s).\n", 957 getusername(statp->st_uid, 958 target, opts), owner); 959 return(US_OUTDATE); 960 } 961 } else { 962 /* 963 * Check numerically. 964 * Allow negative numbers. 965 */ 966 while (*owner && !isdigit(*owner) && (*owner != '-')) 967 ++owner; 968 if (owner && atoi(owner) != statp->st_uid) { 969 debugmsg(DM_MISC, 970 "owner does not match (%d != %s).\n", 971 statp->st_uid, owner); 972 return(US_OUTDATE); 973 } 974 } 975 } 976 977 if (!IS_ON(opts, DO_NOCHKGROUP) && group) { 978 if (!IS_ON(opts, DO_NUMCHKGROUP)) { 979 /* Check by string compare */ 980 if (strcmp(group, getgroupname(statp->st_gid, 981 target, opts)) != 0) { 982 debugmsg(DM_MISC, 983 "group does not match (%s != %s).\n", 984 getgroupname(statp->st_gid, 985 target, opts), group); 986 return(US_OUTDATE); 987 } 988 } else { 989 /* Check numerically */ 990 /* Allow negative gid */ 991 while (*group && !isdigit(*group) && (*group != '-')) 992 ++group; 993 if (group && atoi(group) != statp->st_gid) { 994 debugmsg(DM_MISC, 995 "group does not match (%d != %s).\n", 996 statp->st_gid, group); 997 return(US_OUTDATE); 998 } 999 } 1000 } 1001 1002 return(US_NOTHING); 1003 } 1004 1005 /* 1006 * Stat a file 1007 */ 1008 static int dostat(file, statbuf, opts) 1009 char *file; 1010 struct stat *statbuf; 1011 opt_t opts; 1012 { 1013 int s; 1014 1015 if (IS_ON(opts, DO_FOLLOW)) 1016 s = stat(file, statbuf); 1017 else 1018 s = lstat(file, statbuf); 1019 1020 if (s < 0) 1021 error("%s: %s failed: %s", file, 1022 IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR); 1023 return(s); 1024 } 1025 1026 /* 1027 * Transfer the file or directory in target[]. 1028 * rname is the name of the file on the remote host. 1029 * 1030 * Return < 0 on error. 1031 * Return 0 if nothing happened. 1032 * Return > 0 if anything is updated. 1033 */ 1034 static int sendit(rname, opts, destdir) 1035 char *rname; 1036 opt_t opts; 1037 int destdir; 1038 { 1039 static struct stat stb; 1040 extern struct subcmd *subcmds; 1041 char *user, *group; 1042 int u, len; 1043 int didupdate = 0; 1044 1045 /* 1046 * Remove possible accidental newline 1047 */ 1048 len = strlen(rname); 1049 if (len > 0 && rname[len-1] == '\n') 1050 rname[len-1] = CNULL; 1051 1052 if (checkfilename(rname) != 0) 1053 return(-1); 1054 1055 debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts); 1056 1057 if (except(target)) 1058 return(0); 1059 1060 if (dostat(target, &stb, opts) < 0) 1061 return(-1); 1062 1063 /* 1064 * Does rname need updating? 1065 */ 1066 u = update(rname, opts, &stb); 1067 debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n", 1068 rname, opts, target, u); 1069 1070 /* 1071 * Don't need to update the file, but we may need to save hardlink 1072 * info. 1073 */ 1074 if (u == US_NOTHING) { 1075 if (S_ISREG(stb.st_mode) && stb.st_nlink > 1) 1076 (void) linkinfo(&stb); 1077 return(0); 1078 } 1079 1080 /* 1081 * File mode needs changing 1082 */ 1083 if (u == US_MODE) { 1084 if (IS_ON(opts, DO_VERIFY)) { 1085 message(MT_INFO, "%s: need to chmod to %04o", 1086 target, stb.st_mode & 07777); 1087 runspecial(target, opts, rname, destdir); 1088 return(1); 1089 } 1090 message(MT_CHANGE, "%s: chmod to %04o", 1091 target, stb.st_mode & 07777); 1092 (void) sendcmd(C_CHMOD, "%o %04o %s", 1093 opts, stb.st_mode & 07777, rname); 1094 (void) response(); 1095 return(1); 1096 } 1097 1098 user = getusername(stb.st_uid, target, opts); 1099 group = getgroupname(stb.st_gid, target, opts); 1100 1101 /* 1102 * No entry - need to install 1103 */ 1104 if (u == US_NOENT) { 1105 if (IS_ON(opts, DO_VERIFY)) { 1106 message(MT_INFO, "%s: need to install", target); 1107 runspecial(target, opts, rname, destdir); 1108 return(1); 1109 } 1110 if (!IS_ON(opts, DO_QUIET)) 1111 message(MT_CHANGE, "%s: installing", target); 1112 FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE)); 1113 } 1114 1115 /* 1116 * Handle special file types, including directories and symlinks 1117 */ 1118 if (S_ISDIR(stb.st_mode)) { 1119 if (senddir(rname, opts, &stb, user, group, destdir) > 0) 1120 didupdate = 1; 1121 } else if (S_ISLNK(stb.st_mode)) { 1122 if (u != US_NOENT) 1123 FLAG_ON(opts, DO_COMPARE); 1124 /* 1125 * Since we always send link info to the server 1126 * so the server can determine if the remote link 1127 * is correct, we never get any acknowledge meant 1128 * from the server whether the link was really 1129 * updated or not. 1130 */ 1131 (void) sendlink(rname, opts, &stb, user, group, destdir); 1132 } else if (S_ISREG(stb.st_mode)) { 1133 if (u == US_OUTDATE) { 1134 if (IS_ON(opts, DO_VERIFY)) { 1135 message(MT_INFO, "%s: need to update", target); 1136 runspecial(target, opts, rname, destdir); 1137 return(1); 1138 } 1139 if (!IS_ON(opts, DO_QUIET)) 1140 message(MT_CHANGE, "%s: updating", target); 1141 } 1142 if (sendfile(rname, opts, &stb, user, group, destdir) == 0) 1143 didupdate = 1; 1144 } else 1145 error("%s: unknown file type", target); 1146 1147 return(didupdate); 1148 } 1149 1150 /* 1151 * Remove temporary files and do any cleanup operations before exiting. 1152 */ 1153 extern void cleanup() 1154 { 1155 char *file; 1156 #ifdef USE_STATDB 1157 extern char statfile[]; 1158 1159 (void) unlink(statfile); 1160 #endif 1161 1162 if ((file = getnotifyfile())) 1163 (void) unlink(file); 1164 } 1165 1166 /* 1167 * Update the file(s) if they are different. 1168 * destdir = 1 if destination should be a directory 1169 * (i.e., more than one source is being copied to the same destination). 1170 * 1171 * Return < 0 on error. 1172 * Return 0 if nothing updated. 1173 * Return > 0 if something was updated. 1174 */ 1175 extern int install(src, dest, ddir, destdir, opts) 1176 char *src, *dest; 1177 int ddir, destdir; 1178 opt_t opts; 1179 { 1180 static char destcopy[MAXPATHLEN]; 1181 char *rname; 1182 int didupdate = 0; 1183 1184 debugmsg(DM_CALL, 1185 "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n", 1186 (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts); 1187 /* 1188 * Save source name 1189 */ 1190 if (IS_ON(opts, DO_WHOLE)) 1191 source[0] = CNULL; 1192 else 1193 (void) strcpy(source, src); 1194 1195 if (dest == NULL) { 1196 FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */ 1197 dest = src; 1198 } 1199 1200 if (checkfilename(dest) != 0) 1201 return(-1); 1202 1203 if (nflag || debug) { 1204 static char buff[BUFSIZ]; 1205 char *cp; 1206 1207 cp = getondistoptlist(opts); 1208 (void) sprintf(buff, "%s%s%s %s %s", 1209 IS_ON(opts, DO_VERIFY) ? "verify" : "install", 1210 (cp) ? " -o" : "", (cp) ? cp : "", 1211 src, dest); 1212 if (nflag) { 1213 printf("%s\n", buff); 1214 return(0); 1215 } else 1216 debugmsg(DM_MISC, "%s\n", buff); 1217 } 1218 1219 rname = exptilde(target, src); 1220 if (rname == NULL) 1221 return(-1); 1222 ptarget = target; 1223 while (*ptarget) 1224 ptarget++; 1225 /* 1226 * If we are renaming a directory and we want to preserve 1227 * the directory heirarchy (-w), we must strip off the leading 1228 * directory name and preserve the rest. 1229 */ 1230 if (IS_ON(opts, DO_WHOLE)) { 1231 while (*rname == '/') 1232 rname++; 1233 ddir = 1; 1234 destdir = 1; 1235 } else { 1236 rname = strrchr(target, '/'); 1237 /* Check if no '/' or target ends in '/' */ 1238 if (rname == NULL || 1239 rname+1 == NULL || 1240 *(rname+1) == CNULL) 1241 rname = target; 1242 else 1243 rname++; 1244 } 1245 1246 debugmsg(DM_MISC, 1247 "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n", 1248 target, source, rname, dest, destdir, ddir); 1249 1250 /* 1251 * Pass the destination file/directory name to remote. 1252 */ 1253 if (ddir) 1254 (void) sendcmd(C_DIRTARGET, "%o %s", opts, dest); 1255 else 1256 (void) sendcmd(C_TARGET, "%o %s", opts, dest); 1257 if (response() < 0) 1258 return(-1); 1259 1260 /* 1261 * Save the name of the remote target destination if we are 1262 * in WHOLE mode (destdir > 0) or if the source and destination 1263 * are not the same. This info will be used later for maintaining 1264 * hardlink info. 1265 */ 1266 if (destdir || (src && dest && strcmp(src, dest))) { 1267 (void) strcpy(destcopy, dest); 1268 Tdest = destcopy; 1269 } 1270 1271 didupdate = sendit(rname, opts, destdir); 1272 Tdest = 0; 1273 1274 return(didupdate); 1275 } 1276