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>
41 #include "minix/config.h"
43 /*#define BLOCK_SIZE 1024*/
44 #define LITTLE_ENDIAN (CHIP == INTEL)
45 #define USE_SHADOWING (CHIP == M68000)
47 #define LITTLE_ENDIAN 0
48 #define USE_SHADOWING 0
56 /* There were no symlinks in medieval times. */
57 #define S_ISLNK(mode) (0)
59 #define symlink(path1, path2) (errno= ENOSYS, -1)
60 #define readlink(path, buf, len) (errno= ENOSYS, -1)
63 #define NUMBYTES 4 /* Any number fits in this many bytes. */
64 #define CHUNK 4096 /* Transfer files in this size chunks. */
66 static int install
= 0; /* Install files, do not delete, update if newer. */
67 static int interact
= 0; /* Ask permission to install too. */
68 static int force
= 0; /* Force trees to be completely equal. */
69 static int backup
= 0; /* This tree is for backup. */
71 static char SYNCNAME
[] = "synctree";
72 static char SLAVENAME
[] = "==SLAVE==";
73 static char MASTERNAME
[]= "==MASTER==";
76 static char BACKUP
[] = ".backup"; /* Backup filemodes. */
77 static int filemodes
; /* Filemodes fildes. */
79 static int chan
[2]= { 0, 1 }; /* In and output channel to opposite side. */
81 #define BUCKSIZE (1+NUMBYTES+CHUNK)
82 static char bucket
[BUCKSIZE
]; /* Put a lot of things here before sending. */
83 static char *buckp
= bucket
; /* Fill pointer. */
84 static int buckn
= 0; /* # bytes in bucket. */
86 enum orders
{ /* What back breaking labour should the slave perform? */
87 ENTER
= 128, /* Make ready to process contents of directory. */
88 ADVANCE
, /* Determine next pathname and report it back. */
89 CAT
, /* Send contents of file. */
90 MORE
, /* Send more file contents. */
91 CANCEL
, /* Current pathname is not installed, remove as link. */
92 DIE
, /* Die with exit(0); */
93 DIE_BAD
, /* exit(1); */
94 POSITIVE
, /* Ask a yes/no question expecting yes. */
95 NEGATIVE
, /* Same expecting no. */
96 PASS_YES
, /* Pass this to the master will you. */
97 PASS_NO
/* Same here. */
102 "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD",
103 "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO"
108 PATH
= 128, /* Report pathname, and stat(2) info. */
109 LINK
, /* Report linkname, pathname, and stat(2) info. */
110 DATA
, /* Contents of file follow. */
111 NODATA
, /* Can't read file. */
112 DONE
, /* Nothing more to advance to. */
113 SYMLINK
, /* Report symlinkname, pathname, and stat(2) info. */
114 YES
, NO
/* Result of an ASK. */
119 "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO"
122 #define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg)
124 #define DPRINTF(format, arg)
128 unsigned short md_mode
;
129 unsigned short md_uid
;
130 unsigned short md_gid
;
131 unsigned short md_rdev
;
132 unsigned short md_devsiz
;
135 static char *arg0
; /* basename(argv[0]) */
136 static int ex
= 0; /* exit status. */
138 static void because()
140 fprintf(stderr
, ": %s\n", strerror(errno
));
144 static void perr(label
) char *label
;
146 fprintf(stderr
, "%s: %s: %s\n", arg0
, label
, strerror(errno
));
150 static void perrx(label
) char *label
;
157 /* Support for per achitecture hidden files. */
158 static int transparent
= 0;
160 static void isvisible(name
) char *name
;
162 char *p
= name
+ strlen(name
);
164 while (p
> name
&& *--p
== '/') {}
166 if (p
> name
&& *p
== '@' && p
[-1] != '/') transparent
= 1;
169 #define transparent 0
170 #define isvisible(name) ((void) 0)
173 static void isbackup(slave
) int slave
;
175 if ((filemodes
= open(BACKUP
, slave
? O_RDONLY
: O_RDWR
)) >= 0)
178 if (errno
!= ENOENT
) perrx(BACKUP
);
182 static char path
[PATH_MAX
+1]; /* Holds pathname of file being worked on. */
183 static char lnkpth
[PATH_MAX
+1]; /* (Sym)link to path. */
184 static char *linkpath
; /* What path is, or should be linked to. */
185 static struct stat st
; /* Corresponding stat(2) info. */
186 static char Spath
[PATH_MAX
+1]; /* Slave is looking at this. */
187 static char Slnkpth
[PATH_MAX
+1];/* (Sym)link to Spath. */
188 static char *Slinkpath
=nil
; /* Either nil or Slnkpth. */
189 static struct stat Sst
; /* Slave's stat(2). */
191 static char *addpath(p
, n
) char *p
, *n
;
192 /* Add a name to the path, return pointer to end. */
194 if (p
- path
+ 1 + strlen(n
) > PATH_MAX
) {
196 fprintf(stderr
, "%s: %s/%s is too long.\n", arg0
, path
, n
);
197 fprintf(stderr
, "%s: Unable to continue.\n", arg0
);
200 if (p
== path
+1 && path
[0] == '.') p
= path
;
202 if (p
> path
) *p
++ = '/';
204 while (*n
!= 0) *p
++ = *n
++;
209 struct entry
{ /* A directory entry. */
210 struct entry
*next
; /* Next entry in same directory */
211 struct entry
*dir
; /* It is part of this directory */
212 struct entry
*con
; /* If a dir, its contents */
213 char *name
; /* Name of this dir entry */
216 static struct entry
*E
= nil
; /* File being processed. */
218 static void setpath(e
) struct entry
*e
;
219 /* Set path leading to e. */
227 pend
= addpath(pend
, e
->name
);
231 static void sort(ae
) struct entry
**ae
;
232 /* This is either a stable mergesort, or thermal noise, I'm no longer sure.
233 * It must be called like this: if (L!=nil && L->next!=nil) sort(&L);
236 /* static */ struct entry
*e1
, **mid
; /* Need not be local */
239 e1
= *(mid
= &(*ae
)->next
);
241 if ((e1
= e1
->next
) == nil
) break;
243 } while ((e1
= e1
->next
) != nil
);
248 if ((*ae
)->next
!= nil
) sort(ae
);
249 if (e2
->next
!= nil
) sort(&e2
);
253 if (strcmp(e1
->name
, e2
->name
)<=0) {
254 if ((e1
= *(ae
= &e1
->next
)) == nil
) {
260 e2
= *(ae
= &e2
->next
);
262 if (e2
== nil
) break;
268 /* Collect directory entries of E. */
270 struct entry
**last
= &E
->con
, *new;
274 if ((d
= opendir(path
)) == nil
) {
275 fprintf(stderr
, "%s: Can't read dir %s\n", arg0
, path
);
279 while ((e
= readdir(d
)) != nil
) {
280 if (e
->d_name
[0] == '.' && (e
->d_name
[1] == 0
281 || (e
->d_name
[1] == '.' && e
->d_name
[2] == 0)
284 new= (struct entry
*) malloc(sizeof(*new));
289 new->name
= (char *) malloc(strlen(e
->d_name
) + 1);
290 strcpy(new->name
, e
->d_name
);
295 if (E
->con
!= nil
&& E
->con
->next
!= nil
) sort(&E
->con
);
298 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
299 #define arraylimit(a) ((a) + arraysize(a))
301 static char *link_islink(struct stat
*stp
, const char *file
)
303 /* Tell if a file, which stat(2) information in '*stp', has been seen
304 * earlier by this function under a different name. If not return a
305 * null pointer with errno set to ENOENT, otherwise return the name of
306 * the link. Return a null pointer with an error code in errno for any
307 * error, using E2BIG for a too long file name.
309 * Use link_islink(nil, nil) to reset all bookkeeping.
311 * Call for a file twice to delete it from the store.
314 typedef struct link
{ /* In-memory link store. */
315 struct link
*next
; /* Hash chain on inode number. */
316 ino_t ino
; /* File's inode number. */
317 off_t off
; /* Offset to more info in temp file. */
319 typedef struct dlink
{ /* On-disk link store. */
320 dev_t dev
; /* Device number. */
321 char file
[PATH_MAX
]; /* Name of earlier seen link. */
323 static link_t
*links
[256]; /* Hash list of known links. */
324 static int tfd
= -1; /* Temp file for file name storage. */
325 static dlink_t dlink
;
331 /* Reset everything. */
332 for (plp
= links
; plp
< arraylimit(links
); plp
++) {
333 while ((lp
= *plp
) != nil
) {
338 if (tfd
!= -1) close(tfd
);
343 /* The file must be a non-directory with more than one link. */
344 if (S_ISDIR(stp
->st_mode
) || stp
->st_nlink
<= 1) {
349 plp
= &links
[stp
->st_ino
% arraysize(links
)];
351 while ((lp
= *plp
) != nil
) {
352 if (lp
->ino
== stp
->st_ino
) {
353 /* May have seen this link before. Get it and check. */
354 if (lseek(tfd
, lp
->off
, SEEK_SET
) == -1) return nil
;
355 if (read(tfd
, &dlink
, sizeof(dlink
)) < 0) return nil
;
357 /* Only need to check the device number. */
358 if (dlink
.dev
== stp
->st_dev
) {
359 if (strcmp(file
, dlink
.file
) == 0) {
360 /* Called twice. Forget about this link. */
367 /* Return the name of the earlier link. */
374 /* First time I see this link. Add it to the store. */
380 tfd
= open(tmp
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
382 if (errno
!= EEXIST
) return nil
;
389 if ((len
= strlen(file
)) >= PATH_MAX
) {
394 dlink
.dev
= stp
->st_dev
;
395 strcpy(dlink
.file
, file
);
396 len
+= offsetof(dlink_t
, file
) + 1;
397 if ((off
= lseek(tfd
, 0, SEEK_END
)) == -1) return nil
;
398 if (write(tfd
, &dlink
, len
) != len
) return nil
;
400 if ((lp
= malloc(sizeof(*lp
))) == nil
) return nil
;
402 lp
->ino
= stp
->st_ino
;
409 #define cancellink() ((void) islink())
411 static char *islink()
412 /* Returns the name of the file path is linked to. If no such link can be
413 * found, then path is added to the list and nil is returned. If all the
414 * links of a file have been seen, then it is removed from the list.
415 * Directories are not seen as linkable.
420 name
= link_islink(&st
, path
);
421 if (name
== nil
&& errno
!= ENOENT
) perrx(path
);
425 static void setstat(ino
, stp
) ino_t ino
; struct stat
*stp
;
426 /* Set backup status info, we know that backup is true. */
430 md
.md_mode
= stp
->st_mode
;
431 md
.md_uid
= stp
->st_uid
;
432 md
.md_gid
= stp
->st_gid
;
433 md
.md_rdev
= stp
->st_rdev
;
434 md
.md_devsiz
= stp
->st_size
/ 1024;
436 if (lseek(filemodes
, (off_t
) sizeof(md
) * (off_t
) ino
, 0) == -1
437 || write(filemodes
, (char *) &md
, sizeof(md
)) != sizeof(md
)
441 static int getstat(name
, stp
) char *name
; struct stat
*stp
;
442 /* Get status information of file name, skipping some files. Backup info
443 * is inserted as needed.
448 if (strcmp(name
, BACKUP
) == 0) return -1;
450 if (lstat(name
, stp
) < 0) return -1;
452 if (stp
->st_mode
== S_IFREG
&& stp
->st_mtime
== 0) return -1;
458 (off_t
) sizeof(md
) * (off_t
) stp
->st_ino
, 0) == -1
459 || read(filemodes
, (char *) &md
, sizeof(md
)) != sizeof(md
)
463 setstat(stp
->st_ino
, stp
);
465 stp
->st_mode
= md
.md_mode
;
466 stp
->st_uid
= md
.md_uid
;
467 stp
->st_gid
= md
.md_gid
;
468 stp
->st_rdev
= md
.md_rdev
;
469 if (S_ISBLK(stp
->st_mode
))
470 stp
->st_size
= (off_t
) md
.md_devsiz
* 1024;
477 /* Determine next pathname, return true on success. */
480 if (E
==nil
) { /* First call, enter root dir. */
481 E
= (struct entry
*) malloc(sizeof(*E
));
485 E
->name
= (char *) malloc(3);
486 strcpy(E
->name
, transparent
? ".@" : ".");
488 if (E
->con
!= nil
) /* Dir's files must be processed. */
492 /* Remove E from it's parents list, then
493 * try next entry, if none, go to parent dir.
495 struct entry
*junk
= E
, *parent
= E
->dir
;
497 if (parent
!= nil
) parent
->con
= E
->next
;
504 if ((E
= parent
) == nil
) return 0;
508 if (getstat(path
, &st
) == 0) {
509 if (S_ISLNK(st
.st_mode
)) {
512 if ((n
= readlink(path
, lnkpth
, PATH_MAX
)) >= 0)
521 if (errno
!= 0 && errno
!= ENOENT
) perr(path
);
525 DPRINTF("%s: path = %s\n", path
);
529 static enum orders
request()
530 /* Slave reads command sent by master. */
532 static char buf
[64], *bp
;
538 if ((n
= read(chan
[0], buf
, (int) sizeof(buf
))) <= 0) {
539 if (n
< 0) perrx("request()");
540 /* Master died, try to report it then follow. */
542 "%s: Master died prematurely.\n", arg0
);
549 if (req
>= (int) ENTER
) break;
551 /* Master using slave to print to stdout: */
554 DPRINTF("%s: request() == %s\n", ORDERS
[req
- (int) ENTER
]);
556 return (enum orders
) req
;
563 DPRINTF("%s: reporting now!\n", 0);
569 if (r
> (512 << sizeof(char*))) r
= (512 << sizeof(char*));
571 if ((r
= write(chan
[1], buckp
, r
)) < 0) perrx("report()");
580 static void inform(a
) enum answers a
;
581 /* Slave replies to master. */
583 DPRINTF("%s: inform(%s)\n", ANSWERS
[(int) a
- (int) PATH
]);
589 #define wwrite(buf, n) (memcpy(buckp, (buf), (n)), buckp+= (n), buckn+= (n))
591 static void sendnum(n
) long n
;
592 /* Send number from least to most significant byte. */
595 wwrite((char *) &n
, sizeof(n
));
599 buf
[0]= (char) (n
>> 0);
600 buf
[1]= (char) (n
>> 8);
601 buf
[2]= (char) (n
>> 16);
602 buf
[3]= (char) (n
>> 24);
603 wwrite(buf
, sizeof(buf
));
607 static void send(buf
, n
) char *buf
; int n
;
608 /* Slave sends size and contents of buf. */
611 if (n
> 0) wwrite(buf
, (size_t) n
);
614 static void sendstat(stp
) struct stat
*stp
;
616 sendnum((long) stp
->st_mode
);
617 sendnum((long) stp
->st_uid
);
618 sendnum((long) stp
->st_gid
);
619 sendnum((long) stp
->st_rdev
);
620 sendnum((long) stp
->st_size
);
621 sendnum((long) stp
->st_mtime
);
627 /* Carry out orders from the master, such as transmitting path names.
628 * Note that the slave uses path, not Spath, the master uses Spath.
633 enum { run
, done
, die
} state
= run
;
641 if (!advance() || state
== done
) {
647 send(linkpath
, strlen(linkpath
) + 1);
649 if (S_ISLNK(st
.st_mode
)) {
651 send(lnkpth
, strlen(lnkpth
) + 1);
655 send(path
, strlen(path
) + 1);
660 if ((f
= open(path
, O_RDONLY
))<0) {
661 fprintf(stderr
, "%s: Can't open %s",
669 n
= read(f
, buf
, sizeof(buf
));
670 if (n
< 0) perr(path
);
686 inform(ask('y') ? YES
: NO
);
689 inform(ask('n') ? YES
: NO
);
698 fprintf(stderr
, "%s: strange request\n", arg0
);
702 } while (state
!= die
);
705 static int execute(argv
) char **argv
;
706 /* Execute a command and return its success or failure. */
710 if ((pid
= fork())<0) {
715 execvp(argv
[0], argv
);
718 while ((r
= wait(&status
)) != pid
) {
727 static int removedir(dir
) char *dir
;
728 /* Remove a directory and its contents. */
730 static char *argv
[] = { "rm", "-r", nil
, nil
};
732 printf("(rm -r %s)\n", dir
);
735 return execute(argv
);
738 static void order(o
) enum orders o
;
739 /* Master tells slave what to do. */
743 DPRINTF("%s: order(%s)\n", ORDERS
[o
- (int) ENTER
]);
745 if (write(chan
[1], &c
, 1) != 1) perrx("order()");
748 static void rread(buf
, n
) char *buf
; int n
;
749 /* Master gets buf of size n from slave, doing multiple reads if needed. */
755 switch (buckn
= read(chan
[0], bucket
, BUCKSIZE
)) {
757 perrx("reply channel from slave");
760 "%s: slave died prematurely.\n",
766 r
= n
< buckn
? n
: buckn
;
767 memcpy(buf
, buckp
, r
);
775 static enum answers
answer()
776 /* Master reads slave's reply. */
784 DPRINTF("%s: answer() == %s\n", ANSWERS
[a
- (int) PATH
]);
786 return (enum answers
) a
;
790 /* Read number as pack of bytes from least to most significant. The data
791 * is on the wire in little-endian format. (Mostly run on PC's).
797 rread((char *) &n
, (int) sizeof(n
));
800 unsigned char buf
[NUMBYTES
];
802 rread(buf
, sizeof(buf
));
804 | ((unsigned) buf
[1] << 8)
805 | ((unsigned long) buf
[2] << 16)
806 | ((unsigned long) buf
[3] << 24);
810 static int receive(buf
, max
) char *buf
; int max
;
811 /* Master get's data from slave, by first reading size, then data. */
817 fprintf(stderr
, "%s: panic: Can't read %d bytes\n", arg0
, n
);
820 if (n
> 0) rread(buf
, n
);
824 static void recstat(stp
) struct stat
*stp
;
826 stp
->st_mode
= recnum();
827 stp
->st_uid
= recnum();
828 stp
->st_gid
= recnum();
829 stp
->st_rdev
= recnum();
830 stp
->st_size
= recnum();
831 stp
->st_mtime
= recnum();
839 if (tty
< 0) tty
= isatty(0);
841 if (feof(stdin
) || (c
= getchar()) == EOF
) {
843 if (tty
) putchar('\n');
846 if (!tty
) putchar(c
);
851 static int ask(def
) int def
;
852 /* Ask for a yes or no, anything else means choose def. */
857 /* I'm running remote, tell the slave to ask. */
859 order(def
== 'y' ? POSITIVE
: NEGATIVE
);
860 return answer() == YES
;
863 printf("? (%c) ", def
);
866 do c
= key(); while (c
== ' ' || c
== '\t');
870 while (c
!= '\n') c
= key();
872 if (y
!= 'y' && y
!= 'Y' && y
!= 'n' && y
!= 'N') y
= def
;
874 return y
== 'y' || y
== 'Y';
877 static void setmodes(silent
) int silent
;
885 if (backup
&& silent
) {
886 setstat(st
.st_ino
, &Sst
);
890 if (S_ISLNK(st
.st_mode
)) return;
892 if (errno
== 0 && st
.st_mode
!= Sst
.st_mode
) {
893 if (!backup
) chmod(Spath
, Sst
.st_mode
& 07777);
897 && (st
.st_uid
!= Sst
.st_uid
|| st
.st_gid
!= Sst
.st_gid
)
898 && (backup
|| geteuid() == 0)
901 if (!backup
) chown(Spath
, Sst
.st_uid
, Sst
.st_gid
);
905 if (backup
&& !silent
) setstat(st
.st_ino
, &Sst
);
907 if (errno
== 0 && S_ISREG(Sst
.st_mode
) && st
.st_mtime
!= Sst
.st_mtime
) {
909 tms
.modtime
= Sst
.st_mtime
;
915 fprintf(stderr
, "%s: Can't set modes of %s", arg0
, Spath
);
918 if (change
&& !silent
) {
919 printf("Mode changed of %s\n", Spath
);
923 static void makeold()
925 static struct utimbuf tms
= { 0, 0 };
927 if (utime(Spath
, &tms
) < 0) {
928 if (errno
!= ENOENT
) {
930 "%s: can't make %s look old", arg0
, Spath
);
934 fprintf(stderr
, "%s: made %s look old.\n", arg0
, Spath
);
940 static void bail_out(sig
) int sig
;
942 signal(sig
, SIG_IGN
);
944 fprintf(stderr
, "%s: Exiting after signal %d\n", arg0
, sig
);
947 fprintf(stderr
, "%s: was installing %s\n", arg0
, Spath
);
955 static int makenode(name
, mode
, addr
, size
)
956 char *name
; int mode
; dev_t addr
; off_t size
;
961 r
= mknod(name
, mode
, addr
);
963 if ((r
= creat(name
, 0644)) >= 0) close(r
);
968 static void add(update
) int update
;
969 /* Add Spath to the filesystem. */
973 int forced_update
= force
&& update
;
975 if (Slinkpath
!= nil
&& !S_ISLNK(Sst
.st_mode
)) {
976 if (interact
&& !update
) {
977 printf("Link %s to %s", Spath
, Slinkpath
);
978 if (!ask('n')) return;
980 if (link(Slinkpath
, Spath
) >= 0) {
981 printf("Linked %s to %s\n", Spath
, Slinkpath
);
985 "%s: Can't link %s to %s",
986 arg0
, Slinkpath
, Spath
);
988 /* Try to install instead. */
991 switch (Sst
.st_mode
& S_IFMT
) {
994 printf("Add dir %s", Spath
);
995 if (!ask('n')) return;
997 if (mkdir(Spath
, backup
? 0755 : Sst
.st_mode
) < 0) {
1001 printf("Directory %s created.\n", Spath
);
1007 if (interact
&& !update
) {
1008 printf("Create special file %s", Spath
);
1009 if (!ask('n')) { order(CANCEL
); return; }
1011 if (makenode(Spath
, Sst
.st_mode
, Sst
.st_rdev
, Sst
.st_size
)<0) {
1013 "%s: Can't create special file %s",
1018 printf("Special file %s created\n", Spath
);
1022 if (interact
&& !update
) {
1023 printf("Install %s -> %s", Spath
, Slnkpth
);
1024 if (!ask('n')) { order(CANCEL
); return; }
1026 if (symlink(Slnkpth
, Spath
) < 0) {
1027 fprintf(stderr
, "%s: Can't create symlink %s",
1032 printf("%s %s -> %s\n",
1033 forced_update
? "Updated: " : "Installed:",
1038 if (interact
&& !update
) {
1039 printf("Install %s", Spath
);
1040 if (!ask('n')) { order(CANCEL
); return; }
1043 if (answer() != DATA
) return;
1046 if ((f
= creat(Spath
, backup
? 0644 : Sst
.st_mode
&07777)) < 0) {
1048 fprintf(stderr
, "%s: Can't create %s", arg0
, Spath
);
1052 while ((n
= receive(buf
, sizeof(buf
)))>0) {
1053 if (f
>= 0 && write(f
, buf
, n
) != n
) {
1054 fprintf(stderr
, "%s: Write error on %s",
1061 fprintf(stderr
, "%s: Slave read err on %s\n",
1064 if (f
>= 0) close(f
);
1065 if (n
< 0 || f
< 0) { makeold(); busy
= 0; return; }
1069 Sst
.st_mtime
< st
.st_mtime
? "Restored: " :
1077 "%s: Won't add file with strange mode %05o: %s\n",
1078 arg0
, Sst
.st_mode
, Spath
);
1085 static int delete(update
) int update
;
1088 int forced_update
= force
&& update
;
1090 if (S_ISDIR(st
.st_mode
)) {
1091 if (install
) return 0;
1093 printf("Delete dir %s", path
);
1094 if (!ask('n')) return 0;
1096 if (!removedir(path
)) { ex
= 1; return 0; }
1097 if (!forced_update
) printf("Directory %s deleted.\n", path
);
1101 if (install
&& !update
) return 0;
1104 printf("Delete %s", path
);
1105 if (!ask((interact
&& !update
) ? 'n' : 'y')) return 0;
1108 if (unlink(path
)<0) {
1109 fprintf(stderr
, "Can't delete %s", path
);
1114 if (!forced_update
) printf("Deleted: %s\n", path
);
1118 static int different()
1119 /* Return true iff path and Spath are different. */
1121 if (! ( (linkpath
== nil
&& Slinkpath
== nil
)
1122 || (linkpath
!= nil
&& Slinkpath
!= nil
1123 && strcmp(linkpath
, Slinkpath
) == 0)
1125 linkpath
= Slinkpath
;
1129 if ((st
.st_mode
& S_IFMT
) != (Sst
.st_mode
& S_IFMT
)) return 1;
1131 switch (st
.st_mode
& S_IFMT
) {
1136 return st
.st_rdev
!= Sst
.st_rdev
;
1138 if (install
) return Sst
.st_mtime
> st
.st_mtime
;
1139 return st
.st_size
!= Sst
.st_size
1140 || st
.st_mtime
!= Sst
.st_mtime
;
1141 case S_IFIFO
: return 0;
1143 case S_IFLNK
: return strcmp(lnkpth
, Slnkpth
) != 0;
1149 static void compare()
1150 /* See if path and Spath are same. */
1154 printf("%sing %s (delete + add)\n",
1155 Sst
.st_mtime
< st
.st_mtime
? "Restor" : "Updat",
1158 if (delete(1)) add(1);
1160 if (!install
) setmodes(0);
1162 if (S_ISDIR(st
.st_mode
)) {
1169 static int done
= 0, Sdone
= 0;
1171 static enum action
{ ADD
, COMPARE
, DELETE
} action()
1172 /* Look at path's of master and slave, compare them alphabetically to see
1173 * who is ahead of who, then tell what is to be done.
1179 if (done
) return ADD
; /* Slave still has names. */
1180 if (Sdone
) return DELETE
; /* Master has too many names. */
1182 /* Compare paths. Let "a/a" come before "a.a". */
1185 while (*Sp
== *p
&& *Sp
!= 0) { Sp
++; p
++; }
1186 if (*Sp
== '/') return ADD
;
1187 if (*p
== '/') return DELETE
;
1188 return (c
= strcmp(Sp
, p
)) == 0 ? COMPARE
: c
< 0 ? ADD
: DELETE
;
1191 static void master()
1192 /* Synchronise file tree to that of its slave. */
1194 enum action a
= COMPARE
; /* Trick first advances. */
1196 umask(backup
? 0022 : 0000);
1198 signal(SIGPIPE
, SIG_IGN
);
1199 signal(SIGHUP
, bail_out
);
1200 signal(SIGINT
, bail_out
);
1201 signal(SIGTERM
, bail_out
);
1203 while (!done
|| !Sdone
) {
1204 if (!Sdone
&& (a
== ADD
|| a
== COMPARE
)) {
1205 /* Slave advances. */
1210 receive(Spath
, sizeof(Spath
));
1214 receive(Slnkpth
, sizeof(Slnkpth
));
1216 receive(Spath
, sizeof(Spath
));
1221 receive(Slnkpth
, sizeof(Slnkpth
));
1222 receive(Spath
, sizeof(Spath
));
1230 "%s: Strange answer from slave.\n",
1235 if (!done
&& (a
== COMPARE
|| a
== DELETE
)) {
1236 /* Master advances. */
1237 if (!advance()) done
= 1;
1240 if (done
&& Sdone
) break;
1242 switch (a
= action()) {
1243 case ADD
: /* Spath exists, path doesn't, add? */
1246 case COMPARE
: /* They both exist, are they the same? */
1249 case DELETE
: /* path exists, Spath doesn't, delete? */
1252 fflush(stdout
); /* Don't keep user in suspense. */
1254 order(ex
== 0 ? DIE
: DIE_BAD
);
1257 static void mediator()
1258 /* Sits at the local machine and passes orders from master to slave, both
1259 * on remote machines. Only diagnostics and questions are handled.
1265 switch (req
= request()) {
1273 order(ask('y') ? PASS_YES
: PASS_NO
);
1276 order(ask('n') ? PASS_YES
: PASS_NO
);
1284 #define P_EXIT 1 /* Make sure process doesn't return. */
1285 #define P_SHADOW 2 /* Always use exec on 68000. */
1287 static void startprocess(proc
, machine
, path
, p_flags
)
1288 void (*proc
)(); char *machine
, *path
; int p_flags
;
1290 char *argv
[10], **argp
= argv
;
1291 char flags
[10], *pfl
= flags
;
1293 if (machine
!= nil
) {
1294 char *u
= machine
, *m
;
1297 if ((m
= strchr(machine
, '@')) != nil
) {
1305 /* Without this check it would run like a pig on an non MMU 68000: */
1306 if (!(USE_SHADOWING
&& p_flags
& P_SHADOW
)) {
1307 if (chdir(path
) < 0) {
1308 if (proc
!= master
|| errno
!= ENOENT
1309 || mkdir(path
, 0700) < 0)
1311 if (chdir(path
) < 0) perrx(path
);
1312 printf("Destination directory %s created\n", path
);
1315 isbackup(proc
== slave
);
1317 if (p_flags
& P_EXIT
) exit(ex
);
1322 if (interact
) *pfl
++ = 'i';
1323 if (install
) *pfl
++ = 'u';
1324 if (force
) *pfl
++ = 'f';
1327 *argp
++ = proc
== slave
? SLAVENAME
: MASTERNAME
;
1331 fprintf(stderr
, "execlp(");
1332 for (argp
= argv
; *argp
!= nil
; argp
++) fprintf(stderr
, "%s, ", *argp
);
1333 fprintf(stderr
, "nil);\n");
1335 execvp(argv
[0], argv
);
1339 void splitcolon(path
, amach
, adir
) char *path
, **amach
, **adir
;
1350 if (*dir
== 0 || *dir
== '/') {
1362 "Usage: %s [-iuf] [[user@]machine:]dir1 [[user@]machine:]dir2\n",
1367 main(argc
, argv
) int argc
; char **argv
;
1369 char *s_mach
, *s_dir
;
1370 char *m_mach
, *m_dir
;
1371 int m2s
[2], s2m
[2], m2m
[2];
1372 int s_pid
= 0, m_pid
= 0;
1375 if ((arg0
= strrchr(argv
[0], '/')) == nil
) arg0
= argv
[0]; else arg0
++;
1377 while (argc
>1 && argv
[1][0] == '-') {
1382 case 'i': interact
= 1; break;
1383 case 'u': install
= 1; break;
1384 case 'f': force
= 1; break;
1392 if (argc
!= 3) Usage();
1394 if (strcmp(argv
[1], SLAVENAME
) == 0) {
1396 splitcolon(argv
[2], &s_mach
, &s_dir
);
1397 startprocess(slave
, s_mach
, s_dir
, P_EXIT
);
1399 if (strcmp(argv
[1], MASTERNAME
) == 0) {
1401 splitcolon(argv
[2], &m_mach
, &m_dir
);
1402 startprocess(master
, m_mach
, m_dir
, P_EXIT
);
1405 splitcolon(argv
[1], &s_mach
, &s_dir
);
1406 splitcolon(argv
[2], &m_mach
, &m_dir
);
1408 /* How difficult can plumbing be? */
1409 if (pipe(m2s
) < 0 || pipe(s2m
) < 0) perrx("pipe()");
1411 if (m_mach
== nil
) {
1412 /* synctree [machine:]dir1 dir2 */
1413 switch (s_pid
= fork()) {
1417 dup2(m2s
[0], 0); close(m2s
[0]); close(m2s
[1]);
1418 dup2(s2m
[1], 1); close(s2m
[0]); close(s2m
[1]);
1420 startprocess(slave
, s_mach
, s_dir
, P_EXIT
|P_SHADOW
);
1422 chan
[0]= s2m
[0]; close(s2m
[1]);
1423 chan
[1]= m2s
[1]; close(m2s
[0]);
1424 startprocess(master
, m_mach
, m_dir
, 0);
1426 if (s_mach
== nil
) {
1427 /* synctree dir1 machine:dir2 */
1428 switch (m_pid
= fork()) {
1432 dup2(s2m
[0], 0); close(s2m
[0]); close(s2m
[1]);
1433 dup2(m2s
[1], 1); close(m2s
[0]); close(m2s
[1]);
1435 startprocess(master
, m_mach
, m_dir
, P_EXIT
|P_SHADOW
);
1437 chan
[0]= m2s
[0]; close(m2s
[1]);
1438 chan
[1]= s2m
[1]; close(s2m
[0]);
1439 startprocess(slave
, s_mach
, s_dir
, 0);
1441 /* synctree machine1:dir1 machine2:dir2 */
1442 if (pipe(m2m
) < 0) perrx(pipe
);
1444 switch (s_pid
= fork()) {
1448 dup2(m2s
[0], 0); close(m2s
[0]); close(m2s
[1]);
1449 dup2(s2m
[1], 1); close(s2m
[0]); close(s2m
[1]);
1450 close(m2m
[0]); close(m2m
[1]);
1452 startprocess(slave
, s_mach
, s_dir
, P_EXIT
|P_SHADOW
);
1455 switch (m_pid
= fork()) {
1459 dup2(s2m
[0], 0); close(s2m
[0]); close(s2m
[1]);
1460 close(m2s
[0]); close(m2s
[1]);
1461 dup2(m2m
[1], 1); close(m2m
[0]); close(m2m
[1]);
1463 startprocess(master
, m_mach
, m_dir
, P_EXIT
|P_SHADOW
);
1465 close(s2m
[0]); close(s2m
[1]);
1466 chan
[0]= m2m
[0]; close(m2m
[1]);
1467 chan
[1]= m2s
[1]; close(m2s
[0]);
1473 alarm(15); /* Don't wait(2) forever. */
1475 while (s_pid
!= 0 || m_pid
!= 0) {
1476 if ((r
= wait((int *) nil
)) < 0) perrx("wait()");
1477 if (r
== s_pid
) s_pid
= 0;
1478 if (r
== m_pid
) m_pid
= 0;