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