1433d6423SLionel Sambuc /* synctree 4.16 - Synchronise file tree. Author: Kees J. Bot
2433d6423SLionel Sambuc * 5 Apr 1989
3433d6423SLionel Sambuc * SYNOPSYS
4433d6423SLionel Sambuc * synctree [-iuf] [[user1@machine1:]dir1 [[user2@]machine2:]dir2
5433d6423SLionel Sambuc *
6433d6423SLionel Sambuc * Dir2 will then be synchronized to dir1 with respect to the flags.
7433d6423SLionel Sambuc * The flags mean:
8433d6423SLionel Sambuc * -i Be interactive on files other than directories too.
9433d6423SLionel Sambuc * -u Only install files that are newer, i.e. that need an update.
10433d6423SLionel Sambuc * -f Force. Don't ask for confirmation, all answers are 'yes'.
11433d6423SLionel Sambuc *
12433d6423SLionel Sambuc * Hitting return lets synctree use its proposed answer. Hitting CTRL-D is
13433d6423SLionel Sambuc * like typing return to all questions that follow.
14433d6423SLionel Sambuc *
15433d6423SLionel Sambuc * If either of the directories to be synchronized contains the file ".backup"
16433d6423SLionel Sambuc * then it is a backup directory. The file ".backup" in this directory is
17433d6423SLionel Sambuc * an array of mode information indexed on inode number.
18433d6423SLionel Sambuc *
19433d6423SLionel Sambuc * 89/04/05, Kees J. Bot - Birth of tree synchronizing program.
20433d6423SLionel Sambuc * 92/02/02 - General overhaul, rcp(1) like syntax.
21433d6423SLionel Sambuc */
22433d6423SLionel Sambuc
23433d6423SLionel Sambuc #define nil 0
24433d6423SLionel Sambuc #include <sys/types.h>
25433d6423SLionel Sambuc #include <stddef.h>
26433d6423SLionel Sambuc #include <stdio.h>
27433d6423SLionel Sambuc #include <sys/stat.h>
28433d6423SLionel Sambuc #include <utime.h>
29433d6423SLionel Sambuc #include <string.h>
30433d6423SLionel Sambuc #include <signal.h>
31433d6423SLionel Sambuc #include <dirent.h>
32433d6423SLionel Sambuc #include <errno.h>
33433d6423SLionel Sambuc #include <fcntl.h>
34433d6423SLionel Sambuc #include <stdlib.h>
35433d6423SLionel Sambuc #include <unistd.h>
36433d6423SLionel Sambuc #include <time.h>
37433d6423SLionel Sambuc #include <sys/wait.h>
38433d6423SLionel Sambuc
39433d6423SLionel Sambuc #define USE_SHADOWING 0
40433d6423SLionel Sambuc
41433d6423SLionel Sambuc #ifndef PATH_MAX
42433d6423SLionel Sambuc
43433d6423SLionel Sambuc #define PATH_MAX 1024
44433d6423SLionel Sambuc #endif
45433d6423SLionel Sambuc
46433d6423SLionel Sambuc #ifndef S_ISLNK
47433d6423SLionel Sambuc /* There were no symlinks in medieval times. */
48433d6423SLionel Sambuc #define S_ISLNK(mode) (0)
49433d6423SLionel Sambuc #define lstat stat
50433d6423SLionel Sambuc #define symlink(path1, path2) (errno= ENOSYS, -1)
51433d6423SLionel Sambuc #define readlink(path, buf, len) (errno= ENOSYS, -1)
52433d6423SLionel Sambuc #endif
53433d6423SLionel Sambuc
54433d6423SLionel Sambuc #define NUMBYTES 4 /* Any number fits in this many bytes. */
55433d6423SLionel Sambuc #define CHUNK 4096 /* Transfer files in this size chunks. */
56433d6423SLionel Sambuc
57433d6423SLionel Sambuc static int install= 0; /* Install files, do not delete, update if newer. */
58433d6423SLionel Sambuc static int interact= 0; /* Ask permission to install too. */
59433d6423SLionel Sambuc static int force= 0; /* Force trees to be completely equal. */
60433d6423SLionel Sambuc static int backup= 0; /* This tree is for backup. */
61433d6423SLionel Sambuc
62433d6423SLionel Sambuc static char SYNCNAME[] = "synctree";
63433d6423SLionel Sambuc static char SLAVENAME[] = "==SLAVE==";
64433d6423SLionel Sambuc static char MASTERNAME[]= "==MASTER==";
65433d6423SLionel Sambuc
66433d6423SLionel Sambuc
67433d6423SLionel Sambuc static char BACKUP[] = ".backup"; /* Backup filemodes. */
68433d6423SLionel Sambuc static int filemodes; /* Filemodes fildes. */
69433d6423SLionel Sambuc
70433d6423SLionel Sambuc static int chan[2]= { 0, 1 }; /* In and output channel to opposite side. */
71433d6423SLionel Sambuc
72433d6423SLionel Sambuc #define BUCKSIZE (1+NUMBYTES+CHUNK)
73433d6423SLionel Sambuc static char bucket[BUCKSIZE]; /* Put a lot of things here before sending. */
74433d6423SLionel Sambuc static char *buckp= bucket; /* Fill pointer. */
75433d6423SLionel Sambuc static int buckn= 0; /* # bytes in bucket. */
76433d6423SLionel Sambuc
77433d6423SLionel Sambuc enum orders { /* What back breaking labour should the slave perform? */
78433d6423SLionel Sambuc ENTER= 128, /* Make ready to process contents of directory. */
79433d6423SLionel Sambuc ADVANCE, /* Determine next pathname and report it back. */
80433d6423SLionel Sambuc CAT, /* Send contents of file. */
81433d6423SLionel Sambuc MORE, /* Send more file contents. */
82433d6423SLionel Sambuc ORDER_CANCEL, /* Current pathname is not installed, remove as link. */
83433d6423SLionel Sambuc DIE, /* Die with exit(0); */
84433d6423SLionel Sambuc DIE_BAD, /* exit(1); */
85433d6423SLionel Sambuc POSITIVE, /* Ask a yes/no question expecting yes. */
86433d6423SLionel Sambuc NEGATIVE, /* Same expecting no. */
87433d6423SLionel Sambuc PASS_YES, /* Pass this to the master will you. */
88433d6423SLionel Sambuc PASS_NO /* Same here. */
89433d6423SLionel Sambuc };
90433d6423SLionel Sambuc
91433d6423SLionel Sambuc #ifdef DEBUG
92433d6423SLionel Sambuc char *ORDERS[]= {
93433d6423SLionel Sambuc "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD",
94433d6423SLionel Sambuc "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO"
95433d6423SLionel Sambuc };
96433d6423SLionel Sambuc #endif
97433d6423SLionel Sambuc
98433d6423SLionel Sambuc enum answers {
99433d6423SLionel Sambuc PATH= 128, /* Report pathname, and stat(2) info. */
100433d6423SLionel Sambuc LINK, /* Report linkname, pathname, and stat(2) info. */
101433d6423SLionel Sambuc DATA, /* Contents of file follow. */
102433d6423SLionel Sambuc NODATA, /* Can't read file. */
103433d6423SLionel Sambuc DONE, /* Nothing more to advance to. */
104433d6423SLionel Sambuc SYMLINK, /* Report symlinkname, pathname, and stat(2) info. */
105433d6423SLionel Sambuc YES, NO /* Result of an ASK. */
106433d6423SLionel Sambuc };
107433d6423SLionel Sambuc
108433d6423SLionel Sambuc #ifdef DEBUG
109433d6423SLionel Sambuc char *ANSWERS[]= {
110433d6423SLionel Sambuc "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO"
111433d6423SLionel Sambuc };
112433d6423SLionel Sambuc
113433d6423SLionel Sambuc #define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg)
114433d6423SLionel Sambuc #else
115433d6423SLionel Sambuc #define DPRINTF(format, arg)
116433d6423SLionel Sambuc #endif
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc struct mode {
119433d6423SLionel Sambuc unsigned short md_mode;
120433d6423SLionel Sambuc unsigned short md_uid;
121433d6423SLionel Sambuc unsigned short md_gid;
122433d6423SLionel Sambuc unsigned short md_rdev;
123433d6423SLionel Sambuc unsigned short md_devsiz;
124433d6423SLionel Sambuc };
125433d6423SLionel Sambuc
126433d6423SLionel Sambuc static char *arg0; /* basename(argv[0]) */
127433d6423SLionel Sambuc static int ex= 0; /* exit status. */
128433d6423SLionel Sambuc
because()129433d6423SLionel Sambuc static void because()
130433d6423SLionel Sambuc {
131433d6423SLionel Sambuc fprintf(stderr, ": %s\n", strerror(errno));
132433d6423SLionel Sambuc ex= 1;
133433d6423SLionel Sambuc }
134433d6423SLionel Sambuc
perr(char * label)135433d6423SLionel Sambuc static void perr(char *label)
136433d6423SLionel Sambuc {
137433d6423SLionel Sambuc fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno));
138433d6423SLionel Sambuc ex= 1;
139433d6423SLionel Sambuc }
140433d6423SLionel Sambuc
perrx(char * label)141433d6423SLionel Sambuc static void perrx(char *label)
142433d6423SLionel Sambuc {
143433d6423SLionel Sambuc perr(label);
144433d6423SLionel Sambuc exit(1);
145433d6423SLionel Sambuc }
146433d6423SLionel Sambuc
147433d6423SLionel Sambuc #if S_HIDDEN
148433d6423SLionel Sambuc /* Support for per achitecture hidden files. */
149433d6423SLionel Sambuc static int transparent= 0;
150433d6423SLionel Sambuc
isvisible(char * name)151433d6423SLionel Sambuc static void isvisible(char *name)
152433d6423SLionel Sambuc {
153433d6423SLionel Sambuc char *p= name + strlen(name);
154433d6423SLionel Sambuc
155433d6423SLionel Sambuc while (p > name && *--p == '/') {}
156433d6423SLionel Sambuc
157433d6423SLionel Sambuc if (p > name && *p == '@' && p[-1] != '/') transparent= 1;
158433d6423SLionel Sambuc }
159433d6423SLionel Sambuc #else
160433d6423SLionel Sambuc #define transparent 0
161433d6423SLionel Sambuc #define isvisible(name) ((void) 0)
162433d6423SLionel Sambuc #endif
163433d6423SLionel Sambuc
isbackup(int slave)164433d6423SLionel Sambuc static void isbackup(int slave)
165433d6423SLionel Sambuc {
166433d6423SLionel Sambuc if ((filemodes= open(BACKUP, slave ? O_RDONLY : O_RDWR)) >= 0)
167433d6423SLionel Sambuc backup= 1;
168433d6423SLionel Sambuc else {
169433d6423SLionel Sambuc if (errno != ENOENT) perrx(BACKUP);
170433d6423SLionel Sambuc }
171433d6423SLionel Sambuc }
172433d6423SLionel Sambuc
173433d6423SLionel Sambuc static char path[PATH_MAX+1]; /* Holds pathname of file being worked on. */
174433d6423SLionel Sambuc static char lnkpth[PATH_MAX+1]; /* (Sym)link to path. */
175433d6423SLionel Sambuc static char *linkpath; /* What path is, or should be linked to. */
176433d6423SLionel Sambuc static struct stat st; /* Corresponding stat(2) info. */
177433d6423SLionel Sambuc static char Spath[PATH_MAX+1]; /* Slave is looking at this. */
178433d6423SLionel Sambuc static char Slnkpth[PATH_MAX+1];/* (Sym)link to Spath. */
179433d6423SLionel Sambuc static char *Slinkpath=nil; /* Either nil or Slnkpth. */
180433d6423SLionel Sambuc static struct stat Sst; /* Slave's stat(2). */
181433d6423SLionel Sambuc
addpath(char * p,char * n)182433d6423SLionel Sambuc static char *addpath(char *p, char *n)
183433d6423SLionel Sambuc /* Add a name to the path, return pointer to end. */
184433d6423SLionel Sambuc {
185433d6423SLionel Sambuc if (p - path + 1 + strlen(n) > PATH_MAX) {
186433d6423SLionel Sambuc *p= 0;
187433d6423SLionel Sambuc fprintf(stderr, "%s: %s/%s is too long.\n", arg0, path, n);
188433d6423SLionel Sambuc fprintf(stderr, "%s: Unable to continue.\n", arg0);
189433d6423SLionel Sambuc exit(1);
190433d6423SLionel Sambuc }
191433d6423SLionel Sambuc if (p == path+1 && path[0] == '.') p= path;
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc if (p > path) *p++ = '/';
194433d6423SLionel Sambuc
195433d6423SLionel Sambuc while (*n != 0) *p++ = *n++;
196433d6423SLionel Sambuc *p= 0;
197433d6423SLionel Sambuc return p;
198433d6423SLionel Sambuc }
199433d6423SLionel Sambuc
200433d6423SLionel Sambuc struct entry { /* A directory entry. */
201433d6423SLionel Sambuc struct entry *next; /* Next entry in same directory */
202433d6423SLionel Sambuc struct entry *dir; /* It is part of this directory */
203433d6423SLionel Sambuc struct entry *con; /* If a dir, its contents */
204433d6423SLionel Sambuc char *name; /* Name of this dir entry */
205433d6423SLionel Sambuc };
206433d6423SLionel Sambuc
207433d6423SLionel Sambuc static struct entry *E= nil; /* File being processed. */
208433d6423SLionel Sambuc
setpath(struct entry * e)209433d6423SLionel Sambuc static void setpath(struct entry *e)
210433d6423SLionel Sambuc /* Set path leading to e. */
211433d6423SLionel Sambuc {
212433d6423SLionel Sambuc static char *pend;
213433d6423SLionel Sambuc
214433d6423SLionel Sambuc if (e == nil)
215433d6423SLionel Sambuc pend= path;
216433d6423SLionel Sambuc else {
217433d6423SLionel Sambuc setpath(e->dir);
218433d6423SLionel Sambuc pend= addpath(pend, e->name);
219433d6423SLionel Sambuc }
220433d6423SLionel Sambuc }
221433d6423SLionel Sambuc
sort(struct entry ** ae)222433d6423SLionel Sambuc static void sort(struct entry **ae)
223433d6423SLionel Sambuc /* This is either a stable mergesort, or thermal noise, I'm no longer sure.
224433d6423SLionel Sambuc * It must be called like this: if (L!=nil && L->next!=nil) sort(&L);
225433d6423SLionel Sambuc */
226433d6423SLionel Sambuc {
227433d6423SLionel Sambuc /* static */ struct entry *e1, **mid; /* Need not be local */
228433d6423SLionel Sambuc struct entry *e2;
229433d6423SLionel Sambuc
230433d6423SLionel Sambuc e1= *(mid= &(*ae)->next);
231433d6423SLionel Sambuc do {
232433d6423SLionel Sambuc if ((e1= e1->next) == nil) break;
233433d6423SLionel Sambuc mid= &(*mid)->next;
234433d6423SLionel Sambuc } while ((e1= e1->next) != nil);
235433d6423SLionel Sambuc
236433d6423SLionel Sambuc e2= *mid;
237433d6423SLionel Sambuc *mid= nil;
238433d6423SLionel Sambuc
239433d6423SLionel Sambuc if ((*ae)->next != nil) sort(ae);
240433d6423SLionel Sambuc if (e2->next != nil) sort(&e2);
241433d6423SLionel Sambuc
242433d6423SLionel Sambuc e1= *ae;
243433d6423SLionel Sambuc for (;;) {
244433d6423SLionel Sambuc if (strcmp(e1->name, e2->name)<=0) {
245433d6423SLionel Sambuc if ((e1= *(ae= &e1->next)) == nil) {
246433d6423SLionel Sambuc *ae= e2;
247433d6423SLionel Sambuc break;
248433d6423SLionel Sambuc }
249433d6423SLionel Sambuc } else {
250433d6423SLionel Sambuc *ae= e2;
251433d6423SLionel Sambuc e2= *(ae= &e2->next);
252433d6423SLionel Sambuc *ae= e1;
253433d6423SLionel Sambuc if (e2 == nil) break;
254433d6423SLionel Sambuc }
255433d6423SLionel Sambuc }
256433d6423SLionel Sambuc }
257433d6423SLionel Sambuc
enter()258433d6423SLionel Sambuc static void enter()
259433d6423SLionel Sambuc /* Collect directory entries of E. */
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc struct entry **last= &E->con, *new;
262433d6423SLionel Sambuc struct dirent *e;
263433d6423SLionel Sambuc DIR *d;
264433d6423SLionel Sambuc
265433d6423SLionel Sambuc if ((d= opendir(path)) == nil) {
266433d6423SLionel Sambuc fprintf(stderr, "%s: Can't read dir %s\n", arg0, path);
267433d6423SLionel Sambuc return;
268433d6423SLionel Sambuc }
269433d6423SLionel Sambuc
270433d6423SLionel Sambuc while ((e= readdir(d)) != nil) {
271433d6423SLionel Sambuc if (e->d_name[0] == '.' && (e->d_name[1] == 0
272433d6423SLionel Sambuc || (e->d_name[1] == '.' && e->d_name[2] == 0)
273433d6423SLionel Sambuc )) continue;
274433d6423SLionel Sambuc
275433d6423SLionel Sambuc new= (struct entry *) malloc(sizeof(*new));
276433d6423SLionel Sambuc
277433d6423SLionel Sambuc new->next= nil;
278433d6423SLionel Sambuc new->dir= E;
279433d6423SLionel Sambuc new->con= nil;
280433d6423SLionel Sambuc new->name= (char *) malloc(strlen(e->d_name) + 1);
281433d6423SLionel Sambuc strcpy(new->name, e->d_name);
282433d6423SLionel Sambuc *last= new;
283433d6423SLionel Sambuc last= &new->next;
284433d6423SLionel Sambuc }
285433d6423SLionel Sambuc closedir(d);
286433d6423SLionel Sambuc if (E->con != nil && E->con->next != nil) sort(&E->con);
287433d6423SLionel Sambuc }
288433d6423SLionel Sambuc
289433d6423SLionel Sambuc #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
290433d6423SLionel Sambuc #define arraylimit(a) ((a) + arraysize(a))
291433d6423SLionel Sambuc
link_islink(struct stat * stp,const char * file)292433d6423SLionel Sambuc static char *link_islink(struct stat *stp, const char *file)
293433d6423SLionel Sambuc {
294433d6423SLionel Sambuc /* Tell if a file, which stat(2) information in '*stp', has been seen
295433d6423SLionel Sambuc * earlier by this function under a different name. If not return a
296433d6423SLionel Sambuc * null pointer with errno set to ENOENT, otherwise return the name of
297433d6423SLionel Sambuc * the link. Return a null pointer with an error code in errno for any
298433d6423SLionel Sambuc * error, using E2BIG for a too long file name.
299433d6423SLionel Sambuc *
300433d6423SLionel Sambuc * Use link_islink(nil, nil) to reset all bookkeeping.
301433d6423SLionel Sambuc *
302433d6423SLionel Sambuc * Call for a file twice to delete it from the store.
303433d6423SLionel Sambuc */
304433d6423SLionel Sambuc
305433d6423SLionel Sambuc typedef struct link { /* In-memory link store. */
306433d6423SLionel Sambuc struct link *next; /* Hash chain on inode number. */
307433d6423SLionel Sambuc ino_t ino; /* File's inode number. */
308433d6423SLionel Sambuc off_t off; /* Offset to more info in temp file. */
309433d6423SLionel Sambuc } link_t;
310433d6423SLionel Sambuc typedef struct dlink { /* On-disk link store. */
311433d6423SLionel Sambuc dev_t dev; /* Device number. */
312433d6423SLionel Sambuc char file[PATH_MAX]; /* Name of earlier seen link. */
313433d6423SLionel Sambuc } dlink_t;
314433d6423SLionel Sambuc static link_t *links[256]; /* Hash list of known links. */
315433d6423SLionel Sambuc static int tfd= -1; /* Temp file for file name storage. */
316433d6423SLionel Sambuc static dlink_t dlink;
317433d6423SLionel Sambuc link_t *lp, **plp;
318433d6423SLionel Sambuc size_t len;
319433d6423SLionel Sambuc off_t off;
320433d6423SLionel Sambuc
321433d6423SLionel Sambuc if (file == nil) {
322433d6423SLionel Sambuc /* Reset everything. */
323433d6423SLionel Sambuc for (plp= links; plp < arraylimit(links); plp++) {
324433d6423SLionel Sambuc while ((lp= *plp) != nil) {
325433d6423SLionel Sambuc *plp= lp->next;
326433d6423SLionel Sambuc free(lp);
327433d6423SLionel Sambuc }
328433d6423SLionel Sambuc }
329433d6423SLionel Sambuc if (tfd != -1) close(tfd);
330433d6423SLionel Sambuc tfd= -1;
331433d6423SLionel Sambuc return nil;
332433d6423SLionel Sambuc }
333433d6423SLionel Sambuc
334433d6423SLionel Sambuc /* The file must be a non-directory with more than one link. */
335433d6423SLionel Sambuc if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) {
336433d6423SLionel Sambuc errno= ENOENT;
337433d6423SLionel Sambuc return nil;
338433d6423SLionel Sambuc }
339433d6423SLionel Sambuc
340433d6423SLionel Sambuc plp= &links[stp->st_ino % arraysize(links)];
341433d6423SLionel Sambuc
342433d6423SLionel Sambuc while ((lp= *plp) != nil) {
343433d6423SLionel Sambuc if (lp->ino == stp->st_ino) {
344433d6423SLionel Sambuc /* May have seen this link before. Get it and check. */
345433d6423SLionel Sambuc if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil;
346433d6423SLionel Sambuc if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil;
347433d6423SLionel Sambuc
348433d6423SLionel Sambuc /* Only need to check the device number. */
349433d6423SLionel Sambuc if (dlink.dev == stp->st_dev) {
350433d6423SLionel Sambuc if (strcmp(file, dlink.file) == 0) {
351433d6423SLionel Sambuc /* Called twice. Forget about this link. */
352433d6423SLionel Sambuc *plp= lp->next;
353433d6423SLionel Sambuc free(lp);
354433d6423SLionel Sambuc errno= ENOENT;
355433d6423SLionel Sambuc return nil;
356433d6423SLionel Sambuc }
357433d6423SLionel Sambuc
358433d6423SLionel Sambuc /* Return the name of the earlier link. */
359433d6423SLionel Sambuc return dlink.file;
360433d6423SLionel Sambuc }
361433d6423SLionel Sambuc }
362433d6423SLionel Sambuc plp= &lp->next;
363433d6423SLionel Sambuc }
364433d6423SLionel Sambuc
365433d6423SLionel Sambuc /* First time I see this link. Add it to the store. */
366433d6423SLionel Sambuc if (tfd == -1) {
367433d6423SLionel Sambuc for (;;) {
368433d6423SLionel Sambuc char *tmp;
369433d6423SLionel Sambuc
370433d6423SLionel Sambuc tmp= tmpnam(nil);
371433d6423SLionel Sambuc tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600);
372433d6423SLionel Sambuc if (tfd < 0) {
373433d6423SLionel Sambuc if (errno != EEXIST) return nil;
374433d6423SLionel Sambuc } else {
375433d6423SLionel Sambuc (void) unlink(tmp);
376433d6423SLionel Sambuc break;
377433d6423SLionel Sambuc }
378433d6423SLionel Sambuc }
379433d6423SLionel Sambuc }
380433d6423SLionel Sambuc if ((len= strlen(file)) >= PATH_MAX) {
381433d6423SLionel Sambuc errno= E2BIG;
382433d6423SLionel Sambuc return nil;
383433d6423SLionel Sambuc }
384433d6423SLionel Sambuc
385433d6423SLionel Sambuc dlink.dev= stp->st_dev;
386433d6423SLionel Sambuc strcpy(dlink.file, file);
387433d6423SLionel Sambuc len += offsetof(dlink_t, file) + 1;
388433d6423SLionel Sambuc if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil;
389433d6423SLionel Sambuc if (write(tfd, &dlink, len) != len) return nil;
390433d6423SLionel Sambuc
391433d6423SLionel Sambuc if ((lp= malloc(sizeof(*lp))) == nil) return nil;
392433d6423SLionel Sambuc lp->next= nil;
393433d6423SLionel Sambuc lp->ino= stp->st_ino;
394433d6423SLionel Sambuc lp->off= off;
395433d6423SLionel Sambuc *plp= lp;
396433d6423SLionel Sambuc errno= ENOENT;
397433d6423SLionel Sambuc return nil;
398433d6423SLionel Sambuc }
399433d6423SLionel Sambuc
400433d6423SLionel Sambuc #define cancellink() ((void) islink())
401433d6423SLionel Sambuc
islink()402433d6423SLionel Sambuc static char *islink()
403433d6423SLionel Sambuc /* Returns the name of the file path is linked to. If no such link can be
404433d6423SLionel Sambuc * found, then path is added to the list and nil is returned. If all the
405433d6423SLionel Sambuc * links of a file have been seen, then it is removed from the list.
406433d6423SLionel Sambuc * Directories are not seen as linkable.
407433d6423SLionel Sambuc */
408433d6423SLionel Sambuc {
409433d6423SLionel Sambuc char *name;
410433d6423SLionel Sambuc
411433d6423SLionel Sambuc name= link_islink(&st, path);
412433d6423SLionel Sambuc if (name == nil && errno != ENOENT) perrx(path);
413433d6423SLionel Sambuc return name;
414433d6423SLionel Sambuc }
415433d6423SLionel Sambuc
setstat(ino_t ino,struct stat * stp)416433d6423SLionel Sambuc static void setstat(ino_t ino, struct stat *stp)
417433d6423SLionel Sambuc /* Set backup status info, we know that backup is true. */
418433d6423SLionel Sambuc {
419433d6423SLionel Sambuc struct mode md;
420433d6423SLionel Sambuc
421433d6423SLionel Sambuc md.md_mode = stp->st_mode;
422433d6423SLionel Sambuc md.md_uid = stp->st_uid;
423433d6423SLionel Sambuc md.md_gid = stp->st_gid;
424433d6423SLionel Sambuc md.md_rdev = stp->st_rdev;
425433d6423SLionel Sambuc md.md_devsiz = stp->st_size / 1024;
426433d6423SLionel Sambuc
427433d6423SLionel Sambuc if (lseek(filemodes, (off_t) sizeof(md) * (off_t) ino, 0) == -1
428433d6423SLionel Sambuc || write(filemodes, (char *) &md, sizeof(md)) != sizeof(md)
429433d6423SLionel Sambuc ) perrx(BACKUP);
430433d6423SLionel Sambuc }
431433d6423SLionel Sambuc
getstat(char * name,struct stat * stp)432433d6423SLionel Sambuc static int getstat(char *name, struct stat *stp)
433433d6423SLionel Sambuc /* Get status information of file name, skipping some files. Backup info
434433d6423SLionel Sambuc * is inserted as needed.
435433d6423SLionel Sambuc */
436433d6423SLionel Sambuc {
437433d6423SLionel Sambuc errno= 0;
438433d6423SLionel Sambuc
439433d6423SLionel Sambuc if (strcmp(name, BACKUP) == 0) return -1;
440433d6423SLionel Sambuc
441433d6423SLionel Sambuc if (lstat(name, stp) < 0) return -1;
442433d6423SLionel Sambuc
443433d6423SLionel Sambuc if (stp->st_mode == S_IFREG && stp->st_mtime == 0) return -1;
444433d6423SLionel Sambuc
445433d6423SLionel Sambuc if (backup) {
446433d6423SLionel Sambuc struct mode md;
447433d6423SLionel Sambuc
448433d6423SLionel Sambuc if (lseek(filemodes,
449433d6423SLionel Sambuc (off_t) sizeof(md) * (off_t) stp->st_ino, 0) == -1
450433d6423SLionel Sambuc || read(filemodes, (char *) &md, sizeof(md)) != sizeof(md)
451433d6423SLionel Sambuc || md.md_mode == 0
452433d6423SLionel Sambuc ) {
453433d6423SLionel Sambuc errno= 0;
454433d6423SLionel Sambuc setstat(stp->st_ino, stp);
455433d6423SLionel Sambuc } else {
456433d6423SLionel Sambuc stp->st_mode = md.md_mode;
457433d6423SLionel Sambuc stp->st_uid = md.md_uid;
458433d6423SLionel Sambuc stp->st_gid = md.md_gid;
459433d6423SLionel Sambuc stp->st_rdev = md.md_rdev;
460433d6423SLionel Sambuc if (S_ISBLK(stp->st_mode))
461433d6423SLionel Sambuc stp->st_size= (off_t) md.md_devsiz * 1024;
462433d6423SLionel Sambuc }
463433d6423SLionel Sambuc }
464433d6423SLionel Sambuc return 0;
465433d6423SLionel Sambuc }
466433d6423SLionel Sambuc
advance()467433d6423SLionel Sambuc static int advance()
468433d6423SLionel Sambuc /* Determine next pathname, return true on success. */
469433d6423SLionel Sambuc {
470433d6423SLionel Sambuc for (;;) {
471433d6423SLionel Sambuc if (E==nil) { /* First call, enter root dir. */
472433d6423SLionel Sambuc E= (struct entry *) malloc(sizeof(*E));
473433d6423SLionel Sambuc E->dir= nil;
474433d6423SLionel Sambuc E->con= nil;
475433d6423SLionel Sambuc E->next= nil;
476433d6423SLionel Sambuc E->name= (char *) malloc(3);
477433d6423SLionel Sambuc strcpy(E->name, transparent ? ".@" : ".");
478433d6423SLionel Sambuc } else
479433d6423SLionel Sambuc if (E->con != nil) /* Dir's files must be processed. */
480433d6423SLionel Sambuc E= E->con;
481433d6423SLionel Sambuc else {
482433d6423SLionel Sambuc for (;;) {
483433d6423SLionel Sambuc /* Remove E from it's parents list, then
484433d6423SLionel Sambuc * try next entry, if none, go to parent dir.
485433d6423SLionel Sambuc */
486433d6423SLionel Sambuc struct entry *junk= E, *parent= E->dir;
487433d6423SLionel Sambuc
488433d6423SLionel Sambuc if (parent != nil) parent->con= E->next;
489433d6423SLionel Sambuc E= E->next;
490433d6423SLionel Sambuc free(junk->name);
491433d6423SLionel Sambuc free(junk);
492433d6423SLionel Sambuc
493433d6423SLionel Sambuc if (E != nil) break;
494433d6423SLionel Sambuc
495433d6423SLionel Sambuc if ((E= parent) == nil) return 0;
496433d6423SLionel Sambuc }
497433d6423SLionel Sambuc }
498433d6423SLionel Sambuc setpath(E);
499433d6423SLionel Sambuc if (getstat(path, &st) == 0) {
500433d6423SLionel Sambuc if (S_ISLNK(st.st_mode)) {
501433d6423SLionel Sambuc int n;
502433d6423SLionel Sambuc
503433d6423SLionel Sambuc if ((n= readlink(path, lnkpth, PATH_MAX)) >= 0)
504433d6423SLionel Sambuc {
505433d6423SLionel Sambuc lnkpth[n]= 0;
506433d6423SLionel Sambuc break;
507433d6423SLionel Sambuc }
508433d6423SLionel Sambuc } else {
509433d6423SLionel Sambuc break;
510433d6423SLionel Sambuc }
511433d6423SLionel Sambuc }
512433d6423SLionel Sambuc if (errno != 0 && errno != ENOENT) perr(path);
513433d6423SLionel Sambuc }
514433d6423SLionel Sambuc
515433d6423SLionel Sambuc linkpath= islink();
516433d6423SLionel Sambuc DPRINTF("%s: path = %s\n", path);
517433d6423SLionel Sambuc return 1;
518433d6423SLionel Sambuc }
519433d6423SLionel Sambuc
request()520433d6423SLionel Sambuc static enum orders request()
521433d6423SLionel Sambuc /* Slave reads command sent by master. */
522433d6423SLionel Sambuc {
523433d6423SLionel Sambuc static char buf[64], *bp;
524433d6423SLionel Sambuc static int n= 0;
525433d6423SLionel Sambuc int req;
526433d6423SLionel Sambuc
527433d6423SLionel Sambuc for (;;) {
528433d6423SLionel Sambuc if (n == 0) {
529433d6423SLionel Sambuc if ((n= read(chan[0], buf, (int) sizeof(buf))) <= 0) {
530433d6423SLionel Sambuc if (n < 0) perrx("request()");
531433d6423SLionel Sambuc /* Master died, try to report it then follow. */
532433d6423SLionel Sambuc fprintf(stderr,
533433d6423SLionel Sambuc "%s: Master died prematurely.\n", arg0);
534433d6423SLionel Sambuc exit(1);
535433d6423SLionel Sambuc }
536433d6423SLionel Sambuc bp= buf;
537433d6423SLionel Sambuc }
538433d6423SLionel Sambuc req= *bp++ & 0xFF;
539433d6423SLionel Sambuc n--;
540433d6423SLionel Sambuc if (req >= (int) ENTER) break;
541433d6423SLionel Sambuc
542433d6423SLionel Sambuc /* Master using slave to print to stdout: */
543433d6423SLionel Sambuc putchar(req);
544433d6423SLionel Sambuc }
545433d6423SLionel Sambuc DPRINTF("%s: request() == %s\n", ORDERS[req - (int) ENTER]);
546433d6423SLionel Sambuc
547433d6423SLionel Sambuc return (enum orders) req;
548433d6423SLionel Sambuc }
549433d6423SLionel Sambuc
report()550433d6423SLionel Sambuc static void report()
551433d6423SLionel Sambuc {
552433d6423SLionel Sambuc int r;
553433d6423SLionel Sambuc
554433d6423SLionel Sambuc DPRINTF("%s: reporting now!\n", 0);
555433d6423SLionel Sambuc
556433d6423SLionel Sambuc buckp= bucket;
557433d6423SLionel Sambuc
558433d6423SLionel Sambuc while (buckn > 0) {
559433d6423SLionel Sambuc r = buckn;
560433d6423SLionel Sambuc if (r > (512 << sizeof(char*))) r= (512 << sizeof(char*));
561433d6423SLionel Sambuc
562433d6423SLionel Sambuc if ((r= write(chan[1], buckp, r)) < 0) perrx("report()");
563433d6423SLionel Sambuc
564433d6423SLionel Sambuc buckp += r;
565433d6423SLionel Sambuc buckn -= r;
566433d6423SLionel Sambuc }
567433d6423SLionel Sambuc buckp= bucket;
568433d6423SLionel Sambuc buckn= 0;
569433d6423SLionel Sambuc }
570433d6423SLionel Sambuc
inform(enum answers a)571433d6423SLionel Sambuc static void inform(enum answers a)
572433d6423SLionel Sambuc /* Slave replies to master. */
573433d6423SLionel Sambuc {
574433d6423SLionel Sambuc DPRINTF("%s: inform(%s)\n", ANSWERS[(int) a - (int) PATH]);
575433d6423SLionel Sambuc
576433d6423SLionel Sambuc *buckp++ = (int) a;
577433d6423SLionel Sambuc buckn++;
578433d6423SLionel Sambuc }
579433d6423SLionel Sambuc
580433d6423SLionel Sambuc #define wwrite(buf, n) (memcpy(buckp, (buf), (n)), buckp+= (n), buckn+= (n))
581433d6423SLionel Sambuc
sendnum(long n)582433d6423SLionel Sambuc static void sendnum(long n)
583433d6423SLionel Sambuc /* Send number from least to most significant byte. */
584433d6423SLionel Sambuc {
585433d6423SLionel Sambuc #if BYTE_ORDER == LITTLE_ENDIAN
586433d6423SLionel Sambuc wwrite((char *) &n, sizeof(n));
587433d6423SLionel Sambuc #else
588433d6423SLionel Sambuc char buf[NUMBYTES];
589433d6423SLionel Sambuc
590433d6423SLionel Sambuc buf[0]= (char) (n >> 0);
591433d6423SLionel Sambuc buf[1]= (char) (n >> 8);
592433d6423SLionel Sambuc buf[2]= (char) (n >> 16);
593433d6423SLionel Sambuc buf[3]= (char) (n >> 24);
594433d6423SLionel Sambuc wwrite(buf, sizeof(buf));
595433d6423SLionel Sambuc #endif
596433d6423SLionel Sambuc }
597433d6423SLionel Sambuc
send(char * buf,int n)598433d6423SLionel Sambuc static void send(char *buf, int n)
599433d6423SLionel Sambuc /* Slave sends size and contents of buf. */
600433d6423SLionel Sambuc {
601433d6423SLionel Sambuc sendnum((long) n);
602433d6423SLionel Sambuc if (n > 0) wwrite(buf, (size_t) n);
603433d6423SLionel Sambuc }
604433d6423SLionel Sambuc
sendstat(struct stat * stp)605433d6423SLionel Sambuc static void sendstat(struct stat *stp)
606433d6423SLionel Sambuc {
607433d6423SLionel Sambuc sendnum((long) stp->st_mode);
608433d6423SLionel Sambuc sendnum((long) stp->st_uid);
609433d6423SLionel Sambuc sendnum((long) stp->st_gid);
610433d6423SLionel Sambuc sendnum((long) stp->st_rdev);
611433d6423SLionel Sambuc sendnum((long) stp->st_size);
612433d6423SLionel Sambuc sendnum((long) stp->st_mtime);
613433d6423SLionel Sambuc }
614433d6423SLionel Sambuc
615433d6423SLionel Sambuc static int ask();
616433d6423SLionel Sambuc
slave()617433d6423SLionel Sambuc static void slave()
618433d6423SLionel Sambuc /* Carry out orders from the master, such as transmitting path names.
619433d6423SLionel Sambuc * Note that the slave uses path, not Spath, the master uses Spath.
620433d6423SLionel Sambuc */
621433d6423SLionel Sambuc {
622433d6423SLionel Sambuc int f, n;
623433d6423SLionel Sambuc char buf[CHUNK];
624433d6423SLionel Sambuc enum { run, done, die } state= run;
625433d6423SLionel Sambuc
626433d6423SLionel Sambuc do {
627433d6423SLionel Sambuc switch (request()) {
628433d6423SLionel Sambuc case ENTER:
629433d6423SLionel Sambuc enter();
630433d6423SLionel Sambuc break;
631433d6423SLionel Sambuc case ADVANCE:
632433d6423SLionel Sambuc if (!advance() || state == done) {
633433d6423SLionel Sambuc inform(DONE);
634433d6423SLionel Sambuc state= done;
635433d6423SLionel Sambuc } else {
636433d6423SLionel Sambuc if (linkpath!=nil) {
637433d6423SLionel Sambuc inform(LINK);
638433d6423SLionel Sambuc send(linkpath, strlen(linkpath) + 1);
639433d6423SLionel Sambuc } else
640433d6423SLionel Sambuc if (S_ISLNK(st.st_mode)) {
641433d6423SLionel Sambuc inform(SYMLINK);
642433d6423SLionel Sambuc send(lnkpth, strlen(lnkpth) + 1);
643433d6423SLionel Sambuc } else {
644433d6423SLionel Sambuc inform(PATH);
645433d6423SLionel Sambuc }
646433d6423SLionel Sambuc send(path, strlen(path) + 1);
647433d6423SLionel Sambuc sendstat(&st);
648433d6423SLionel Sambuc }
649433d6423SLionel Sambuc break;
650433d6423SLionel Sambuc case CAT:
651433d6423SLionel Sambuc if ((f= open(path, O_RDONLY))<0) {
652433d6423SLionel Sambuc fprintf(stderr, "%s: Can't open %s",
653433d6423SLionel Sambuc arg0, path);
654433d6423SLionel Sambuc because();
655433d6423SLionel Sambuc inform(NODATA);
656433d6423SLionel Sambuc break;
657433d6423SLionel Sambuc }
658433d6423SLionel Sambuc inform(DATA);
659433d6423SLionel Sambuc do {
660433d6423SLionel Sambuc n= read(f, buf, sizeof(buf));
661433d6423SLionel Sambuc if (n < 0) perr(path);
662433d6423SLionel Sambuc send(buf, n);
663433d6423SLionel Sambuc if (n > 0) report();
664433d6423SLionel Sambuc } while (n > 0);
665433d6423SLionel Sambuc close(f);
666433d6423SLionel Sambuc break;
667433d6423SLionel Sambuc case ORDER_CANCEL:
668433d6423SLionel Sambuc cancellink();
669433d6423SLionel Sambuc break;
670433d6423SLionel Sambuc case DIE_BAD:
671433d6423SLionel Sambuc ex= 1;
672433d6423SLionel Sambuc /*FALL THROUGH*/
673433d6423SLionel Sambuc case DIE:
674433d6423SLionel Sambuc state= die;
675433d6423SLionel Sambuc break;
676433d6423SLionel Sambuc case POSITIVE:
677433d6423SLionel Sambuc inform(ask('y') ? YES : NO);
678433d6423SLionel Sambuc break;
679433d6423SLionel Sambuc case NEGATIVE:
680433d6423SLionel Sambuc inform(ask('n') ? YES : NO);
681433d6423SLionel Sambuc break;
682433d6423SLionel Sambuc case PASS_YES:
683433d6423SLionel Sambuc inform(YES);
684433d6423SLionel Sambuc break;
685433d6423SLionel Sambuc case PASS_NO:
686433d6423SLionel Sambuc inform(NO);
687433d6423SLionel Sambuc break;
688433d6423SLionel Sambuc default:
689433d6423SLionel Sambuc fprintf(stderr, "%s: strange request\n", arg0);
690433d6423SLionel Sambuc exit(1);
691433d6423SLionel Sambuc }
692433d6423SLionel Sambuc report();
693433d6423SLionel Sambuc } while (state != die);
694433d6423SLionel Sambuc }
695433d6423SLionel Sambuc
execute(char ** argv)696433d6423SLionel Sambuc static int execute(char **argv)
697433d6423SLionel Sambuc /* Execute a command and return its success or failure. */
698433d6423SLionel Sambuc {
699433d6423SLionel Sambuc int pid, r, status;
700433d6423SLionel Sambuc
701433d6423SLionel Sambuc if ((pid= fork())<0) {
702433d6423SLionel Sambuc perr("fork()");
703433d6423SLionel Sambuc return 0;
704433d6423SLionel Sambuc }
705433d6423SLionel Sambuc if (pid == 0) {
706433d6423SLionel Sambuc execvp(argv[0], argv);
707433d6423SLionel Sambuc perrx(argv[0]);
708433d6423SLionel Sambuc }
709433d6423SLionel Sambuc while ((r= wait(&status)) != pid) {
710433d6423SLionel Sambuc if (r < 0) {
711433d6423SLionel Sambuc perr(argv[0]);
712433d6423SLionel Sambuc return 0;
713433d6423SLionel Sambuc }
714433d6423SLionel Sambuc }
715433d6423SLionel Sambuc return status == 0;
716433d6423SLionel Sambuc }
717433d6423SLionel Sambuc
removedir(char * dir)718433d6423SLionel Sambuc static int removedir(char *dir)
719433d6423SLionel Sambuc /* Remove a directory and its contents. */
720433d6423SLionel Sambuc {
721433d6423SLionel Sambuc static char *argv[] = { "rm", "-r", nil, nil };
722433d6423SLionel Sambuc
723433d6423SLionel Sambuc printf("(rm -r %s)\n", dir);
724433d6423SLionel Sambuc
725433d6423SLionel Sambuc argv[2]= dir;
726433d6423SLionel Sambuc return execute(argv);
727433d6423SLionel Sambuc }
728433d6423SLionel Sambuc
order(enum orders o)729433d6423SLionel Sambuc static void order(enum orders o)
730433d6423SLionel Sambuc /* Master tells slave what to do. */
731433d6423SLionel Sambuc {
732433d6423SLionel Sambuc char c= (char) o;
733433d6423SLionel Sambuc
734433d6423SLionel Sambuc DPRINTF("%s: order(%s)\n", ORDERS[o - (int) ENTER]);
735433d6423SLionel Sambuc
736433d6423SLionel Sambuc if (write(chan[1], &c, 1) != 1) perrx("order()");
737433d6423SLionel Sambuc }
738433d6423SLionel Sambuc
rread(char * buf,int n)739433d6423SLionel Sambuc static void rread(char *buf, int n)
740433d6423SLionel Sambuc /* Master gets buf of size n from slave, doing multiple reads if needed. */
741433d6423SLionel Sambuc {
742433d6423SLionel Sambuc int r;
743433d6423SLionel Sambuc
744433d6423SLionel Sambuc while (n > 0) {
745433d6423SLionel Sambuc if (buckn == 0) {
746433d6423SLionel Sambuc switch (buckn= read(chan[0], bucket, BUCKSIZE)) {
747433d6423SLionel Sambuc case -1:
748433d6423SLionel Sambuc perrx("reply channel from slave");
749433d6423SLionel Sambuc case 0:
750433d6423SLionel Sambuc fprintf(stderr,
751433d6423SLionel Sambuc "%s: slave died prematurely.\n",
752433d6423SLionel Sambuc arg0);
753433d6423SLionel Sambuc exit(1);
754433d6423SLionel Sambuc }
755433d6423SLionel Sambuc buckp= bucket;
756433d6423SLionel Sambuc }
757433d6423SLionel Sambuc r= n < buckn ? n : buckn;
758433d6423SLionel Sambuc memcpy(buf, buckp, r);
759433d6423SLionel Sambuc buckp+= r;
760433d6423SLionel Sambuc buckn-= r;
761433d6423SLionel Sambuc buf+= r;
762433d6423SLionel Sambuc n-= r;
763433d6423SLionel Sambuc }
764433d6423SLionel Sambuc }
765433d6423SLionel Sambuc
answer()766433d6423SLionel Sambuc static enum answers answer()
767433d6423SLionel Sambuc /* Master reads slave's reply. */
768433d6423SLionel Sambuc {
769433d6423SLionel Sambuc char c;
770433d6423SLionel Sambuc int a;
771433d6423SLionel Sambuc
772433d6423SLionel Sambuc rread(&c, 1);
773433d6423SLionel Sambuc a= c & 0xFF;
774433d6423SLionel Sambuc
775433d6423SLionel Sambuc DPRINTF("%s: answer() == %s\n", ANSWERS[a - (int) PATH]);
776433d6423SLionel Sambuc
777433d6423SLionel Sambuc return (enum answers) a;
778433d6423SLionel Sambuc }
779433d6423SLionel Sambuc
recnum()780433d6423SLionel Sambuc static long recnum()
781433d6423SLionel Sambuc /* Read number as pack of bytes from least to most significant. The data
782433d6423SLionel Sambuc * is on the wire in little-endian format. (Mostly run on PC's).
783433d6423SLionel Sambuc */
784433d6423SLionel Sambuc {
785433d6423SLionel Sambuc #if BYTE_ORDER == LITTLE_ENDIAN
786433d6423SLionel Sambuc long n;
787433d6423SLionel Sambuc
788433d6423SLionel Sambuc rread((char *) &n, (int) sizeof(n));
789433d6423SLionel Sambuc return n;
790433d6423SLionel Sambuc #else
791433d6423SLionel Sambuc unsigned char buf[NUMBYTES];
792433d6423SLionel Sambuc
793433d6423SLionel Sambuc rread(buf, sizeof(buf));
794433d6423SLionel Sambuc return buf[0]
795433d6423SLionel Sambuc | ((unsigned) buf[1] << 8)
796433d6423SLionel Sambuc | ((unsigned long) buf[2] << 16)
797433d6423SLionel Sambuc | ((unsigned long) buf[3] << 24);
798433d6423SLionel Sambuc #endif
799433d6423SLionel Sambuc }
800433d6423SLionel Sambuc
receive(char * buf,int max)801433d6423SLionel Sambuc static int receive(char *buf, int max)
802433d6423SLionel Sambuc /* Master get's data from slave, by first reading size, then data. */
803433d6423SLionel Sambuc {
804433d6423SLionel Sambuc int n;
805433d6423SLionel Sambuc
806433d6423SLionel Sambuc n= recnum();
807433d6423SLionel Sambuc if (n > max) {
808433d6423SLionel Sambuc fprintf(stderr, "%s: panic: Can't read %d bytes\n", arg0, n);
809433d6423SLionel Sambuc exit(1);
810433d6423SLionel Sambuc }
811433d6423SLionel Sambuc if (n > 0) rread(buf, n);
812433d6423SLionel Sambuc return n;
813433d6423SLionel Sambuc }
814433d6423SLionel Sambuc
recstat(struct stat * stp)815433d6423SLionel Sambuc static void recstat(struct stat *stp)
816433d6423SLionel Sambuc {
817433d6423SLionel Sambuc stp->st_mode= recnum();
818433d6423SLionel Sambuc stp->st_uid= recnum();
819433d6423SLionel Sambuc stp->st_gid= recnum();
820433d6423SLionel Sambuc stp->st_rdev= recnum();
821433d6423SLionel Sambuc stp->st_size= recnum();
822433d6423SLionel Sambuc stp->st_mtime= recnum();
823433d6423SLionel Sambuc }
824433d6423SLionel Sambuc
key()825433d6423SLionel Sambuc static int key()
826433d6423SLionel Sambuc {
827433d6423SLionel Sambuc int c;
828433d6423SLionel Sambuc static int tty= -1;
829433d6423SLionel Sambuc
830433d6423SLionel Sambuc if (tty < 0) tty= isatty(0);
831433d6423SLionel Sambuc
832433d6423SLionel Sambuc if (feof(stdin) || (c= getchar()) == EOF) {
833433d6423SLionel Sambuc c= '\n';
834433d6423SLionel Sambuc if (tty) putchar('\n');
835433d6423SLionel Sambuc }
836433d6423SLionel Sambuc
837433d6423SLionel Sambuc if (!tty) putchar(c);
838433d6423SLionel Sambuc
839433d6423SLionel Sambuc return c;
840433d6423SLionel Sambuc }
841433d6423SLionel Sambuc
ask(int def)842433d6423SLionel Sambuc static int ask(int def)
843433d6423SLionel Sambuc /* Ask for a yes or no, anything else means choose def. */
844433d6423SLionel Sambuc {
845433d6423SLionel Sambuc int y, c;
846433d6423SLionel Sambuc
847433d6423SLionel Sambuc if (chan[0] == 0) {
848433d6423SLionel Sambuc /* I'm running remote, tell the slave to ask. */
849433d6423SLionel Sambuc fflush(stdout);
850433d6423SLionel Sambuc order(def == 'y' ? POSITIVE : NEGATIVE);
851433d6423SLionel Sambuc return answer() == YES;
852433d6423SLionel Sambuc }
853433d6423SLionel Sambuc
854433d6423SLionel Sambuc printf("? (%c) ", def);
855433d6423SLionel Sambuc fflush(stdout);
856433d6423SLionel Sambuc
857433d6423SLionel Sambuc do c= key(); while (c == ' ' || c == '\t');
858433d6423SLionel Sambuc
859433d6423SLionel Sambuc y= c;
860433d6423SLionel Sambuc
861433d6423SLionel Sambuc while (c != '\n') c= key();
862433d6423SLionel Sambuc
863433d6423SLionel Sambuc if (y != 'y' && y != 'Y' && y != 'n' && y != 'N') y= def;
864433d6423SLionel Sambuc
865433d6423SLionel Sambuc return y == 'y' || y == 'Y';
866433d6423SLionel Sambuc }
867433d6423SLionel Sambuc
setmodes(int silent)868433d6423SLionel Sambuc static void setmodes(int silent)
869433d6423SLionel Sambuc {
870433d6423SLionel Sambuc struct stat st;
871433d6423SLionel Sambuc int change= 0;
872433d6423SLionel Sambuc struct utimbuf tms;
873433d6423SLionel Sambuc
874433d6423SLionel Sambuc errno= 0;
875433d6423SLionel Sambuc getstat(Spath, &st);
876433d6423SLionel Sambuc if (backup && silent) {
877433d6423SLionel Sambuc setstat(st.st_ino, &Sst);
878433d6423SLionel Sambuc getstat(Spath, &st);
879433d6423SLionel Sambuc }
880433d6423SLionel Sambuc
881433d6423SLionel Sambuc if (S_ISLNK(st.st_mode)) return;
882433d6423SLionel Sambuc
883433d6423SLionel Sambuc if (errno == 0 && st.st_mode != Sst.st_mode) {
884433d6423SLionel Sambuc if (!backup) chmod(Spath, Sst.st_mode & 07777);
885433d6423SLionel Sambuc change= 1;
886433d6423SLionel Sambuc }
887433d6423SLionel Sambuc if (errno == 0
888433d6423SLionel Sambuc && (st.st_uid != Sst.st_uid || st.st_gid != Sst.st_gid)
889433d6423SLionel Sambuc && (backup || geteuid() == 0)
890433d6423SLionel Sambuc ) {
891433d6423SLionel Sambuc errno= 0;
892433d6423SLionel Sambuc if (!backup) chown(Spath, Sst.st_uid, Sst.st_gid);
893433d6423SLionel Sambuc change= 1;
894433d6423SLionel Sambuc }
895433d6423SLionel Sambuc
896433d6423SLionel Sambuc if (backup && !silent) setstat(st.st_ino, &Sst);
897433d6423SLionel Sambuc
898433d6423SLionel Sambuc if (errno == 0 && S_ISREG(Sst.st_mode) && st.st_mtime != Sst.st_mtime) {
899433d6423SLionel Sambuc time(&tms.actime);
900433d6423SLionel Sambuc tms.modtime= Sst.st_mtime;
901433d6423SLionel Sambuc errno= 0;
902433d6423SLionel Sambuc utime(Spath, &tms);
903433d6423SLionel Sambuc change= 1;
904433d6423SLionel Sambuc }
905433d6423SLionel Sambuc if (errno != 0) {
906433d6423SLionel Sambuc fprintf(stderr, "%s: Can't set modes of %s", arg0, Spath);
907433d6423SLionel Sambuc because();
908433d6423SLionel Sambuc } else
909433d6423SLionel Sambuc if (change && !silent) {
910433d6423SLionel Sambuc printf("Mode changed of %s\n", Spath);
911433d6423SLionel Sambuc }
912433d6423SLionel Sambuc }
913433d6423SLionel Sambuc
makeold()914433d6423SLionel Sambuc static void makeold()
915433d6423SLionel Sambuc {
916433d6423SLionel Sambuc static struct utimbuf tms= { 0, 0 };
917433d6423SLionel Sambuc
918433d6423SLionel Sambuc if (utime(Spath, &tms) < 0) {
919433d6423SLionel Sambuc if (errno != ENOENT) {
920433d6423SLionel Sambuc fprintf(stderr,
921433d6423SLionel Sambuc "%s: can't make %s look old", arg0, Spath);
922433d6423SLionel Sambuc because();
923433d6423SLionel Sambuc }
924433d6423SLionel Sambuc } else {
925433d6423SLionel Sambuc fprintf(stderr, "%s: made %s look old.\n", arg0, Spath);
926433d6423SLionel Sambuc }
927433d6423SLionel Sambuc }
928433d6423SLionel Sambuc
929433d6423SLionel Sambuc static int busy= 0;
930433d6423SLionel Sambuc
bail_out(int sig)931433d6423SLionel Sambuc static void bail_out(int sig)
932433d6423SLionel Sambuc {
933433d6423SLionel Sambuc signal(sig, SIG_IGN);
934433d6423SLionel Sambuc
935433d6423SLionel Sambuc fprintf(stderr, "%s: Exiting after signal %d\n", arg0, sig);
936433d6423SLionel Sambuc
937433d6423SLionel Sambuc if (busy) {
938433d6423SLionel Sambuc fprintf(stderr, "%s: was installing %s\n", arg0, Spath);
939433d6423SLionel Sambuc makeold();
940433d6423SLionel Sambuc }
941433d6423SLionel Sambuc order(DIE_BAD);
942433d6423SLionel Sambuc
943433d6423SLionel Sambuc exit(sig);
944433d6423SLionel Sambuc }
945433d6423SLionel Sambuc
makenode(char * name,int mode,dev_t addr,off_t size)946433d6423SLionel Sambuc static int makenode(char *name, int mode, dev_t addr, off_t size)
947433d6423SLionel Sambuc {
948433d6423SLionel Sambuc int r;
949433d6423SLionel Sambuc
950433d6423SLionel Sambuc if (!backup) {
951433d6423SLionel Sambuc r= mknod(name, mode, addr);
952433d6423SLionel Sambuc } else {
953433d6423SLionel Sambuc if ((r= creat(name, 0644)) >= 0) close(r);
954433d6423SLionel Sambuc }
955433d6423SLionel Sambuc return r;
956433d6423SLionel Sambuc }
957433d6423SLionel Sambuc
add(int update)958433d6423SLionel Sambuc static void add(int update)
959433d6423SLionel Sambuc /* Add Spath to the filesystem. */
960433d6423SLionel Sambuc {
961433d6423SLionel Sambuc int f, n;
962433d6423SLionel Sambuc char buf[CHUNK];
963433d6423SLionel Sambuc int forced_update= force && update;
964433d6423SLionel Sambuc
965433d6423SLionel Sambuc if (Slinkpath != nil && !S_ISLNK(Sst.st_mode)) {
966433d6423SLionel Sambuc if (interact && !update) {
967433d6423SLionel Sambuc printf("Link %s to %s", Spath, Slinkpath);
968433d6423SLionel Sambuc if (!ask('n')) return;
969433d6423SLionel Sambuc }
970433d6423SLionel Sambuc if (link(Slinkpath, Spath) >= 0) {
971433d6423SLionel Sambuc printf("Linked %s to %s\n", Spath, Slinkpath);
972433d6423SLionel Sambuc return;
973433d6423SLionel Sambuc } else {
974433d6423SLionel Sambuc fprintf(stderr,
975433d6423SLionel Sambuc "%s: Can't link %s to %s",
976433d6423SLionel Sambuc arg0, Slinkpath, Spath);
977433d6423SLionel Sambuc because();
978433d6423SLionel Sambuc /* Try to install instead. */
979433d6423SLionel Sambuc }
980433d6423SLionel Sambuc }
981433d6423SLionel Sambuc switch (Sst.st_mode & S_IFMT) {
982433d6423SLionel Sambuc case S_IFDIR:
983433d6423SLionel Sambuc if (!force) {
984433d6423SLionel Sambuc printf("Add dir %s", Spath);
985433d6423SLionel Sambuc if (!ask('n')) return;
986433d6423SLionel Sambuc }
987433d6423SLionel Sambuc if (mkdir(Spath, backup ? 0755 : Sst.st_mode) < 0) {
988433d6423SLionel Sambuc perr(Spath);
989433d6423SLionel Sambuc return;
990433d6423SLionel Sambuc }
991433d6423SLionel Sambuc printf("Directory %s created.\n", Spath);
992433d6423SLionel Sambuc order(ENTER);
993433d6423SLionel Sambuc break;
994433d6423SLionel Sambuc case S_IFBLK:
995433d6423SLionel Sambuc case S_IFCHR:
996433d6423SLionel Sambuc case S_IFIFO:
997433d6423SLionel Sambuc if (interact && !update) {
998433d6423SLionel Sambuc printf("Create special file %s", Spath);
999433d6423SLionel Sambuc if (!ask('n')) { order(ORDER_CANCEL); return; }
1000433d6423SLionel Sambuc }
1001433d6423SLionel Sambuc if (makenode(Spath, Sst.st_mode, Sst.st_rdev, Sst.st_size)<0) {
1002433d6423SLionel Sambuc fprintf(stderr,
1003433d6423SLionel Sambuc "%s: Can't create special file %s",
1004433d6423SLionel Sambuc arg0, Spath);
1005433d6423SLionel Sambuc because();
1006433d6423SLionel Sambuc return;
1007433d6423SLionel Sambuc }
1008433d6423SLionel Sambuc printf("Special file %s created\n", Spath);
1009433d6423SLionel Sambuc break;
1010433d6423SLionel Sambuc #ifdef S_IFLNK
1011433d6423SLionel Sambuc case S_IFLNK:
1012433d6423SLionel Sambuc if (interact && !update) {
1013433d6423SLionel Sambuc printf("Install %s -> %s", Spath, Slnkpth);
1014433d6423SLionel Sambuc if (!ask('n')) { order(ORDER_CANCEL); return; }
1015433d6423SLionel Sambuc }
1016433d6423SLionel Sambuc if (symlink(Slnkpth, Spath) < 0) {
1017433d6423SLionel Sambuc fprintf(stderr, "%s: Can't create symlink %s",
1018433d6423SLionel Sambuc arg0, Spath);
1019433d6423SLionel Sambuc because();
1020433d6423SLionel Sambuc return;
1021433d6423SLionel Sambuc }
1022433d6423SLionel Sambuc printf("%s %s -> %s\n",
1023433d6423SLionel Sambuc forced_update ? "Updated: " : "Installed:",
1024433d6423SLionel Sambuc Spath, Slnkpth);
1025433d6423SLionel Sambuc break;
1026433d6423SLionel Sambuc #endif
1027433d6423SLionel Sambuc case S_IFREG:
1028433d6423SLionel Sambuc if (interact && !update) {
1029433d6423SLionel Sambuc printf("Install %s", Spath);
1030433d6423SLionel Sambuc if (!ask('n')) { order(ORDER_CANCEL); return; }
1031433d6423SLionel Sambuc }
1032433d6423SLionel Sambuc order(CAT);
1033433d6423SLionel Sambuc if (answer() != DATA) return;
1034433d6423SLionel Sambuc
1035433d6423SLionel Sambuc busy= 1;
1036433d6423SLionel Sambuc if ((f= creat(Spath, backup ? 0644 : Sst.st_mode&07777)) < 0) {
1037433d6423SLionel Sambuc busy= 0;
1038433d6423SLionel Sambuc fprintf(stderr, "%s: Can't create %s", arg0, Spath);
1039433d6423SLionel Sambuc because();
1040433d6423SLionel Sambuc }
1041433d6423SLionel Sambuc
1042433d6423SLionel Sambuc while ((n= receive(buf, sizeof(buf)))>0) {
1043433d6423SLionel Sambuc if (f >= 0 && write(f, buf, n) != n) {
1044433d6423SLionel Sambuc fprintf(stderr, "%s: Write error on %s",
1045433d6423SLionel Sambuc arg0, Spath);
1046433d6423SLionel Sambuc because();
1047433d6423SLionel Sambuc close(f); f= -1;
1048433d6423SLionel Sambuc }
1049433d6423SLionel Sambuc }
1050433d6423SLionel Sambuc if (n < 0) {
1051433d6423SLionel Sambuc fprintf(stderr, "%s: Slave read err on %s\n",
1052433d6423SLionel Sambuc arg0, Spath);
1053433d6423SLionel Sambuc }
1054433d6423SLionel Sambuc if (f >= 0) close(f);
1055433d6423SLionel Sambuc if (n < 0 || f < 0) { makeold(); busy= 0; return; }
1056433d6423SLionel Sambuc busy= 0;
1057433d6423SLionel Sambuc printf("%s %s\n",
1058433d6423SLionel Sambuc forced_update ?
1059433d6423SLionel Sambuc Sst.st_mtime < st.st_mtime ? "Restored: " :
1060433d6423SLionel Sambuc "Updated: " :
1061433d6423SLionel Sambuc "Installed:",
1062433d6423SLionel Sambuc Spath
1063433d6423SLionel Sambuc );
1064433d6423SLionel Sambuc break;
1065433d6423SLionel Sambuc default:
1066433d6423SLionel Sambuc fprintf(stderr,
1067433d6423SLionel Sambuc "%s: Won't add file with strange mode %05o: %s\n",
1068433d6423SLionel Sambuc arg0, Sst.st_mode, Spath);
1069433d6423SLionel Sambuc order(ORDER_CANCEL);
1070433d6423SLionel Sambuc return;
1071433d6423SLionel Sambuc }
1072433d6423SLionel Sambuc setmodes(1);
1073433d6423SLionel Sambuc }
1074433d6423SLionel Sambuc
delete(int update)1075433d6423SLionel Sambuc static int delete(int update)
1076433d6423SLionel Sambuc /* Delete path. */
1077433d6423SLionel Sambuc {
1078433d6423SLionel Sambuc int forced_update= force && update;
1079433d6423SLionel Sambuc
1080433d6423SLionel Sambuc if (S_ISDIR(st.st_mode)) {
1081433d6423SLionel Sambuc if (install) return 0;
1082433d6423SLionel Sambuc if (!force) {
1083433d6423SLionel Sambuc printf("Delete dir %s", path);
1084433d6423SLionel Sambuc if (!ask('n')) return 0;
1085433d6423SLionel Sambuc }
1086433d6423SLionel Sambuc if (!removedir(path)) { ex= 1; return 0; }
1087433d6423SLionel Sambuc if (!forced_update) printf("Directory %s deleted.\n", path);
1088433d6423SLionel Sambuc return 1;
1089433d6423SLionel Sambuc }
1090433d6423SLionel Sambuc
1091433d6423SLionel Sambuc if (install && !update) return 0;
1092433d6423SLionel Sambuc
1093433d6423SLionel Sambuc if (!force) {
1094433d6423SLionel Sambuc printf("Delete %s", path);
1095433d6423SLionel Sambuc if (!ask((interact && !update) ? 'n' : 'y')) return 0;
1096433d6423SLionel Sambuc }
1097433d6423SLionel Sambuc
1098433d6423SLionel Sambuc if (unlink(path)<0) {
1099433d6423SLionel Sambuc fprintf(stderr, "Can't delete %s", path);
1100433d6423SLionel Sambuc because();
1101433d6423SLionel Sambuc return 0;
1102433d6423SLionel Sambuc }
1103433d6423SLionel Sambuc cancellink();
1104433d6423SLionel Sambuc if (!forced_update) printf("Deleted: %s\n", path);
1105433d6423SLionel Sambuc return 1;
1106433d6423SLionel Sambuc }
1107433d6423SLionel Sambuc
different()1108433d6423SLionel Sambuc static int different()
1109433d6423SLionel Sambuc /* Return true iff path and Spath are different. */
1110433d6423SLionel Sambuc {
1111433d6423SLionel Sambuc if (! ( (linkpath == nil && Slinkpath == nil)
1112433d6423SLionel Sambuc || (linkpath != nil && Slinkpath != nil
1113433d6423SLionel Sambuc && strcmp(linkpath, Slinkpath) == 0)
1114433d6423SLionel Sambuc )) {
1115433d6423SLionel Sambuc linkpath= Slinkpath;
1116433d6423SLionel Sambuc return 1;
1117433d6423SLionel Sambuc }
1118433d6423SLionel Sambuc
1119433d6423SLionel Sambuc if ((st.st_mode & S_IFMT) != (Sst.st_mode & S_IFMT)) return 1;
1120433d6423SLionel Sambuc
1121433d6423SLionel Sambuc switch (st.st_mode & S_IFMT) {
1122433d6423SLionel Sambuc case S_IFDIR:
1123433d6423SLionel Sambuc return 0;
1124433d6423SLionel Sambuc case S_IFBLK:
1125433d6423SLionel Sambuc case S_IFCHR:
1126433d6423SLionel Sambuc return st.st_rdev != Sst.st_rdev;
1127433d6423SLionel Sambuc case S_IFREG:
1128433d6423SLionel Sambuc if (install) return Sst.st_mtime > st.st_mtime;
1129433d6423SLionel Sambuc return st.st_size != Sst.st_size
1130433d6423SLionel Sambuc || st.st_mtime != Sst.st_mtime;
1131433d6423SLionel Sambuc case S_IFIFO: return 0;
1132433d6423SLionel Sambuc #ifdef S_IFLNK
1133433d6423SLionel Sambuc case S_IFLNK: return strcmp(lnkpth, Slnkpth) != 0;
1134433d6423SLionel Sambuc #endif
1135433d6423SLionel Sambuc default: return 1;
1136433d6423SLionel Sambuc }
1137433d6423SLionel Sambuc }
1138433d6423SLionel Sambuc
compare()1139433d6423SLionel Sambuc static void compare()
1140433d6423SLionel Sambuc /* See if path and Spath are same. */
1141433d6423SLionel Sambuc {
1142433d6423SLionel Sambuc if (different()) {
1143433d6423SLionel Sambuc if (!force) {
1144433d6423SLionel Sambuc printf("%sing %s (delete + add)\n",
1145433d6423SLionel Sambuc Sst.st_mtime < st.st_mtime ? "Restor" : "Updat",
1146433d6423SLionel Sambuc path);
1147433d6423SLionel Sambuc }
1148433d6423SLionel Sambuc if (delete(1)) add(1);
1149433d6423SLionel Sambuc } else {
1150433d6423SLionel Sambuc if (!install) setmodes(0);
1151433d6423SLionel Sambuc
1152433d6423SLionel Sambuc if (S_ISDIR(st.st_mode)) {
1153433d6423SLionel Sambuc order(ENTER);
1154433d6423SLionel Sambuc enter();
1155433d6423SLionel Sambuc }
1156433d6423SLionel Sambuc }
1157433d6423SLionel Sambuc }
1158433d6423SLionel Sambuc
1159433d6423SLionel Sambuc static int done= 0, Sdone= 0;
1160433d6423SLionel Sambuc
action()1161433d6423SLionel Sambuc static enum action { ADD, COMPARE, DELETE } action()
1162433d6423SLionel Sambuc /* Look at path's of master and slave, compare them alphabetically to see
1163433d6423SLionel Sambuc * who is ahead of who, then tell what is to be done.
1164433d6423SLionel Sambuc */
1165433d6423SLionel Sambuc {
1166433d6423SLionel Sambuc int c;
1167433d6423SLionel Sambuc char *Sp, *p;
1168433d6423SLionel Sambuc
1169433d6423SLionel Sambuc if (done) return ADD; /* Slave still has names. */
1170433d6423SLionel Sambuc if (Sdone) return DELETE; /* Master has too many names. */
1171433d6423SLionel Sambuc
1172433d6423SLionel Sambuc /* Compare paths. Let "a/a" come before "a.a". */
1173433d6423SLionel Sambuc Sp= Spath;
1174433d6423SLionel Sambuc p= path;
1175433d6423SLionel Sambuc while (*Sp == *p && *Sp != 0) { Sp++; p++; }
1176433d6423SLionel Sambuc if (*Sp == '/') return ADD;
1177433d6423SLionel Sambuc if (*p == '/') return DELETE;
1178433d6423SLionel Sambuc return (c= strcmp(Sp, p)) == 0 ? COMPARE : c < 0 ? ADD : DELETE;
1179433d6423SLionel Sambuc }
1180433d6423SLionel Sambuc
master()1181433d6423SLionel Sambuc static void master()
1182433d6423SLionel Sambuc /* Synchronise file tree to that of its slave. */
1183433d6423SLionel Sambuc {
1184433d6423SLionel Sambuc enum action a= COMPARE; /* Trick first advances. */
1185433d6423SLionel Sambuc
1186433d6423SLionel Sambuc umask(backup ? 0022 : 0000);
1187433d6423SLionel Sambuc
1188433d6423SLionel Sambuc signal(SIGPIPE, SIG_IGN);
1189433d6423SLionel Sambuc signal(SIGHUP, bail_out);
1190433d6423SLionel Sambuc signal(SIGINT, bail_out);
1191433d6423SLionel Sambuc signal(SIGTERM, bail_out);
1192433d6423SLionel Sambuc
1193433d6423SLionel Sambuc while (!done || !Sdone) {
1194433d6423SLionel Sambuc if (!Sdone && (a == ADD || a == COMPARE)) {
1195433d6423SLionel Sambuc /* Slave advances. */
1196433d6423SLionel Sambuc order(ADVANCE);
1197433d6423SLionel Sambuc switch (answer()) {
1198433d6423SLionel Sambuc case PATH:
1199433d6423SLionel Sambuc Slinkpath= nil;
1200433d6423SLionel Sambuc receive(Spath, sizeof(Spath));
1201433d6423SLionel Sambuc recstat(&Sst);
1202433d6423SLionel Sambuc break;
1203433d6423SLionel Sambuc case LINK:
1204433d6423SLionel Sambuc receive(Slnkpth, sizeof(Slnkpth));
1205433d6423SLionel Sambuc Slinkpath= Slnkpth;
1206433d6423SLionel Sambuc receive(Spath, sizeof(Spath));
1207433d6423SLionel Sambuc recstat(&Sst);
1208433d6423SLionel Sambuc break;
1209433d6423SLionel Sambuc case SYMLINK:
1210433d6423SLionel Sambuc Slinkpath= nil;
1211433d6423SLionel Sambuc receive(Slnkpth, sizeof(Slnkpth));
1212433d6423SLionel Sambuc receive(Spath, sizeof(Spath));
1213433d6423SLionel Sambuc recstat(&Sst);
1214433d6423SLionel Sambuc break;
1215433d6423SLionel Sambuc case DONE:
1216433d6423SLionel Sambuc Sdone= 1;
1217433d6423SLionel Sambuc break;
1218433d6423SLionel Sambuc default:
1219433d6423SLionel Sambuc fprintf(stderr,
1220433d6423SLionel Sambuc "%s: Strange answer from slave.\n",
1221433d6423SLionel Sambuc arg0);
1222433d6423SLionel Sambuc exit(1);
1223433d6423SLionel Sambuc }
1224433d6423SLionel Sambuc }
1225433d6423SLionel Sambuc if (!done && (a == COMPARE || a == DELETE)) {
1226433d6423SLionel Sambuc /* Master advances. */
1227433d6423SLionel Sambuc if (!advance()) done= 1;
1228433d6423SLionel Sambuc }
1229433d6423SLionel Sambuc
1230433d6423SLionel Sambuc if (done && Sdone) break;
1231433d6423SLionel Sambuc
1232433d6423SLionel Sambuc switch (a= action()) {
1233433d6423SLionel Sambuc case ADD: /* Spath exists, path doesn't, add? */
1234433d6423SLionel Sambuc add(0);
1235433d6423SLionel Sambuc break;
1236433d6423SLionel Sambuc case COMPARE: /* They both exist, are they the same? */
1237433d6423SLionel Sambuc compare();
1238433d6423SLionel Sambuc break;
1239433d6423SLionel Sambuc case DELETE: /* path exists, Spath doesn't, delete? */
1240433d6423SLionel Sambuc delete(0);
1241433d6423SLionel Sambuc }
1242433d6423SLionel Sambuc fflush(stdout); /* Don't keep user in suspense. */
1243433d6423SLionel Sambuc }
1244433d6423SLionel Sambuc order(ex == 0 ? DIE : DIE_BAD);
1245433d6423SLionel Sambuc }
1246433d6423SLionel Sambuc
mediator()1247433d6423SLionel Sambuc static void mediator()
1248433d6423SLionel Sambuc /* Sits at the local machine and passes orders from master to slave, both
1249433d6423SLionel Sambuc * on remote machines. Only diagnostics and questions are handled.
1250433d6423SLionel Sambuc */
1251433d6423SLionel Sambuc {
1252433d6423SLionel Sambuc enum orders req;
1253433d6423SLionel Sambuc
1254433d6423SLionel Sambuc for (;;) {
1255433d6423SLionel Sambuc switch (req= request()) {
1256433d6423SLionel Sambuc case DIE_BAD:
1257433d6423SLionel Sambuc ex= 1;
1258433d6423SLionel Sambuc /*FALL THROUGH*/
1259433d6423SLionel Sambuc case DIE:
1260433d6423SLionel Sambuc order(DIE);
1261433d6423SLionel Sambuc return;
1262433d6423SLionel Sambuc case POSITIVE:
1263433d6423SLionel Sambuc order(ask('y') ? PASS_YES : PASS_NO);
1264433d6423SLionel Sambuc break;
1265433d6423SLionel Sambuc case NEGATIVE:
1266433d6423SLionel Sambuc order(ask('n') ? PASS_YES : PASS_NO);
1267433d6423SLionel Sambuc break;
1268433d6423SLionel Sambuc default:
1269433d6423SLionel Sambuc order(req);
1270433d6423SLionel Sambuc }
1271433d6423SLionel Sambuc }
1272433d6423SLionel Sambuc }
1273433d6423SLionel Sambuc
1274433d6423SLionel Sambuc #define P_EXIT 1 /* Make sure process doesn't return. */
1275433d6423SLionel Sambuc #define P_SHADOW 2 /* Always use exec on 68000. */
1276433d6423SLionel Sambuc
startprocess(void (* proc)(),char * machine,char * path,int p_flags)1277433d6423SLionel Sambuc static void startprocess(void (*proc)(), char *machine, char *path,
1278433d6423SLionel Sambuc int p_flags)
1279433d6423SLionel Sambuc {
1280433d6423SLionel Sambuc char *argv[10], **argp= argv;
1281433d6423SLionel Sambuc char flags[10], *pfl= flags;
1282433d6423SLionel Sambuc
1283433d6423SLionel Sambuc if (machine != nil) {
1284433d6423SLionel Sambuc char *u= machine, *m;
1285433d6423SLionel Sambuc
1286433d6423SLionel Sambuc *argp++ = "rsh";
1287433d6423SLionel Sambuc if ((m= strchr(machine, '@')) != nil) {
1288433d6423SLionel Sambuc *m++ = 0;
1289433d6423SLionel Sambuc *argp++ = "-l";
1290433d6423SLionel Sambuc *argp++ = u;
1291433d6423SLionel Sambuc machine= m;
1292433d6423SLionel Sambuc }
1293433d6423SLionel Sambuc *argp++ = machine;
1294433d6423SLionel Sambuc } else
1295433d6423SLionel Sambuc /* Without this check it would run like a pig on an non MMU 68000: */
1296433d6423SLionel Sambuc if (!(USE_SHADOWING && p_flags & P_SHADOW)) {
1297433d6423SLionel Sambuc if (chdir(path) < 0) {
1298433d6423SLionel Sambuc if (proc != master || errno != ENOENT
1299433d6423SLionel Sambuc || mkdir(path, 0700) < 0)
1300433d6423SLionel Sambuc perrx(path);
1301433d6423SLionel Sambuc if (chdir(path) < 0) perrx(path);
1302433d6423SLionel Sambuc printf("Destination directory %s created\n", path);
1303433d6423SLionel Sambuc }
1304433d6423SLionel Sambuc isvisible(path);
1305433d6423SLionel Sambuc isbackup(proc == slave);
1306433d6423SLionel Sambuc (*proc)();
1307433d6423SLionel Sambuc if (p_flags & P_EXIT) exit(ex);
1308433d6423SLionel Sambuc return;
1309433d6423SLionel Sambuc }
1310433d6423SLionel Sambuc *argp++ = SYNCNAME;
1311433d6423SLionel Sambuc *pfl++ = '-';
1312433d6423SLionel Sambuc if (interact) *pfl++ = 'i';
1313433d6423SLionel Sambuc if (install) *pfl++ = 'u';
1314433d6423SLionel Sambuc if (force) *pfl++ = 'f';
1315433d6423SLionel Sambuc *pfl= 0;
1316433d6423SLionel Sambuc *argp++ = flags;
1317433d6423SLionel Sambuc *argp++ = proc == slave ? SLAVENAME : MASTERNAME;
1318433d6423SLionel Sambuc *argp++ = path;
1319433d6423SLionel Sambuc *argp++ = nil;
1320433d6423SLionel Sambuc #ifdef DEBUG
1321433d6423SLionel Sambuc fprintf(stderr, "execlp(");
1322433d6423SLionel Sambuc for (argp= argv; *argp != nil; argp++) fprintf(stderr, "%s, ", *argp);
1323433d6423SLionel Sambuc fprintf(stderr, "nil);\n");
1324433d6423SLionel Sambuc #endif
1325433d6423SLionel Sambuc execvp(argv[0], argv);
1326433d6423SLionel Sambuc perrx(argv[0]);
1327433d6423SLionel Sambuc }
1328433d6423SLionel Sambuc
splitcolon(char * path,char ** amach,char ** adir)1329433d6423SLionel Sambuc void splitcolon(char *path, char **amach, char **adir)
1330433d6423SLionel Sambuc {
1331433d6423SLionel Sambuc char *dir= path;
1332433d6423SLionel Sambuc
1333433d6423SLionel Sambuc for (;;) {
1334433d6423SLionel Sambuc if (*dir == ':') {
1335433d6423SLionel Sambuc *dir++ = 0;
1336433d6423SLionel Sambuc *amach= path;
1337433d6423SLionel Sambuc *adir= dir;
1338433d6423SLionel Sambuc break;
1339433d6423SLionel Sambuc }
1340433d6423SLionel Sambuc if (*dir == 0 || *dir == '/') {
1341433d6423SLionel Sambuc *amach= nil;
1342433d6423SLionel Sambuc *adir= path;
1343433d6423SLionel Sambuc break;
1344433d6423SLionel Sambuc }
1345433d6423SLionel Sambuc dir++;
1346433d6423SLionel Sambuc }
1347433d6423SLionel Sambuc }
1348433d6423SLionel Sambuc
Usage()1349433d6423SLionel Sambuc static void Usage()
1350433d6423SLionel Sambuc {
1351433d6423SLionel Sambuc fprintf(stderr,
1352433d6423SLionel Sambuc "Usage: %s [-iuf] [[user@]machine:]dir1 [[user@]machine:]dir2\n",
1353433d6423SLionel Sambuc arg0);
1354433d6423SLionel Sambuc exit(1);
1355433d6423SLionel Sambuc }
1356433d6423SLionel Sambuc
main(int argc,char ** argv)1357433d6423SLionel Sambuc int main(int argc, char **argv)
1358433d6423SLionel Sambuc {
1359433d6423SLionel Sambuc char *s_mach, *s_dir;
1360433d6423SLionel Sambuc char *m_mach, *m_dir;
1361433d6423SLionel Sambuc int m2s[2], s2m[2], m2m[2];
1362433d6423SLionel Sambuc int s_pid= 0, m_pid= 0;
1363433d6423SLionel Sambuc int r;
1364433d6423SLionel Sambuc
1365433d6423SLionel Sambuc if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++;
1366433d6423SLionel Sambuc
1367433d6423SLionel Sambuc while (argc>1 && argv[1][0] == '-') {
1368433d6423SLionel Sambuc char *f= argv[1]+1;
1369433d6423SLionel Sambuc
1370433d6423SLionel Sambuc while (*f != 0) {
1371433d6423SLionel Sambuc switch (*f++) {
1372433d6423SLionel Sambuc case 'i': interact= 1; break;
1373433d6423SLionel Sambuc case 'u': install= 1; break;
1374433d6423SLionel Sambuc case 'f': force= 1; break;
1375433d6423SLionel Sambuc default: Usage();
1376433d6423SLionel Sambuc }
1377433d6423SLionel Sambuc }
1378433d6423SLionel Sambuc argc--;
1379433d6423SLionel Sambuc argv++;
1380433d6423SLionel Sambuc }
1381433d6423SLionel Sambuc
1382433d6423SLionel Sambuc if (argc != 3) Usage();
1383433d6423SLionel Sambuc
1384433d6423SLionel Sambuc if (strcmp(argv[1], SLAVENAME) == 0) {
1385433d6423SLionel Sambuc arg0= "Slave";
1386433d6423SLionel Sambuc splitcolon(argv[2], &s_mach, &s_dir);
1387433d6423SLionel Sambuc startprocess(slave, s_mach, s_dir, P_EXIT);
1388433d6423SLionel Sambuc } else
1389433d6423SLionel Sambuc if (strcmp(argv[1], MASTERNAME) == 0) {
1390433d6423SLionel Sambuc arg0= "Master";
1391433d6423SLionel Sambuc splitcolon(argv[2], &m_mach, &m_dir);
1392433d6423SLionel Sambuc startprocess(master, m_mach, m_dir, P_EXIT);
1393433d6423SLionel Sambuc }
1394433d6423SLionel Sambuc
1395433d6423SLionel Sambuc splitcolon(argv[1], &s_mach, &s_dir);
1396433d6423SLionel Sambuc splitcolon(argv[2], &m_mach, &m_dir);
1397433d6423SLionel Sambuc
1398433d6423SLionel Sambuc /* How difficult can plumbing be? */
1399433d6423SLionel Sambuc if (pipe(m2s) < 0 || pipe(s2m) < 0) perrx("pipe()");
1400433d6423SLionel Sambuc
1401433d6423SLionel Sambuc if (m_mach == nil) {
1402433d6423SLionel Sambuc /* synctree [machine:]dir1 dir2 */
1403433d6423SLionel Sambuc switch (s_pid= fork()) {
1404433d6423SLionel Sambuc case -1:
1405433d6423SLionel Sambuc perrx("fork()");
1406433d6423SLionel Sambuc case 0:
1407433d6423SLionel Sambuc dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]);
1408433d6423SLionel Sambuc dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]);
1409433d6423SLionel Sambuc arg0= "Slave";
1410433d6423SLionel Sambuc startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW);
1411433d6423SLionel Sambuc }
1412433d6423SLionel Sambuc chan[0]= s2m[0]; close(s2m[1]);
1413433d6423SLionel Sambuc chan[1]= m2s[1]; close(m2s[0]);
1414433d6423SLionel Sambuc startprocess(master, m_mach, m_dir, 0);
1415433d6423SLionel Sambuc } else
1416433d6423SLionel Sambuc if (s_mach == nil) {
1417433d6423SLionel Sambuc /* synctree dir1 machine:dir2 */
1418433d6423SLionel Sambuc switch (m_pid= fork()) {
1419433d6423SLionel Sambuc case -1:
1420433d6423SLionel Sambuc perrx("fork()");
1421433d6423SLionel Sambuc case 0:
1422433d6423SLionel Sambuc dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]);
1423433d6423SLionel Sambuc dup2(m2s[1], 1); close(m2s[0]); close(m2s[1]);
1424433d6423SLionel Sambuc arg0= "Master";
1425433d6423SLionel Sambuc startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW);
1426433d6423SLionel Sambuc }
1427433d6423SLionel Sambuc chan[0]= m2s[0]; close(m2s[1]);
1428433d6423SLionel Sambuc chan[1]= s2m[1]; close(s2m[0]);
1429433d6423SLionel Sambuc startprocess(slave, s_mach, s_dir, 0);
1430433d6423SLionel Sambuc } else {
1431433d6423SLionel Sambuc /* synctree machine1:dir1 machine2:dir2 */
1432*d0055759SDavid van Moolenbroek if (pipe(m2m) < 0) perrx("pipe");
1433433d6423SLionel Sambuc
1434433d6423SLionel Sambuc switch (s_pid= fork()) {
1435433d6423SLionel Sambuc case -1:
1436433d6423SLionel Sambuc perrx("fork()");
1437433d6423SLionel Sambuc case 0:
1438433d6423SLionel Sambuc dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]);
1439433d6423SLionel Sambuc dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]);
1440433d6423SLionel Sambuc close(m2m[0]); close(m2m[1]);
1441433d6423SLionel Sambuc arg0= "Slave";
1442433d6423SLionel Sambuc startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW);
1443433d6423SLionel Sambuc }
1444433d6423SLionel Sambuc
1445433d6423SLionel Sambuc switch (m_pid= fork()) {
1446433d6423SLionel Sambuc case -1:
1447433d6423SLionel Sambuc perrx("fork()");
1448433d6423SLionel Sambuc case 0:
1449433d6423SLionel Sambuc dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]);
1450433d6423SLionel Sambuc close(m2s[0]); close(m2s[1]);
1451433d6423SLionel Sambuc dup2(m2m[1], 1); close(m2m[0]); close(m2m[1]);
1452433d6423SLionel Sambuc arg0= "Master";
1453433d6423SLionel Sambuc startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW);
1454433d6423SLionel Sambuc }
1455433d6423SLionel Sambuc close(s2m[0]); close(s2m[1]);
1456433d6423SLionel Sambuc chan[0]= m2m[0]; close(m2m[1]);
1457433d6423SLionel Sambuc chan[1]= m2s[1]; close(m2s[0]);
1458433d6423SLionel Sambuc mediator();
1459433d6423SLionel Sambuc }
1460433d6423SLionel Sambuc close(chan[0]);
1461433d6423SLionel Sambuc close(chan[1]);
1462433d6423SLionel Sambuc
1463433d6423SLionel Sambuc alarm(15); /* Don't wait(2) forever. */
1464433d6423SLionel Sambuc
1465433d6423SLionel Sambuc while (s_pid != 0 || m_pid != 0) {
1466433d6423SLionel Sambuc if ((r= wait((int *) nil)) < 0) perrx("wait()");
1467433d6423SLionel Sambuc if (r == s_pid) s_pid= 0;
1468433d6423SLionel Sambuc if (r == m_pid) m_pid= 0;
1469433d6423SLionel Sambuc }
1470433d6423SLionel Sambuc exit(ex);
1471433d6423SLionel Sambuc }
1472