xref: /minix3/minix/commands/synctree/synctree.c (revision d0055759dd8892194db7fce6acc5085d5c9aeaee)
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