1 /* synctree 4.16 - Synchronise file tree. Author: Kees J. Bot
4 * synctree [-iuf] [[user1@machine1:]dir1 [[user2@]machine2:]dir2
6 * Dir2 will then be synchronized to dir1 with respect to the flags.
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'.
12 * Hitting return lets synctree use its proposed answer. Hitting CTRL-D is
13 * like typing return to all questions that follow.
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.
19 * 89/04/05, Kees J. Bot - Birth of tree synchronizing program.
20 * 92/02/02 - General overhaul, rcp(1) like syntax.
24 #include <sys/types.h>
39 #define USE_SHADOWING 0
47 /* There were no symlinks in medieval times. */
48 #define S_ISLNK(mode) (0)
50 #define symlink(path1, path2) (errno= ENOSYS, -1)
51 #define readlink(path, buf, len) (errno= ENOSYS, -1)
54 #define NUMBYTES 4 /* Any number fits in this many bytes. */
55 #define CHUNK 4096 /* Transfer files in this size chunks. */
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. */
62 static char SYNCNAME
[] = "synctree";
63 static char SLAVENAME
[] = "==SLAVE==";
64 static char MASTERNAME
[]= "==MASTER==";
67 static char BACKUP
[] = ".backup"; /* Backup filemodes. */
68 static int filemodes
; /* Filemodes fildes. */
70 static int chan
[2]= { 0, 1 }; /* In and output channel to opposite side. */
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. */
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. */
93 "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD",
94 "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO"
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. */
110 "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO"
113 #define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg)
115 #define DPRINTF(format, arg)
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
;
126 static char *arg0
; /* basename(argv[0]) */
127 static int ex
= 0; /* exit status. */
129 static void because()
131 fprintf(stderr
, ": %s\n", strerror(errno
));
135 static void perr(label
) char *label
;
137 fprintf(stderr
, "%s: %s: %s\n", arg0
, label
, strerror(errno
));
141 static void perrx(label
) char *label
;
148 /* Support for per achitecture hidden files. */
149 static int transparent
= 0;
151 static void isvisible(name
) char *name
;
153 char *p
= name
+ strlen(name
);
155 while (p
> name
&& *--p
== '/') {}
157 if (p
> name
&& *p
== '@' && p
[-1] != '/') transparent
= 1;
160 #define transparent 0
161 #define isvisible(name) ((void) 0)
164 static void isbackup(slave
) int slave
;
166 if ((filemodes
= open(BACKUP
, slave
? O_RDONLY
: O_RDWR
)) >= 0)
169 if (errno
!= ENOENT
) perrx(BACKUP
);
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). */
182 static char *addpath(p
, n
) char *p
, *n
;
183 /* Add a name to the path, return pointer to end. */
185 if (p
- path
+ 1 + strlen(n
) > PATH_MAX
) {
187 fprintf(stderr
, "%s: %s/%s is too long.\n", arg0
, path
, n
);
188 fprintf(stderr
, "%s: Unable to continue.\n", arg0
);
191 if (p
== path
+1 && path
[0] == '.') p
= path
;
193 if (p
> path
) *p
++ = '/';
195 while (*n
!= 0) *p
++ = *n
++;
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 */
207 static struct entry
*E
= nil
; /* File being processed. */
209 static void setpath(e
) struct entry
*e
;
210 /* Set path leading to e. */
218 pend
= addpath(pend
, e
->name
);
222 static void sort(ae
) 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);
227 /* static */ struct entry
*e1
, **mid
; /* Need not be local */
230 e1
= *(mid
= &(*ae
)->next
);
232 if ((e1
= e1
->next
) == nil
) break;
234 } while ((e1
= e1
->next
) != nil
);
239 if ((*ae
)->next
!= nil
) sort(ae
);
240 if (e2
->next
!= nil
) sort(&e2
);
244 if (strcmp(e1
->name
, e2
->name
)<=0) {
245 if ((e1
= *(ae
= &e1
->next
)) == nil
) {
251 e2
= *(ae
= &e2
->next
);
253 if (e2
== nil
) break;
259 /* Collect directory entries of E. */
261 struct entry
**last
= &E
->con
, *new;
265 if ((d
= opendir(path
)) == nil
) {
266 fprintf(stderr
, "%s: Can't read dir %s\n", arg0
, path
);
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)
275 new= (struct entry
*) malloc(sizeof(*new));
280 new->name
= (char *) malloc(strlen(e
->d_name
) + 1);
281 strcpy(new->name
, e
->d_name
);
286 if (E
->con
!= nil
&& E
->con
->next
!= nil
) sort(&E
->con
);
289 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
290 #define arraylimit(a) ((a) + arraysize(a))
292 static char *link_islink(struct stat
*stp
, const char *file
)
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.
300 * Use link_islink(nil, nil) to reset all bookkeeping.
302 * Call for a file twice to delete it from the store.
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. */
310 typedef struct dlink
{ /* On-disk link store. */
311 dev_t dev
; /* Device number. */
312 char file
[PATH_MAX
]; /* Name of earlier seen link. */
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
;
322 /* Reset everything. */
323 for (plp
= links
; plp
< arraylimit(links
); plp
++) {
324 while ((lp
= *plp
) != nil
) {
329 if (tfd
!= -1) close(tfd
);
334 /* The file must be a non-directory with more than one link. */
335 if (S_ISDIR(stp
->st_mode
) || stp
->st_nlink
<= 1) {
340 plp
= &links
[stp
->st_ino
% arraysize(links
)];
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
;
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. */
358 /* Return the name of the earlier link. */
365 /* First time I see this link. Add it to the store. */
371 tfd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
373 if (errno
!= EEXIST
) return nil
;
380 if ((len
= strlen(file
)) >= PATH_MAX
) {
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
;
391 if ((lp
= malloc(sizeof(*lp
))) == nil
) return nil
;
393 lp
->ino
= stp
->st_ino
;
400 #define cancellink() ((void) islink())
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.
411 name
= link_islink(&st
, path
);
412 if (name
== nil
&& errno
!= ENOENT
) perrx(path
);
416 static void setstat(ino
, stp
) ino_t ino
; struct stat
*stp
;
417 /* Set backup status info, we know that backup is true. */
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;
427 if (lseek(filemodes
, (off_t
) sizeof(md
) * (off_t
) ino
, 0) == -1
428 || write(filemodes
, (char *) &md
, sizeof(md
)) != sizeof(md
)
432 static int getstat(name
, stp
) char *name
; struct stat
*stp
;
433 /* Get status information of file name, skipping some files. Backup info
434 * is inserted as needed.
439 if (strcmp(name
, BACKUP
) == 0) return -1;
441 if (lstat(name
, stp
) < 0) return -1;
443 if (stp
->st_mode
== S_IFREG
&& stp
->st_mtime
== 0) return -1;
449 (off_t
) sizeof(md
) * (off_t
) stp
->st_ino
, 0) == -1
450 || read(filemodes
, (char *) &md
, sizeof(md
)) != sizeof(md
)
454 setstat(stp
->st_ino
, stp
);
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;
468 /* Determine next pathname, return true on success. */
471 if (E
==nil
) { /* First call, enter root dir. */
472 E
= (struct entry
*) malloc(sizeof(*E
));
476 E
->name
= (char *) malloc(3);
477 strcpy(E
->name
, transparent
? ".@" : ".");
479 if (E
->con
!= nil
) /* Dir's files must be processed. */
483 /* Remove E from it's parents list, then
484 * try next entry, if none, go to parent dir.
486 struct entry
*junk
= E
, *parent
= E
->dir
;
488 if (parent
!= nil
) parent
->con
= E
->next
;
495 if ((E
= parent
) == nil
) return 0;
499 if (getstat(path
, &st
) == 0) {
500 if (S_ISLNK(st
.st_mode
)) {
503 if ((n
= readlink(path
, lnkpth
, PATH_MAX
)) >= 0)
512 if (errno
!= 0 && errno
!= ENOENT
) perr(path
);
516 DPRINTF("%s: path = %s\n", path
);
520 static enum orders
request()
521 /* Slave reads command sent by master. */
523 static char buf
[64], *bp
;
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. */
533 "%s: Master died prematurely.\n", arg0
);
540 if (req
>= (int) ENTER
) break;
542 /* Master using slave to print to stdout: */
545 DPRINTF("%s: request() == %s\n", ORDERS
[req
- (int) ENTER
]);
547 return (enum orders
) req
;
554 DPRINTF("%s: reporting now!\n", 0);
560 if (r
> (512 << sizeof(char*))) r
= (512 << sizeof(char*));
562 if ((r
= write(chan
[1], buckp
, r
)) < 0) perrx("report()");
571 static void inform(a
) enum answers a
;
572 /* Slave replies to master. */
574 DPRINTF("%s: inform(%s)\n", ANSWERS
[(int) a
- (int) PATH
]);
580 #define wwrite(buf, n) (memcpy(buckp, (buf), (n)), buckp+= (n), buckn+= (n))
582 static void sendnum(n
) long n
;
583 /* Send number from least to most significant byte. */
585 #if BYTE_ORDER == LITTLE_ENDIAN
586 wwrite((char *) &n
, sizeof(n
));
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
));
598 static void send(buf
, n
) char *buf
; int n
;
599 /* Slave sends size and contents of buf. */
602 if (n
> 0) wwrite(buf
, (size_t) n
);
605 static void sendstat(stp
) struct stat
*stp
;
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
);
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.
624 enum { run
, done
, die
} state
= run
;
632 if (!advance() || state
== done
) {
638 send(linkpath
, strlen(linkpath
) + 1);
640 if (S_ISLNK(st
.st_mode
)) {
642 send(lnkpth
, strlen(lnkpth
) + 1);
646 send(path
, strlen(path
) + 1);
651 if ((f
= open(path
, O_RDONLY
))<0) {
652 fprintf(stderr
, "%s: Can't open %s",
660 n
= read(f
, buf
, sizeof(buf
));
661 if (n
< 0) perr(path
);
677 inform(ask('y') ? YES
: NO
);
680 inform(ask('n') ? YES
: NO
);
689 fprintf(stderr
, "%s: strange request\n", arg0
);
693 } while (state
!= die
);
696 static int execute(argv
) char **argv
;
697 /* Execute a command and return its success or failure. */
701 if ((pid
= fork())<0) {
706 execvp(argv
[0], argv
);
709 while ((r
= wait(&status
)) != pid
) {
718 static int removedir(dir
) char *dir
;
719 /* Remove a directory and its contents. */
721 static char *argv
[] = { "rm", "-r", nil
, nil
};
723 printf("(rm -r %s)\n", dir
);
726 return execute(argv
);
729 static void order(o
) enum orders o
;
730 /* Master tells slave what to do. */
734 DPRINTF("%s: order(%s)\n", ORDERS
[o
- (int) ENTER
]);
736 if (write(chan
[1], &c
, 1) != 1) perrx("order()");
739 static void rread(buf
, n
) char *buf
; int n
;
740 /* Master gets buf of size n from slave, doing multiple reads if needed. */
746 switch (buckn
= read(chan
[0], bucket
, BUCKSIZE
)) {
748 perrx("reply channel from slave");
751 "%s: slave died prematurely.\n",
757 r
= n
< buckn
? n
: buckn
;
758 memcpy(buf
, buckp
, r
);
766 static enum answers
answer()
767 /* Master reads slave's reply. */
775 DPRINTF("%s: answer() == %s\n", ANSWERS
[a
- (int) PATH
]);
777 return (enum answers
) a
;
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).
785 #if BYTE_ORDER == LITTLE_ENDIAN
788 rread((char *) &n
, (int) sizeof(n
));
791 unsigned char buf
[NUMBYTES
];
793 rread(buf
, sizeof(buf
));
795 | ((unsigned) buf
[1] << 8)
796 | ((unsigned long) buf
[2] << 16)
797 | ((unsigned long) buf
[3] << 24);
801 static int receive(buf
, max
) char *buf
; int max
;
802 /* Master get's data from slave, by first reading size, then data. */
808 fprintf(stderr
, "%s: panic: Can't read %d bytes\n", arg0
, n
);
811 if (n
> 0) rread(buf
, n
);
815 static void recstat(stp
) struct stat
*stp
;
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();
830 if (tty
< 0) tty
= isatty(0);
832 if (feof(stdin
) || (c
= getchar()) == EOF
) {
834 if (tty
) putchar('\n');
837 if (!tty
) putchar(c
);
842 static int ask(def
) int def
;
843 /* Ask for a yes or no, anything else means choose def. */
848 /* I'm running remote, tell the slave to ask. */
850 order(def
== 'y' ? POSITIVE
: NEGATIVE
);
851 return answer() == YES
;
854 printf("? (%c) ", def
);
857 do c
= key(); while (c
== ' ' || c
== '\t');
861 while (c
!= '\n') c
= key();
863 if (y
!= 'y' && y
!= 'Y' && y
!= 'n' && y
!= 'N') y
= def
;
865 return y
== 'y' || y
== 'Y';
868 static void setmodes(silent
) int silent
;
876 if (backup
&& silent
) {
877 setstat(st
.st_ino
, &Sst
);
881 if (S_ISLNK(st
.st_mode
)) return;
883 if (errno
== 0 && st
.st_mode
!= Sst
.st_mode
) {
884 if (!backup
) chmod(Spath
, Sst
.st_mode
& 07777);
888 && (st
.st_uid
!= Sst
.st_uid
|| st
.st_gid
!= Sst
.st_gid
)
889 && (backup
|| geteuid() == 0)
892 if (!backup
) chown(Spath
, Sst
.st_uid
, Sst
.st_gid
);
896 if (backup
&& !silent
) setstat(st
.st_ino
, &Sst
);
898 if (errno
== 0 && S_ISREG(Sst
.st_mode
) && st
.st_mtime
!= Sst
.st_mtime
) {
900 tms
.modtime
= Sst
.st_mtime
;
906 fprintf(stderr
, "%s: Can't set modes of %s", arg0
, Spath
);
909 if (change
&& !silent
) {
910 printf("Mode changed of %s\n", Spath
);
914 static void makeold()
916 static struct utimbuf tms
= { 0, 0 };
918 if (utime(Spath
, &tms
) < 0) {
919 if (errno
!= ENOENT
) {
921 "%s: can't make %s look old", arg0
, Spath
);
925 fprintf(stderr
, "%s: made %s look old.\n", arg0
, Spath
);
931 static void bail_out(sig
) int sig
;
933 signal(sig
, SIG_IGN
);
935 fprintf(stderr
, "%s: Exiting after signal %d\n", arg0
, sig
);
938 fprintf(stderr
, "%s: was installing %s\n", arg0
, Spath
);
946 static int makenode(name
, mode
, addr
, size
)
947 char *name
; int mode
; dev_t addr
; off_t size
;
952 r
= mknod(name
, mode
, addr
);
954 if ((r
= creat(name
, 0644)) >= 0) close(r
);
959 static void add(update
) int update
;
960 /* Add Spath to the filesystem. */
964 int forced_update
= force
&& update
;
966 if (Slinkpath
!= nil
&& !S_ISLNK(Sst
.st_mode
)) {
967 if (interact
&& !update
) {
968 printf("Link %s to %s", Spath
, Slinkpath
);
969 if (!ask('n')) return;
971 if (link(Slinkpath
, Spath
) >= 0) {
972 printf("Linked %s to %s\n", Spath
, Slinkpath
);
976 "%s: Can't link %s to %s",
977 arg0
, Slinkpath
, Spath
);
979 /* Try to install instead. */
982 switch (Sst
.st_mode
& S_IFMT
) {
985 printf("Add dir %s", Spath
);
986 if (!ask('n')) return;
988 if (mkdir(Spath
, backup
? 0755 : Sst
.st_mode
) < 0) {
992 printf("Directory %s created.\n", Spath
);
998 if (interact
&& !update
) {
999 printf("Create special file %s", Spath
);
1000 if (!ask('n')) { order(ORDER_CANCEL
); return; }
1002 if (makenode(Spath
, Sst
.st_mode
, Sst
.st_rdev
, Sst
.st_size
)<0) {
1004 "%s: Can't create special file %s",
1009 printf("Special file %s created\n", Spath
);
1013 if (interact
&& !update
) {
1014 printf("Install %s -> %s", Spath
, Slnkpth
);
1015 if (!ask('n')) { order(ORDER_CANCEL
); return; }
1017 if (symlink(Slnkpth
, Spath
) < 0) {
1018 fprintf(stderr
, "%s: Can't create symlink %s",
1023 printf("%s %s -> %s\n",
1024 forced_update
? "Updated: " : "Installed:",
1029 if (interact
&& !update
) {
1030 printf("Install %s", Spath
);
1031 if (!ask('n')) { order(ORDER_CANCEL
); return; }
1034 if (answer() != DATA
) return;
1037 if ((f
= creat(Spath
, backup
? 0644 : Sst
.st_mode
&07777)) < 0) {
1039 fprintf(stderr
, "%s: Can't create %s", arg0
, Spath
);
1043 while ((n
= receive(buf
, sizeof(buf
)))>0) {
1044 if (f
>= 0 && write(f
, buf
, n
) != n
) {
1045 fprintf(stderr
, "%s: Write error on %s",
1052 fprintf(stderr
, "%s: Slave read err on %s\n",
1055 if (f
>= 0) close(f
);
1056 if (n
< 0 || f
< 0) { makeold(); busy
= 0; return; }
1060 Sst
.st_mtime
< st
.st_mtime
? "Restored: " :
1068 "%s: Won't add file with strange mode %05o: %s\n",
1069 arg0
, Sst
.st_mode
, Spath
);
1070 order(ORDER_CANCEL
);
1076 static int delete(update
) int update
;
1079 int forced_update
= force
&& update
;
1081 if (S_ISDIR(st
.st_mode
)) {
1082 if (install
) return 0;
1084 printf("Delete dir %s", path
);
1085 if (!ask('n')) return 0;
1087 if (!removedir(path
)) { ex
= 1; return 0; }
1088 if (!forced_update
) printf("Directory %s deleted.\n", path
);
1092 if (install
&& !update
) return 0;
1095 printf("Delete %s", path
);
1096 if (!ask((interact
&& !update
) ? 'n' : 'y')) return 0;
1099 if (unlink(path
)<0) {
1100 fprintf(stderr
, "Can't delete %s", path
);
1105 if (!forced_update
) printf("Deleted: %s\n", path
);
1109 static int different()
1110 /* Return true iff path and Spath are different. */
1112 if (! ( (linkpath
== nil
&& Slinkpath
== nil
)
1113 || (linkpath
!= nil
&& Slinkpath
!= nil
1114 && strcmp(linkpath
, Slinkpath
) == 0)
1116 linkpath
= Slinkpath
;
1120 if ((st
.st_mode
& S_IFMT
) != (Sst
.st_mode
& S_IFMT
)) return 1;
1122 switch (st
.st_mode
& S_IFMT
) {
1127 return st
.st_rdev
!= Sst
.st_rdev
;
1129 if (install
) return Sst
.st_mtime
> st
.st_mtime
;
1130 return st
.st_size
!= Sst
.st_size
1131 || st
.st_mtime
!= Sst
.st_mtime
;
1132 case S_IFIFO
: return 0;
1134 case S_IFLNK
: return strcmp(lnkpth
, Slnkpth
) != 0;
1140 static void compare()
1141 /* See if path and Spath are same. */
1145 printf("%sing %s (delete + add)\n",
1146 Sst
.st_mtime
< st
.st_mtime
? "Restor" : "Updat",
1149 if (delete(1)) add(1);
1151 if (!install
) setmodes(0);
1153 if (S_ISDIR(st
.st_mode
)) {
1160 static int done
= 0, Sdone
= 0;
1162 static enum action
{ ADD
, COMPARE
, DELETE
} action()
1163 /* Look at path's of master and slave, compare them alphabetically to see
1164 * who is ahead of who, then tell what is to be done.
1170 if (done
) return ADD
; /* Slave still has names. */
1171 if (Sdone
) return DELETE
; /* Master has too many names. */
1173 /* Compare paths. Let "a/a" come before "a.a". */
1176 while (*Sp
== *p
&& *Sp
!= 0) { Sp
++; p
++; }
1177 if (*Sp
== '/') return ADD
;
1178 if (*p
== '/') return DELETE
;
1179 return (c
= strcmp(Sp
, p
)) == 0 ? COMPARE
: c
< 0 ? ADD
: DELETE
;
1182 static void master()
1183 /* Synchronise file tree to that of its slave. */
1185 enum action a
= COMPARE
; /* Trick first advances. */
1187 umask(backup
? 0022 : 0000);
1189 signal(SIGPIPE
, SIG_IGN
);
1190 signal(SIGHUP
, bail_out
);
1191 signal(SIGINT
, bail_out
);
1192 signal(SIGTERM
, bail_out
);
1194 while (!done
|| !Sdone
) {
1195 if (!Sdone
&& (a
== ADD
|| a
== COMPARE
)) {
1196 /* Slave advances. */
1201 receive(Spath
, sizeof(Spath
));
1205 receive(Slnkpth
, sizeof(Slnkpth
));
1207 receive(Spath
, sizeof(Spath
));
1212 receive(Slnkpth
, sizeof(Slnkpth
));
1213 receive(Spath
, sizeof(Spath
));
1221 "%s: Strange answer from slave.\n",
1226 if (!done
&& (a
== COMPARE
|| a
== DELETE
)) {
1227 /* Master advances. */
1228 if (!advance()) done
= 1;
1231 if (done
&& Sdone
) break;
1233 switch (a
= action()) {
1234 case ADD
: /* Spath exists, path doesn't, add? */
1237 case COMPARE
: /* They both exist, are they the same? */
1240 case DELETE
: /* path exists, Spath doesn't, delete? */
1243 fflush(stdout
); /* Don't keep user in suspense. */
1245 order(ex
== 0 ? DIE
: DIE_BAD
);
1248 static void mediator()
1249 /* Sits at the local machine and passes orders from master to slave, both
1250 * on remote machines. Only diagnostics and questions are handled.
1256 switch (req
= request()) {
1264 order(ask('y') ? PASS_YES
: PASS_NO
);
1267 order(ask('n') ? PASS_YES
: PASS_NO
);
1275 #define P_EXIT 1 /* Make sure process doesn't return. */
1276 #define P_SHADOW 2 /* Always use exec on 68000. */
1278 static void startprocess(proc
, machine
, path
, p_flags
)
1279 void (*proc
)(); char *machine
, *path
; int p_flags
;
1281 char *argv
[10], **argp
= argv
;
1282 char flags
[10], *pfl
= flags
;
1284 if (machine
!= nil
) {
1285 char *u
= machine
, *m
;
1288 if ((m
= strchr(machine
, '@')) != nil
) {
1296 /* Without this check it would run like a pig on an non MMU 68000: */
1297 if (!(USE_SHADOWING
&& p_flags
& P_SHADOW
)) {
1298 if (chdir(path
) < 0) {
1299 if (proc
!= master
|| errno
!= ENOENT
1300 || mkdir(path
, 0700) < 0)
1302 if (chdir(path
) < 0) perrx(path
);
1303 printf("Destination directory %s created\n", path
);
1306 isbackup(proc
== slave
);
1308 if (p_flags
& P_EXIT
) exit(ex
);
1313 if (interact
) *pfl
++ = 'i';
1314 if (install
) *pfl
++ = 'u';
1315 if (force
) *pfl
++ = 'f';
1318 *argp
++ = proc
== slave
? SLAVENAME
: MASTERNAME
;
1322 fprintf(stderr
, "execlp(");
1323 for (argp
= argv
; *argp
!= nil
; argp
++) fprintf(stderr
, "%s, ", *argp
);
1324 fprintf(stderr
, "nil);\n");
1326 execvp(argv
[0], argv
);
1330 void splitcolon(path
, amach
, adir
) char *path
, **amach
, **adir
;
1341 if (*dir
== 0 || *dir
== '/') {
1353 "Usage: %s [-iuf] [[user@]machine:]dir1 [[user@]machine:]dir2\n",
1358 main(argc
, argv
) int argc
; char **argv
;
1360 char *s_mach
, *s_dir
;
1361 char *m_mach
, *m_dir
;
1362 int m2s
[2], s2m
[2], m2m
[2];
1363 int s_pid
= 0, m_pid
= 0;
1366 if ((arg0
= strrchr(argv
[0], '/')) == nil
) arg0
= argv
[0]; else arg0
++;
1368 while (argc
>1 && argv
[1][0] == '-') {
1373 case 'i': interact
= 1; break;
1374 case 'u': install
= 1; break;
1375 case 'f': force
= 1; break;
1383 if (argc
!= 3) Usage();
1385 if (strcmp(argv
[1], SLAVENAME
) == 0) {
1387 splitcolon(argv
[2], &s_mach
, &s_dir
);
1388 startprocess(slave
, s_mach
, s_dir
, P_EXIT
);
1390 if (strcmp(argv
[1], MASTERNAME
) == 0) {
1392 splitcolon(argv
[2], &m_mach
, &m_dir
);
1393 startprocess(master
, m_mach
, m_dir
, P_EXIT
);
1396 splitcolon(argv
[1], &s_mach
, &s_dir
);
1397 splitcolon(argv
[2], &m_mach
, &m_dir
);
1399 /* How difficult can plumbing be? */
1400 if (pipe(m2s
) < 0 || pipe(s2m
) < 0) perrx("pipe()");
1402 if (m_mach
== nil
) {
1403 /* synctree [machine:]dir1 dir2 */
1404 switch (s_pid
= fork()) {
1408 dup2(m2s
[0], 0); close(m2s
[0]); close(m2s
[1]);
1409 dup2(s2m
[1], 1); close(s2m
[0]); close(s2m
[1]);
1411 startprocess(slave
, s_mach
, s_dir
, P_EXIT
|P_SHADOW
);
1413 chan
[0]= s2m
[0]; close(s2m
[1]);
1414 chan
[1]= m2s
[1]; close(m2s
[0]);
1415 startprocess(master
, m_mach
, m_dir
, 0);
1417 if (s_mach
== nil
) {
1418 /* synctree dir1 machine:dir2 */
1419 switch (m_pid
= fork()) {
1423 dup2(s2m
[0], 0); close(s2m
[0]); close(s2m
[1]);
1424 dup2(m2s
[1], 1); close(m2s
[0]); close(m2s
[1]);
1426 startprocess(master
, m_mach
, m_dir
, P_EXIT
|P_SHADOW
);
1428 chan
[0]= m2s
[0]; close(m2s
[1]);
1429 chan
[1]= s2m
[1]; close(s2m
[0]);
1430 startprocess(slave
, s_mach
, s_dir
, 0);
1432 /* synctree machine1:dir1 machine2:dir2 */
1433 if (pipe(m2m
) < 0) perrx(pipe
);
1435 switch (s_pid
= fork()) {
1439 dup2(m2s
[0], 0); close(m2s
[0]); close(m2s
[1]);
1440 dup2(s2m
[1], 1); close(s2m
[0]); close(s2m
[1]);
1441 close(m2m
[0]); close(m2m
[1]);
1443 startprocess(slave
, s_mach
, s_dir
, P_EXIT
|P_SHADOW
);
1446 switch (m_pid
= fork()) {
1450 dup2(s2m
[0], 0); close(s2m
[0]); close(s2m
[1]);
1451 close(m2s
[0]); close(m2s
[1]);
1452 dup2(m2m
[1], 1); close(m2m
[0]); close(m2m
[1]);
1454 startprocess(master
, m_mach
, m_dir
, P_EXIT
|P_SHADOW
);
1456 close(s2m
[0]); close(s2m
[1]);
1457 chan
[0]= m2m
[0]; close(m2m
[1]);
1458 chan
[1]= m2s
[1]; close(m2s
[0]);
1464 alarm(15); /* Don't wait(2) forever. */
1466 while (s_pid
!= 0 || m_pid
!= 0) {
1467 if ((r
= wait((int *) nil
)) < 0) perrx("wait()");
1468 if (r
== s_pid
) s_pid
= 0;
1469 if (r
== m_pid
) m_pid
= 0;