Commit | Line | Data |
---|---|---|
2f8aab68 | 1 | #ifndef lint |
024fde5b | 2 | static char *sccsid = "@(#)server.c 4.6 (Berkeley) 83/10/26"; |
2f8aab68 RC |
3 | #endif |
4 | ||
5 | #include "defs.h" | |
6 | ||
024fde5b | 7 | #define ga() (void) write(rem, "\0\n", 2) |
2f8aab68 | 8 | |
82572cb6 | 9 | char buf[BUFSIZ]; /* general purpose buffer */ |
2f8aab68 RC |
10 | char target[BUFSIZ]; /* target/source directory name */ |
11 | char *tp; /* pointer to end of target name */ | |
12 | int catname; /* cat name to target name */ | |
3024eb6f RC |
13 | char *stp[32]; /* stack of saved tp's for directories */ |
14 | int sumask; /* saved umask for creating files */ | |
2f8aab68 | 15 | |
82572cb6 RC |
16 | static struct passwd *p = NULL; |
17 | static struct group *g = NULL; | |
18 | ||
19 | extern FILE *lfp; /* log file for mailing changes */ | |
20 | ||
3024eb6f RC |
21 | extern char *exptilde(); |
22 | ||
2f8aab68 RC |
23 | /* |
24 | * Server routine to read requests and process them. | |
25 | * Commands are: | |
26 | * Tname - Transmit file if out of date | |
27 | * Vname - Verify if file out of date or not | |
28 | * Qname - Query if file exists. Return mtime & size if it does. | |
29 | */ | |
30 | server() | |
31 | { | |
32 | char cmdbuf[BUFSIZ]; | |
33 | register char *cp; | |
34 | register struct block *bp, *last = NULL; | |
d1dee8e8 | 35 | int opts; |
2f8aab68 | 36 | |
3024eb6f | 37 | sumask = umask(0); |
2f8aab68 RC |
38 | ga(); |
39 | ||
40 | for (;;) { | |
41 | cp = cmdbuf; | |
42 | if (read(rem, cp, 1) <= 0) | |
43 | return; | |
44 | if (*cp++ == '\n') { | |
45 | error("expected control record\n"); | |
46 | continue; | |
47 | } | |
48 | do { | |
49 | if (read(rem, cp, 1) != 1) | |
50 | lostconn(); | |
82572cb6 | 51 | } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]); |
2f8aab68 RC |
52 | *--cp = '\0'; |
53 | cp = cmdbuf; | |
54 | switch (*cp++) { | |
55 | case 'X': /* add name to list of files to exclude */ | |
56 | if (*cp == '\0') | |
57 | continue; | |
024fde5b RC |
58 | if (*cp == '~') { |
59 | exptilde(buf, cp); | |
60 | cp = buf; | |
61 | } | |
62 | bp = makeblock(EXCEPT); | |
63 | bp->b_args = expand(makeblock(NAME, cp), 0); | |
64 | if (last == NULL) | |
65 | except = last = bp; | |
66 | else { | |
2f8aab68 RC |
67 | last->b_next = bp; |
68 | last = bp; | |
69 | } | |
70 | continue; | |
71 | ||
72 | case 'T': /* init target file/directory name */ | |
82572cb6 RC |
73 | catname = 1; /* target should be directory */ |
74 | goto dotarget; | |
75 | ||
76 | case 't': /* init target file/directory name */ | |
2f8aab68 | 77 | catname = 0; |
82572cb6 | 78 | dotarget: |
3024eb6f | 79 | (void) exptilde(target, cp); |
2f8aab68 RC |
80 | tp = target; |
81 | while (*tp) | |
82 | tp++; | |
83 | continue; | |
84 | ||
85 | case 'S': /* Send. Transfer file if out of date. */ | |
3024eb6f | 86 | sendf(cp, NULL, 0); |
2f8aab68 RC |
87 | continue; |
88 | ||
89 | case 'V': /* Verify. See if file is out of date. */ | |
3024eb6f | 90 | sendf(cp, NULL, VERIFY); |
2f8aab68 RC |
91 | continue; |
92 | ||
93 | case 'R': /* Receive. Transfer file. */ | |
94 | recvf(cp, 0); | |
95 | continue; | |
96 | ||
97 | case 'D': /* Directory. Transfer file. */ | |
98 | recvf(cp, 1); | |
99 | continue; | |
100 | ||
101 | case 'E': /* End. (of directory) */ | |
102 | *tp = '\0'; | |
82572cb6 | 103 | if (--catname < 0) { |
2f8aab68 RC |
104 | error("too many 'E's\n"); |
105 | continue; | |
106 | } | |
3024eb6f RC |
107 | tp = stp[catname]; |
108 | *tp = '\0'; | |
2f8aab68 RC |
109 | ga(); |
110 | continue; | |
111 | ||
d1dee8e8 RC |
112 | case 'C': /* Clean. Cleanup a directory */ |
113 | if (*cp < '0' || *cp > '7') { | |
114 | error("bad options\n"); | |
115 | continue; | |
116 | } | |
117 | opts = *cp++ - '0'; | |
118 | if (*cp++ != ' ') { | |
119 | error("options not delimited\n"); | |
120 | continue; | |
121 | } | |
122 | clean(cp, opts, 1); | |
123 | continue; | |
124 | ||
3024eb6f RC |
125 | case 'Q': /* Query. Does directory exist? */ |
126 | query(cp, 1); | |
127 | continue; | |
128 | ||
129 | case 'q': /* query. Does file exist? */ | |
130 | query(cp, 0); | |
2f8aab68 RC |
131 | continue; |
132 | ||
133 | case 'L': /* Log. save message in log file */ | |
3024eb6f | 134 | log(lfp, cp); |
2f8aab68 RC |
135 | continue; |
136 | ||
82572cb6 RC |
137 | case '\1': |
138 | errs++; | |
139 | continue; | |
140 | ||
141 | case '\2': | |
142 | return; | |
143 | ||
2f8aab68 RC |
144 | default: |
145 | error("unknown command type %s\n", cp); | |
146 | case '\0': | |
147 | continue; | |
148 | } | |
149 | } | |
150 | } | |
151 | ||
152 | /* | |
153 | * Transfer the file or directory 'name'. | |
154 | */ | |
f7770429 | 155 | sendf(lname, rname, opts) |
3024eb6f | 156 | char *lname, *rname; |
f7770429 | 157 | int opts; |
2f8aab68 | 158 | { |
3024eb6f | 159 | register char *cp; |
2f8aab68 | 160 | struct stat stb; |
82572cb6 | 161 | int sizerr, f, u; |
2f8aab68 RC |
162 | off_t i; |
163 | ||
164 | if (debug) | |
3024eb6f | 165 | printf("sendf(%s, %s, %x)\n", lname, |
f7770429 | 166 | rname != NULL ? rname : "NULL", opts); |
2f8aab68 | 167 | |
82572cb6 | 168 | /* |
3024eb6f | 169 | * First time sendf() is called? |
82572cb6 | 170 | */ |
3024eb6f RC |
171 | if (rname == NULL) { |
172 | rname = exptilde(target, lname); | |
173 | if (rname == NULL) | |
174 | return; | |
175 | tp = lname = target; | |
82572cb6 RC |
176 | while (*tp) |
177 | tp++; | |
3024eb6f RC |
178 | /* |
179 | * If we are renaming a directory and we want to preserve | |
180 | * the directory heirarchy (-w), we must strip off the first | |
181 | * directory name and preserve the rest. | |
182 | */ | |
f7770429 RC |
183 | if (opts & STRIP) { |
184 | opts &= ~STRIP; | |
3024eb6f RC |
185 | rname = index(rname, '/'); |
186 | if (rname == NULL) | |
187 | rname = tp; | |
188 | else | |
189 | rname++; | |
f7770429 | 190 | } else if (!(opts & WHOLE)) { |
3024eb6f RC |
191 | rname = rindex(lname, '/'); |
192 | if (rname == NULL) | |
193 | rname = lname; | |
194 | else | |
195 | rname++; | |
196 | } | |
82572cb6 | 197 | } |
024fde5b RC |
198 | if (exclude(lname)) |
199 | return; | |
3024eb6f RC |
200 | if (access(lname, 4) < 0 || stat(lname, &stb) < 0) { |
201 | error("%s: %s\n", lname, sys_errlist[errno]); | |
2f8aab68 RC |
202 | return; |
203 | } | |
f7770429 | 204 | if ((u = update(lname, rname, opts, &stb)) == 0) |
2f8aab68 RC |
205 | return; |
206 | ||
82572cb6 RC |
207 | if (p == NULL || p->pw_uid != stb.st_uid) |
208 | if ((p = getpwuid(stb.st_uid)) == NULL) { | |
024fde5b RC |
209 | error("%s: no password entry for uid %d\n", |
210 | lname, stb.st_uid); | |
82572cb6 RC |
211 | return; |
212 | } | |
213 | if (g == NULL || g->gr_gid != stb.st_gid) | |
214 | if ((g = getgrgid(stb.st_gid)) == NULL) { | |
024fde5b RC |
215 | error("%s: no name for group %d\n", |
216 | lname, stb.st_gid); | |
82572cb6 RC |
217 | return; |
218 | } | |
024fde5b RC |
219 | if (u == 1) { |
220 | log(lfp, "installing: %s\n", lname); | |
221 | if (opts & VERIFY) | |
222 | return; | |
223 | opts &= ~COMPARE; | |
224 | } | |
2f8aab68 RC |
225 | |
226 | switch (stb.st_mode & S_IFMT) { | |
227 | case S_IFREG: | |
228 | break; | |
229 | ||
230 | case S_IFDIR: | |
f7770429 | 231 | rsendf(lname, rname, opts, &stb, p->pw_name, g->gr_name); |
2f8aab68 RC |
232 | return; |
233 | ||
234 | default: | |
3024eb6f | 235 | error("%s: not a plain file\n", lname); |
2f8aab68 RC |
236 | return; |
237 | } | |
238 | ||
024fde5b RC |
239 | if (u == 2) { |
240 | log(lfp, "updating: %s\n", lname); | |
241 | if (opts & VERIFY) | |
242 | return; | |
243 | } | |
2f8aab68 | 244 | |
3024eb6f RC |
245 | if ((f = open(lname, 0)) < 0) { |
246 | error("%s: %s\n", lname, sys_errlist[errno]); | |
82572cb6 RC |
247 | return; |
248 | } | |
024fde5b | 249 | (void) sprintf(buf, "R%o %04o %D %D %s %s %s\n", opts, |
f7770429 RC |
250 | stb.st_mode & 07777, stb.st_size, stb.st_mtime, |
251 | p->pw_name, g->gr_name, rname); | |
2f8aab68 RC |
252 | if (debug) |
253 | printf("buf = %s", buf); | |
254 | (void) write(rem, buf, strlen(buf)); | |
255 | if (response() < 0) { | |
256 | (void) close(f); | |
257 | return; | |
258 | } | |
259 | sizerr = 0; | |
260 | for (i = 0; i < stb.st_size; i += BUFSIZ) { | |
261 | int amt = BUFSIZ; | |
262 | if (i + amt > stb.st_size) | |
263 | amt = stb.st_size - i; | |
264 | if (sizerr == 0 && read(f, buf, amt) != amt) | |
265 | sizerr = 1; | |
266 | (void) write(rem, buf, amt); | |
267 | } | |
268 | (void) close(f); | |
269 | if (sizerr) | |
3024eb6f | 270 | error("%s: file changed size\n", lname); |
2f8aab68 RC |
271 | else |
272 | ga(); | |
273 | (void) response(); | |
274 | } | |
275 | ||
f7770429 | 276 | rsendf(lname, rname, opts, st, owner, group) |
3024eb6f | 277 | char *lname, *rname; |
f7770429 | 278 | int opts; |
2f8aab68 RC |
279 | struct stat *st; |
280 | char *owner, *group; | |
281 | { | |
282 | DIR *d; | |
283 | struct direct *dp; | |
3024eb6f | 284 | char *otp, *cp; |
82572cb6 | 285 | int len; |
2f8aab68 RC |
286 | |
287 | if (debug) | |
3024eb6f | 288 | printf("rsendf(%s, %s, %x, %x, %s, %s)\n", lname, rname, |
f7770429 | 289 | opts, st, owner, group); |
2f8aab68 | 290 | |
3024eb6f RC |
291 | if ((d = opendir(lname)) == NULL) { |
292 | error("%s: %s\n", lname, sys_errlist[errno]); | |
2f8aab68 RC |
293 | return; |
294 | } | |
024fde5b | 295 | (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts, |
f7770429 | 296 | st->st_mode & 07777, owner, group, rname); |
2f8aab68 RC |
297 | if (debug) |
298 | printf("buf = %s", buf); | |
299 | (void) write(rem, buf, strlen(buf)); | |
300 | if (response() < 0) { | |
301 | closedir(d); | |
302 | return; | |
303 | } | |
2f8aab68 | 304 | otp = tp; |
82572cb6 | 305 | len = tp - target; |
2f8aab68 | 306 | while (dp = readdir(d)) { |
2f8aab68 RC |
307 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) |
308 | continue; | |
82572cb6 | 309 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { |
3024eb6f | 310 | error("%s/%s: Name too long\n", target, dp->d_name); |
2f8aab68 RC |
311 | continue; |
312 | } | |
313 | tp = otp; | |
314 | *tp++ = '/'; | |
3024eb6f RC |
315 | cp = dp->d_name; |
316 | while (*tp++ = *cp++) | |
2f8aab68 RC |
317 | ; |
318 | tp--; | |
f7770429 | 319 | sendf(target, dp->d_name, opts); |
2f8aab68 RC |
320 | } |
321 | closedir(d); | |
322 | (void) write(rem, "E\n", 2); | |
323 | (void) response(); | |
324 | tp = otp; | |
325 | *tp = '\0'; | |
326 | } | |
327 | ||
328 | /* | |
329 | * Check to see if file needs to be updated on the remote machine. | |
82572cb6 | 330 | * Returns 0 if no update, 1 if remote doesn't exist, and 2 if out of date. |
2f8aab68 | 331 | */ |
f7770429 | 332 | update(lname, rname, opts, st) |
3024eb6f | 333 | char *lname, *rname; |
f7770429 | 334 | int opts; |
2f8aab68 RC |
335 | struct stat *st; |
336 | { | |
337 | register char *cp; | |
338 | register off_t size; | |
339 | register time_t mtime; | |
340 | ||
341 | if (debug) | |
f7770429 | 342 | printf("update(%s, %s, %x, %x)\n", lname, rname, opts, st); |
2f8aab68 RC |
343 | |
344 | /* | |
345 | * Check to see if the file exists on the remote machine. | |
346 | */ | |
d1dee8e8 | 347 | (void) sprintf(buf, "%c%s\n", ISDIR(st->st_mode) ? 'Q' : 'q', rname); |
2f8aab68 RC |
348 | if (debug) |
349 | printf("buf = %s", buf); | |
350 | (void) write(rem, buf, strlen(buf)); | |
351 | cp = buf; | |
352 | do { | |
353 | if (read(rem, cp, 1) != 1) | |
354 | lostconn(); | |
82572cb6 | 355 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); |
2f8aab68 | 356 | |
3024eb6f | 357 | switch (buf[0]) { |
2f8aab68 RC |
358 | case 'Y': |
359 | break; | |
360 | ||
82572cb6 | 361 | case 'N': /* file doesn't exist so install it */ |
2f8aab68 RC |
362 | return(1); |
363 | ||
364 | case '\1': | |
82572cb6 | 365 | errs++; |
3024eb6f | 366 | if (cp > &buf[2]) { |
82572cb6 RC |
367 | if (!iamremote) { |
368 | fflush(stdout); | |
3024eb6f | 369 | (void) write(2, cp, cp - buf); |
82572cb6 RC |
370 | } |
371 | if (lfp != NULL) | |
3024eb6f | 372 | (void) fwrite(cp, 1, cp - buf, lfp); |
82572cb6 | 373 | } |
2f8aab68 RC |
374 | return(0); |
375 | ||
376 | default: | |
3024eb6f | 377 | error("unexpected response '%c' to query\n", buf[0]); |
2f8aab68 RC |
378 | return(0); |
379 | } | |
380 | ||
3024eb6f RC |
381 | cp = &buf[1]; |
382 | if (*cp == '\n') | |
383 | return(2); | |
2f8aab68 | 384 | |
024fde5b RC |
385 | if (opts & COMPARE) |
386 | return(3); | |
387 | ||
2f8aab68 RC |
388 | size = 0; |
389 | while (isdigit(*cp)) | |
390 | size = size * 10 + (*cp++ - '0'); | |
391 | if (*cp++ != ' ') { | |
392 | error("size not delimited\n"); | |
393 | return(0); | |
394 | } | |
395 | mtime = 0; | |
396 | while (isdigit(*cp)) | |
397 | mtime = mtime * 10 + (*cp++ - '0'); | |
3024eb6f | 398 | if (*cp != '\n') { |
2f8aab68 RC |
399 | error("mtime not delimited\n"); |
400 | return(0); | |
401 | } | |
402 | /* | |
403 | * File needs to be updated? | |
404 | */ | |
f7770429 | 405 | if (opts & YOUNGER) { |
3024eb6f RC |
406 | if (st->st_mtime == mtime) |
407 | return(0); | |
408 | if (st->st_mtime < mtime) { | |
d1dee8e8 | 409 | log(lfp, "Warning: %s: remote copy is newer\n", lname); |
3024eb6f RC |
410 | return(0); |
411 | } | |
412 | } else if (st->st_mtime == mtime && st->st_size == size) | |
2f8aab68 | 413 | return(0); |
82572cb6 | 414 | return(2); |
2f8aab68 RC |
415 | } |
416 | ||
417 | /* | |
418 | * Query. Check to see if file exists. Return one of the following: | |
419 | * N\n - doesn't exist | |
420 | * Ysize mtime\n - exists and its a regular file (size & mtime of file) | |
421 | * Y\n - exists and its a directory | |
422 | * ^Aerror message\n | |
423 | */ | |
3024eb6f | 424 | query(name, isdir) |
2f8aab68 | 425 | char *name; |
024fde5b | 426 | int isdir; |
2f8aab68 RC |
427 | { |
428 | struct stat stb; | |
429 | ||
430 | if (catname) | |
431 | (void) sprintf(tp, "/%s", name); | |
3024eb6f RC |
432 | |
433 | again: | |
2f8aab68 RC |
434 | if (stat(target, &stb) < 0) { |
435 | (void) write(rem, "N\n", 2); | |
436 | *tp = '\0'; | |
437 | return; | |
438 | } | |
439 | ||
440 | switch (stb.st_mode & S_IFMT) { | |
441 | case S_IFREG: | |
442 | (void) sprintf(buf, "Y%D %D\n", stb.st_size, stb.st_mtime); | |
443 | (void) write(rem, buf, strlen(buf)); | |
444 | break; | |
445 | ||
446 | case S_IFDIR: | |
3024eb6f RC |
447 | /* |
448 | * If file -> directory, need to cat name to target and stat. | |
449 | */ | |
450 | if (!isdir && !catname) { | |
451 | isdir = 1; | |
452 | (void) sprintf(tp, "/%s", name); | |
453 | goto again; | |
454 | } | |
2f8aab68 RC |
455 | (void) write(rem, "Y\n", 2); |
456 | break; | |
457 | ||
458 | default: | |
459 | error("%s: not a plain file\n", name); | |
460 | break; | |
461 | } | |
462 | *tp = '\0'; | |
463 | } | |
464 | ||
465 | recvf(cmd, isdir) | |
466 | char *cmd; | |
467 | int isdir; | |
468 | { | |
469 | register char *cp; | |
024fde5b | 470 | int f, mode, opts, wrerr, olderrno, u; |
2f8aab68 RC |
471 | off_t i, size; |
472 | time_t mtime; | |
473 | struct stat stb; | |
474 | struct timeval tvp[2]; | |
475 | char *owner, *group, *dir; | |
476 | char new[BUFSIZ]; | |
82572cb6 | 477 | extern char *tmpname; |
2f8aab68 | 478 | |
f7770429 | 479 | cp = cmd; |
024fde5b RC |
480 | opts = 0; |
481 | while (*cp >= '0' && *cp <= '7') | |
482 | opts = (opts << 3) | (*cp++ - '0'); | |
f7770429 RC |
483 | if (*cp++ != ' ') { |
484 | error("options not delimited\n"); | |
485 | return; | |
486 | } | |
2f8aab68 | 487 | mode = 0; |
024fde5b | 488 | while (*cp >= '0' && *cp <= '7') |
f7770429 | 489 | mode = (mode << 3) | (*cp++ - '0'); |
2f8aab68 RC |
490 | if (*cp++ != ' ') { |
491 | error("mode not delimited\n"); | |
492 | return; | |
493 | } | |
494 | size = 0; | |
495 | while (isdigit(*cp)) | |
496 | size = size * 10 + (*cp++ - '0'); | |
497 | if (*cp++ != ' ') { | |
498 | error("size not delimited\n"); | |
499 | return; | |
500 | } | |
501 | mtime = 0; | |
502 | while (isdigit(*cp)) | |
503 | mtime = mtime * 10 + (*cp++ - '0'); | |
504 | if (*cp++ != ' ') { | |
505 | error("mtime not delimited\n"); | |
506 | return; | |
507 | } | |
508 | owner = cp; | |
509 | while (*cp && *cp != ' ') | |
510 | cp++; | |
511 | if (*cp != ' ') { | |
512 | error("owner name not delimited\n"); | |
513 | return; | |
514 | } | |
515 | *cp++ = '\0'; | |
516 | group = cp; | |
517 | while (*cp && *cp != ' ') | |
518 | cp++; | |
519 | if (*cp != ' ') { | |
520 | error("group name not delimited\n"); | |
521 | return; | |
522 | } | |
523 | *cp++ = '\0'; | |
524 | ||
82572cb6 | 525 | new[0] = '\0'; |
2f8aab68 | 526 | if (isdir) { |
3024eb6f RC |
527 | if (catname >= sizeof(stp)) { |
528 | error("%s: too many directory levels\n", target); | |
529 | return; | |
530 | } | |
531 | stp[catname] = tp; | |
2f8aab68 RC |
532 | if (catname++) { |
533 | *tp++ = '/'; | |
534 | while (*tp++ = *cp++) | |
535 | ; | |
536 | tp--; | |
537 | } | |
f7770429 | 538 | if (opts & VERIFY) { |
82572cb6 RC |
539 | ga(); |
540 | return; | |
541 | } | |
2f8aab68 | 542 | if (stat(target, &stb) == 0) { |
d1dee8e8 | 543 | if (!ISDIR(stb.st_mode)) { |
2f8aab68 RC |
544 | errno = ENOTDIR; |
545 | goto bad; | |
546 | } | |
547 | } else { | |
3024eb6f RC |
548 | if (chkparent(target) < 0) |
549 | goto bad; | |
2f8aab68 RC |
550 | if (mkdir(target, mode) < 0) |
551 | goto bad; | |
82572cb6 RC |
552 | if (chog(target, owner, group, mode) < 0) |
553 | return; | |
2f8aab68 | 554 | } |
2f8aab68 RC |
555 | ga(); |
556 | return; | |
557 | } | |
558 | ||
559 | if (catname) | |
560 | (void) sprintf(tp, "/%s", cp); | |
561 | if (stat(target, &stb) == 0) { | |
562 | switch (stb.st_mode & S_IFMT) { | |
563 | case S_IFREG: | |
564 | break; | |
565 | ||
566 | case S_IFDIR: | |
567 | if (!catname) { | |
568 | (void) sprintf(tp, "/%s", cp); | |
569 | break; | |
570 | } | |
571 | ||
572 | default: | |
573 | error("%s: not a regular file\n", target); | |
574 | return; | |
575 | } | |
024fde5b RC |
576 | u = 2; |
577 | } else | |
578 | u = 1; | |
3024eb6f RC |
579 | if (chkparent(target) < 0) |
580 | goto bad; | |
2f8aab68 RC |
581 | cp = rindex(target, '/'); |
582 | if (cp == NULL) | |
583 | dir = "."; | |
584 | else if (cp == target) { | |
585 | dir = "/"; | |
586 | cp = NULL; | |
587 | } else { | |
588 | dir = target; | |
589 | *cp = '\0'; | |
590 | } | |
82572cb6 | 591 | (void) sprintf(new, "%s/%s", dir, tmpname); |
2f8aab68 RC |
592 | if (cp != NULL) |
593 | *cp = '/'; | |
2f8aab68 RC |
594 | if ((f = creat(new, mode)) < 0) |
595 | goto bad1; | |
82572cb6 RC |
596 | if (chog(new, owner, group, mode) < 0) { |
597 | (void) close(f); | |
598 | (void) unlink(new); | |
2f8aab68 | 599 | return; |
82572cb6 | 600 | } |
2f8aab68 RC |
601 | ga(); |
602 | ||
603 | wrerr = 0; | |
604 | for (i = 0; i < size; i += BUFSIZ) { | |
605 | int amt = BUFSIZ; | |
2f8aab68 | 606 | |
3024eb6f | 607 | cp = buf; |
2f8aab68 RC |
608 | if (i + amt > size) |
609 | amt = size - i; | |
610 | do { | |
611 | int j = read(rem, cp, amt); | |
612 | ||
82572cb6 RC |
613 | if (j <= 0) { |
614 | (void) close(f); | |
615 | (void) unlink(new); | |
2f8aab68 | 616 | cleanup(); |
82572cb6 | 617 | } |
2f8aab68 RC |
618 | amt -= j; |
619 | cp += j; | |
620 | } while (amt > 0); | |
621 | amt = BUFSIZ; | |
622 | if (i + amt > size) | |
623 | amt = size - i; | |
624 | if (wrerr == 0 && write(f, buf, amt) != amt) { | |
625 | olderrno = errno; | |
626 | wrerr++; | |
627 | } | |
628 | } | |
024fde5b | 629 | (void) close(f); |
2f8aab68 RC |
630 | (void) response(); |
631 | if (wrerr) { | |
632 | error("%s: %s\n", cp, sys_errlist[olderrno]); | |
82572cb6 | 633 | (void) unlink(new); |
2f8aab68 RC |
634 | return; |
635 | } | |
024fde5b RC |
636 | if (opts & COMPARE) { |
637 | FILE *f1, *f2; | |
638 | int c; | |
639 | ||
640 | if ((f1 = fopen(target, "r")) == NULL) | |
641 | goto bad; | |
642 | if ((f2 = fopen(new, "r")) == NULL) | |
643 | goto bad1; | |
644 | while ((c = getc(f1)) == getc(f2)) | |
645 | if (c == EOF) { | |
646 | (void) fclose(f1); | |
647 | (void) fclose(f2); | |
648 | (void) unlink(new); | |
649 | ga(); | |
650 | return; | |
651 | } | |
652 | (void) fclose(f1); | |
653 | (void) fclose(f2); | |
654 | if (opts & VERIFY) { | |
655 | (void) unlink(new); | |
656 | buf[0] = '\0'; | |
657 | sprintf(buf + 1, "updating %s:%s\n", host, target); | |
658 | (void) write(rem, buf, strlen(buf + 1) + 1); | |
659 | return; | |
660 | } | |
661 | } | |
2f8aab68 RC |
662 | |
663 | /* | |
664 | * Set last modified time | |
665 | */ | |
024fde5b | 666 | tvp[0].tv_sec = stb.st_atime; /* old accessed time from target */ |
2f8aab68 RC |
667 | tvp[0].tv_usec = 0; |
668 | tvp[1].tv_sec = mtime; | |
669 | tvp[1].tv_usec = 0; | |
670 | if (utimes(new, tvp) < 0) { | |
671 | bad1: | |
672 | error("%s: %s\n", new, sys_errlist[errno]); | |
82572cb6 RC |
673 | if (new[0]) |
674 | (void) unlink(new); | |
2f8aab68 RC |
675 | return; |
676 | } | |
677 | ||
678 | if (rename(new, target) < 0) { | |
679 | bad: | |
680 | error("%s: %s\n", target, sys_errlist[errno]); | |
82572cb6 RC |
681 | if (new[0]) |
682 | (void) unlink(new); | |
2f8aab68 RC |
683 | return; |
684 | } | |
024fde5b RC |
685 | if (opts & COMPARE) { |
686 | buf[0] = '\0'; | |
687 | sprintf(buf + 1, "updated %s:%s\n", host, target); | |
688 | (void) write(rem, buf, strlen(buf + 1) + 1); | |
689 | } else | |
690 | ga(); | |
2f8aab68 RC |
691 | } |
692 | ||
3024eb6f RC |
693 | /* |
694 | * Check parent directory for write permission and create if it doesn't | |
695 | * exist. | |
696 | */ | |
697 | chkparent(name) | |
698 | char *name; | |
699 | { | |
700 | register char *cp, *dir; | |
701 | extern int userid, groupid; | |
702 | ||
703 | cp = rindex(name, '/'); | |
704 | if (cp == NULL) | |
705 | dir = "."; | |
706 | else if (cp == name) { | |
707 | dir = "/"; | |
708 | cp = NULL; | |
709 | } else { | |
710 | dir = name; | |
711 | *cp = '\0'; | |
712 | } | |
713 | if (access(dir, 2) == 0) { | |
714 | if (cp != NULL) | |
715 | *cp = '/'; | |
716 | return(0); | |
717 | } | |
718 | if (errno == ENOENT) { | |
719 | if (rindex(dir, '/') != NULL && chkparent(dir) < 0) | |
720 | goto bad; | |
721 | if (!strcmp(dir, ".") || !strcmp(dir, "/")) | |
722 | goto bad; | |
723 | if (mkdir(dir, 0777 & ~sumask) < 0) | |
724 | goto bad; | |
725 | if (chown(dir, userid, groupid) < 0) { | |
726 | (void) unlink(dir); | |
727 | goto bad; | |
728 | } | |
729 | if (cp != NULL) | |
730 | *cp = '/'; | |
731 | return(0); | |
732 | } | |
733 | ||
734 | bad: | |
735 | if (cp != NULL) | |
736 | *cp = '/'; | |
737 | return(-1); | |
738 | } | |
739 | ||
2f8aab68 RC |
740 | /* |
741 | * Change owner and group of file. | |
742 | */ | |
82572cb6 | 743 | chog(file, owner, group, mode) |
2f8aab68 | 744 | char *file, *owner, *group; |
82572cb6 | 745 | int mode; |
2f8aab68 | 746 | { |
82572cb6 RC |
747 | extern int userid, groupid; |
748 | extern char user[]; | |
2f8aab68 RC |
749 | register int i; |
750 | int uid, gid; | |
751 | ||
752 | uid = userid; | |
753 | if (userid == 0) { | |
82572cb6 RC |
754 | if (p == NULL || strcmp(owner, p->pw_name) != 0) { |
755 | if ((p = getpwnam(owner)) == NULL) { | |
756 | if (mode & 04000) { | |
757 | error("%s: unknown login name\n", owner); | |
758 | return(-1); | |
759 | } | |
760 | } else | |
761 | uid = p->pw_uid; | |
762 | } else | |
763 | uid = p->pw_uid; | |
764 | } | |
765 | gid = groupid; | |
766 | if (g == NULL || strcmp(group, g->gr_name) != 0) { | |
767 | if ((g = getgrnam(group)) == NULL) { | |
768 | if (mode & 02000) { | |
769 | error("%s: unknown group\n", group); | |
770 | return(-1); | |
771 | } | |
772 | } else | |
773 | gid = g->gr_gid; | |
774 | } else | |
775 | gid = g->gr_gid; | |
776 | if (userid && groupid != gid) { | |
777 | for (i = 0; g->gr_mem[i] != NULL; i++) | |
2f8aab68 RC |
778 | if (!(strcmp(user, g->gr_mem[i]))) |
779 | goto ok; | |
82572cb6 | 780 | gid = groupid; |
2f8aab68 RC |
781 | } |
782 | ok: | |
783 | if (chown(file, uid, gid) < 0) { | |
784 | error("%s: %s\n", file, sys_errlist[errno]); | |
785 | return(-1); | |
786 | } | |
787 | return(0); | |
788 | } | |
789 | ||
d1dee8e8 RC |
790 | /* |
791 | * Check for files on the machine being updated that are not on the master | |
792 | * machine and remove them. | |
793 | */ | |
794 | rmchk(lname, rname, opts) | |
795 | char *lname, *rname; | |
796 | int opts; | |
797 | { | |
798 | register char *cp; | |
799 | struct stat stb; | |
800 | ||
801 | if (debug) | |
802 | printf("rmchk(%s, %s, %x)\n", lname, | |
803 | rname != NULL ? rname : "NULL", opts); | |
804 | ||
d1dee8e8 RC |
805 | /* |
806 | * First time rmchk() is called? | |
807 | */ | |
808 | if (rname == NULL) { | |
809 | rname = exptilde(target, lname); | |
810 | if (rname == NULL) | |
811 | return; | |
812 | tp = lname = target; | |
813 | while (*tp) | |
814 | tp++; | |
815 | /* | |
816 | * If we are renaming a directory and we want to preserve | |
817 | * the directory heirarchy (-w), we must strip off the first | |
818 | * directory name and preserve the rest. | |
819 | */ | |
820 | if (opts & STRIP) { | |
821 | opts &= ~STRIP; | |
822 | rname = index(rname, '/'); | |
823 | if (rname == NULL) | |
824 | rname = tp; | |
825 | else | |
826 | rname++; | |
827 | } else if (!(opts & WHOLE)) { | |
828 | rname = rindex(lname, '/'); | |
829 | if (rname == NULL) | |
830 | rname = lname; | |
831 | else | |
832 | rname++; | |
833 | } | |
834 | } | |
024fde5b RC |
835 | if (exclude(lname)) |
836 | return; | |
d1dee8e8 RC |
837 | if (access(lname, 4) < 0 || stat(lname, &stb) < 0) { |
838 | error("%s: %s\n", lname, sys_errlist[errno]); | |
839 | return; | |
840 | } | |
841 | if (!ISDIR(stb.st_mode)) | |
842 | return; | |
024fde5b | 843 | |
d1dee8e8 RC |
844 | /* |
845 | * Tell the remote to clean the files from the last directory sent. | |
846 | */ | |
847 | (void) sprintf(buf, "C%o %s\n", opts & VERIFY, rname); | |
848 | if (debug) | |
849 | printf("buf = %s", buf); | |
850 | (void) write(rem, buf, strlen(buf)); | |
851 | if (response() < 0) | |
852 | return; | |
853 | catname = 0; | |
854 | for (;;) { | |
855 | cp = buf; | |
856 | do { | |
857 | if (read(rem, cp, 1) != 1) | |
858 | lostconn(); | |
859 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); | |
860 | ||
d1dee8e8 RC |
861 | switch (buf[0]) { |
862 | case 'Q': /* its a directory on the remote end */ | |
863 | case 'q': /* its a regular file on the remote end */ | |
024fde5b RC |
864 | /* |
865 | * Return the following codes to remove query. | |
866 | * N\n -- file does not exisit, remove. | |
867 | * Y\n -- file exists and is a directory. | |
868 | * y\n -- file exists and is a regular file. | |
869 | */ | |
d1dee8e8 RC |
870 | *--cp = '\0'; |
871 | (void) sprintf(tp, "/%s", buf + 1); | |
872 | if (debug) | |
873 | printf("check %s\n", target); | |
024fde5b RC |
874 | if (exclude(target)) { |
875 | (void) write(rem, "y\n", 2); | |
876 | break; | |
877 | } | |
d1dee8e8 RC |
878 | if (stat(target, &stb) < 0) |
879 | (void) write(rem, "N\n", 2); | |
880 | else if (buf[0] == 'Q' && ISDIR(stb.st_mode)) { | |
881 | if (catname >= sizeof(stp)) { | |
882 | error("%s: too many directory levels\n", target); | |
883 | break; | |
884 | } | |
885 | (void) write(rem, "Y\n", 2); | |
886 | if (response() < 0) | |
887 | break; | |
888 | stp[catname++] = tp; | |
889 | while (*tp) | |
890 | tp++; | |
891 | } else | |
892 | (void) write(rem, "y\n", 2); | |
893 | break; | |
894 | ||
895 | case 'E': | |
896 | if (catname < 0) | |
897 | fatal("too many 'E's\n"); | |
898 | ga(); | |
899 | if (catname == 0) | |
900 | return; | |
901 | tp = stp[--catname]; | |
902 | *tp = '\0'; | |
903 | break; | |
904 | ||
905 | case '\0': | |
906 | *--cp = '\0'; | |
024fde5b | 907 | if (buf[1] != '\0') |
d1dee8e8 RC |
908 | log(lfp, "%s\n", buf + 1); |
909 | break; | |
910 | ||
911 | case '\1': | |
912 | case '\2': | |
913 | errs++; | |
914 | if (buf[1] != '\n') { | |
915 | if (!iamremote) { | |
916 | fflush(stdout); | |
917 | (void) write(2, buf + 1, cp - buf + 1); | |
918 | } | |
919 | if (lfp != NULL) | |
920 | (void) fwrite(buf + 1, 1, cp - buf + 1, lfp); | |
921 | } | |
922 | if (buf[0] == '\2') | |
923 | cleanup(); | |
924 | break; | |
925 | ||
926 | default: | |
927 | error("unknown response type %s\n", buf[0]); | |
928 | } | |
929 | } | |
930 | } | |
931 | ||
932 | /* | |
024fde5b RC |
933 | * Check the directory initialized by the 'T' command to server() |
934 | * for extraneous files and remove them. | |
d1dee8e8 RC |
935 | */ |
936 | clean(lname, opts, first) | |
937 | char *lname; | |
938 | int opts, first; | |
939 | { | |
940 | DIR *d; | |
941 | struct direct *dp; | |
942 | register char *cp; | |
943 | struct stat stb; | |
944 | char *ootp, *otp; | |
945 | int len; | |
946 | ||
947 | if (first) { | |
948 | ootp = tp; | |
949 | if (catname) { | |
950 | *tp++ = '/'; | |
951 | cp = lname; | |
952 | while (*tp++ = *cp++) | |
953 | ; | |
954 | tp--; | |
955 | } | |
956 | if (stat(target, &stb) < 0) { | |
957 | if (errno == ENOENT) { | |
958 | ga(); | |
959 | goto done; | |
960 | } | |
961 | bad: | |
962 | error("%s: %s\n", target, sys_errlist[errno]); | |
963 | tp = otp; | |
964 | *tp = '\0'; | |
965 | return; | |
966 | } | |
967 | /* | |
968 | * This should be a directory because its a directory on the | |
969 | * master machine. If not, let install complain about it. | |
970 | */ | |
971 | if (!ISDIR(stb.st_mode)) { | |
972 | ga(); | |
973 | goto done; | |
974 | } | |
975 | } | |
024fde5b | 976 | |
d1dee8e8 RC |
977 | if (access(target, 6) < 0 || (d = opendir(target)) == NULL) |
978 | goto bad; | |
979 | ga(); | |
980 | ||
981 | otp = tp; | |
982 | len = tp - target; | |
983 | while (dp = readdir(d)) { | |
984 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
985 | continue; | |
986 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
987 | error("%s/%s: Name too long\n", target, dp->d_name); | |
988 | continue; | |
989 | } | |
990 | tp = otp; | |
991 | *tp++ = '/'; | |
992 | cp = dp->d_name;; | |
993 | while (*tp++ = *cp++) | |
994 | ; | |
995 | tp--; | |
996 | if (stat(target, &stb) < 0) { | |
997 | error("%s: %s\n", target, sys_errlist[errno]); | |
998 | continue; | |
999 | } | |
1000 | (void) sprintf(buf, "%c%s\n", ISDIR(stb.st_mode) ? 'Q' : 'q', | |
1001 | dp->d_name); | |
1002 | (void) write(rem, buf, strlen(buf)); | |
1003 | cp = buf; | |
1004 | do { | |
1005 | if (read(rem, cp, 1) != 1) | |
1006 | lostconn(); | |
1007 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); | |
1008 | *--cp = '\0'; | |
1009 | cp = buf; | |
1010 | if (*cp != 'N') { | |
1011 | if (*cp == 'Y' && ISDIR(stb.st_mode)) | |
1012 | clean(dp->d_name, opts, 0); | |
1013 | continue; | |
1014 | } | |
1015 | if (!(opts & VERIFY)) | |
1016 | remove(&stb); | |
1017 | } | |
1018 | closedir(d); | |
1019 | done: | |
1020 | tp = (first) ? ootp : otp; | |
1021 | *tp = '\0'; | |
1022 | (void) write(rem, "E\n", 2); | |
1023 | (void) response(); | |
1024 | } | |
1025 | ||
1026 | remove(st) | |
1027 | struct stat *st; | |
1028 | { | |
1029 | DIR *d; | |
1030 | struct direct *dp; | |
1031 | register char *cp; | |
1032 | struct stat stb; | |
1033 | char *otp; | |
1034 | int len; | |
1035 | ||
1036 | switch (st->st_mode & S_IFMT) { | |
1037 | case S_IFREG: | |
1038 | if (unlink(target) < 0) | |
1039 | goto bad; | |
1040 | goto removed; | |
1041 | ||
1042 | case S_IFDIR: | |
1043 | break; | |
1044 | ||
1045 | default: | |
1046 | error("%s: not a plain file\n", target); | |
1047 | return; | |
1048 | } | |
1049 | ||
1050 | if (access(target, 6) < 0 || (d = opendir(target)) == NULL) | |
1051 | goto bad; | |
1052 | ||
1053 | otp = tp; | |
1054 | len = tp - target; | |
1055 | while (dp = readdir(d)) { | |
1056 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
1057 | continue; | |
1058 | if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
1059 | error("%s/%s: Name too long\n", target, dp->d_name); | |
1060 | continue; | |
1061 | } | |
1062 | tp = otp; | |
1063 | *tp++ = '/'; | |
1064 | cp = dp->d_name;; | |
1065 | while (*tp++ = *cp++) | |
1066 | ; | |
1067 | tp--; | |
1068 | if (stat(target, &stb) < 0) { | |
1069 | error("%s: %s\n", target, sys_errlist[errno]); | |
1070 | continue; | |
1071 | } | |
1072 | remove(&stb); | |
1073 | } | |
1074 | closedir(d); | |
1075 | tp = otp; | |
1076 | *tp = '\0'; | |
1077 | if (rmdir(target) < 0) { | |
1078 | bad: | |
1079 | error("%s: %s\n", target, sys_errlist[errno]); | |
1080 | return; | |
1081 | } | |
1082 | removed: | |
1083 | cp = buf; | |
1084 | *cp++ = '\0'; | |
1085 | (void) sprintf(cp, "removed %s\n", target); | |
1086 | (void) write(rem, buf, strlen(cp) + 1); | |
1087 | } | |
1088 | ||
3024eb6f | 1089 | /*VARARGS2*/ |
82572cb6 RC |
1090 | log(fp, fmt, a1, a2, a3) |
1091 | FILE *fp; | |
2f8aab68 RC |
1092 | char *fmt; |
1093 | int a1, a2, a3; | |
1094 | { | |
1095 | /* Print changes locally if not quiet mode */ | |
1096 | if (!qflag) | |
1097 | printf(fmt, a1, a2, a3); | |
1098 | ||
1099 | /* Save changes (for mailing) if really updating files */ | |
f7770429 | 1100 | if (!(options & VERIFY) && fp != NULL) |
82572cb6 | 1101 | fprintf(fp, fmt, a1, a2, a3); |
2f8aab68 RC |
1102 | } |
1103 | ||
3024eb6f | 1104 | /*VARARGS1*/ |
2f8aab68 RC |
1105 | error(fmt, a1, a2, a3) |
1106 | char *fmt; | |
1107 | int a1, a2, a3; | |
1108 | { | |
1109 | errs++; | |
1110 | strcpy(buf, "\1rdist: "); | |
1111 | (void) sprintf(buf+8, fmt, a1, a2, a3); | |
1112 | (void) write(rem, buf, strlen(buf)); | |
3024eb6f RC |
1113 | if (!iamremote) { |
1114 | fflush(stdout); | |
1115 | (void) write(2, buf+1, strlen(buf+1)); | |
2f8aab68 | 1116 | } |
3024eb6f RC |
1117 | if (lfp != NULL) |
1118 | (void) fwrite(buf+1, 1, strlen(buf+1), lfp); | |
2f8aab68 RC |
1119 | } |
1120 | ||
3024eb6f | 1121 | /*VARARGS1*/ |
2f8aab68 RC |
1122 | fatal(fmt, a1, a2,a3) |
1123 | char *fmt; | |
1124 | int a1, a2, a3; | |
1125 | { | |
1126 | errs++; | |
1127 | strcpy(buf, "\2rdist: "); | |
1128 | (void) sprintf(buf+8, fmt, a1, a2, a3); | |
1129 | (void) write(rem, buf, strlen(buf)); | |
3024eb6f RC |
1130 | if (!iamremote) { |
1131 | fflush(stdout); | |
1132 | (void) write(2, buf+1, strlen(buf+1)); | |
2f8aab68 | 1133 | } |
3024eb6f RC |
1134 | if (lfp != NULL) |
1135 | (void) fwrite(buf+1, 1, strlen(buf+1), lfp); | |
2f8aab68 RC |
1136 | cleanup(); |
1137 | } | |
1138 | ||
1139 | response() | |
1140 | { | |
024fde5b | 1141 | char *cp, *s; |
2f8aab68 RC |
1142 | |
1143 | if (debug) | |
1144 | printf("response()\n"); | |
1145 | ||
024fde5b RC |
1146 | cp = s = buf; |
1147 | do { | |
1148 | if (read(rem, cp, 1) != 1) | |
1149 | lostconn(); | |
1150 | } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); | |
2f8aab68 | 1151 | |
024fde5b | 1152 | switch (*s++) { |
2f8aab68 | 1153 | case '\0': |
024fde5b RC |
1154 | *--cp = '\0'; |
1155 | if (*s != '\0') | |
1156 | log(lfp, "%s\n", s); | |
2f8aab68 RC |
1157 | return(0); |
1158 | ||
1159 | default: | |
024fde5b | 1160 | s--; |
2f8aab68 RC |
1161 | /* fall into... */ |
1162 | case '\1': | |
1163 | case '\2': | |
1164 | errs++; | |
024fde5b | 1165 | if (*s != '\n') { |
82572cb6 RC |
1166 | if (!iamremote) { |
1167 | fflush(stdout); | |
024fde5b | 1168 | (void) write(2, s, cp - s); |
82572cb6 | 1169 | } |
2f8aab68 | 1170 | if (lfp != NULL) |
024fde5b | 1171 | (void) fwrite(s, 1, cp - s, lfp); |
2f8aab68 | 1172 | } |
024fde5b | 1173 | if (buf[0] == '\1') |
2f8aab68 RC |
1174 | return(-1); |
1175 | cleanup(); | |
1176 | } | |
1177 | /*NOTREACHED*/ | |
1178 | } | |
1179 | ||
1180 | lostconn() | |
1181 | { | |
024fde5b RC |
1182 | if (!iamremote) { |
1183 | fflush(stdout); | |
2f8aab68 | 1184 | fprintf(stderr, "rdist: lost connection\n"); |
024fde5b | 1185 | } |
2f8aab68 RC |
1186 | cleanup(); |
1187 | } |