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