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