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