1 /* synctree 4.16 - Synchronise file tree. Author: Kees J. Bot 2 * 5 Apr 1989 3 * SYNOPSYS 4 * synctree [-iuf] [[user1@machine1:]dir1 [[user2@]machine2:]dir2 5 * 6 * Dir2 will then be synchronized to dir1 with respect to the flags. 7 * The flags mean: 8 * -i Be interactive on files other than directories too. 9 * -u Only install files that are newer, i.e. that need an update. 10 * -f Force. Don't ask for confirmation, all answers are 'yes'. 11 * 12 * Hitting return lets synctree use its proposed answer. Hitting CTRL-D is 13 * like typing return to all questions that follow. 14 * 15 * If either of the directories to be synchronized contains the file ".backup" 16 * then it is a backup directory. The file ".backup" in this directory is 17 * an array of mode information indexed on inode number. 18 * 19 * 89/04/05, Kees J. Bot - Birth of tree synchronizing program. 20 * 92/02/02 - General overhaul, rcp(1) like syntax. 21 */ 22 23 #define nil 0 24 #include <sys/types.h> 25 #include <stddef.h> 26 #include <stdio.h> 27 #include <sys/stat.h> 28 #include <utime.h> 29 #include <string.h> 30 #include <signal.h> 31 #include <dirent.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <time.h> 37 #include <sys/wait.h> 38 39 #define USE_SHADOWING 0 40 41 #ifndef PATH_MAX 42 43 #define PATH_MAX 1024 44 #endif 45 46 #ifndef S_ISLNK 47 /* There were no symlinks in medieval times. */ 48 #define S_ISLNK(mode) (0) 49 #define lstat stat 50 #define symlink(path1, path2) (errno= ENOSYS, -1) 51 #define readlink(path, buf, len) (errno= ENOSYS, -1) 52 #endif 53 54 #define NUMBYTES 4 /* Any number fits in this many bytes. */ 55 #define CHUNK 4096 /* Transfer files in this size chunks. */ 56 57 static int install= 0; /* Install files, do not delete, update if newer. */ 58 static int interact= 0; /* Ask permission to install too. */ 59 static int force= 0; /* Force trees to be completely equal. */ 60 static int backup= 0; /* This tree is for backup. */ 61 62 static char SYNCNAME[] = "synctree"; 63 static char SLAVENAME[] = "==SLAVE=="; 64 static char MASTERNAME[]= "==MASTER=="; 65 66 67 static char BACKUP[] = ".backup"; /* Backup filemodes. */ 68 static int filemodes; /* Filemodes fildes. */ 69 70 static int chan[2]= { 0, 1 }; /* In and output channel to opposite side. */ 71 72 #define BUCKSIZE (1+NUMBYTES+CHUNK) 73 static char bucket[BUCKSIZE]; /* Put a lot of things here before sending. */ 74 static char *buckp= bucket; /* Fill pointer. */ 75 static int buckn= 0; /* # bytes in bucket. */ 76 77 enum orders { /* What back breaking labour should the slave perform? */ 78 ENTER= 128, /* Make ready to process contents of directory. */ 79 ADVANCE, /* Determine next pathname and report it back. */ 80 CAT, /* Send contents of file. */ 81 MORE, /* Send more file contents. */ 82 ORDER_CANCEL, /* Current pathname is not installed, remove as link. */ 83 DIE, /* Die with exit(0); */ 84 DIE_BAD, /* exit(1); */ 85 POSITIVE, /* Ask a yes/no question expecting yes. */ 86 NEGATIVE, /* Same expecting no. */ 87 PASS_YES, /* Pass this to the master will you. */ 88 PASS_NO /* Same here. */ 89 }; 90 91 #ifdef DEBUG 92 char *ORDERS[]= { 93 "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD", 94 "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO" 95 }; 96 #endif 97 98 enum answers { 99 PATH= 128, /* Report pathname, and stat(2) info. */ 100 LINK, /* Report linkname, pathname, and stat(2) info. */ 101 DATA, /* Contents of file follow. */ 102 NODATA, /* Can't read file. */ 103 DONE, /* Nothing more to advance to. */ 104 SYMLINK, /* Report symlinkname, pathname, and stat(2) info. */ 105 YES, NO /* Result of an ASK. */ 106 }; 107 108 #ifdef DEBUG 109 char *ANSWERS[]= { 110 "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO" 111 }; 112 113 #define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg) 114 #else 115 #define DPRINTF(format, arg) 116 #endif 117 118 struct mode { 119 unsigned short md_mode; 120 unsigned short md_uid; 121 unsigned short md_gid; 122 unsigned short md_rdev; 123 unsigned short md_devsiz; 124 }; 125 126 static char *arg0; /* basename(argv[0]) */ 127 static int ex= 0; /* exit status. */ 128 129 static void because() 130 { 131 fprintf(stderr, ": %s\n", strerror(errno)); 132 ex= 1; 133 } 134 135 static void perr(char *label) 136 { 137 fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno)); 138 ex= 1; 139 } 140 141 static void perrx(char *label) 142 { 143 perr(label); 144 exit(1); 145 } 146 147 #if S_HIDDEN 148 /* Support for per achitecture hidden files. */ 149 static int transparent= 0; 150 151 static void isvisible(char *name) 152 { 153 char *p= name + strlen(name); 154 155 while (p > name && *--p == '/') {} 156 157 if (p > name && *p == '@' && p[-1] != '/') transparent= 1; 158 } 159 #else 160 #define transparent 0 161 #define isvisible(name) ((void) 0) 162 #endif 163 164 static void isbackup(int slave) 165 { 166 if ((filemodes= open(BACKUP, slave ? O_RDONLY : O_RDWR)) >= 0) 167 backup= 1; 168 else { 169 if (errno != ENOENT) perrx(BACKUP); 170 } 171 } 172 173 static char path[PATH_MAX+1]; /* Holds pathname of file being worked on. */ 174 static char lnkpth[PATH_MAX+1]; /* (Sym)link to path. */ 175 static char *linkpath; /* What path is, or should be linked to. */ 176 static struct stat st; /* Corresponding stat(2) info. */ 177 static char Spath[PATH_MAX+1]; /* Slave is looking at this. */ 178 static char Slnkpth[PATH_MAX+1];/* (Sym)link to Spath. */ 179 static char *Slinkpath=nil; /* Either nil or Slnkpth. */ 180 static struct stat Sst; /* Slave's stat(2). */ 181 182 static char *addpath(char *p, char *n) 183 /* Add a name to the path, return pointer to end. */ 184 { 185 if (p - path + 1 + strlen(n) > PATH_MAX) { 186 *p= 0; 187 fprintf(stderr, "%s: %s/%s is too long.\n", arg0, path, n); 188 fprintf(stderr, "%s: Unable to continue.\n", arg0); 189 exit(1); 190 } 191 if (p == path+1 && path[0] == '.') p= path; 192 193 if (p > path) *p++ = '/'; 194 195 while (*n != 0) *p++ = *n++; 196 *p= 0; 197 return p; 198 } 199 200 struct entry { /* A directory entry. */ 201 struct entry *next; /* Next entry in same directory */ 202 struct entry *dir; /* It is part of this directory */ 203 struct entry *con; /* If a dir, its contents */ 204 char *name; /* Name of this dir entry */ 205 }; 206 207 static struct entry *E= nil; /* File being processed. */ 208 209 static void setpath(struct entry *e) 210 /* Set path leading to e. */ 211 { 212 static char *pend; 213 214 if (e == nil) 215 pend= path; 216 else { 217 setpath(e->dir); 218 pend= addpath(pend, e->name); 219 } 220 } 221 222 static void sort(struct entry **ae) 223 /* This is either a stable mergesort, or thermal noise, I'm no longer sure. 224 * It must be called like this: if (L!=nil && L->next!=nil) sort(&L); 225 */ 226 { 227 /* static */ struct entry *e1, **mid; /* Need not be local */ 228 struct entry *e2; 229 230 e1= *(mid= &(*ae)->next); 231 do { 232 if ((e1= e1->next) == nil) break; 233 mid= &(*mid)->next; 234 } while ((e1= e1->next) != nil); 235 236 e2= *mid; 237 *mid= nil; 238 239 if ((*ae)->next != nil) sort(ae); 240 if (e2->next != nil) sort(&e2); 241 242 e1= *ae; 243 for (;;) { 244 if (strcmp(e1->name, e2->name)<=0) { 245 if ((e1= *(ae= &e1->next)) == nil) { 246 *ae= e2; 247 break; 248 } 249 } else { 250 *ae= e2; 251 e2= *(ae= &e2->next); 252 *ae= e1; 253 if (e2 == nil) break; 254 } 255 } 256 } 257 258 static void enter() 259 /* Collect directory entries of E. */ 260 { 261 struct entry **last= &E->con, *new; 262 struct dirent *e; 263 DIR *d; 264 265 if ((d= opendir(path)) == nil) { 266 fprintf(stderr, "%s: Can't read dir %s\n", arg0, path); 267 return; 268 } 269 270 while ((e= readdir(d)) != nil) { 271 if (e->d_name[0] == '.' && (e->d_name[1] == 0 272 || (e->d_name[1] == '.' && e->d_name[2] == 0) 273 )) continue; 274 275 new= (struct entry *) malloc(sizeof(*new)); 276 277 new->next= nil; 278 new->dir= E; 279 new->con= nil; 280 new->name= (char *) malloc(strlen(e->d_name) + 1); 281 strcpy(new->name, e->d_name); 282 *last= new; 283 last= &new->next; 284 } 285 closedir(d); 286 if (E->con != nil && E->con->next != nil) sort(&E->con); 287 } 288 289 #define arraysize(a) (sizeof(a) / sizeof((a)[0])) 290 #define arraylimit(a) ((a) + arraysize(a)) 291 292 static char *link_islink(struct stat *stp, const char *file) 293 { 294 /* Tell if a file, which stat(2) information in '*stp', has been seen 295 * earlier by this function under a different name. If not return a 296 * null pointer with errno set to ENOENT, otherwise return the name of 297 * the link. Return a null pointer with an error code in errno for any 298 * error, using E2BIG for a too long file name. 299 * 300 * Use link_islink(nil, nil) to reset all bookkeeping. 301 * 302 * Call for a file twice to delete it from the store. 303 */ 304 305 typedef struct link { /* In-memory link store. */ 306 struct link *next; /* Hash chain on inode number. */ 307 ino_t ino; /* File's inode number. */ 308 off_t off; /* Offset to more info in temp file. */ 309 } link_t; 310 typedef struct dlink { /* On-disk link store. */ 311 dev_t dev; /* Device number. */ 312 char file[PATH_MAX]; /* Name of earlier seen link. */ 313 } dlink_t; 314 static link_t *links[256]; /* Hash list of known links. */ 315 static int tfd= -1; /* Temp file for file name storage. */ 316 static dlink_t dlink; 317 link_t *lp, **plp; 318 size_t len; 319 off_t off; 320 321 if (file == nil) { 322 /* Reset everything. */ 323 for (plp= links; plp < arraylimit(links); plp++) { 324 while ((lp= *plp) != nil) { 325 *plp= lp->next; 326 free(lp); 327 } 328 } 329 if (tfd != -1) close(tfd); 330 tfd= -1; 331 return nil; 332 } 333 334 /* The file must be a non-directory with more than one link. */ 335 if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) { 336 errno= ENOENT; 337 return nil; 338 } 339 340 plp= &links[stp->st_ino % arraysize(links)]; 341 342 while ((lp= *plp) != nil) { 343 if (lp->ino == stp->st_ino) { 344 /* May have seen this link before. Get it and check. */ 345 if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil; 346 if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil; 347 348 /* Only need to check the device number. */ 349 if (dlink.dev == stp->st_dev) { 350 if (strcmp(file, dlink.file) == 0) { 351 /* Called twice. Forget about this link. */ 352 *plp= lp->next; 353 free(lp); 354 errno= ENOENT; 355 return nil; 356 } 357 358 /* Return the name of the earlier link. */ 359 return dlink.file; 360 } 361 } 362 plp= &lp->next; 363 } 364 365 /* First time I see this link. Add it to the store. */ 366 if (tfd == -1) { 367 for (;;) { 368 char *tmp; 369 370 tmp= tmpnam(nil); 371 tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600); 372 if (tfd < 0) { 373 if (errno != EEXIST) return nil; 374 } else { 375 (void) unlink(tmp); 376 break; 377 } 378 } 379 } 380 if ((len= strlen(file)) >= PATH_MAX) { 381 errno= E2BIG; 382 return nil; 383 } 384 385 dlink.dev= stp->st_dev; 386 strcpy(dlink.file, file); 387 len += offsetof(dlink_t, file) + 1; 388 if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil; 389 if (write(tfd, &dlink, len) != len) return nil; 390 391 if ((lp= malloc(sizeof(*lp))) == nil) return nil; 392 lp->next= nil; 393 lp->ino= stp->st_ino; 394 lp->off= off; 395 *plp= lp; 396 errno= ENOENT; 397 return nil; 398 } 399 400 #define cancellink() ((void) islink()) 401 402 static char *islink() 403 /* Returns the name of the file path is linked to. If no such link can be 404 * found, then path is added to the list and nil is returned. If all the 405 * links of a file have been seen, then it is removed from the list. 406 * Directories are not seen as linkable. 407 */ 408 { 409 char *name; 410 411 name= link_islink(&st, path); 412 if (name == nil && errno != ENOENT) perrx(path); 413 return name; 414 } 415 416 static void setstat(ino_t ino, struct stat *stp) 417 /* Set backup status info, we know that backup is true. */ 418 { 419 struct mode md; 420 421 md.md_mode = stp->st_mode; 422 md.md_uid = stp->st_uid; 423 md.md_gid = stp->st_gid; 424 md.md_rdev = stp->st_rdev; 425 md.md_devsiz = stp->st_size / 1024; 426 427 if (lseek(filemodes, (off_t) sizeof(md) * (off_t) ino, 0) == -1 428 || write(filemodes, (char *) &md, sizeof(md)) != sizeof(md) 429 ) perrx(BACKUP); 430 } 431 432 static int getstat(char *name, struct stat *stp) 433 /* Get status information of file name, skipping some files. Backup info 434 * is inserted as needed. 435 */ 436 { 437 errno= 0; 438 439 if (strcmp(name, BACKUP) == 0) return -1; 440 441 if (lstat(name, stp) < 0) return -1; 442 443 if (stp->st_mode == S_IFREG && stp->st_mtime == 0) return -1; 444 445 if (backup) { 446 struct mode md; 447 448 if (lseek(filemodes, 449 (off_t) sizeof(md) * (off_t) stp->st_ino, 0) == -1 450 || read(filemodes, (char *) &md, sizeof(md)) != sizeof(md) 451 || md.md_mode == 0 452 ) { 453 errno= 0; 454 setstat(stp->st_ino, stp); 455 } else { 456 stp->st_mode = md.md_mode; 457 stp->st_uid = md.md_uid; 458 stp->st_gid = md.md_gid; 459 stp->st_rdev = md.md_rdev; 460 if (S_ISBLK(stp->st_mode)) 461 stp->st_size= (off_t) md.md_devsiz * 1024; 462 } 463 } 464 return 0; 465 } 466 467 static int advance() 468 /* Determine next pathname, return true on success. */ 469 { 470 for (;;) { 471 if (E==nil) { /* First call, enter root dir. */ 472 E= (struct entry *) malloc(sizeof(*E)); 473 E->dir= nil; 474 E->con= nil; 475 E->next= nil; 476 E->name= (char *) malloc(3); 477 strcpy(E->name, transparent ? ".@" : "."); 478 } else 479 if (E->con != nil) /* Dir's files must be processed. */ 480 E= E->con; 481 else { 482 for (;;) { 483 /* Remove E from it's parents list, then 484 * try next entry, if none, go to parent dir. 485 */ 486 struct entry *junk= E, *parent= E->dir; 487 488 if (parent != nil) parent->con= E->next; 489 E= E->next; 490 free(junk->name); 491 free(junk); 492 493 if (E != nil) break; 494 495 if ((E= parent) == nil) return 0; 496 } 497 } 498 setpath(E); 499 if (getstat(path, &st) == 0) { 500 if (S_ISLNK(st.st_mode)) { 501 int n; 502 503 if ((n= readlink(path, lnkpth, PATH_MAX)) >= 0) 504 { 505 lnkpth[n]= 0; 506 break; 507 } 508 } else { 509 break; 510 } 511 } 512 if (errno != 0 && errno != ENOENT) perr(path); 513 } 514 515 linkpath= islink(); 516 DPRINTF("%s: path = %s\n", path); 517 return 1; 518 } 519 520 static enum orders request() 521 /* Slave reads command sent by master. */ 522 { 523 static char buf[64], *bp; 524 static int n= 0; 525 int req; 526 527 for (;;) { 528 if (n == 0) { 529 if ((n= read(chan[0], buf, (int) sizeof(buf))) <= 0) { 530 if (n < 0) perrx("request()"); 531 /* Master died, try to report it then follow. */ 532 fprintf(stderr, 533 "%s: Master died prematurely.\n", arg0); 534 exit(1); 535 } 536 bp= buf; 537 } 538 req= *bp++ & 0xFF; 539 n--; 540 if (req >= (int) ENTER) break; 541 542 /* Master using slave to print to stdout: */ 543 putchar(req); 544 } 545 DPRINTF("%s: request() == %s\n", ORDERS[req - (int) ENTER]); 546 547 return (enum orders) req; 548 } 549 550 static void report() 551 { 552 int r; 553 554 DPRINTF("%s: reporting now!\n", 0); 555 556 buckp= bucket; 557 558 while (buckn > 0) { 559 r = buckn; 560 if (r > (512 << sizeof(char*))) r= (512 << sizeof(char*)); 561 562 if ((r= write(chan[1], buckp, r)) < 0) perrx("report()"); 563 564 buckp += r; 565 buckn -= r; 566 } 567 buckp= bucket; 568 buckn= 0; 569 } 570 571 static void inform(enum answers a) 572 /* Slave replies to master. */ 573 { 574 DPRINTF("%s: inform(%s)\n", ANSWERS[(int) a - (int) PATH]); 575 576 *buckp++ = (int) a; 577 buckn++; 578 } 579 580 #define wwrite(buf, n) (memcpy(buckp, (buf), (n)), buckp+= (n), buckn+= (n)) 581 582 static void sendnum(long n) 583 /* Send number from least to most significant byte. */ 584 { 585 #if BYTE_ORDER == LITTLE_ENDIAN 586 wwrite((char *) &n, sizeof(n)); 587 #else 588 char buf[NUMBYTES]; 589 590 buf[0]= (char) (n >> 0); 591 buf[1]= (char) (n >> 8); 592 buf[2]= (char) (n >> 16); 593 buf[3]= (char) (n >> 24); 594 wwrite(buf, sizeof(buf)); 595 #endif 596 } 597 598 static void send(char *buf, int n) 599 /* Slave sends size and contents of buf. */ 600 { 601 sendnum((long) n); 602 if (n > 0) wwrite(buf, (size_t) n); 603 } 604 605 static void sendstat(struct stat *stp) 606 { 607 sendnum((long) stp->st_mode); 608 sendnum((long) stp->st_uid); 609 sendnum((long) stp->st_gid); 610 sendnum((long) stp->st_rdev); 611 sendnum((long) stp->st_size); 612 sendnum((long) stp->st_mtime); 613 } 614 615 static int ask(); 616 617 static void slave() 618 /* Carry out orders from the master, such as transmitting path names. 619 * Note that the slave uses path, not Spath, the master uses Spath. 620 */ 621 { 622 int f, n; 623 char buf[CHUNK]; 624 enum { run, done, die } state= run; 625 626 do { 627 switch (request()) { 628 case ENTER: 629 enter(); 630 break; 631 case ADVANCE: 632 if (!advance() || state == done) { 633 inform(DONE); 634 state= done; 635 } else { 636 if (linkpath!=nil) { 637 inform(LINK); 638 send(linkpath, strlen(linkpath) + 1); 639 } else 640 if (S_ISLNK(st.st_mode)) { 641 inform(SYMLINK); 642 send(lnkpth, strlen(lnkpth) + 1); 643 } else { 644 inform(PATH); 645 } 646 send(path, strlen(path) + 1); 647 sendstat(&st); 648 } 649 break; 650 case CAT: 651 if ((f= open(path, O_RDONLY))<0) { 652 fprintf(stderr, "%s: Can't open %s", 653 arg0, path); 654 because(); 655 inform(NODATA); 656 break; 657 } 658 inform(DATA); 659 do { 660 n= read(f, buf, sizeof(buf)); 661 if (n < 0) perr(path); 662 send(buf, n); 663 if (n > 0) report(); 664 } while (n > 0); 665 close(f); 666 break; 667 case ORDER_CANCEL: 668 cancellink(); 669 break; 670 case DIE_BAD: 671 ex= 1; 672 /*FALL THROUGH*/ 673 case DIE: 674 state= die; 675 break; 676 case POSITIVE: 677 inform(ask('y') ? YES : NO); 678 break; 679 case NEGATIVE: 680 inform(ask('n') ? YES : NO); 681 break; 682 case PASS_YES: 683 inform(YES); 684 break; 685 case PASS_NO: 686 inform(NO); 687 break; 688 default: 689 fprintf(stderr, "%s: strange request\n", arg0); 690 exit(1); 691 } 692 report(); 693 } while (state != die); 694 } 695 696 static int execute(char **argv) 697 /* Execute a command and return its success or failure. */ 698 { 699 int pid, r, status; 700 701 if ((pid= fork())<0) { 702 perr("fork()"); 703 return 0; 704 } 705 if (pid == 0) { 706 execvp(argv[0], argv); 707 perrx(argv[0]); 708 } 709 while ((r= wait(&status)) != pid) { 710 if (r < 0) { 711 perr(argv[0]); 712 return 0; 713 } 714 } 715 return status == 0; 716 } 717 718 static int removedir(char *dir) 719 /* Remove a directory and its contents. */ 720 { 721 static char *argv[] = { "rm", "-r", nil, nil }; 722 723 printf("(rm -r %s)\n", dir); 724 725 argv[2]= dir; 726 return execute(argv); 727 } 728 729 static void order(enum orders o) 730 /* Master tells slave what to do. */ 731 { 732 char c= (char) o; 733 734 DPRINTF("%s: order(%s)\n", ORDERS[o - (int) ENTER]); 735 736 if (write(chan[1], &c, 1) != 1) perrx("order()"); 737 } 738 739 static void rread(char *buf, int n) 740 /* Master gets buf of size n from slave, doing multiple reads if needed. */ 741 { 742 int r; 743 744 while (n > 0) { 745 if (buckn == 0) { 746 switch (buckn= read(chan[0], bucket, BUCKSIZE)) { 747 case -1: 748 perrx("reply channel from slave"); 749 case 0: 750 fprintf(stderr, 751 "%s: slave died prematurely.\n", 752 arg0); 753 exit(1); 754 } 755 buckp= bucket; 756 } 757 r= n < buckn ? n : buckn; 758 memcpy(buf, buckp, r); 759 buckp+= r; 760 buckn-= r; 761 buf+= r; 762 n-= r; 763 } 764 } 765 766 static enum answers answer() 767 /* Master reads slave's reply. */ 768 { 769 char c; 770 int a; 771 772 rread(&c, 1); 773 a= c & 0xFF; 774 775 DPRINTF("%s: answer() == %s\n", ANSWERS[a - (int) PATH]); 776 777 return (enum answers) a; 778 } 779 780 static long recnum() 781 /* Read number as pack of bytes from least to most significant. The data 782 * is on the wire in little-endian format. (Mostly run on PC's). 783 */ 784 { 785 #if BYTE_ORDER == LITTLE_ENDIAN 786 long n; 787 788 rread((char *) &n, (int) sizeof(n)); 789 return n; 790 #else 791 unsigned char buf[NUMBYTES]; 792 793 rread(buf, sizeof(buf)); 794 return buf[0] 795 | ((unsigned) buf[1] << 8) 796 | ((unsigned long) buf[2] << 16) 797 | ((unsigned long) buf[3] << 24); 798 #endif 799 } 800 801 static int receive(char *buf, int max) 802 /* Master get's data from slave, by first reading size, then data. */ 803 { 804 int n; 805 806 n= recnum(); 807 if (n > max) { 808 fprintf(stderr, "%s: panic: Can't read %d bytes\n", arg0, n); 809 exit(1); 810 } 811 if (n > 0) rread(buf, n); 812 return n; 813 } 814 815 static void recstat(struct stat *stp) 816 { 817 stp->st_mode= recnum(); 818 stp->st_uid= recnum(); 819 stp->st_gid= recnum(); 820 stp->st_rdev= recnum(); 821 stp->st_size= recnum(); 822 stp->st_mtime= recnum(); 823 } 824 825 static int key() 826 { 827 int c; 828 static int tty= -1; 829 830 if (tty < 0) tty= isatty(0); 831 832 if (feof(stdin) || (c= getchar()) == EOF) { 833 c= '\n'; 834 if (tty) putchar('\n'); 835 } 836 837 if (!tty) putchar(c); 838 839 return c; 840 } 841 842 static int ask(int def) 843 /* Ask for a yes or no, anything else means choose def. */ 844 { 845 int y, c; 846 847 if (chan[0] == 0) { 848 /* I'm running remote, tell the slave to ask. */ 849 fflush(stdout); 850 order(def == 'y' ? POSITIVE : NEGATIVE); 851 return answer() == YES; 852 } 853 854 printf("? (%c) ", def); 855 fflush(stdout); 856 857 do c= key(); while (c == ' ' || c == '\t'); 858 859 y= c; 860 861 while (c != '\n') c= key(); 862 863 if (y != 'y' && y != 'Y' && y != 'n' && y != 'N') y= def; 864 865 return y == 'y' || y == 'Y'; 866 } 867 868 static void setmodes(int silent) 869 { 870 struct stat st; 871 int change= 0; 872 struct utimbuf tms; 873 874 errno= 0; 875 getstat(Spath, &st); 876 if (backup && silent) { 877 setstat(st.st_ino, &Sst); 878 getstat(Spath, &st); 879 } 880 881 if (S_ISLNK(st.st_mode)) return; 882 883 if (errno == 0 && st.st_mode != Sst.st_mode) { 884 if (!backup) chmod(Spath, Sst.st_mode & 07777); 885 change= 1; 886 } 887 if (errno == 0 888 && (st.st_uid != Sst.st_uid || st.st_gid != Sst.st_gid) 889 && (backup || geteuid() == 0) 890 ) { 891 errno= 0; 892 if (!backup) chown(Spath, Sst.st_uid, Sst.st_gid); 893 change= 1; 894 } 895 896 if (backup && !silent) setstat(st.st_ino, &Sst); 897 898 if (errno == 0 && S_ISREG(Sst.st_mode) && st.st_mtime != Sst.st_mtime) { 899 time(&tms.actime); 900 tms.modtime= Sst.st_mtime; 901 errno= 0; 902 utime(Spath, &tms); 903 change= 1; 904 } 905 if (errno != 0) { 906 fprintf(stderr, "%s: Can't set modes of %s", arg0, Spath); 907 because(); 908 } else 909 if (change && !silent) { 910 printf("Mode changed of %s\n", Spath); 911 } 912 } 913 914 static void makeold() 915 { 916 static struct utimbuf tms= { 0, 0 }; 917 918 if (utime(Spath, &tms) < 0) { 919 if (errno != ENOENT) { 920 fprintf(stderr, 921 "%s: can't make %s look old", arg0, Spath); 922 because(); 923 } 924 } else { 925 fprintf(stderr, "%s: made %s look old.\n", arg0, Spath); 926 } 927 } 928 929 static int busy= 0; 930 931 static void bail_out(int sig) 932 { 933 signal(sig, SIG_IGN); 934 935 fprintf(stderr, "%s: Exiting after signal %d\n", arg0, sig); 936 937 if (busy) { 938 fprintf(stderr, "%s: was installing %s\n", arg0, Spath); 939 makeold(); 940 } 941 order(DIE_BAD); 942 943 exit(sig); 944 } 945 946 static int makenode(char *name, int mode, dev_t addr, off_t size) 947 { 948 int r; 949 950 if (!backup) { 951 r= mknod(name, mode, addr); 952 } else { 953 if ((r= creat(name, 0644)) >= 0) close(r); 954 } 955 return r; 956 } 957 958 static void add(int update) 959 /* Add Spath to the filesystem. */ 960 { 961 int f, n; 962 char buf[CHUNK]; 963 int forced_update= force && update; 964 965 if (Slinkpath != nil && !S_ISLNK(Sst.st_mode)) { 966 if (interact && !update) { 967 printf("Link %s to %s", Spath, Slinkpath); 968 if (!ask('n')) return; 969 } 970 if (link(Slinkpath, Spath) >= 0) { 971 printf("Linked %s to %s\n", Spath, Slinkpath); 972 return; 973 } else { 974 fprintf(stderr, 975 "%s: Can't link %s to %s", 976 arg0, Slinkpath, Spath); 977 because(); 978 /* Try to install instead. */ 979 } 980 } 981 switch (Sst.st_mode & S_IFMT) { 982 case S_IFDIR: 983 if (!force) { 984 printf("Add dir %s", Spath); 985 if (!ask('n')) return; 986 } 987 if (mkdir(Spath, backup ? 0755 : Sst.st_mode) < 0) { 988 perr(Spath); 989 return; 990 } 991 printf("Directory %s created.\n", Spath); 992 order(ENTER); 993 break; 994 case S_IFBLK: 995 case S_IFCHR: 996 case S_IFIFO: 997 if (interact && !update) { 998 printf("Create special file %s", Spath); 999 if (!ask('n')) { order(ORDER_CANCEL); return; } 1000 } 1001 if (makenode(Spath, Sst.st_mode, Sst.st_rdev, Sst.st_size)<0) { 1002 fprintf(stderr, 1003 "%s: Can't create special file %s", 1004 arg0, Spath); 1005 because(); 1006 return; 1007 } 1008 printf("Special file %s created\n", Spath); 1009 break; 1010 #ifdef S_IFLNK 1011 case S_IFLNK: 1012 if (interact && !update) { 1013 printf("Install %s -> %s", Spath, Slnkpth); 1014 if (!ask('n')) { order(ORDER_CANCEL); return; } 1015 } 1016 if (symlink(Slnkpth, Spath) < 0) { 1017 fprintf(stderr, "%s: Can't create symlink %s", 1018 arg0, Spath); 1019 because(); 1020 return; 1021 } 1022 printf("%s %s -> %s\n", 1023 forced_update ? "Updated: " : "Installed:", 1024 Spath, Slnkpth); 1025 break; 1026 #endif 1027 case S_IFREG: 1028 if (interact && !update) { 1029 printf("Install %s", Spath); 1030 if (!ask('n')) { order(ORDER_CANCEL); return; } 1031 } 1032 order(CAT); 1033 if (answer() != DATA) return; 1034 1035 busy= 1; 1036 if ((f= creat(Spath, backup ? 0644 : Sst.st_mode&07777)) < 0) { 1037 busy= 0; 1038 fprintf(stderr, "%s: Can't create %s", arg0, Spath); 1039 because(); 1040 } 1041 1042 while ((n= receive(buf, sizeof(buf)))>0) { 1043 if (f >= 0 && write(f, buf, n) != n) { 1044 fprintf(stderr, "%s: Write error on %s", 1045 arg0, Spath); 1046 because(); 1047 close(f); f= -1; 1048 } 1049 } 1050 if (n < 0) { 1051 fprintf(stderr, "%s: Slave read err on %s\n", 1052 arg0, Spath); 1053 } 1054 if (f >= 0) close(f); 1055 if (n < 0 || f < 0) { makeold(); busy= 0; return; } 1056 busy= 0; 1057 printf("%s %s\n", 1058 forced_update ? 1059 Sst.st_mtime < st.st_mtime ? "Restored: " : 1060 "Updated: " : 1061 "Installed:", 1062 Spath 1063 ); 1064 break; 1065 default: 1066 fprintf(stderr, 1067 "%s: Won't add file with strange mode %05o: %s\n", 1068 arg0, Sst.st_mode, Spath); 1069 order(ORDER_CANCEL); 1070 return; 1071 } 1072 setmodes(1); 1073 } 1074 1075 static int delete(int update) 1076 /* Delete path. */ 1077 { 1078 int forced_update= force && update; 1079 1080 if (S_ISDIR(st.st_mode)) { 1081 if (install) return 0; 1082 if (!force) { 1083 printf("Delete dir %s", path); 1084 if (!ask('n')) return 0; 1085 } 1086 if (!removedir(path)) { ex= 1; return 0; } 1087 if (!forced_update) printf("Directory %s deleted.\n", path); 1088 return 1; 1089 } 1090 1091 if (install && !update) return 0; 1092 1093 if (!force) { 1094 printf("Delete %s", path); 1095 if (!ask((interact && !update) ? 'n' : 'y')) return 0; 1096 } 1097 1098 if (unlink(path)<0) { 1099 fprintf(stderr, "Can't delete %s", path); 1100 because(); 1101 return 0; 1102 } 1103 cancellink(); 1104 if (!forced_update) printf("Deleted: %s\n", path); 1105 return 1; 1106 } 1107 1108 static int different() 1109 /* Return true iff path and Spath are different. */ 1110 { 1111 if (! ( (linkpath == nil && Slinkpath == nil) 1112 || (linkpath != nil && Slinkpath != nil 1113 && strcmp(linkpath, Slinkpath) == 0) 1114 )) { 1115 linkpath= Slinkpath; 1116 return 1; 1117 } 1118 1119 if ((st.st_mode & S_IFMT) != (Sst.st_mode & S_IFMT)) return 1; 1120 1121 switch (st.st_mode & S_IFMT) { 1122 case S_IFDIR: 1123 return 0; 1124 case S_IFBLK: 1125 case S_IFCHR: 1126 return st.st_rdev != Sst.st_rdev; 1127 case S_IFREG: 1128 if (install) return Sst.st_mtime > st.st_mtime; 1129 return st.st_size != Sst.st_size 1130 || st.st_mtime != Sst.st_mtime; 1131 case S_IFIFO: return 0; 1132 #ifdef S_IFLNK 1133 case S_IFLNK: return strcmp(lnkpth, Slnkpth) != 0; 1134 #endif 1135 default: return 1; 1136 } 1137 } 1138 1139 static void compare() 1140 /* See if path and Spath are same. */ 1141 { 1142 if (different()) { 1143 if (!force) { 1144 printf("%sing %s (delete + add)\n", 1145 Sst.st_mtime < st.st_mtime ? "Restor" : "Updat", 1146 path); 1147 } 1148 if (delete(1)) add(1); 1149 } else { 1150 if (!install) setmodes(0); 1151 1152 if (S_ISDIR(st.st_mode)) { 1153 order(ENTER); 1154 enter(); 1155 } 1156 } 1157 } 1158 1159 static int done= 0, Sdone= 0; 1160 1161 static enum action { ADD, COMPARE, DELETE } action() 1162 /* Look at path's of master and slave, compare them alphabetically to see 1163 * who is ahead of who, then tell what is to be done. 1164 */ 1165 { 1166 int c; 1167 char *Sp, *p; 1168 1169 if (done) return ADD; /* Slave still has names. */ 1170 if (Sdone) return DELETE; /* Master has too many names. */ 1171 1172 /* Compare paths. Let "a/a" come before "a.a". */ 1173 Sp= Spath; 1174 p= path; 1175 while (*Sp == *p && *Sp != 0) { Sp++; p++; } 1176 if (*Sp == '/') return ADD; 1177 if (*p == '/') return DELETE; 1178 return (c= strcmp(Sp, p)) == 0 ? COMPARE : c < 0 ? ADD : DELETE; 1179 } 1180 1181 static void master() 1182 /* Synchronise file tree to that of its slave. */ 1183 { 1184 enum action a= COMPARE; /* Trick first advances. */ 1185 1186 umask(backup ? 0022 : 0000); 1187 1188 signal(SIGPIPE, SIG_IGN); 1189 signal(SIGHUP, bail_out); 1190 signal(SIGINT, bail_out); 1191 signal(SIGTERM, bail_out); 1192 1193 while (!done || !Sdone) { 1194 if (!Sdone && (a == ADD || a == COMPARE)) { 1195 /* Slave advances. */ 1196 order(ADVANCE); 1197 switch (answer()) { 1198 case PATH: 1199 Slinkpath= nil; 1200 receive(Spath, sizeof(Spath)); 1201 recstat(&Sst); 1202 break; 1203 case LINK: 1204 receive(Slnkpth, sizeof(Slnkpth)); 1205 Slinkpath= Slnkpth; 1206 receive(Spath, sizeof(Spath)); 1207 recstat(&Sst); 1208 break; 1209 case SYMLINK: 1210 Slinkpath= nil; 1211 receive(Slnkpth, sizeof(Slnkpth)); 1212 receive(Spath, sizeof(Spath)); 1213 recstat(&Sst); 1214 break; 1215 case DONE: 1216 Sdone= 1; 1217 break; 1218 default: 1219 fprintf(stderr, 1220 "%s: Strange answer from slave.\n", 1221 arg0); 1222 exit(1); 1223 } 1224 } 1225 if (!done && (a == COMPARE || a == DELETE)) { 1226 /* Master advances. */ 1227 if (!advance()) done= 1; 1228 } 1229 1230 if (done && Sdone) break; 1231 1232 switch (a= action()) { 1233 case ADD: /* Spath exists, path doesn't, add? */ 1234 add(0); 1235 break; 1236 case COMPARE: /* They both exist, are they the same? */ 1237 compare(); 1238 break; 1239 case DELETE: /* path exists, Spath doesn't, delete? */ 1240 delete(0); 1241 } 1242 fflush(stdout); /* Don't keep user in suspense. */ 1243 } 1244 order(ex == 0 ? DIE : DIE_BAD); 1245 } 1246 1247 static void mediator() 1248 /* Sits at the local machine and passes orders from master to slave, both 1249 * on remote machines. Only diagnostics and questions are handled. 1250 */ 1251 { 1252 enum orders req; 1253 1254 for (;;) { 1255 switch (req= request()) { 1256 case DIE_BAD: 1257 ex= 1; 1258 /*FALL THROUGH*/ 1259 case DIE: 1260 order(DIE); 1261 return; 1262 case POSITIVE: 1263 order(ask('y') ? PASS_YES : PASS_NO); 1264 break; 1265 case NEGATIVE: 1266 order(ask('n') ? PASS_YES : PASS_NO); 1267 break; 1268 default: 1269 order(req); 1270 } 1271 } 1272 } 1273 1274 #define P_EXIT 1 /* Make sure process doesn't return. */ 1275 #define P_SHADOW 2 /* Always use exec on 68000. */ 1276 1277 static void startprocess(void (*proc)(), char *machine, char *path, 1278 int p_flags) 1279 { 1280 char *argv[10], **argp= argv; 1281 char flags[10], *pfl= flags; 1282 1283 if (machine != nil) { 1284 char *u= machine, *m; 1285 1286 *argp++ = "rsh"; 1287 if ((m= strchr(machine, '@')) != nil) { 1288 *m++ = 0; 1289 *argp++ = "-l"; 1290 *argp++ = u; 1291 machine= m; 1292 } 1293 *argp++ = machine; 1294 } else 1295 /* Without this check it would run like a pig on an non MMU 68000: */ 1296 if (!(USE_SHADOWING && p_flags & P_SHADOW)) { 1297 if (chdir(path) < 0) { 1298 if (proc != master || errno != ENOENT 1299 || mkdir(path, 0700) < 0) 1300 perrx(path); 1301 if (chdir(path) < 0) perrx(path); 1302 printf("Destination directory %s created\n", path); 1303 } 1304 isvisible(path); 1305 isbackup(proc == slave); 1306 (*proc)(); 1307 if (p_flags & P_EXIT) exit(ex); 1308 return; 1309 } 1310 *argp++ = SYNCNAME; 1311 *pfl++ = '-'; 1312 if (interact) *pfl++ = 'i'; 1313 if (install) *pfl++ = 'u'; 1314 if (force) *pfl++ = 'f'; 1315 *pfl= 0; 1316 *argp++ = flags; 1317 *argp++ = proc == slave ? SLAVENAME : MASTERNAME; 1318 *argp++ = path; 1319 *argp++ = nil; 1320 #ifdef DEBUG 1321 fprintf(stderr, "execlp("); 1322 for (argp= argv; *argp != nil; argp++) fprintf(stderr, "%s, ", *argp); 1323 fprintf(stderr, "nil);\n"); 1324 #endif 1325 execvp(argv[0], argv); 1326 perrx(argv[0]); 1327 } 1328 1329 void splitcolon(char *path, char **amach, char **adir) 1330 { 1331 char *dir= path; 1332 1333 for (;;) { 1334 if (*dir == ':') { 1335 *dir++ = 0; 1336 *amach= path; 1337 *adir= dir; 1338 break; 1339 } 1340 if (*dir == 0 || *dir == '/') { 1341 *amach= nil; 1342 *adir= path; 1343 break; 1344 } 1345 dir++; 1346 } 1347 } 1348 1349 static void Usage() 1350 { 1351 fprintf(stderr, 1352 "Usage: %s [-iuf] [[user@]machine:]dir1 [[user@]machine:]dir2\n", 1353 arg0); 1354 exit(1); 1355 } 1356 1357 int main(int argc, char **argv) 1358 { 1359 char *s_mach, *s_dir; 1360 char *m_mach, *m_dir; 1361 int m2s[2], s2m[2], m2m[2]; 1362 int s_pid= 0, m_pid= 0; 1363 int r; 1364 1365 if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++; 1366 1367 while (argc>1 && argv[1][0] == '-') { 1368 char *f= argv[1]+1; 1369 1370 while (*f != 0) { 1371 switch (*f++) { 1372 case 'i': interact= 1; break; 1373 case 'u': install= 1; break; 1374 case 'f': force= 1; break; 1375 default: Usage(); 1376 } 1377 } 1378 argc--; 1379 argv++; 1380 } 1381 1382 if (argc != 3) Usage(); 1383 1384 if (strcmp(argv[1], SLAVENAME) == 0) { 1385 arg0= "Slave"; 1386 splitcolon(argv[2], &s_mach, &s_dir); 1387 startprocess(slave, s_mach, s_dir, P_EXIT); 1388 } else 1389 if (strcmp(argv[1], MASTERNAME) == 0) { 1390 arg0= "Master"; 1391 splitcolon(argv[2], &m_mach, &m_dir); 1392 startprocess(master, m_mach, m_dir, P_EXIT); 1393 } 1394 1395 splitcolon(argv[1], &s_mach, &s_dir); 1396 splitcolon(argv[2], &m_mach, &m_dir); 1397 1398 /* How difficult can plumbing be? */ 1399 if (pipe(m2s) < 0 || pipe(s2m) < 0) perrx("pipe()"); 1400 1401 if (m_mach == nil) { 1402 /* synctree [machine:]dir1 dir2 */ 1403 switch (s_pid= fork()) { 1404 case -1: 1405 perrx("fork()"); 1406 case 0: 1407 dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]); 1408 dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]); 1409 arg0= "Slave"; 1410 startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW); 1411 } 1412 chan[0]= s2m[0]; close(s2m[1]); 1413 chan[1]= m2s[1]; close(m2s[0]); 1414 startprocess(master, m_mach, m_dir, 0); 1415 } else 1416 if (s_mach == nil) { 1417 /* synctree dir1 machine:dir2 */ 1418 switch (m_pid= fork()) { 1419 case -1: 1420 perrx("fork()"); 1421 case 0: 1422 dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]); 1423 dup2(m2s[1], 1); close(m2s[0]); close(m2s[1]); 1424 arg0= "Master"; 1425 startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW); 1426 } 1427 chan[0]= m2s[0]; close(m2s[1]); 1428 chan[1]= s2m[1]; close(s2m[0]); 1429 startprocess(slave, s_mach, s_dir, 0); 1430 } else { 1431 /* synctree machine1:dir1 machine2:dir2 */ 1432 if (pipe(m2m) < 0) perrx(pipe); 1433 1434 switch (s_pid= fork()) { 1435 case -1: 1436 perrx("fork()"); 1437 case 0: 1438 dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]); 1439 dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]); 1440 close(m2m[0]); close(m2m[1]); 1441 arg0= "Slave"; 1442 startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW); 1443 } 1444 1445 switch (m_pid= fork()) { 1446 case -1: 1447 perrx("fork()"); 1448 case 0: 1449 dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]); 1450 close(m2s[0]); close(m2s[1]); 1451 dup2(m2m[1], 1); close(m2m[0]); close(m2m[1]); 1452 arg0= "Master"; 1453 startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW); 1454 } 1455 close(s2m[0]); close(s2m[1]); 1456 chan[0]= m2m[0]; close(m2m[1]); 1457 chan[1]= m2s[1]; close(m2s[0]); 1458 mediator(); 1459 } 1460 close(chan[0]); 1461 close(chan[1]); 1462 1463 alarm(15); /* Don't wait(2) forever. */ 1464 1465 while (s_pid != 0 || m_pid != 0) { 1466 if ((r= wait((int *) nil)) < 0) perrx("wait()"); 1467 if (r == s_pid) s_pid= 0; 1468 if (r == m_pid) m_pid= 0; 1469 } 1470 exit(ex); 1471 } 1472