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