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