Commit | Line | Data |
---|---|---|
2f8aab68 | 1 | #ifndef lint |
85b60cd9 | 2 | static char *sccsid = "@(#)server.c 4.19 (Berkeley) 84/07/02"; |
2f8aab68 RC |
3 | #endif |
4 | ||
5 | #include "defs.h" | |
6 | ||
d6bccb44 RC |
7 | #define ack() (void) write(rem, "\0\n", 2) |
8 | #define err() (void) write(rem, "\1\n", 2) | |
2f8aab68 | 9 | |
a3e6fd64 | 10 | struct linkbuf *ihead; /* list of files with more than one link */ |
82572cb6 | 11 | char buf[BUFSIZ]; /* general purpose buffer */ |
2f8aab68 RC |
12 | char target[BUFSIZ]; /* target/source directory name */ |
13 | char *tp; /* pointer to end of target name */ | |
14 | int catname; /* cat name to target name */ | |
3024eb6f | 15 | char *stp[32]; /* stack of saved tp's for directories */ |
e8109cf8 | 16 | int oumask; /* old umask for creating files */ |
82572cb6 RC |
17 | |
18 | extern FILE *lfp; /* log file for mailing changes */ | |
19 | ||
0fccdfef | 20 | int cleanup(); |
a3e6fd64 | 21 | struct linkbuf *savelink(); |
0fccdfef | 22 | |
2f8aab68 RC |
23 | /* |
24 | * Server routine to read requests and process them. | |
25 | * Commands are: | |
26 | * Tname - Transmit file if out of date | |
27 | * Vname - Verify if file out of date or not | |
28 | * Qname - Query if file exists. Return mtime & size if it does. | |
29 | */ | |
30 | server() | |
31 | { | |
32 | char cmdbuf[BUFSIZ]; | |
33 | register char *cp; | |
2f8aab68 | 34 | |
0fccdfef RC |
35 | signal(SIGHUP, cleanup); |
36 | signal(SIGINT, cleanup); | |
37 | signal(SIGQUIT, cleanup); | |
38 | signal(SIGTERM, cleanup); | |
39 | signal(SIGPIPE, cleanup); | |
40 | ||
41 | rem = 0; | |
e8109cf8 | 42 | oumask = umask(0); |
6e6d779a RC |
43 | (void) sprintf(buf, "V%d\n", VERSION); |
44 | (void) write(rem, buf, strlen(buf)); | |
2f8aab68 RC |
45 | |
46 | for (;;) { | |
47 | cp = cmdbuf; | |
48 | if (read(rem, cp, 1) <= 0) | |
49 | return; | |
50 | if (*cp++ == '\n') { | |
e8109cf8 | 51 | error("server: expected control record\n"); |
2f8aab68 RC |
52 | continue; |
53 | } | |
54 | do { | |
55 | if (read(rem, cp, 1) != 1) | |
0fccdfef | 56 | cleanup(); |
82572cb6 | 57 | } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]); |
2f8aab68 RC |
58 | *--cp = '\0'; |
59 | cp = cmdbuf; | |
60 | switch (*cp++) { | |
2f8aab68 | 61 | case 'T': /* init target file/directory name */ |
82572cb6 RC |
62 | catname = 1; /* target should be directory */ |
63 | goto dotarget; | |
64 | ||
65 | case 't': /* init target file/directory name */ | |
2f8aab68 | 66 | catname = 0; |
82572cb6 | 67 | dotarget: |
e8109cf8 RC |
68 | if (exptilde(target, cp) == NULL) |
69 | continue; | |
2f8aab68 RC |
70 | tp = target; |
71 | while (*tp) | |
72 | tp++; | |
d6bccb44 | 73 | ack(); |
2f8aab68 RC |
74 | continue; |
75 | ||
a3e6fd64 RC |
76 | case 'R': /* Transfer a regular file. */ |
77 | recvf(cp, S_IFREG); | |
2f8aab68 RC |
78 | continue; |
79 | ||
a3e6fd64 RC |
80 | case 'D': /* Transfer a directory. */ |
81 | recvf(cp, S_IFDIR); | |
82 | continue; | |
83 | ||
84 | case 'K': /* Transfer symbolic link. */ | |
85 | recvf(cp, S_IFLNK); | |
86 | continue; | |
87 | ||
88 | case 'k': /* Transfer hard link. */ | |
89 | hardlink(cp); | |
2f8aab68 RC |
90 | continue; |
91 | ||
92 | case 'E': /* End. (of directory) */ | |
93 | *tp = '\0'; | |
e8109cf8 RC |
94 | if (catname <= 0) { |
95 | error("server: too many 'E's\n"); | |
2f8aab68 RC |
96 | continue; |
97 | } | |
e8109cf8 | 98 | tp = stp[--catname]; |
3024eb6f | 99 | *tp = '\0'; |
d6bccb44 | 100 | ack(); |
2f8aab68 RC |
101 | continue; |
102 | ||
d1dee8e8 | 103 | case 'C': /* Clean. Cleanup a directory */ |
d6bccb44 | 104 | clean(cp); |
e8109cf8 RC |
105 | continue; |
106 | ||
107 | case 'Q': /* Query. Does the file/directory exist? */ | |
108 | query(cp); | |
109 | continue; | |
110 | ||
d6bccb44 RC |
111 | case 'S': /* Special. Execute commands */ |
112 | dospecial(cp); | |
113 | continue; | |
114 | ||
e8109cf8 RC |
115 | #ifdef notdef |
116 | /* | |
117 | * These entries are reserved but not currently used. | |
118 | * The intent is to allow remote hosts to have master copies. | |
119 | * Currently, only the host rdist runs on can have masters. | |
120 | */ | |
121 | case 'X': /* start a new list of files to exclude */ | |
122 | except = bp = NULL; | |
123 | case 'x': /* add name to list of files to exclude */ | |
124 | if (*cp == '\0') { | |
d6bccb44 | 125 | ack(); |
d1dee8e8 RC |
126 | continue; |
127 | } | |
e8109cf8 RC |
128 | if (*cp == '~') { |
129 | if (exptilde(buf, cp) == NULL) | |
130 | continue; | |
131 | cp = buf; | |
d1dee8e8 | 132 | } |
e8109cf8 | 133 | if (bp == NULL) |
d6bccb44 | 134 | except = bp = expand(makeblock(NAME, cp), E_VARS); |
e8109cf8 | 135 | else |
d6bccb44 | 136 | bp->b_next = expand(makeblock(NAME, cp), E_VARS); |
e8109cf8 RC |
137 | while (bp->b_next != NULL) |
138 | bp = bp->b_next; | |
d6bccb44 | 139 | ack(); |
3024eb6f RC |
140 | continue; |
141 | ||
d6bccb44 | 142 | case 'I': /* Install. Transfer file if out of date. */ |
e8109cf8 RC |
143 | opts = 0; |
144 | while (*cp >= '0' && *cp <= '7') | |
145 | opts = (opts << 3) | (*cp++ - '0'); | |
146 | if (*cp++ != ' ') { | |
147 | error("server: options not delimited\n"); | |
148 | return; | |
149 | } | |
d6bccb44 | 150 | install(cp, opts); |
2f8aab68 RC |
151 | continue; |
152 | ||
153 | case 'L': /* Log. save message in log file */ | |
3024eb6f | 154 | log(lfp, cp); |
2f8aab68 | 155 | continue; |
e8109cf8 | 156 | #endif |
2f8aab68 | 157 | |
82572cb6 | 158 | case '\1': |
0fccdfef | 159 | nerrs++; |
82572cb6 RC |
160 | continue; |
161 | ||
162 | case '\2': | |
163 | return; | |
164 | ||
2f8aab68 | 165 | default: |
e8109cf8 | 166 | error("server: unknown command '%s'\n", cp); |
2f8aab68 RC |
167 | case '\0': |
168 | continue; | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | /* | |
e8109cf8 RC |
174 | * Update the file(s) if they are different. |
175 | * destdir = 1 if destination should be a directory | |
176 | * (i.e., more than one source is being copied to the same destination). | |
2f8aab68 | 177 | */ |
e8109cf8 RC |
178 | install(src, dest, destdir, opts) |
179 | char *src, *dest; | |
180 | int destdir, opts; | |
2f8aab68 | 181 | { |
e8109cf8 | 182 | char *rname; |
2f8aab68 | 183 | |
e8109cf8 RC |
184 | if (dest == NULL) { |
185 | opts &= ~WHOLE; /* WHOLE mode only useful if renaming */ | |
186 | dest = src; | |
187 | } | |
2f8aab68 | 188 | |
e8109cf8 RC |
189 | if (nflag || debug) { |
190 | printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install", | |
191 | opts & WHOLE ? " -w" : "", | |
192 | opts & YOUNGER ? " -y" : "", | |
193 | opts & COMPARE ? " -b" : "", | |
d6bccb44 | 194 | opts & REMOVE ? " -R" : "", src, dest); |
e8109cf8 RC |
195 | if (nflag) |
196 | return; | |
197 | } | |
198 | ||
199 | rname = exptilde(target, src); | |
200 | if (rname == NULL) | |
201 | return; | |
202 | tp = target; | |
203 | while (*tp) | |
204 | tp++; | |
82572cb6 | 205 | /* |
e8109cf8 | 206 | * If we are renaming a directory and we want to preserve |
d6bccb44 | 207 | * the directory heirarchy (-w), we must strip off the leading |
e8109cf8 | 208 | * directory name and preserve the rest. |
82572cb6 | 209 | */ |
e8109cf8 | 210 | if (opts & WHOLE) { |
d6bccb44 RC |
211 | while (*rname == '/') |
212 | rname++; | |
213 | destdir = 1; | |
e8109cf8 RC |
214 | } else { |
215 | rname = rindex(target, '/'); | |
216 | if (rname == NULL) | |
217 | rname = target; | |
218 | else | |
219 | rname++; | |
82572cb6 | 220 | } |
e8109cf8 RC |
221 | if (debug) |
222 | printf("target = %s, rname = %s\n", target, rname); | |
223 | /* | |
224 | * Pass the destination file/directory name to remote. | |
225 | */ | |
226 | (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest); | |
227 | if (debug) | |
228 | printf("buf = %s", buf); | |
229 | (void) write(rem, buf, strlen(buf)); | |
230 | if (response() < 0) | |
231 | return; | |
232 | ||
233 | sendf(rname, opts); | |
234 | } | |
235 | ||
236 | /* | |
237 | * Transfer the file or directory in target[]. | |
238 | * rname is the name of the file on the remote host. | |
239 | */ | |
240 | sendf(rname, opts) | |
241 | char *rname; | |
242 | int opts; | |
243 | { | |
0fccdfef | 244 | register struct subcmd *sc; |
e8109cf8 | 245 | struct stat stb; |
a3e6fd64 | 246 | int sizerr, f, u, len; |
e8109cf8 | 247 | off_t i; |
a3e6fd64 RC |
248 | DIR *d; |
249 | struct direct *dp; | |
250 | char *otp, *cp; | |
4085f36f | 251 | extern struct subcmd *subcmds; |
e8109cf8 RC |
252 | |
253 | if (debug) | |
254 | printf("sendf(%s, %x)\n", rname, opts); | |
255 | ||
4085f36f | 256 | if (except(target)) |
024fde5b | 257 | return; |
a3e6fd64 RC |
258 | if (access(target, 4) < 0 || |
259 | (opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) { | |
e8109cf8 | 260 | error("%s: %s\n", target, sys_errlist[errno]); |
2f8aab68 RC |
261 | return; |
262 | } | |
a3e6fd64 RC |
263 | if ((u = update(rname, opts, &stb)) == 0) { |
264 | if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1) | |
265 | (void) savelink(&stb); | |
2f8aab68 | 266 | return; |
a3e6fd64 | 267 | } |
2f8aab68 | 268 | |
e8109cf8 RC |
269 | if (pw == NULL || pw->pw_uid != stb.st_uid) |
270 | if ((pw = getpwuid(stb.st_uid)) == NULL) { | |
271 | error("%s: no password entry for uid %d\n", target, | |
272 | stb.st_uid); | |
82572cb6 RC |
273 | return; |
274 | } | |
e8109cf8 RC |
275 | if (gr == NULL || gr->gr_gid != stb.st_gid) |
276 | if ((gr = getgrgid(stb.st_gid)) == NULL) { | |
277 | error("%s: no name for group %d\n", target, stb.st_gid); | |
82572cb6 RC |
278 | return; |
279 | } | |
024fde5b | 280 | if (u == 1) { |
d6bccb44 RC |
281 | if (opts & VERIFY) { |
282 | log(lfp, "need to install: %s\n", target); | |
283 | goto dospecial; | |
284 | } | |
e8109cf8 | 285 | log(lfp, "installing: %s\n", target); |
d6bccb44 | 286 | opts &= ~(COMPARE|REMOVE); |
024fde5b | 287 | } |
2f8aab68 RC |
288 | |
289 | switch (stb.st_mode & S_IFMT) { | |
a3e6fd64 RC |
290 | case S_IFDIR: |
291 | if ((d = opendir(target)) == NULL) { | |
292 | error("%s: %s\n", target, sys_errlist[errno]); | |
293 | return; | |
294 | } | |
295 | (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts, | |
296 | stb.st_mode & 07777, pw->pw_name, gr->gr_name, rname); | |
297 | if (debug) | |
85b60cd9 | 298 | printf("buf = %s", buf); |
a3e6fd64 RC |
299 | (void) write(rem, buf, strlen(buf)); |
300 | if (response() < 0) { | |
301 | closedir(d); | |
302 | return; | |
303 | } | |
2f8aab68 | 304 | |
a3e6fd64 RC |
305 | if (opts & REMOVE) |
306 | rmchk(opts); | |
d6bccb44 | 307 | |
a3e6fd64 RC |
308 | otp = tp; |
309 | len = tp - target; | |
310 | while (dp = readdir(d)) { | |
311 | if (!strcmp(dp->d_name, ".") || | |
312 | !strcmp(dp->d_name, "..")) | |
313 | continue; | |
314 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
315 | error("%s/%s: Name too long\n", target, | |
316 | dp->d_name); | |
317 | continue; | |
318 | } | |
319 | tp = otp; | |
320 | *tp++ = '/'; | |
321 | cp = dp->d_name; | |
322 | while (*tp++ = *cp++) | |
323 | ; | |
324 | tp--; | |
325 | sendf(dp->d_name, opts); | |
326 | } | |
327 | closedir(d); | |
328 | (void) write(rem, "E\n", 2); | |
329 | (void) response(); | |
330 | tp = otp; | |
331 | *tp = '\0'; | |
2f8aab68 RC |
332 | return; |
333 | ||
a3e6fd64 RC |
334 | case S_IFLNK: |
335 | if (u != 1) | |
336 | opts |= COMPARE; | |
337 | (void) sprintf(buf, "K%o %o %D %D %s %s %s\n", opts, | |
338 | stb.st_mode & 07777, stb.st_size, stb.st_mtime, | |
339 | pw->pw_name, gr->gr_name, rname); | |
340 | if (debug) | |
341 | printf("buf = %s", buf); | |
342 | (void) write(rem, buf, strlen(buf)); | |
343 | if (response() < 0) | |
344 | return; | |
345 | sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size); | |
346 | (void) write(rem, buf, stb.st_size); | |
347 | if (debug) | |
348 | printf("readlink = %.*s\n", stb.st_size, buf); | |
349 | goto done; | |
350 | ||
351 | case S_IFREG: | |
352 | break; | |
353 | ||
2f8aab68 | 354 | default: |
a3e6fd64 | 355 | error("%s: not a file or directory\n", target); |
2f8aab68 RC |
356 | return; |
357 | } | |
358 | ||
024fde5b | 359 | if (u == 2) { |
d6bccb44 RC |
360 | if (opts & VERIFY) { |
361 | log(lfp, "need to update: %s\n", target); | |
362 | goto dospecial; | |
363 | } | |
e8109cf8 | 364 | log(lfp, "updating: %s\n", target); |
024fde5b | 365 | } |
a3e6fd64 RC |
366 | |
367 | if (stb.st_nlink > 1) { | |
368 | struct linkbuf *lp; | |
369 | ||
370 | if ((lp = savelink(&stb)) != NULL) { | |
371 | /* install link */ | |
372 | (void) sprintf(buf, "k%o %s %s\n", opts, | |
373 | lp->pathname, rname); | |
374 | if (debug) | |
375 | printf("buf = %s", buf); | |
376 | (void) write(rem, buf, strlen(buf)); | |
377 | (void) response(); | |
378 | return; | |
379 | } | |
380 | } | |
2f8aab68 | 381 | |
e8109cf8 RC |
382 | if ((f = open(target, 0)) < 0) { |
383 | error("%s: %s\n", target, sys_errlist[errno]); | |
82572cb6 RC |
384 | return; |
385 | } | |
a3e6fd64 | 386 | (void) sprintf(buf, "R%o %o %D %D %s %s %s\n", opts, |
f7770429 | 387 | stb.st_mode & 07777, stb.st_size, stb.st_mtime, |
e8109cf8 | 388 | pw->pw_name, gr->gr_name, rname); |
2f8aab68 RC |
389 | if (debug) |
390 | printf("buf = %s", buf); | |
391 | (void) write(rem, buf, strlen(buf)); | |
392 | if (response() < 0) { | |
393 | (void) close(f); | |
394 | return; | |
395 | } | |
396 | sizerr = 0; | |
397 | for (i = 0; i < stb.st_size; i += BUFSIZ) { | |
398 | int amt = BUFSIZ; | |
399 | if (i + amt > stb.st_size) | |
400 | amt = stb.st_size - i; | |
401 | if (sizerr == 0 && read(f, buf, amt) != amt) | |
402 | sizerr = 1; | |
403 | (void) write(rem, buf, amt); | |
404 | } | |
405 | (void) close(f); | |
a3e6fd64 | 406 | done: |
d6bccb44 | 407 | if (sizerr) { |
e8109cf8 | 408 | error("%s: file changed size\n", target); |
d6bccb44 RC |
409 | err(); |
410 | } else | |
411 | ack(); | |
a3e6fd64 RC |
412 | f = response(); |
413 | if (f < 0 || f == 0 && (opts & COMPARE)) | |
d6bccb44 RC |
414 | return; |
415 | dospecial: | |
4085f36f | 416 | for (sc = subcmds; sc != NULL; sc = sc->sc_next) { |
0fccdfef | 417 | if (sc->sc_type != SPECIAL) |
d6bccb44 | 418 | continue; |
0fccdfef | 419 | if (!inlist(sc->sc_args, target)) |
d6bccb44 | 420 | continue; |
0fccdfef | 421 | log(lfp, "special \"%s\"\n", sc->sc_name); |
d6bccb44 RC |
422 | if (opts & VERIFY) |
423 | continue; | |
0fccdfef | 424 | (void) sprintf(buf, "S%s\n", sc->sc_name); |
d6bccb44 RC |
425 | if (debug) |
426 | printf("buf = %s", buf); | |
427 | (void) write(rem, buf, strlen(buf)); | |
428 | while (response() > 0) | |
429 | ; | |
430 | } | |
2f8aab68 RC |
431 | } |
432 | ||
a3e6fd64 RC |
433 | struct linkbuf * |
434 | savelink(stp) | |
435 | struct stat *stp; | |
2f8aab68 | 436 | { |
a3e6fd64 RC |
437 | struct linkbuf *lp; |
438 | int found = 0; | |
2f8aab68 | 439 | |
a3e6fd64 RC |
440 | for (lp = ihead; lp != NULL; lp = lp->nextp) |
441 | if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) { | |
442 | lp->count--; | |
443 | return(lp); | |
2f8aab68 | 444 | } |
a3e6fd64 RC |
445 | lp = (struct linkbuf *) malloc(sizeof(*lp)); |
446 | if (lp == NULL) | |
447 | log(lfp, "out of memory, link information lost\n"); | |
448 | else { | |
449 | lp->nextp = ihead; | |
450 | ihead = lp; | |
451 | lp->inum = stp->st_ino; | |
452 | lp->devnum = stp->st_dev; | |
453 | lp->count = stp->st_nlink - 1; | |
454 | strcpy(lp->pathname, target); | |
2f8aab68 | 455 | } |
a3e6fd64 | 456 | return(NULL); |
2f8aab68 RC |
457 | } |
458 | ||
459 | /* | |
460 | * Check to see if file needs to be updated on the remote machine. | |
e8109cf8 RC |
461 | * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date |
462 | * and 3 if comparing binaries to determine if out of date. | |
2f8aab68 | 463 | */ |
a3e6fd64 | 464 | update(rname, opts, stp) |
e8109cf8 | 465 | char *rname; |
f7770429 | 466 | int opts; |
a3e6fd64 | 467 | struct stat *stp; |
2f8aab68 | 468 | { |
e8109cf8 | 469 | register char *cp, *s; |
2f8aab68 RC |
470 | register off_t size; |
471 | register time_t mtime; | |
472 | ||
473 | if (debug) | |
a3e6fd64 | 474 | printf("update(%s, %x, %x)\n", rname, opts, stp); |
2f8aab68 RC |
475 | |
476 | /* | |
477 | * Check to see if the file exists on the remote machine. | |
478 | */ | |
e8109cf8 | 479 | (void) sprintf(buf, "Q%s\n", rname); |
2f8aab68 RC |
480 | if (debug) |
481 | printf("buf = %s", buf); | |
482 | (void) write(rem, buf, strlen(buf)); | |
e8109cf8 | 483 | cp = s = buf; |
2f8aab68 RC |
484 | do { |
485 | if (read(rem, cp, 1) != 1) | |
346dad94 | 486 | lostconn(); |
82572cb6 | 487 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); |
2f8aab68 | 488 | |
e8109cf8 | 489 | switch (*s++) { |
2f8aab68 RC |
490 | case 'Y': |
491 | break; | |
492 | ||
82572cb6 | 493 | case 'N': /* file doesn't exist so install it */ |
2f8aab68 RC |
494 | return(1); |
495 | ||
496 | case '\1': | |
0fccdfef | 497 | nerrs++; |
e8109cf8 | 498 | if (*s != '\n') { |
82572cb6 RC |
499 | if (!iamremote) { |
500 | fflush(stdout); | |
e8109cf8 | 501 | (void) write(2, s, cp - s); |
82572cb6 RC |
502 | } |
503 | if (lfp != NULL) | |
e8109cf8 | 504 | (void) fwrite(s, 1, cp - s, lfp); |
82572cb6 | 505 | } |
2f8aab68 RC |
506 | return(0); |
507 | ||
508 | default: | |
85b60cd9 RC |
509 | *--cp = '\0'; |
510 | error("update: unexpected response '%s'\n", buf); | |
2f8aab68 RC |
511 | return(0); |
512 | } | |
513 | ||
e8109cf8 | 514 | if (*s == '\n') |
3024eb6f | 515 | return(2); |
2f8aab68 | 516 | |
024fde5b RC |
517 | if (opts & COMPARE) |
518 | return(3); | |
519 | ||
2f8aab68 | 520 | size = 0; |
e8109cf8 RC |
521 | while (isdigit(*s)) |
522 | size = size * 10 + (*s++ - '0'); | |
523 | if (*s++ != ' ') { | |
524 | error("update: size not delimited\n"); | |
2f8aab68 RC |
525 | return(0); |
526 | } | |
527 | mtime = 0; | |
e8109cf8 RC |
528 | while (isdigit(*s)) |
529 | mtime = mtime * 10 + (*s++ - '0'); | |
530 | if (*s != '\n') { | |
531 | error("update: mtime not delimited\n"); | |
2f8aab68 RC |
532 | return(0); |
533 | } | |
534 | /* | |
535 | * File needs to be updated? | |
536 | */ | |
f7770429 | 537 | if (opts & YOUNGER) { |
a3e6fd64 | 538 | if (stp->st_mtime == mtime) |
3024eb6f | 539 | return(0); |
a3e6fd64 | 540 | if (stp->st_mtime < mtime) { |
e8109cf8 | 541 | log(lfp, "Warning: %s: remote copy is newer\n", target); |
3024eb6f RC |
542 | return(0); |
543 | } | |
a3e6fd64 | 544 | } else if (stp->st_mtime == mtime && stp->st_size == size) |
2f8aab68 | 545 | return(0); |
82572cb6 | 546 | return(2); |
2f8aab68 RC |
547 | } |
548 | ||
549 | /* | |
550 | * Query. Check to see if file exists. Return one of the following: | |
551 | * N\n - doesn't exist | |
552 | * Ysize mtime\n - exists and its a regular file (size & mtime of file) | |
a3e6fd64 | 553 | * Y\n - exists and its a directory or symbolic link |
2f8aab68 RC |
554 | * ^Aerror message\n |
555 | */ | |
e8109cf8 | 556 | query(name) |
2f8aab68 RC |
557 | char *name; |
558 | { | |
559 | struct stat stb; | |
560 | ||
561 | if (catname) | |
562 | (void) sprintf(tp, "/%s", name); | |
3024eb6f | 563 | |
a3e6fd64 | 564 | if (lstat(target, &stb) < 0) { |
2f8aab68 RC |
565 | (void) write(rem, "N\n", 2); |
566 | *tp = '\0'; | |
567 | return; | |
568 | } | |
569 | ||
570 | switch (stb.st_mode & S_IFMT) { | |
571 | case S_IFREG: | |
572 | (void) sprintf(buf, "Y%D %D\n", stb.st_size, stb.st_mtime); | |
573 | (void) write(rem, buf, strlen(buf)); | |
574 | break; | |
575 | ||
a3e6fd64 | 576 | case S_IFLNK: |
2f8aab68 RC |
577 | case S_IFDIR: |
578 | (void) write(rem, "Y\n", 2); | |
579 | break; | |
580 | ||
581 | default: | |
a3e6fd64 | 582 | error("%s: not a file or directory\n", name); |
2f8aab68 RC |
583 | break; |
584 | } | |
585 | *tp = '\0'; | |
586 | } | |
587 | ||
a3e6fd64 | 588 | recvf(cmd, type) |
2f8aab68 | 589 | char *cmd; |
a3e6fd64 | 590 | int type; |
2f8aab68 RC |
591 | { |
592 | register char *cp; | |
6e570eb0 | 593 | int f, mode, opts, wrerr, olderrno; |
2f8aab68 RC |
594 | off_t i, size; |
595 | time_t mtime; | |
596 | struct stat stb; | |
597 | struct timeval tvp[2]; | |
598 | char *owner, *group, *dir; | |
599 | char new[BUFSIZ]; | |
82572cb6 | 600 | extern char *tmpname; |
2f8aab68 | 601 | |
f7770429 | 602 | cp = cmd; |
024fde5b RC |
603 | opts = 0; |
604 | while (*cp >= '0' && *cp <= '7') | |
605 | opts = (opts << 3) | (*cp++ - '0'); | |
f7770429 | 606 | if (*cp++ != ' ') { |
e8109cf8 | 607 | error("recvf: options not delimited\n"); |
f7770429 RC |
608 | return; |
609 | } | |
2f8aab68 | 610 | mode = 0; |
024fde5b | 611 | while (*cp >= '0' && *cp <= '7') |
f7770429 | 612 | mode = (mode << 3) | (*cp++ - '0'); |
2f8aab68 | 613 | if (*cp++ != ' ') { |
e8109cf8 | 614 | error("recvf: mode not delimited\n"); |
2f8aab68 RC |
615 | return; |
616 | } | |
617 | size = 0; | |
618 | while (isdigit(*cp)) | |
619 | size = size * 10 + (*cp++ - '0'); | |
620 | if (*cp++ != ' ') { | |
e8109cf8 | 621 | error("recvf: size not delimited\n"); |
2f8aab68 RC |
622 | return; |
623 | } | |
624 | mtime = 0; | |
625 | while (isdigit(*cp)) | |
626 | mtime = mtime * 10 + (*cp++ - '0'); | |
627 | if (*cp++ != ' ') { | |
e8109cf8 | 628 | error("recvf: mtime not delimited\n"); |
2f8aab68 RC |
629 | return; |
630 | } | |
631 | owner = cp; | |
632 | while (*cp && *cp != ' ') | |
633 | cp++; | |
634 | if (*cp != ' ') { | |
e8109cf8 | 635 | error("recvf: owner name not delimited\n"); |
2f8aab68 RC |
636 | return; |
637 | } | |
638 | *cp++ = '\0'; | |
639 | group = cp; | |
640 | while (*cp && *cp != ' ') | |
641 | cp++; | |
642 | if (*cp != ' ') { | |
e8109cf8 | 643 | error("recvf: group name not delimited\n"); |
2f8aab68 RC |
644 | return; |
645 | } | |
646 | *cp++ = '\0'; | |
647 | ||
a3e6fd64 | 648 | if (type == S_IFDIR) { |
3024eb6f | 649 | if (catname >= sizeof(stp)) { |
a3e6fd64 RC |
650 | error("%s:%s: too many directory levels\n", |
651 | host, target); | |
3024eb6f RC |
652 | return; |
653 | } | |
654 | stp[catname] = tp; | |
2f8aab68 RC |
655 | if (catname++) { |
656 | *tp++ = '/'; | |
657 | while (*tp++ = *cp++) | |
658 | ; | |
659 | tp--; | |
660 | } | |
f7770429 | 661 | if (opts & VERIFY) { |
d6bccb44 | 662 | ack(); |
82572cb6 RC |
663 | return; |
664 | } | |
a3e6fd64 | 665 | if (lstat(target, &stb) == 0) { |
d6bccb44 | 666 | if (ISDIR(stb.st_mode)) { |
9e1930d6 | 667 | if ((stb.st_mode & 0777) == mode) { |
48568d94 RC |
668 | ack(); |
669 | return; | |
670 | } | |
671 | buf[0] = '\0'; | |
672 | (void) sprintf(buf + 1, | |
673 | "%s:%s: Warning: mode %o != %o\n", | |
674 | host, target, stb.st_mode & 0777, mode); | |
675 | (void) write(rem, buf, strlen(buf + 1) + 1); | |
82572cb6 | 676 | return; |
d6bccb44 | 677 | } |
a3e6fd64 | 678 | error("%s:%s: %s\n", host,target,sys_errlist[ENOTDIR]); |
d6bccb44 | 679 | } else if (chkparent(target) < 0 || mkdir(target, mode) < 0) |
a3e6fd64 | 680 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); |
d6bccb44 RC |
681 | else if (chog(target, owner, group, mode) == 0) { |
682 | ack(); | |
683 | return; | |
2f8aab68 | 684 | } |
d6bccb44 RC |
685 | tp = stp[--catname]; |
686 | *tp = '\0'; | |
2f8aab68 RC |
687 | return; |
688 | } | |
689 | ||
3f32107d | 690 | new[0] = '\0'; |
2f8aab68 RC |
691 | if (catname) |
692 | (void) sprintf(tp, "/%s", cp); | |
a3e6fd64 RC |
693 | if (lstat(target, &stb) == 0 && (f = stb.st_mode & S_IFMT) != S_IFREG && |
694 | f != S_IFLNK) { | |
695 | error("%s:%s: not a regular file\n", host, target); | |
6e570eb0 RC |
696 | return; |
697 | } | |
3024eb6f RC |
698 | if (chkparent(target) < 0) |
699 | goto bad; | |
2f8aab68 RC |
700 | cp = rindex(target, '/'); |
701 | if (cp == NULL) | |
702 | dir = "."; | |
703 | else if (cp == target) { | |
e8109cf8 | 704 | dir = ""; |
2f8aab68 RC |
705 | cp = NULL; |
706 | } else { | |
707 | dir = target; | |
708 | *cp = '\0'; | |
709 | } | |
82572cb6 | 710 | (void) sprintf(new, "%s/%s", dir, tmpname); |
2f8aab68 RC |
711 | if (cp != NULL) |
712 | *cp = '/'; | |
a3e6fd64 RC |
713 | |
714 | if (type == S_IFLNK) { | |
715 | int j; | |
716 | ||
717 | ack(); | |
718 | cp = buf; | |
719 | for (i = 0; i < size; i += j) { | |
720 | if ((j = read(rem, cp, size - i)) <= 0) | |
721 | cleanup(); | |
722 | cp += j; | |
723 | } | |
724 | *cp = '\0'; | |
725 | umask(~mode & 0777); | |
726 | j = symlink(buf, new); | |
727 | umask(0); | |
728 | (void) response(); | |
729 | if (j < 0) | |
730 | goto bad1; | |
731 | mode &= 0777; | |
732 | if (opts & COMPARE) { | |
733 | char tbuf[BUFSIZ]; | |
734 | ||
735 | if ((i = readlink(target, tbuf, BUFSIZ)) < 0) | |
736 | goto bad; | |
85b60cd9 | 737 | if (i == size && strncmp(buf, tbuf, size) == 0) { |
a3e6fd64 RC |
738 | (void) unlink(new); |
739 | ack(); | |
740 | return; | |
741 | } | |
85b60cd9 RC |
742 | if (opts & VERIFY) |
743 | goto differ; | |
a3e6fd64 RC |
744 | } |
745 | goto fixup; | |
746 | } | |
747 | ||
2f8aab68 RC |
748 | if ((f = creat(new, mode)) < 0) |
749 | goto bad1; | |
d6bccb44 | 750 | ack(); |
2f8aab68 RC |
751 | wrerr = 0; |
752 | for (i = 0; i < size; i += BUFSIZ) { | |
753 | int amt = BUFSIZ; | |
2f8aab68 | 754 | |
3024eb6f | 755 | cp = buf; |
2f8aab68 RC |
756 | if (i + amt > size) |
757 | amt = size - i; | |
758 | do { | |
759 | int j = read(rem, cp, amt); | |
760 | ||
82572cb6 RC |
761 | if (j <= 0) { |
762 | (void) close(f); | |
763 | (void) unlink(new); | |
2f8aab68 | 764 | cleanup(); |
82572cb6 | 765 | } |
2f8aab68 RC |
766 | amt -= j; |
767 | cp += j; | |
768 | } while (amt > 0); | |
769 | amt = BUFSIZ; | |
770 | if (i + amt > size) | |
771 | amt = size - i; | |
772 | if (wrerr == 0 && write(f, buf, amt) != amt) { | |
773 | olderrno = errno; | |
774 | wrerr++; | |
775 | } | |
776 | } | |
024fde5b | 777 | (void) close(f); |
2f8aab68 RC |
778 | (void) response(); |
779 | if (wrerr) { | |
a3e6fd64 | 780 | error("%s:%s: %s\n", host, cp, sys_errlist[olderrno]); |
82572cb6 | 781 | (void) unlink(new); |
2f8aab68 RC |
782 | return; |
783 | } | |
024fde5b RC |
784 | if (opts & COMPARE) { |
785 | FILE *f1, *f2; | |
786 | int c; | |
787 | ||
788 | if ((f1 = fopen(target, "r")) == NULL) | |
789 | goto bad; | |
790 | if ((f2 = fopen(new, "r")) == NULL) | |
791 | goto bad1; | |
792 | while ((c = getc(f1)) == getc(f2)) | |
793 | if (c == EOF) { | |
794 | (void) fclose(f1); | |
795 | (void) fclose(f2); | |
796 | (void) unlink(new); | |
d6bccb44 | 797 | ack(); |
024fde5b RC |
798 | return; |
799 | } | |
800 | (void) fclose(f1); | |
801 | (void) fclose(f2); | |
802 | if (opts & VERIFY) { | |
a3e6fd64 | 803 | differ: |
024fde5b RC |
804 | (void) unlink(new); |
805 | buf[0] = '\0'; | |
6e570eb0 RC |
806 | (void) sprintf(buf + 1, "need to update %s:%s\n", |
807 | host, target); | |
024fde5b RC |
808 | (void) write(rem, buf, strlen(buf + 1) + 1); |
809 | return; | |
810 | } | |
811 | } | |
2f8aab68 RC |
812 | |
813 | /* | |
814 | * Set last modified time | |
815 | */ | |
a3e6fd64 | 816 | tvp[0].tv_sec = stb.st_atime; /* old atime from target */ |
2f8aab68 RC |
817 | tvp[0].tv_usec = 0; |
818 | tvp[1].tv_sec = mtime; | |
819 | tvp[1].tv_usec = 0; | |
3f32107d | 820 | if (utimes(new, tvp) < 0) { |
2f8aab68 | 821 | bad1: |
a3e6fd64 | 822 | error("%s:%s: %s\n", host, new, sys_errlist[errno]); |
3f32107d RC |
823 | (void) unlink(new); |
824 | return; | |
825 | } | |
a3e6fd64 | 826 | fixup: |
3f32107d RC |
827 | if (chog(new, owner, group, mode) < 0) { |
828 | (void) unlink(new); | |
2f8aab68 RC |
829 | return; |
830 | } | |
831 | ||
832 | if (rename(new, target) < 0) { | |
833 | bad: | |
a3e6fd64 | 834 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); |
82572cb6 RC |
835 | if (new[0]) |
836 | (void) unlink(new); | |
2f8aab68 RC |
837 | return; |
838 | } | |
024fde5b RC |
839 | if (opts & COMPARE) { |
840 | buf[0] = '\0'; | |
85b60cd9 | 841 | (void) sprintf(buf + 1, "%s: updated %s\n", host, target); |
024fde5b RC |
842 | (void) write(rem, buf, strlen(buf + 1) + 1); |
843 | } else | |
d6bccb44 | 844 | ack(); |
2f8aab68 RC |
845 | } |
846 | ||
a3e6fd64 RC |
847 | /* |
848 | * Creat a hard link to existing file. | |
849 | */ | |
850 | hardlink(cmd) | |
851 | char *cmd; | |
852 | { | |
853 | register char *cp; | |
854 | struct stat stb; | |
855 | char *oldname; | |
856 | int opts, exists = 0; | |
857 | ||
858 | cp = cmd; | |
859 | opts = 0; | |
860 | while (*cp >= '0' && *cp <= '7') | |
861 | opts = (opts << 3) | (*cp++ - '0'); | |
862 | if (*cp++ != ' ') { | |
863 | error("hardlink: options not delimited\n"); | |
864 | return; | |
865 | } | |
866 | oldname = cp; | |
867 | while (*cp && *cp != ' ') | |
868 | cp++; | |
869 | if (*cp != ' ') { | |
870 | error("hardlink: oldname name not delimited\n"); | |
871 | return; | |
872 | } | |
873 | *cp++ = '\0'; | |
874 | ||
875 | if (catname) | |
876 | (void) sprintf(tp, "/%s", cp); | |
877 | if (lstat(target, &stb) == 0) { | |
878 | if ((stb.st_mode & S_IFMT) != S_IFREG) { | |
879 | error("%s:%s: not a regular file\n", host, target); | |
880 | return; | |
881 | } | |
882 | exists = 1; | |
883 | } | |
884 | if (chkparent(target) < 0 || | |
885 | exists && unlink(target) < 0 || | |
886 | link(oldname, target) < 0) { | |
887 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); | |
888 | return; | |
889 | } | |
890 | ack(); | |
891 | } | |
892 | ||
3024eb6f RC |
893 | /* |
894 | * Check parent directory for write permission and create if it doesn't | |
895 | * exist. | |
896 | */ | |
897 | chkparent(name) | |
898 | char *name; | |
899 | { | |
900 | register char *cp, *dir; | |
901 | extern int userid, groupid; | |
902 | ||
903 | cp = rindex(name, '/'); | |
904 | if (cp == NULL) | |
905 | dir = "."; | |
906 | else if (cp == name) { | |
907 | dir = "/"; | |
908 | cp = NULL; | |
909 | } else { | |
910 | dir = name; | |
911 | *cp = '\0'; | |
912 | } | |
913 | if (access(dir, 2) == 0) { | |
914 | if (cp != NULL) | |
915 | *cp = '/'; | |
916 | return(0); | |
917 | } | |
918 | if (errno == ENOENT) { | |
919 | if (rindex(dir, '/') != NULL && chkparent(dir) < 0) | |
920 | goto bad; | |
921 | if (!strcmp(dir, ".") || !strcmp(dir, "/")) | |
922 | goto bad; | |
e8109cf8 | 923 | if (mkdir(dir, 0777 & ~oumask) < 0) |
3024eb6f RC |
924 | goto bad; |
925 | if (chown(dir, userid, groupid) < 0) { | |
926 | (void) unlink(dir); | |
927 | goto bad; | |
928 | } | |
929 | if (cp != NULL) | |
930 | *cp = '/'; | |
931 | return(0); | |
932 | } | |
933 | ||
934 | bad: | |
935 | if (cp != NULL) | |
936 | *cp = '/'; | |
937 | return(-1); | |
938 | } | |
939 | ||
2f8aab68 | 940 | /* |
d6bccb44 | 941 | * Change owner, group and mode of file. |
2f8aab68 | 942 | */ |
82572cb6 | 943 | chog(file, owner, group, mode) |
2f8aab68 | 944 | char *file, *owner, *group; |
82572cb6 | 945 | int mode; |
2f8aab68 | 946 | { |
82572cb6 RC |
947 | extern int userid, groupid; |
948 | extern char user[]; | |
2f8aab68 RC |
949 | register int i; |
950 | int uid, gid; | |
951 | ||
952 | uid = userid; | |
953 | if (userid == 0) { | |
e8109cf8 RC |
954 | if (pw == NULL || strcmp(owner, pw->pw_name) != 0) { |
955 | if ((pw = getpwnam(owner)) == NULL) { | |
82572cb6 | 956 | if (mode & 04000) { |
a3e6fd64 RC |
957 | error("%s:%s: unknown login name\n", |
958 | host, owner); | |
82572cb6 RC |
959 | return(-1); |
960 | } | |
961 | } else | |
e8109cf8 | 962 | uid = pw->pw_uid; |
82572cb6 | 963 | } else |
e8109cf8 | 964 | uid = pw->pw_uid; |
d6bccb44 RC |
965 | } else if ((mode & 04000) && strcmp(user, owner) != 0) |
966 | mode &= ~04000; | |
82572cb6 | 967 | gid = groupid; |
e8109cf8 RC |
968 | if (gr == NULL || strcmp(group, gr->gr_name) != 0) { |
969 | if ((gr = getgrnam(group)) == NULL) { | |
82572cb6 | 970 | if (mode & 02000) { |
a3e6fd64 | 971 | error("%s:%s: unknown group\n", host, group); |
82572cb6 RC |
972 | return(-1); |
973 | } | |
974 | } else | |
e8109cf8 | 975 | gid = gr->gr_gid; |
82572cb6 | 976 | } else |
e8109cf8 | 977 | gid = gr->gr_gid; |
82572cb6 | 978 | if (userid && groupid != gid) { |
e8109cf8 RC |
979 | for (i = 0; gr->gr_mem[i] != NULL; i++) |
980 | if (!(strcmp(user, gr->gr_mem[i]))) | |
2f8aab68 | 981 | goto ok; |
d6bccb44 | 982 | mode &= ~02000; |
82572cb6 | 983 | gid = groupid; |
2f8aab68 RC |
984 | } |
985 | ok: | |
986 | if (chown(file, uid, gid) < 0) { | |
a3e6fd64 | 987 | error("%s:%s: %s\n", host, file, sys_errlist[errno]); |
2f8aab68 RC |
988 | return(-1); |
989 | } | |
d6bccb44 RC |
990 | /* |
991 | * Restore set-user-id or set-group-id bit if appropriate. | |
992 | */ | |
993 | if ((mode & 06000) && chmod(file, mode) < 0) { | |
a3e6fd64 | 994 | error("%s:%s: %s\n", host, file, sys_errlist[errno]); |
d6bccb44 RC |
995 | return(-1); |
996 | } | |
2f8aab68 RC |
997 | return(0); |
998 | } | |
999 | ||
d1dee8e8 RC |
1000 | /* |
1001 | * Check for files on the machine being updated that are not on the master | |
1002 | * machine and remove them. | |
1003 | */ | |
d6bccb44 | 1004 | rmchk(opts) |
d1dee8e8 RC |
1005 | int opts; |
1006 | { | |
e8109cf8 | 1007 | register char *cp, *s; |
d1dee8e8 RC |
1008 | struct stat stb; |
1009 | ||
1010 | if (debug) | |
d6bccb44 | 1011 | printf("rmchk()\n"); |
024fde5b | 1012 | |
d1dee8e8 RC |
1013 | /* |
1014 | * Tell the remote to clean the files from the last directory sent. | |
1015 | */ | |
d6bccb44 | 1016 | (void) sprintf(buf, "C%o\n", opts & VERIFY); |
d1dee8e8 RC |
1017 | if (debug) |
1018 | printf("buf = %s", buf); | |
1019 | (void) write(rem, buf, strlen(buf)); | |
1020 | if (response() < 0) | |
1021 | return; | |
d1dee8e8 | 1022 | for (;;) { |
e8109cf8 | 1023 | cp = s = buf; |
d1dee8e8 RC |
1024 | do { |
1025 | if (read(rem, cp, 1) != 1) | |
346dad94 | 1026 | lostconn(); |
d1dee8e8 RC |
1027 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); |
1028 | ||
e8109cf8 | 1029 | switch (*s++) { |
d6bccb44 | 1030 | case 'Q': /* Query if file should be removed */ |
024fde5b RC |
1031 | /* |
1032 | * Return the following codes to remove query. | |
d6bccb44 RC |
1033 | * N\n -- file exists - DON'T remove. |
1034 | * Y\n -- file doesn't exist - REMOVE. | |
024fde5b | 1035 | */ |
d1dee8e8 | 1036 | *--cp = '\0'; |
e8109cf8 | 1037 | (void) sprintf(tp, "/%s", s); |
d1dee8e8 RC |
1038 | if (debug) |
1039 | printf("check %s\n", target); | |
4085f36f | 1040 | if (except(target)) |
d1dee8e8 | 1041 | (void) write(rem, "N\n", 2); |
a3e6fd64 | 1042 | else if (lstat(target, &stb) < 0) |
d1dee8e8 | 1043 | (void) write(rem, "Y\n", 2); |
d6bccb44 RC |
1044 | else |
1045 | (void) write(rem, "N\n", 2); | |
d1dee8e8 RC |
1046 | break; |
1047 | ||
1048 | case '\0': | |
1049 | *--cp = '\0'; | |
e8109cf8 RC |
1050 | if (*s != '\0') |
1051 | log(lfp, "%s\n", s); | |
d1dee8e8 RC |
1052 | break; |
1053 | ||
d6bccb44 RC |
1054 | case 'E': |
1055 | *tp = '\0'; | |
1056 | ack(); | |
1057 | return; | |
1058 | ||
d1dee8e8 RC |
1059 | case '\1': |
1060 | case '\2': | |
0fccdfef | 1061 | nerrs++; |
e8109cf8 | 1062 | if (*s != '\n') { |
d1dee8e8 RC |
1063 | if (!iamremote) { |
1064 | fflush(stdout); | |
e8109cf8 | 1065 | (void) write(2, s, cp - s); |
d1dee8e8 RC |
1066 | } |
1067 | if (lfp != NULL) | |
e8109cf8 | 1068 | (void) fwrite(s, 1, cp - s, lfp); |
d1dee8e8 RC |
1069 | } |
1070 | if (buf[0] == '\2') | |
346dad94 | 1071 | lostconn(); |
d1dee8e8 RC |
1072 | break; |
1073 | ||
1074 | default: | |
d6bccb44 RC |
1075 | error("rmchk: unexpected response '%s'\n", buf); |
1076 | err(); | |
d1dee8e8 RC |
1077 | } |
1078 | } | |
1079 | } | |
1080 | ||
1081 | /* | |
d6bccb44 | 1082 | * Check the current directory (initialized by the 'T' command to server()) |
024fde5b | 1083 | * for extraneous files and remove them. |
d1dee8e8 | 1084 | */ |
855cc4f4 RC |
1085 | clean(cp) |
1086 | register char *cp; | |
d1dee8e8 RC |
1087 | { |
1088 | DIR *d; | |
d6bccb44 | 1089 | register struct direct *dp; |
d1dee8e8 | 1090 | struct stat stb; |
d6bccb44 RC |
1091 | char *otp; |
1092 | int len, opts; | |
d1dee8e8 | 1093 | |
d6bccb44 RC |
1094 | opts = 0; |
1095 | while (*cp >= '0' && *cp <= '7') | |
1096 | opts = (opts << 3) | (*cp++ - '0'); | |
1097 | if (*cp != '\0') { | |
1098 | error("clean: options not delimited\n"); | |
1099 | return; | |
d1dee8e8 | 1100 | } |
e8109cf8 | 1101 | if (access(target, 6) < 0 || (d = opendir(target)) == NULL) { |
a3e6fd64 | 1102 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); |
e8109cf8 RC |
1103 | return; |
1104 | } | |
d6bccb44 | 1105 | ack(); |
d1dee8e8 RC |
1106 | |
1107 | otp = tp; | |
1108 | len = tp - target; | |
1109 | while (dp = readdir(d)) { | |
1110 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
1111 | continue; | |
1112 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
a3e6fd64 RC |
1113 | error("%s:%s/%s: Name too long\n", |
1114 | host, target, dp->d_name); | |
d1dee8e8 RC |
1115 | continue; |
1116 | } | |
1117 | tp = otp; | |
1118 | *tp++ = '/'; | |
1119 | cp = dp->d_name;; | |
1120 | while (*tp++ = *cp++) | |
1121 | ; | |
1122 | tp--; | |
a3e6fd64 RC |
1123 | if (lstat(target, &stb) < 0) { |
1124 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); | |
d1dee8e8 RC |
1125 | continue; |
1126 | } | |
d6bccb44 | 1127 | (void) sprintf(buf, "Q%s\n", dp->d_name); |
d1dee8e8 RC |
1128 | (void) write(rem, buf, strlen(buf)); |
1129 | cp = buf; | |
1130 | do { | |
1131 | if (read(rem, cp, 1) != 1) | |
0fccdfef | 1132 | cleanup(); |
d1dee8e8 RC |
1133 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); |
1134 | *--cp = '\0'; | |
1135 | cp = buf; | |
d6bccb44 | 1136 | if (*cp != 'Y') |
d1dee8e8 | 1137 | continue; |
e8109cf8 RC |
1138 | if (opts & VERIFY) { |
1139 | cp = buf; | |
1140 | *cp++ = '\0'; | |
d6bccb44 | 1141 | (void) sprintf(cp, "need to remove %s\n", target); |
e8109cf8 RC |
1142 | (void) write(rem, buf, strlen(cp) + 1); |
1143 | } else | |
d1dee8e8 RC |
1144 | remove(&stb); |
1145 | } | |
1146 | closedir(d); | |
d1dee8e8 RC |
1147 | (void) write(rem, "E\n", 2); |
1148 | (void) response(); | |
d6bccb44 | 1149 | tp = otp; |
e8109cf8 | 1150 | *tp = '\0'; |
d1dee8e8 RC |
1151 | } |
1152 | ||
e8109cf8 RC |
1153 | /* |
1154 | * Remove a file or directory (recursively) and send back an acknowledge | |
1155 | * or an error message. | |
1156 | */ | |
a3e6fd64 RC |
1157 | remove(stp) |
1158 | struct stat *stp; | |
d1dee8e8 RC |
1159 | { |
1160 | DIR *d; | |
1161 | struct direct *dp; | |
1162 | register char *cp; | |
1163 | struct stat stb; | |
1164 | char *otp; | |
1165 | int len; | |
1166 | ||
a3e6fd64 | 1167 | switch (stp->st_mode & S_IFMT) { |
d1dee8e8 | 1168 | case S_IFREG: |
a3e6fd64 | 1169 | case S_IFLNK: |
d1dee8e8 RC |
1170 | if (unlink(target) < 0) |
1171 | goto bad; | |
1172 | goto removed; | |
1173 | ||
1174 | case S_IFDIR: | |
1175 | break; | |
1176 | ||
1177 | default: | |
a3e6fd64 | 1178 | error("%s:%s: not a plain file\n", host, target); |
d1dee8e8 RC |
1179 | return; |
1180 | } | |
1181 | ||
1182 | if (access(target, 6) < 0 || (d = opendir(target)) == NULL) | |
1183 | goto bad; | |
1184 | ||
1185 | otp = tp; | |
1186 | len = tp - target; | |
1187 | while (dp = readdir(d)) { | |
1188 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
1189 | continue; | |
1190 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
a3e6fd64 RC |
1191 | error("%s:%s/%s: Name too long\n", |
1192 | host, target, dp->d_name); | |
d1dee8e8 RC |
1193 | continue; |
1194 | } | |
1195 | tp = otp; | |
1196 | *tp++ = '/'; | |
1197 | cp = dp->d_name;; | |
1198 | while (*tp++ = *cp++) | |
1199 | ; | |
1200 | tp--; | |
a3e6fd64 RC |
1201 | if (lstat(target, &stb) < 0) { |
1202 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); | |
d1dee8e8 RC |
1203 | continue; |
1204 | } | |
1205 | remove(&stb); | |
1206 | } | |
1207 | closedir(d); | |
1208 | tp = otp; | |
1209 | *tp = '\0'; | |
1210 | if (rmdir(target) < 0) { | |
1211 | bad: | |
a3e6fd64 | 1212 | error("%s:%s: %s\n", host, target, sys_errlist[errno]); |
d1dee8e8 RC |
1213 | return; |
1214 | } | |
1215 | removed: | |
1216 | cp = buf; | |
1217 | *cp++ = '\0'; | |
1218 | (void) sprintf(cp, "removed %s\n", target); | |
1219 | (void) write(rem, buf, strlen(cp) + 1); | |
1220 | } | |
1221 | ||
d6bccb44 RC |
1222 | /* |
1223 | * Execute a shell command to handle special cases. | |
1224 | */ | |
1225 | dospecial(cmd) | |
1226 | char *cmd; | |
1227 | { | |
1228 | int fd[2], status, pid, i; | |
1229 | register char *cp, *s; | |
1230 | char sbuf[BUFSIZ]; | |
1231 | ||
1232 | if (pipe(fd) < 0) { | |
1233 | error("%s\n", sys_errlist[errno]); | |
1234 | return; | |
1235 | } | |
1236 | if ((pid = fork()) == 0) { | |
1237 | /* | |
1238 | * Return everything the shell commands print. | |
1239 | */ | |
1240 | (void) close(0); | |
1241 | (void) close(1); | |
1242 | (void) close(2); | |
1243 | (void) open("/dev/null", 0); | |
1244 | (void) dup(fd[1]); | |
1245 | (void) dup(fd[1]); | |
1246 | (void) close(fd[0]); | |
1247 | (void) close(fd[1]); | |
1248 | execl("/bin/sh", "sh", "-c", cmd, 0); | |
1249 | _exit(127); | |
1250 | } | |
1251 | (void) close(fd[1]); | |
1252 | s = sbuf; | |
1253 | *s++ = '\0'; | |
1254 | while ((i = read(fd[0], buf, sizeof(buf))) > 0) { | |
1255 | cp = buf; | |
1256 | do { | |
1257 | *s++ = *cp++; | |
1258 | if (cp[-1] != '\n') { | |
1259 | if (s < &sbuf[sizeof(sbuf)-1]) | |
1260 | continue; | |
1261 | *s++ = '\n'; | |
1262 | } | |
1263 | /* | |
1264 | * Throw away blank lines. | |
1265 | */ | |
1266 | if (s == &sbuf[2]) { | |
1267 | s--; | |
1268 | continue; | |
1269 | } | |
1270 | (void) write(rem, sbuf, s - sbuf); | |
1271 | s = &sbuf[1]; | |
1272 | } while (--i); | |
1273 | } | |
1274 | if (s > &sbuf[1]) { | |
1275 | *s++ = '\n'; | |
1276 | (void) write(rem, sbuf, s - sbuf); | |
1277 | } | |
1278 | while ((i = wait(&status)) != pid && i != -1) | |
1279 | ; | |
1280 | if (i == -1) | |
1281 | status = -1; | |
1282 | if (status) | |
1283 | error("shell returned %d\n", status); | |
1284 | else | |
1285 | ack(); | |
1286 | } | |
1287 | ||
3024eb6f | 1288 | /*VARARGS2*/ |
82572cb6 RC |
1289 | log(fp, fmt, a1, a2, a3) |
1290 | FILE *fp; | |
2f8aab68 RC |
1291 | char *fmt; |
1292 | int a1, a2, a3; | |
1293 | { | |
1294 | /* Print changes locally if not quiet mode */ | |
1295 | if (!qflag) | |
1296 | printf(fmt, a1, a2, a3); | |
1297 | ||
1298 | /* Save changes (for mailing) if really updating files */ | |
f7770429 | 1299 | if (!(options & VERIFY) && fp != NULL) |
82572cb6 | 1300 | fprintf(fp, fmt, a1, a2, a3); |
2f8aab68 RC |
1301 | } |
1302 | ||
3024eb6f | 1303 | /*VARARGS1*/ |
2f8aab68 RC |
1304 | error(fmt, a1, a2, a3) |
1305 | char *fmt; | |
1306 | int a1, a2, a3; | |
1307 | { | |
0fccdfef | 1308 | nerrs++; |
2f8aab68 RC |
1309 | strcpy(buf, "\1rdist: "); |
1310 | (void) sprintf(buf+8, fmt, a1, a2, a3); | |
3024eb6f RC |
1311 | if (!iamremote) { |
1312 | fflush(stdout); | |
1313 | (void) write(2, buf+1, strlen(buf+1)); | |
d6bccb44 RC |
1314 | } else |
1315 | (void) write(rem, buf, strlen(buf)); | |
3024eb6f RC |
1316 | if (lfp != NULL) |
1317 | (void) fwrite(buf+1, 1, strlen(buf+1), lfp); | |
2f8aab68 RC |
1318 | } |
1319 | ||
3024eb6f | 1320 | /*VARARGS1*/ |
2f8aab68 RC |
1321 | fatal(fmt, a1, a2,a3) |
1322 | char *fmt; | |
1323 | int a1, a2, a3; | |
1324 | { | |
0fccdfef | 1325 | nerrs++; |
2f8aab68 RC |
1326 | strcpy(buf, "\2rdist: "); |
1327 | (void) sprintf(buf+8, fmt, a1, a2, a3); | |
3024eb6f RC |
1328 | if (!iamremote) { |
1329 | fflush(stdout); | |
1330 | (void) write(2, buf+1, strlen(buf+1)); | |
d6bccb44 RC |
1331 | } else |
1332 | (void) write(rem, buf, strlen(buf)); | |
3024eb6f RC |
1333 | if (lfp != NULL) |
1334 | (void) fwrite(buf+1, 1, strlen(buf+1), lfp); | |
2f8aab68 RC |
1335 | cleanup(); |
1336 | } | |
1337 | ||
1338 | response() | |
1339 | { | |
024fde5b | 1340 | char *cp, *s; |
85b60cd9 | 1341 | char resp[BUFSIZ]; |
2f8aab68 RC |
1342 | |
1343 | if (debug) | |
1344 | printf("response()\n"); | |
1345 | ||
85b60cd9 | 1346 | cp = s = resp; |
024fde5b RC |
1347 | do { |
1348 | if (read(rem, cp, 1) != 1) | |
346dad94 | 1349 | lostconn(); |
85b60cd9 | 1350 | } while (*cp++ != '\n' && cp < &resp[BUFSIZ]); |
2f8aab68 | 1351 | |
024fde5b | 1352 | switch (*s++) { |
2f8aab68 | 1353 | case '\0': |
024fde5b | 1354 | *--cp = '\0'; |
d6bccb44 | 1355 | if (*s != '\0') { |
024fde5b | 1356 | log(lfp, "%s\n", s); |
d6bccb44 RC |
1357 | return(1); |
1358 | } | |
2f8aab68 RC |
1359 | return(0); |
1360 | ||
1361 | default: | |
024fde5b | 1362 | s--; |
2f8aab68 RC |
1363 | /* fall into... */ |
1364 | case '\1': | |
1365 | case '\2': | |
0fccdfef | 1366 | nerrs++; |
024fde5b | 1367 | if (*s != '\n') { |
82572cb6 RC |
1368 | if (!iamremote) { |
1369 | fflush(stdout); | |
024fde5b | 1370 | (void) write(2, s, cp - s); |
82572cb6 | 1371 | } |
2f8aab68 | 1372 | if (lfp != NULL) |
024fde5b | 1373 | (void) fwrite(s, 1, cp - s, lfp); |
2f8aab68 | 1374 | } |
85b60cd9 | 1375 | if (resp[0] == '\2') |
346dad94 | 1376 | lostconn(); |
e8109cf8 | 1377 | return(-1); |
2f8aab68 | 1378 | } |
2f8aab68 RC |
1379 | } |
1380 | ||
0fccdfef RC |
1381 | /* |
1382 | * Remove temporary files and do any cleanup operations before exiting. | |
1383 | */ | |
1384 | cleanup() | |
2f8aab68 | 1385 | { |
0fccdfef RC |
1386 | (void) unlink(tmpfile); |
1387 | exit(1); | |
2f8aab68 | 1388 | } |