synctree.c

Go to the documentation of this file.
00001 /*      synctree 4.16 - Synchronise file tree.          Author: Kees J. Bot
00002  *                                                              5 Apr 1989
00003  * SYNOPSYS
00004  *      synctree [-iuf] [[user1@machine1:]dir1 [[user2@]machine2:]dir2
00005  *
00006  * Dir2 will then be synchronized to dir1 with respect to the flags.
00007  * The flags mean:
00008  *      -i  Be interactive on files other than directories too.
00009  *      -u  Only install files that are newer, i.e. that need an update.
00010  *      -f  Force.  Don't ask for confirmation, all answers are 'yes'.
00011  *
00012  * Hitting return lets synctree use its proposed answer.  Hitting CTRL-D is
00013  * like typing return to all questions that follow.
00014  *
00015  * If either of the directories to be synchronized contains the file ".backup"
00016  * then it is a backup directory.  The file ".backup" in this directory is
00017  * an array of mode information indexed on inode number.
00018  *
00019  * 89/04/05, Kees J. Bot - Birth of tree synchronizing program.
00020  * 92/02/02              - General overhaul, rcp(1) like syntax.
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 /*#define BLOCK_SIZE    1024*/
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 /* There were no symlinks in medieval times. */
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  /* Any number fits in this many bytes. */
00064 #define CHUNK     4096  /* Transfer files in this size chunks. */
00065 
00066 static int install= 0;  /* Install files, do not delete, update if newer. */
00067 static int interact= 0; /* Ask permission to install too. */
00068 static int force= 0;    /* Force trees to be completely equal. */
00069 static int backup= 0;   /* This tree is for backup. */
00070 
00071 static char SYNCNAME[]  = "synctree";
00072 static char SLAVENAME[] = "==SLAVE==";
00073 static char MASTERNAME[]= "==MASTER==";
00074 
00075 
00076 static char BACKUP[] = ".backup";       /* Backup filemodes. */
00077 static int filemodes;                   /* Filemodes fildes. */
00078 
00079 static int chan[2]= { 0, 1 };   /* In and output channel to opposite side. */
00080 
00081 #define BUCKSIZE (1+NUMBYTES+CHUNK)
00082 static char bucket[BUCKSIZE];   /* Put a lot of things here before sending. */
00083 static char *buckp= bucket;     /* Fill pointer. */
00084 static int buckn= 0;            /* # bytes in bucket. */
00085 
00086 enum orders {   /* What back breaking labour should the slave perform? */
00087         ENTER= 128,     /* Make ready to process contents of directory. */
00088         ADVANCE,        /* Determine next pathname and report it back. */
00089         CAT,            /* Send contents of file. */
00090         MORE,           /* Send more file contents. */
00091         CANCEL,         /* Current pathname is not installed, remove as link. */
00092         DIE,            /* Die with exit(0); */
00093         DIE_BAD,        /* exit(1); */
00094         POSITIVE,       /* Ask a yes/no question expecting yes. */
00095         NEGATIVE,       /* Same expecting no. */
00096         PASS_YES,       /* Pass this to the master will you. */
00097         PASS_NO         /* Same here. */
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,      /* Report pathname, and stat(2) info. */
00109         LINK,           /* Report linkname, pathname, and stat(2) info. */
00110         DATA,           /* Contents of file follow. */
00111         NODATA,         /* Can't read file. */
00112         DONE,           /* Nothing more to advance to. */
00113         SYMLINK,        /* Report symlinkname, pathname, and stat(2) info. */
00114         YES, NO         /* Result of an ASK. */
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;      /* basename(argv[0]) */
00136 static int ex= 0;       /* exit status. */
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 /* Support for per achitecture hidden files. */
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];   /* Holds pathname of file being worked on. */
00183 static char lnkpth[PATH_MAX+1]; /* (Sym)link to path. */
00184 static char *linkpath;          /* What path is, or should be linked to. */
00185 static struct stat st;          /* Corresponding stat(2) info. */
00186 static char Spath[PATH_MAX+1];  /* Slave is looking at this. */
00187 static char Slnkpth[PATH_MAX+1];/* (Sym)link to Spath. */
00188 static char *Slinkpath=nil;     /* Either nil or Slnkpth. */
00189 static struct stat Sst;         /* Slave's stat(2). */
00190 
00191 static char *addpath(p, n) char *p, *n;
00192 /* Add a name to the path, return pointer to end. */
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 {  /* A directory entry. */
00210         struct entry    *next;  /* Next entry in same directory */
00211         struct entry    *dir;   /* It is part of this directory */
00212         struct entry    *con;   /* If a dir, its contents */
00213         char            *name;  /* Name of this dir entry */
00214 };
00215 
00216 static struct entry *E= nil;            /* File being processed. */
00217 
00218 static void setpath(e) struct entry *e;
00219 /* Set path leading to e. */
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 /* This is either a stable mergesort, or thermal noise, I'm no longer sure.
00233  * It must be called like this: if (L!=nil && L->next!=nil) sort(&L);
00234  */
00235 {
00236         /* static */ struct entry *e1, **mid;  /* Need not be local */
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 /* Collect directory entries of E. */
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     /* Tell if a file, which stat(2) information in '*stp', has been seen
00304      * earlier by this function under a different name.  If not return a
00305      * null pointer with errno set to ENOENT, otherwise return the name of
00306      * the link.  Return a null pointer with an error code in errno for any
00307      * error, using E2BIG for a too long file name.
00308      *
00309      * Use link_islink(nil, nil) to reset all bookkeeping.
00310      *
00311      * Call for a file twice to delete it from the store.
00312      */
00313 
00314     typedef struct link {       /* In-memory link store. */
00315         struct link     *next;          /* Hash chain on inode number. */
00316         ino_t           ino;            /* File's inode number. */
00317         off_t           off;            /* Offset to more info in temp file. */
00318     } link_t;
00319     typedef struct dlink {      /* On-disk link store. */
00320         dev_t           dev;            /* Device number. */
00321         char            file[PATH_MAX]; /* Name of earlier seen link. */
00322     } dlink_t;
00323     static link_t *links[256];          /* Hash list of known links. */
00324     static int tfd= -1;                 /* Temp file for file name storage. */
00325     static dlink_t dlink;
00326     link_t *lp, **plp;
00327     size_t len;
00328     off_t off;
00329 
00330     if (file == nil) {
00331         /* Reset everything. */
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     /* The file must be a non-directory with more than one link. */
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             /* May have seen this link before.  Get it and check. */
00354             if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil;
00355             if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil;
00356 
00357             /* Only need to check the device number. */
00358             if (dlink.dev == stp->st_dev) {
00359                 if (strcmp(file, dlink.file) == 0) {
00360                     /* Called twice.  Forget about this link. */
00361                     *plp= lp->next;
00362                     free(lp);
00363                     errno= ENOENT;
00364                     return nil;
00365                 }
00366 
00367                 /* Return the name of the earlier link. */
00368                 return dlink.file;
00369             }
00370         }
00371         plp= &lp->next;
00372     }
00373 
00374     /* First time I see this link.  Add it to the store. */
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 /* Returns the name of the file path is linked to.  If no such link can be
00413  * found, then path is added to the list and nil is returned.  If all the
00414  * links of a file have been seen, then it is removed from the list.
00415  * Directories are not seen as linkable.
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 /* Set backup status info, we know that backup is true. */
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 /* Get status information of file name, skipping some files.  Backup info
00443  * is inserted as needed.
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 /* Determine next pathname, return true on success. */
00478 {
00479         for (;;) {
00480                 if (E==nil) {   /* First call, enter root dir. */
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)      /* Dir's files must be processed. */
00489                         E= E->con;
00490                 else {
00491                         for (;;) {
00492                                 /* Remove E from it's parents list, then
00493                                  * try next entry, if none, go to parent dir.
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 /* Slave reads command sent by master. */
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                                 /* Master died, try to report it then follow. */
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                 /* Master using slave to print to stdout: */
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 /* Slave replies to master. */
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 /* Send number from least to most significant byte. */
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 /* Slave sends size and contents of buf. */
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 /* Carry out orders from the master, such as transmitting path names.
00628  * Note that the slave uses path, not Spath, the master uses Spath.
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                         /*FALL THROUGH*/
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 /* Execute a command and return its success or failure. */
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 /* Remove a directory and its contents. */
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 /* Master tells slave what to do. */
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 /* Master gets buf of size n from slave, doing multiple reads if needed. */
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 /* Master reads slave's reply. */
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 /* Read number as pack of bytes from least to most significant.  The data
00791  * is on the wire in little-endian format.  (Mostly run on PC's).
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 /* Master get's data from slave, by first reading size, then data. */
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 /* Ask for a yes or no, anything else means choose def. */
00853 {
00854         int y, c;
00855 
00856         if (chan[0] == 0) {
00857                 /* I'm running remote, tell the slave to ask. */
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 /* Add Spath to the filesystem. */
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                         /* Try to install instead. */
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 /* Delete path. */
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 /* Return true iff path and Spath are different. */
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 /* See if path and Spath are same. */
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 /* Look at path's of master and slave, compare them alphabetically to see
01173  * who is ahead of who, then tell what is to be done.
01174  */
01175 {
01176         int c;
01177         char *Sp, *p;
01178 
01179         if (done) return ADD;           /* Slave still has names. */
01180         if (Sdone) return DELETE;       /* Master has too many names. */
01181 
01182         /* Compare paths.  Let "a/a" come before "a.a". */
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 /* Synchronise file tree to that of its slave. */
01193 {
01194         enum action a= COMPARE; /* Trick first advances. */
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                         /* Slave advances. */
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                         /* Master advances. */
01237                         if (!advance()) done= 1;
01238                 }
01239 
01240                 if (done && Sdone) break;
01241 
01242                 switch (a= action()) {
01243                 case ADD:       /* Spath exists, path doesn't, add? */
01244                         add(0);
01245                         break;
01246                 case COMPARE:   /* They both exist, are they the same? */
01247                         compare();
01248                         break;
01249                 case DELETE:    /* path exists, Spath doesn't, delete? */
01250                         delete(0);
01251                 }
01252                 fflush(stdout); /* Don't keep user in suspense. */
01253         }
01254         order(ex == 0 ? DIE : DIE_BAD);
01255 }
01256 
01257 static void mediator()
01258 /* Sits at the local machine and passes orders from master to slave, both
01259  * on remote machines.  Only diagnostics and questions are handled.
01260  */
01261 {
01262         enum orders req;
01263 
01264         for (;;) {
01265                 switch (req= request()) {
01266                 case DIE_BAD:
01267                         ex= 1;
01268                         /*FALL THROUGH*/
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       /* Make sure process doesn't return. */
01285 #define P_SHADOW        2       /* Always use exec on 68000. */
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         /* Without this check it would run like a pig on an non MMU 68000: */
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         /* How difficult can plumbing be? */
01409         if (pipe(m2s) < 0 || pipe(s2m) < 0) perrx("pipe()");
01410 
01411         if (m_mach == nil) {
01412                 /* synctree [machine:]dir1 dir2 */
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                 /* synctree dir1 machine:dir2 */
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                 /* synctree machine1:dir1 machine2:dir2 */
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); /* Don't wait(2) forever. */
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 }

Generated on Fri Apr 14 22:57:12 2006 for minix by  doxygen 1.4.6