00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #define nil 0
00024 #include <sys/types.h>
00025 #include <stddef.h>
00026 #include <stdio.h>
00027 #include <sys/stat.h>
00028 #include <utime.h>
00029 #include <string.h>
00030 #include <signal.h>
00031 #include <dirent.h>
00032 #include <errno.h>
00033 #include <fcntl.h>
00034 #include <stdlib.h>
00035 #include <unistd.h>
00036 #include <time.h>
00037 #include <sys/wait.h>
00038
00039 #if _MINIX
00040 #include "limits.h"
00041 #include "minix/config.h"
00042
00043
00044 #define LITTLE_ENDIAN (CHIP == INTEL)
00045 #define USE_SHADOWING (CHIP == M68000)
00046 #else
00047 #define LITTLE_ENDIAN 0
00048 #define USE_SHADOWING 0
00049 #endif
00050
00051 #ifndef PATH_MAX
00052 #define PATH_MAX 1024
00053 #endif
00054
00055 #ifndef S_ISLNK
00056
00057 #define S_ISLNK(mode) (0)
00058 #define lstat stat
00059 #define symlink(path1, path2) (errno= ENOSYS, -1)
00060 #define readlink(path, buf, len) (errno= ENOSYS, -1)
00061 #endif
00062
00063 #define NUMBYTES 4
00064 #define CHUNK 4096
00065
00066 static int install= 0;
00067 static int interact= 0;
00068 static int force= 0;
00069 static int backup= 0;
00070
00071 static char SYNCNAME[] = "synctree";
00072 static char SLAVENAME[] = "==SLAVE==";
00073 static char MASTERNAME[]= "==MASTER==";
00074
00075
00076 static char BACKUP[] = ".backup";
00077 static int filemodes;
00078
00079 static int chan[2]= { 0, 1 };
00080
00081 #define BUCKSIZE (1+NUMBYTES+CHUNK)
00082 static char bucket[BUCKSIZE];
00083 static char *buckp= bucket;
00084 static int buckn= 0;
00085
00086 enum orders {
00087 ENTER= 128,
00088 ADVANCE,
00089 CAT,
00090 MORE,
00091 CANCEL,
00092 DIE,
00093 DIE_BAD,
00094 POSITIVE,
00095 NEGATIVE,
00096 PASS_YES,
00097 PASS_NO
00098 };
00099
00100 #ifdef DEBUG
00101 char *ORDERS[]= {
00102 "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD",
00103 "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO"
00104 };
00105 #endif
00106
00107 enum answers {
00108 PATH= 128,
00109 LINK,
00110 DATA,
00111 NODATA,
00112 DONE,
00113 SYMLINK,
00114 YES, NO
00115 };
00116
00117 #ifdef DEBUG
00118 char *ANSWERS[]= {
00119 "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO"
00120 };
00121
00122 #define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg)
00123 #else
00124 #define DPRINTF(format, arg)
00125 #endif
00126
00127 struct mode {
00128 unsigned short md_mode;
00129 unsigned short md_uid;
00130 unsigned short md_gid;
00131 unsigned short md_rdev;
00132 unsigned short md_devsiz;
00133 };
00134
00135 static char *arg0;
00136 static int ex= 0;
00137
00138 static void because()
00139 {
00140 fprintf(stderr, ": %s\n", strerror(errno));
00141 ex= 1;
00142 }
00143
00144 static void perr(label) char *label;
00145 {
00146 fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno));
00147 ex= 1;
00148 }
00149
00150 static void perrx(label) char *label;
00151 {
00152 perr(label);
00153 exit(1);
00154 }
00155
00156 #if S_HIDDEN
00157
00158 static int transparent= 0;
00159
00160 static void isvisible(name) char *name;
00161 {
00162 char *p= name + strlen(name);
00163
00164 while (p > name && *--p == '/') {}
00165
00166 if (p > name && *p == '@' && p[-1] != '/') transparent= 1;
00167 }
00168 #else
00169 #define transparent 0
00170 #define isvisible(name) ((void) 0)
00171 #endif
00172
00173 static void isbackup(slave) int slave;
00174 {
00175 if ((filemodes= open(BACKUP, slave ? O_RDONLY : O_RDWR)) >= 0)
00176 backup= 1;
00177 else {
00178 if (errno != ENOENT) perrx(BACKUP);
00179 }
00180 }
00181
00182 static char path[PATH_MAX+1];
00183 static char lnkpth[PATH_MAX+1];
00184 static char *linkpath;
00185 static struct stat st;
00186 static char Spath[PATH_MAX+1];
00187 static char Slnkpth[PATH_MAX+1];
00188 static char *Slinkpath=nil;
00189 static struct stat Sst;
00190
00191 static char *addpath(p, n) char *p, *n;
00192
00193 {
00194 if (p - path + 1 + strlen(n) > PATH_MAX) {
00195 *p= 0;
00196 fprintf(stderr, "%s: %s/%s is too long.\n", arg0, path, n);
00197 fprintf(stderr, "%s: Unable to continue.\n", arg0);
00198 exit(1);
00199 }
00200 if (p == path+1 && path[0] == '.') p= path;
00201
00202 if (p > path) *p++ = '/';
00203
00204 while (*n != 0) *p++ = *n++;
00205 *p= 0;
00206 return p;
00207 }
00208
00209 struct entry {
00210 struct entry *next;
00211 struct entry *dir;
00212 struct entry *con;
00213 char *name;
00214 };
00215
00216 static struct entry *E= nil;
00217
00218 static void setpath(e) struct entry *e;
00219
00220 {
00221 static char *pend;
00222
00223 if (e == nil)
00224 pend= path;
00225 else {
00226 setpath(e->dir);
00227 pend= addpath(pend, e->name);
00228 }
00229 }
00230
00231 static void sort(ae) struct entry **ae;
00232
00233
00234
00235 {
00236 struct entry *e1, **mid;
00237 struct entry *e2;
00238
00239 e1= *(mid= &(*ae)->next);
00240 do {
00241 if ((e1= e1->next) == nil) break;
00242 mid= &(*mid)->next;
00243 } while ((e1= e1->next) != nil);
00244
00245 e2= *mid;
00246 *mid= nil;
00247
00248 if ((*ae)->next != nil) sort(ae);
00249 if (e2->next != nil) sort(&e2);
00250
00251 e1= *ae;
00252 for (;;) {
00253 if (strcmp(e1->name, e2->name)<=0) {
00254 if ((e1= *(ae= &e1->next)) == nil) {
00255 *ae= e2;
00256 break;
00257 }
00258 } else {
00259 *ae= e2;
00260 e2= *(ae= &e2->next);
00261 *ae= e1;
00262 if (e2 == nil) break;
00263 }
00264 }
00265 }
00266
00267 static void enter()
00268
00269 {
00270 struct entry **last= &E->con, *new;
00271 struct dirent *e;
00272 DIR *d;
00273
00274 if ((d= opendir(path)) == nil) {
00275 fprintf(stderr, "%s: Can't read dir %s\n", arg0, path);
00276 return;
00277 }
00278
00279 while ((e= readdir(d)) != nil) {
00280 if (e->d_name[0] == '.' && (e->d_name[1] == 0
00281 || (e->d_name[1] == '.' && e->d_name[2] == 0)
00282 )) continue;
00283
00284 new= (struct entry *) malloc(sizeof(*new));
00285
00286 new->next= nil;
00287 new->dir= E;
00288 new->con= nil;
00289 new->name= (char *) malloc(strlen(e->d_name) + 1);
00290 strcpy(new->name, e->d_name);
00291 *last= new;
00292 last= &new->next;
00293 }
00294 closedir(d);
00295 if (E->con != nil && E->con->next != nil) sort(&E->con);
00296 }
00297
00298 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
00299 #define arraylimit(a) ((a) + arraysize(a))
00300
00301 static char *link_islink(struct stat *stp, const char *file)
00302 {
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314 typedef struct link {
00315 struct link *next;
00316 ino_t ino;
00317 off_t off;
00318 } link_t;
00319 typedef struct dlink {
00320 dev_t dev;
00321 char file[PATH_MAX];
00322 } dlink_t;
00323 static link_t *links[256];
00324 static int tfd= -1;
00325 static dlink_t dlink;
00326 link_t *lp, **plp;
00327 size_t len;
00328 off_t off;
00329
00330 if (file == nil) {
00331
00332 for (plp= links; plp < arraylimit(links); plp++) {
00333 while ((lp= *plp) != nil) {
00334 *plp= lp->next;
00335 free(lp);
00336 }
00337 }
00338 if (tfd != -1) close(tfd);
00339 tfd= -1;
00340 return nil;
00341 }
00342
00343
00344 if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) {
00345 errno= ENOENT;
00346 return nil;
00347 }
00348
00349 plp= &links[stp->st_ino % arraysize(links)];
00350
00351 while ((lp= *plp) != nil) {
00352 if (lp->ino == stp->st_ino) {
00353
00354 if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil;
00355 if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil;
00356
00357
00358 if (dlink.dev == stp->st_dev) {
00359 if (strcmp(file, dlink.file) == 0) {
00360
00361 *plp= lp->next;
00362 free(lp);
00363 errno= ENOENT;
00364 return nil;
00365 }
00366
00367
00368 return dlink.file;
00369 }
00370 }
00371 plp= &lp->next;
00372 }
00373
00374
00375 if (tfd == -1) {
00376 for (;;) {
00377 char *tmp;
00378
00379 tmp= tmpnam(nil);
00380 tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600);
00381 if (tfd < 0) {
00382 if (errno != EEXIST) return nil;
00383 } else {
00384 (void) unlink(tmp);
00385 break;
00386 }
00387 }
00388 }
00389 if ((len= strlen(file)) >= PATH_MAX) {
00390 errno= E2BIG;
00391 return nil;
00392 }
00393
00394 dlink.dev= stp->st_dev;
00395 strcpy(dlink.file, file);
00396 len += offsetof(dlink_t, file) + 1;
00397 if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil;
00398 if (write(tfd, &dlink, len) != len) return nil;
00399
00400 if ((lp= malloc(sizeof(*lp))) == nil) return nil;
00401 lp->next= nil;
00402 lp->ino= stp->st_ino;
00403 lp->off= off;
00404 *plp= lp;
00405 errno= ENOENT;
00406 return nil;
00407 }
00408
00409 #define cancellink() ((void) islink())
00410
00411 static char *islink()
00412
00413
00414
00415
00416
00417 {
00418 char *name;
00419
00420 name= link_islink(&st, path);
00421 if (name == nil && errno != ENOENT) perrx(path);
00422 return name;
00423 }
00424
00425 static void setstat(ino, stp) ino_t ino; struct stat *stp;
00426
00427 {
00428 struct mode md;
00429
00430 md.md_mode = stp->st_mode;
00431 md.md_uid = stp->st_uid;
00432 md.md_gid = stp->st_gid;
00433 md.md_rdev = stp->st_rdev;
00434 md.md_devsiz = stp->st_size / 1024;
00435
00436 if (lseek(filemodes, (off_t) sizeof(md) * (off_t) ino, 0) == -1
00437 || write(filemodes, (char *) &md, sizeof(md)) != sizeof(md)
00438 ) perrx(BACKUP);
00439 }
00440
00441 static int getstat(name, stp) char *name; struct stat *stp;
00442
00443
00444
00445 {
00446 errno= 0;
00447
00448 if (strcmp(name, BACKUP) == 0) return -1;
00449
00450 if (lstat(name, stp) < 0) return -1;
00451
00452 if (stp->st_mode == S_IFREG && stp->st_mtime == 0) return -1;
00453
00454 if (backup) {
00455 struct mode md;
00456
00457 if (lseek(filemodes,
00458 (off_t) sizeof(md) * (off_t) stp->st_ino, 0) == -1
00459 || read(filemodes, (char *) &md, sizeof(md)) != sizeof(md)
00460 || md.md_mode == 0
00461 ) {
00462 errno= 0;
00463 setstat(stp->st_ino, stp);
00464 } else {
00465 stp->st_mode = md.md_mode;
00466 stp->st_uid = md.md_uid;
00467 stp->st_gid = md.md_gid;
00468 stp->st_rdev = md.md_rdev;
00469 if (S_ISBLK(stp->st_mode))
00470 stp->st_size= (off_t) md.md_devsiz * 1024;
00471 }
00472 }
00473 return 0;
00474 }
00475
00476 static int advance()
00477
00478 {
00479 for (;;) {
00480 if (E==nil) {
00481 E= (struct entry *) malloc(sizeof(*E));
00482 E->dir= nil;
00483 E->con= nil;
00484 E->next= nil;
00485 E->name= (char *) malloc(3);
00486 strcpy(E->name, transparent ? ".@" : ".");
00487 } else
00488 if (E->con != nil)
00489 E= E->con;
00490 else {
00491 for (;;) {
00492
00493
00494
00495 struct entry *junk= E, *parent= E->dir;
00496
00497 if (parent != nil) parent->con= E->next;
00498 E= E->next;
00499 free(junk->name);
00500 free(junk);
00501
00502 if (E != nil) break;
00503
00504 if ((E= parent) == nil) return 0;
00505 }
00506 }
00507 setpath(E);
00508 if (getstat(path, &st) == 0) {
00509 if (S_ISLNK(st.st_mode)) {
00510 int n;
00511
00512 if ((n= readlink(path, lnkpth, PATH_MAX)) >= 0)
00513 {
00514 lnkpth[n]= 0;
00515 break;
00516 }
00517 } else {
00518 break;
00519 }
00520 }
00521 if (errno != 0 && errno != ENOENT) perr(path);
00522 }
00523
00524 linkpath= islink();
00525 DPRINTF("%s: path = %s\n", path);
00526 return 1;
00527 }
00528
00529 static enum orders request()
00530
00531 {
00532 static char buf[64], *bp;
00533 static int n= 0;
00534 int req;
00535
00536 for (;;) {
00537 if (n == 0) {
00538 if ((n= read(chan[0], buf, (int) sizeof(buf))) <= 0) {
00539 if (n < 0) perrx("request()");
00540
00541 fprintf(stderr,
00542 "%s: Master died prematurely.\n", arg0);
00543 exit(1);
00544 }
00545 bp= buf;
00546 }
00547 req= *bp++ & 0xFF;
00548 n--;
00549 if (req >= (int) ENTER) break;
00550
00551
00552 putchar(req);
00553 }
00554 DPRINTF("%s: request() == %s\n", ORDERS[req - (int) ENTER]);
00555
00556 return (enum orders) req;
00557 }
00558
00559 static void report()
00560 {
00561 int r;
00562
00563 DPRINTF("%s: reporting now!\n", 0);
00564
00565 buckp= bucket;
00566
00567 while (buckn > 0) {
00568 r = buckn;
00569 if (r > (512 << sizeof(char*))) r= (512 << sizeof(char*));
00570
00571 if ((r= write(chan[1], buckp, r)) < 0) perrx("report()");
00572
00573 buckp += r;
00574 buckn -= r;
00575 }
00576 buckp= bucket;
00577 buckn= 0;
00578 }
00579
00580 static void inform(a) enum answers a;
00581
00582 {
00583 DPRINTF("%s: inform(%s)\n", ANSWERS[(int) a - (int) PATH]);
00584
00585 *buckp++ = (int) a;
00586 buckn++;
00587 }
00588
00589 #define wwrite(buf, n) (memcpy(buckp, (buf), (n)), buckp+= (n), buckn+= (n))
00590
00591 static void sendnum(n) long n;
00592
00593 {
00594 #if LITTLE_ENDIAN
00595 wwrite((char *) &n, sizeof(n));
00596 #else
00597 char buf[NUMBYTES];
00598
00599 buf[0]= (char) (n >> 0);
00600 buf[1]= (char) (n >> 8);
00601 buf[2]= (char) (n >> 16);
00602 buf[3]= (char) (n >> 24);
00603 wwrite(buf, sizeof(buf));
00604 #endif
00605 }
00606
00607 static void send(buf, n) char *buf; int n;
00608
00609 {
00610 sendnum((long) n);
00611 if (n > 0) wwrite(buf, (size_t) n);
00612 }
00613
00614 static void sendstat(stp) struct stat *stp;
00615 {
00616 sendnum((long) stp->st_mode);
00617 sendnum((long) stp->st_uid);
00618 sendnum((long) stp->st_gid);
00619 sendnum((long) stp->st_rdev);
00620 sendnum((long) stp->st_size);
00621 sendnum((long) stp->st_mtime);
00622 }
00623
00624 static int ask();
00625
00626 static void slave()
00627
00628
00629
00630 {
00631 int f, n;
00632 char buf[CHUNK];
00633 enum { run, done, die } state= run;
00634
00635 do {
00636 switch (request()) {
00637 case ENTER:
00638 enter();
00639 break;
00640 case ADVANCE:
00641 if (!advance() || state == done) {
00642 inform(DONE);
00643 state= done;
00644 } else {
00645 if (linkpath!=nil) {
00646 inform(LINK);
00647 send(linkpath, strlen(linkpath) + 1);
00648 } else
00649 if (S_ISLNK(st.st_mode)) {
00650 inform(SYMLINK);
00651 send(lnkpth, strlen(lnkpth) + 1);
00652 } else {
00653 inform(PATH);
00654 }
00655 send(path, strlen(path) + 1);
00656 sendstat(&st);
00657 }
00658 break;
00659 case CAT:
00660 if ((f= open(path, O_RDONLY))<0) {
00661 fprintf(stderr, "%s: Can't open %s",
00662 arg0, path);
00663 because();
00664 inform(NODATA);
00665 break;
00666 }
00667 inform(DATA);
00668 do {
00669 n= read(f, buf, sizeof(buf));
00670 if (n < 0) perr(path);
00671 send(buf, n);
00672 if (n > 0) report();
00673 } while (n > 0);
00674 close(f);
00675 break;
00676 case CANCEL:
00677 cancellink();
00678 break;
00679 case DIE_BAD:
00680 ex= 1;
00681
00682 case DIE:
00683 state= die;
00684 break;
00685 case POSITIVE:
00686 inform(ask('y') ? YES : NO);
00687 break;
00688 case NEGATIVE:
00689 inform(ask('n') ? YES : NO);
00690 break;
00691 case PASS_YES:
00692 inform(YES);
00693 break;
00694 case PASS_NO:
00695 inform(NO);
00696 break;
00697 default:
00698 fprintf(stderr, "%s: strange request\n", arg0);
00699 exit(1);
00700 }
00701 report();
00702 } while (state != die);
00703 }
00704
00705 static int execute(argv) char **argv;
00706
00707 {
00708 int pid, r, status;
00709
00710 if ((pid= fork())<0) {
00711 perr("fork()");
00712 return 0;
00713 }
00714 if (pid == 0) {
00715 execvp(argv[0], argv);
00716 perrx(argv[0]);
00717 }
00718 while ((r= wait(&status)) != pid) {
00719 if (r < 0) {
00720 perr(argv[0]);
00721 return 0;
00722 }
00723 }
00724 return status == 0;
00725 }
00726
00727 static int removedir(dir) char *dir;
00728
00729 {
00730 static char *argv[] = { "rm", "-r", nil, nil };
00731
00732 printf("(rm -r %s)\n", dir);
00733
00734 argv[2]= dir;
00735 return execute(argv);
00736 }
00737
00738 static void order(o) enum orders o;
00739
00740 {
00741 char c= (char) o;
00742
00743 DPRINTF("%s: order(%s)\n", ORDERS[o - (int) ENTER]);
00744
00745 if (write(chan[1], &c, 1) != 1) perrx("order()");
00746 }
00747
00748 static void rread(buf, n) char *buf; int n;
00749
00750 {
00751 int r;
00752
00753 while (n > 0) {
00754 if (buckn == 0) {
00755 switch (buckn= read(chan[0], bucket, BUCKSIZE)) {
00756 case -1:
00757 perrx("reply channel from slave");
00758 case 0:
00759 fprintf(stderr,
00760 "%s: slave died prematurely.\n",
00761 arg0);
00762 exit(1);
00763 }
00764 buckp= bucket;
00765 }
00766 r= n < buckn ? n : buckn;
00767 memcpy(buf, buckp, r);
00768 buckp+= r;
00769 buckn-= r;
00770 buf+= r;
00771 n-= r;
00772 }
00773 }
00774
00775 static enum answers answer()
00776
00777 {
00778 char c;
00779 int a;
00780
00781 rread(&c, 1);
00782 a= c & 0xFF;
00783
00784 DPRINTF("%s: answer() == %s\n", ANSWERS[a - (int) PATH]);
00785
00786 return (enum answers) a;
00787 }
00788
00789 static long recnum()
00790
00791
00792
00793 {
00794 #if LITTLE_ENDIAN
00795 long n;
00796
00797 rread((char *) &n, (int) sizeof(n));
00798 return n;
00799 #else
00800 unsigned char buf[NUMBYTES];
00801
00802 rread(buf, sizeof(buf));
00803 return buf[0]
00804 | ((unsigned) buf[1] << 8)
00805 | ((unsigned long) buf[2] << 16)
00806 | ((unsigned long) buf[3] << 24);
00807 #endif
00808 }
00809
00810 static int receive(buf, max) char *buf; int max;
00811
00812 {
00813 int n;
00814
00815 n= recnum();
00816 if (n > max) {
00817 fprintf(stderr, "%s: panic: Can't read %d bytes\n", arg0, n);
00818 exit(1);
00819 }
00820 if (n > 0) rread(buf, n);
00821 return n;
00822 }
00823
00824 static void recstat(stp) struct stat *stp;
00825 {
00826 stp->st_mode= recnum();
00827 stp->st_uid= recnum();
00828 stp->st_gid= recnum();
00829 stp->st_rdev= recnum();
00830 stp->st_size= recnum();
00831 stp->st_mtime= recnum();
00832 }
00833
00834 static int key()
00835 {
00836 int c;
00837 static int tty= -1;
00838
00839 if (tty < 0) tty= isatty(0);
00840
00841 if (feof(stdin) || (c= getchar()) == EOF) {
00842 c= '\n';
00843 if (tty) putchar('\n');
00844 }
00845
00846 if (!tty) putchar(c);
00847
00848 return c;
00849 }
00850
00851 static int ask(def) int def;
00852
00853 {
00854 int y, c;
00855
00856 if (chan[0] == 0) {
00857
00858 fflush(stdout);
00859 order(def == 'y' ? POSITIVE : NEGATIVE);
00860 return answer() == YES;
00861 }
00862
00863 printf("? (%c) ", def);
00864 fflush(stdout);
00865
00866 do c= key(); while (c == ' ' || c == '\t');
00867
00868 y= c;
00869
00870 while (c != '\n') c= key();
00871
00872 if (y != 'y' && y != 'Y' && y != 'n' && y != 'N') y= def;
00873
00874 return y == 'y' || y == 'Y';
00875 }
00876
00877 static void setmodes(silent) int silent;
00878 {
00879 struct stat st;
00880 int change= 0;
00881 struct utimbuf tms;
00882
00883 errno= 0;
00884 getstat(Spath, &st);
00885 if (backup && silent) {
00886 setstat(st.st_ino, &Sst);
00887 getstat(Spath, &st);
00888 }
00889
00890 if (S_ISLNK(st.st_mode)) return;
00891
00892 if (errno == 0 && st.st_mode != Sst.st_mode) {
00893 if (!backup) chmod(Spath, Sst.st_mode & 07777);
00894 change= 1;
00895 }
00896 if (errno == 0
00897 && (st.st_uid != Sst.st_uid || st.st_gid != Sst.st_gid)
00898 && (backup || geteuid() == 0)
00899 ) {
00900 errno= 0;
00901 if (!backup) chown(Spath, Sst.st_uid, Sst.st_gid);
00902 change= 1;
00903 }
00904
00905 if (backup && !silent) setstat(st.st_ino, &Sst);
00906
00907 if (errno == 0 && S_ISREG(Sst.st_mode) && st.st_mtime != Sst.st_mtime) {
00908 time(&tms.actime);
00909 tms.modtime= Sst.st_mtime;
00910 errno= 0;
00911 utime(Spath, &tms);
00912 change= 1;
00913 }
00914 if (errno != 0) {
00915 fprintf(stderr, "%s: Can't set modes of %s", arg0, Spath);
00916 because();
00917 } else
00918 if (change && !silent) {
00919 printf("Mode changed of %s\n", Spath);
00920 }
00921 }
00922
00923 static void makeold()
00924 {
00925 static struct utimbuf tms= { 0, 0 };
00926
00927 if (utime(Spath, &tms) < 0) {
00928 if (errno != ENOENT) {
00929 fprintf(stderr,
00930 "%s: can't make %s look old", arg0, Spath);
00931 because();
00932 }
00933 } else {
00934 fprintf(stderr, "%s: made %s look old.\n", arg0, Spath);
00935 }
00936 }
00937
00938 static int busy= 0;
00939
00940 static void bail_out(sig) int sig;
00941 {
00942 signal(sig, SIG_IGN);
00943
00944 fprintf(stderr, "%s: Exiting after signal %d\n", arg0, sig);
00945
00946 if (busy) {
00947 fprintf(stderr, "%s: was installing %s\n", arg0, Spath);
00948 makeold();
00949 }
00950 order(DIE_BAD);
00951
00952 exit(sig);
00953 }
00954
00955 static int makenode(name, mode, addr, size)
00956 char *name; int mode; dev_t addr; off_t size;
00957 {
00958 int r;
00959
00960 if (!backup) {
00961 r= mknod(name, mode, addr);
00962 } else {
00963 if ((r= creat(name, 0644)) >= 0) close(r);
00964 }
00965 return r;
00966 }
00967
00968 static void add(update) int update;
00969
00970 {
00971 int f, n;
00972 char buf[CHUNK];
00973 int forced_update= force && update;
00974
00975 if (Slinkpath != nil && !S_ISLNK(Sst.st_mode)) {
00976 if (interact && !update) {
00977 printf("Link %s to %s", Spath, Slinkpath);
00978 if (!ask('n')) return;
00979 }
00980 if (link(Slinkpath, Spath) >= 0) {
00981 printf("Linked %s to %s\n", Spath, Slinkpath);
00982 return;
00983 } else {
00984 fprintf(stderr,
00985 "%s: Can't link %s to %s",
00986 arg0, Slinkpath, Spath);
00987 because();
00988
00989 }
00990 }
00991 switch (Sst.st_mode & S_IFMT) {
00992 case S_IFDIR:
00993 if (!force) {
00994 printf("Add dir %s", Spath);
00995 if (!ask('n')) return;
00996 }
00997 if (mkdir(Spath, backup ? 0755 : Sst.st_mode) < 0) {
00998 perr(Spath);
00999 return;
01000 }
01001 printf("Directory %s created.\n", Spath);
01002 order(ENTER);
01003 break;
01004 case S_IFBLK:
01005 case S_IFCHR:
01006 case S_IFIFO:
01007 if (interact && !update) {
01008 printf("Create special file %s", Spath);
01009 if (!ask('n')) { order(CANCEL); return; }
01010 }
01011 if (makenode(Spath, Sst.st_mode, Sst.st_rdev, Sst.st_size)<0) {
01012 fprintf(stderr,
01013 "%s: Can't create special file %s",
01014 arg0, Spath);
01015 because();
01016 return;
01017 }
01018 printf("Special file %s created\n", Spath);
01019 break;
01020 #ifdef S_IFLNK
01021 case S_IFLNK:
01022 if (interact && !update) {
01023 printf("Install %s -> %s", Spath, Slnkpth);
01024 if (!ask('n')) { order(CANCEL); return; }
01025 }
01026 if (symlink(Slnkpth, Spath) < 0) {
01027 fprintf(stderr, "%s: Can't create symlink %s",
01028 arg0, Spath);
01029 because();
01030 return;
01031 }
01032 printf("%s %s -> %s\n",
01033 forced_update ? "Updated: " : "Installed:",
01034 Spath, Slnkpth);
01035 break;
01036 #endif
01037 case S_IFREG:
01038 if (interact && !update) {
01039 printf("Install %s", Spath);
01040 if (!ask('n')) { order(CANCEL); return; }
01041 }
01042 order(CAT);
01043 if (answer() != DATA) return;
01044
01045 busy= 1;
01046 if ((f= creat(Spath, backup ? 0644 : Sst.st_mode&07777)) < 0) {
01047 busy= 0;
01048 fprintf(stderr, "%s: Can't create %s", arg0, Spath);
01049 because();
01050 }
01051
01052 while ((n= receive(buf, sizeof(buf)))>0) {
01053 if (f >= 0 && write(f, buf, n) != n) {
01054 fprintf(stderr, "%s: Write error on %s",
01055 arg0, Spath);
01056 because();
01057 close(f); f= -1;
01058 }
01059 }
01060 if (n < 0) {
01061 fprintf(stderr, "%s: Slave read err on %s\n",
01062 arg0, Spath);
01063 }
01064 if (f >= 0) close(f);
01065 if (n < 0 || f < 0) { makeold(); busy= 0; return; }
01066 busy= 0;
01067 printf("%s %s\n",
01068 forced_update ?
01069 Sst.st_mtime < st.st_mtime ? "Restored: " :
01070 "Updated: " :
01071 "Installed:",
01072 Spath
01073 );
01074 break;
01075 default:
01076 fprintf(stderr,
01077 "%s: Won't add file with strange mode %05o: %s\n",
01078 arg0, Sst.st_mode, Spath);
01079 order(CANCEL);
01080 return;
01081 }
01082 setmodes(1);
01083 }
01084
01085 static int delete(update) int update;
01086
01087 {
01088 int forced_update= force && update;
01089
01090 if (S_ISDIR(st.st_mode)) {
01091 if (install) return 0;
01092 if (!force) {
01093 printf("Delete dir %s", path);
01094 if (!ask('n')) return 0;
01095 }
01096 if (!removedir(path)) { ex= 1; return 0; }
01097 if (!forced_update) printf("Directory %s deleted.\n", path);
01098 return 1;
01099 }
01100
01101 if (install && !update) return 0;
01102
01103 if (!force) {
01104 printf("Delete %s", path);
01105 if (!ask((interact && !update) ? 'n' : 'y')) return 0;
01106 }
01107
01108 if (unlink(path)<0) {
01109 fprintf(stderr, "Can't delete %s", path);
01110 because();
01111 return 0;
01112 }
01113 cancellink();
01114 if (!forced_update) printf("Deleted: %s\n", path);
01115 return 1;
01116 }
01117
01118 static int different()
01119
01120 {
01121 if (! ( (linkpath == nil && Slinkpath == nil)
01122 || (linkpath != nil && Slinkpath != nil
01123 && strcmp(linkpath, Slinkpath) == 0)
01124 )) {
01125 linkpath= Slinkpath;
01126 return 1;
01127 }
01128
01129 if ((st.st_mode & S_IFMT) != (Sst.st_mode & S_IFMT)) return 1;
01130
01131 switch (st.st_mode & S_IFMT) {
01132 case S_IFDIR:
01133 return 0;
01134 case S_IFBLK:
01135 case S_IFCHR:
01136 return st.st_rdev != Sst.st_rdev;
01137 case S_IFREG:
01138 if (install) return Sst.st_mtime > st.st_mtime;
01139 return st.st_size != Sst.st_size
01140 || st.st_mtime != Sst.st_mtime;
01141 case S_IFIFO: return 0;
01142 #ifdef S_IFLNK
01143 case S_IFLNK: return strcmp(lnkpth, Slnkpth) != 0;
01144 #endif
01145 default: return 1;
01146 }
01147 }
01148
01149 static void compare()
01150
01151 {
01152 if (different()) {
01153 if (!force) {
01154 printf("%sing %s (delete + add)\n",
01155 Sst.st_mtime < st.st_mtime ? "Restor" : "Updat",
01156 path);
01157 }
01158 if (delete(1)) add(1);
01159 } else {
01160 if (!install) setmodes(0);
01161
01162 if (S_ISDIR(st.st_mode)) {
01163 order(ENTER);
01164 enter();
01165 }
01166 }
01167 }
01168
01169 static int done= 0, Sdone= 0;
01170
01171 static enum action { ADD, COMPARE, DELETE } action()
01172
01173
01174
01175 {
01176 int c;
01177 char *Sp, *p;
01178
01179 if (done) return ADD;
01180 if (Sdone) return DELETE;
01181
01182
01183 Sp= Spath;
01184 p= path;
01185 while (*Sp == *p && *Sp != 0) { Sp++; p++; }
01186 if (*Sp == '/') return ADD;
01187 if (*p == '/') return DELETE;
01188 return (c= strcmp(Sp, p)) == 0 ? COMPARE : c < 0 ? ADD : DELETE;
01189 }
01190
01191 static void master()
01192
01193 {
01194 enum action a= COMPARE;
01195
01196 umask(backup ? 0022 : 0000);
01197
01198 signal(SIGPIPE, SIG_IGN);
01199 signal(SIGHUP, bail_out);
01200 signal(SIGINT, bail_out);
01201 signal(SIGTERM, bail_out);
01202
01203 while (!done || !Sdone) {
01204 if (!Sdone && (a == ADD || a == COMPARE)) {
01205
01206 order(ADVANCE);
01207 switch (answer()) {
01208 case PATH:
01209 Slinkpath= nil;
01210 receive(Spath, sizeof(Spath));
01211 recstat(&Sst);
01212 break;
01213 case LINK:
01214 receive(Slnkpth, sizeof(Slnkpth));
01215 Slinkpath= Slnkpth;
01216 receive(Spath, sizeof(Spath));
01217 recstat(&Sst);
01218 break;
01219 case SYMLINK:
01220 Slinkpath= nil;
01221 receive(Slnkpth, sizeof(Slnkpth));
01222 receive(Spath, sizeof(Spath));
01223 recstat(&Sst);
01224 break;
01225 case DONE:
01226 Sdone= 1;
01227 break;
01228 default:
01229 fprintf(stderr,
01230 "%s: Strange answer from slave.\n",
01231 arg0);
01232 exit(1);
01233 }
01234 }
01235 if (!done && (a == COMPARE || a == DELETE)) {
01236
01237 if (!advance()) done= 1;
01238 }
01239
01240 if (done && Sdone) break;
01241
01242 switch (a= action()) {
01243 case ADD:
01244 add(0);
01245 break;
01246 case COMPARE:
01247 compare();
01248 break;
01249 case DELETE:
01250 delete(0);
01251 }
01252 fflush(stdout);
01253 }
01254 order(ex == 0 ? DIE : DIE_BAD);
01255 }
01256
01257 static void mediator()
01258
01259
01260
01261 {
01262 enum orders req;
01263
01264 for (;;) {
01265 switch (req= request()) {
01266 case DIE_BAD:
01267 ex= 1;
01268
01269 case DIE:
01270 order(DIE);
01271 return;
01272 case POSITIVE:
01273 order(ask('y') ? PASS_YES : PASS_NO);
01274 break;
01275 case NEGATIVE:
01276 order(ask('n') ? PASS_YES : PASS_NO);
01277 break;
01278 default:
01279 order(req);
01280 }
01281 }
01282 }
01283
01284 #define P_EXIT 1
01285 #define P_SHADOW 2
01286
01287 static void startprocess(proc, machine, path, p_flags)
01288 void (*proc)(); char *machine, *path; int p_flags;
01289 {
01290 char *argv[10], **argp= argv;
01291 char flags[10], *pfl= flags;
01292
01293 if (machine != nil) {
01294 char *u= machine, *m;
01295
01296 *argp++ = "rsh";
01297 if ((m= strchr(machine, '@')) != nil) {
01298 *m++ = 0;
01299 *argp++ = "-l";
01300 *argp++ = u;
01301 machine= m;
01302 }
01303 *argp++ = machine;
01304 } else
01305
01306 if (!(USE_SHADOWING && p_flags & P_SHADOW)) {
01307 if (chdir(path) < 0) {
01308 if (proc != master || errno != ENOENT
01309 || mkdir(path, 0700) < 0)
01310 perrx(path);
01311 if (chdir(path) < 0) perrx(path);
01312 printf("Destination directory %s created\n", path);
01313 }
01314 isvisible(path);
01315 isbackup(proc == slave);
01316 (*proc)();
01317 if (p_flags & P_EXIT) exit(ex);
01318 return;
01319 }
01320 *argp++ = SYNCNAME;
01321 *pfl++ = '-';
01322 if (interact) *pfl++ = 'i';
01323 if (install) *pfl++ = 'u';
01324 if (force) *pfl++ = 'f';
01325 *pfl= 0;
01326 *argp++ = flags;
01327 *argp++ = proc == slave ? SLAVENAME : MASTERNAME;
01328 *argp++ = path;
01329 *argp++ = nil;
01330 #ifdef DEBUG
01331 fprintf(stderr, "execlp(");
01332 for (argp= argv; *argp != nil; argp++) fprintf(stderr, "%s, ", *argp);
01333 fprintf(stderr, "nil);\n");
01334 #endif
01335 execvp(argv[0], argv);
01336 perrx(argv[0]);
01337 }
01338
01339 void splitcolon(path, amach, adir) char *path, **amach, **adir;
01340 {
01341 char *dir= path;
01342
01343 for (;;) {
01344 if (*dir == ':') {
01345 *dir++ = 0;
01346 *amach= path;
01347 *adir= dir;
01348 break;
01349 }
01350 if (*dir == 0 || *dir == '/') {
01351 *amach= nil;
01352 *adir= path;
01353 break;
01354 }
01355 dir++;
01356 }
01357 }
01358
01359 static void Usage()
01360 {
01361 fprintf(stderr,
01362 "Usage: %s [-iuf] [[user@]machine:]dir1 [[user@]machine:]dir2\n",
01363 arg0);
01364 exit(1);
01365 }
01366
01367 main(argc, argv) int argc; char **argv;
01368 {
01369 char *s_mach, *s_dir;
01370 char *m_mach, *m_dir;
01371 int m2s[2], s2m[2], m2m[2];
01372 int s_pid= 0, m_pid= 0;
01373 int r;
01374
01375 if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++;
01376
01377 while (argc>1 && argv[1][0] == '-') {
01378 char *f= argv[1]+1;
01379
01380 while (*f != 0) {
01381 switch (*f++) {
01382 case 'i': interact= 1; break;
01383 case 'u': install= 1; break;
01384 case 'f': force= 1; break;
01385 default: Usage();
01386 }
01387 }
01388 argc--;
01389 argv++;
01390 }
01391
01392 if (argc != 3) Usage();
01393
01394 if (strcmp(argv[1], SLAVENAME) == 0) {
01395 arg0= "Slave";
01396 splitcolon(argv[2], &s_mach, &s_dir);
01397 startprocess(slave, s_mach, s_dir, P_EXIT);
01398 } else
01399 if (strcmp(argv[1], MASTERNAME) == 0) {
01400 arg0= "Master";
01401 splitcolon(argv[2], &m_mach, &m_dir);
01402 startprocess(master, m_mach, m_dir, P_EXIT);
01403 }
01404
01405 splitcolon(argv[1], &s_mach, &s_dir);
01406 splitcolon(argv[2], &m_mach, &m_dir);
01407
01408
01409 if (pipe(m2s) < 0 || pipe(s2m) < 0) perrx("pipe()");
01410
01411 if (m_mach == nil) {
01412
01413 switch (s_pid= fork()) {
01414 case -1:
01415 perrx("fork()");
01416 case 0:
01417 dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]);
01418 dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]);
01419 arg0= "Slave";
01420 startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW);
01421 }
01422 chan[0]= s2m[0]; close(s2m[1]);
01423 chan[1]= m2s[1]; close(m2s[0]);
01424 startprocess(master, m_mach, m_dir, 0);
01425 } else
01426 if (s_mach == nil) {
01427
01428 switch (m_pid= fork()) {
01429 case -1:
01430 perrx("fork()");
01431 case 0:
01432 dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]);
01433 dup2(m2s[1], 1); close(m2s[0]); close(m2s[1]);
01434 arg0= "Master";
01435 startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW);
01436 }
01437 chan[0]= m2s[0]; close(m2s[1]);
01438 chan[1]= s2m[1]; close(s2m[0]);
01439 startprocess(slave, s_mach, s_dir, 0);
01440 } else {
01441
01442 if (pipe(m2m) < 0) perrx(pipe);
01443
01444 switch (s_pid= fork()) {
01445 case -1:
01446 perrx("fork()");
01447 case 0:
01448 dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]);
01449 dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]);
01450 close(m2m[0]); close(m2m[1]);
01451 arg0= "Slave";
01452 startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW);
01453 }
01454
01455 switch (m_pid= fork()) {
01456 case -1:
01457 perrx("fork()");
01458 case 0:
01459 dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]);
01460 close(m2s[0]); close(m2s[1]);
01461 dup2(m2m[1], 1); close(m2m[0]); close(m2m[1]);
01462 arg0= "Master";
01463 startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW);
01464 }
01465 close(s2m[0]); close(s2m[1]);
01466 chan[0]= m2m[0]; close(m2m[1]);
01467 chan[1]= m2s[1]; close(m2s[0]);
01468 mediator();
01469 }
01470 close(chan[0]);
01471 close(chan[1]);
01472
01473 alarm(15);
01474
01475 while (s_pid != 0 || m_pid != 0) {
01476 if ((r= wait((int *) nil)) < 0) perrx("wait()");
01477 if (r == s_pid) s_pid= 0;
01478 if (r == m_pid) m_pid= 0;
01479 }
01480 exit(ex);
01481 }