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