1 /* $OpenBSD: server.c,v 1.49 2022/12/26 19:16:02 jmc Exp $ */
2
3 /*
4 * Copyright (c) 1983 Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <ctype.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <grp.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #include "server.h"
45
46 /*
47 * Server routines
48 */
49
50 char tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */
51 char buf[BUFSIZ]; /* general purpose buffer */
52 char target[PATH_MAX]; /* target/source directory name */
53 char *ptarget; /* pointer to end of target name */
54 int catname = 0; /* cat name to target name */
55 char *sptarget[32]; /* stack of saved ptarget's for directories */
56 char *fromhost = NULL; /* Client hostname */
57 static int64_t min_freespace = 0; /* Minimum free space on a filesystem */
58 static int64_t min_freefiles = 0; /* Minimum free # files on a filesystem */
59 int oumask; /* Old umask */
60
61 static int cattarget(char *);
62 static int setownership(char *, int, uid_t, gid_t, int);
63 static int setfilemode(char *, int, int, int);
64 static int fchog(int, char *, char *, char *, int);
65 static int removefile(struct stat *, int);
66 static void doclean(char *);
67 static void clean(char *);
68 static void dospecial(char *);
69 static void docmdspecial(void);
70 static void query(char *);
71 static int chkparent(char *, opt_t);
72 static char *savetarget(char *, opt_t);
73 static void recvfile(char *, opt_t, int, char *, char *, time_t, time_t, off_t);
74 static void recvdir(opt_t, int, char *, char *);
75 static void recvlink(char *, opt_t, int, off_t);
76 static void hardlink(char *);
77 static void setconfig(char *);
78 static void recvit(char *, int);
79 static void dochmog(char *);
80 static void settarget(char *, int);
81
82 /*
83 * Cat "string" onto the target buffer with error checking.
84 */
85 static int
cattarget(char * string)86 cattarget(char *string)
87 {
88 if (strlen(string) + strlen(target) + 2 > sizeof(target)) {
89 message(MT_INFO, "target buffer is not large enough.");
90 return(-1);
91 }
92 if (!ptarget) {
93 message(MT_INFO, "NULL target pointer set.");
94 return(-10);
95 }
96
97 (void) snprintf(ptarget, sizeof(target) - (ptarget - target),
98 "/%s", string);
99
100 return(0);
101 }
102
103 /*
104 * Set uid and gid ownership of a file.
105 */
106 static int
setownership(char * file,int fd,uid_t uid,gid_t gid,int islink)107 setownership(char *file, int fd, uid_t uid, gid_t gid, int islink)
108 {
109 static int is_root = -1;
110 int status = -1;
111
112 /*
113 * We assume only the Superuser can change uid ownership.
114 */
115 switch (is_root) {
116 case -1:
117 is_root = getuid() == 0;
118 if (is_root)
119 break;
120 /* FALLTHROUGH */
121 case 0:
122 uid = -1;
123 break;
124 case 1:
125 break;
126 }
127
128 if (fd != -1 && !islink)
129 status = fchown(fd, uid, gid);
130 else
131 status = fchownat(AT_FDCWD, file, uid, gid,
132 AT_SYMLINK_NOFOLLOW);
133
134 if (status == -1) {
135 if (uid == (uid_t)-1)
136 message(MT_NOTICE, "%s: chgrp %d failed: %s",
137 target, gid, SYSERR);
138 else
139 message(MT_NOTICE, "%s: chown %d:%d failed: %s",
140 target, uid, gid, SYSERR);
141 return(-1);
142 }
143
144 return(0);
145 }
146
147 /*
148 * Set mode of a file
149 */
150 static int
setfilemode(char * file,int fd,int mode,int islink)151 setfilemode(char *file, int fd, int mode, int islink)
152 {
153 int status = -1;
154
155 if (mode == -1)
156 return(0);
157
158 if (islink)
159 status = fchmodat(AT_FDCWD, file, mode, AT_SYMLINK_NOFOLLOW);
160
161 if (fd != -1 && !islink)
162 status = fchmod(fd, mode);
163
164 if (status == -1 && !islink)
165 status = chmod(file, mode);
166
167 if (status == -1) {
168 message(MT_NOTICE, "%s: chmod failed: %s", target, SYSERR);
169 return(-1);
170 }
171
172 return(0);
173 }
174 /*
175 * Change owner, group and mode of file.
176 */
177 static int
fchog(int fd,char * file,char * owner,char * group,int mode)178 fchog(int fd, char *file, char *owner, char *group, int mode)
179 {
180 int i;
181 struct stat st;
182 uid_t uid;
183 gid_t gid;
184 gid_t primegid = (gid_t)-2;
185
186 uid = userid;
187 if (userid == 0) { /* running as root; take anything */
188 if (*owner == ':') {
189 uid = (uid_t) atoi(owner + 1);
190 } else if (strcmp(owner, locuser) != 0) {
191 if (uid_from_user(owner, &uid) == -1) {
192 if (mode != -1 && IS_ON(mode, S_ISUID)) {
193 message(MT_NOTICE,
194 "%s: unknown login name \"%s\", clearing setuid",
195 target, owner);
196 mode &= ~S_ISUID;
197 uid = 0;
198 } else
199 message(MT_NOTICE,
200 "%s: unknown login name \"%s\"",
201 target, owner);
202 }
203 } else {
204 uid = userid;
205 primegid = groupid;
206 }
207 if (*group == ':') {
208 gid = (gid_t)atoi(group + 1);
209 goto ok;
210 }
211 } else { /* not root, setuid only if user==owner */
212 if (mode != -1) {
213 if (IS_ON(mode, S_ISUID) &&
214 strcmp(locuser, owner) != 0)
215 mode &= ~S_ISUID;
216 if (mode)
217 mode &= ~S_ISVTX; /* and strip sticky too */
218 }
219 primegid = groupid;
220 }
221
222 gid = (gid_t)-1;
223 if (*group == ':') {
224 gid = (gid_t) atoi(group + 1);
225 } else if (gid_from_group(group, &gid) == -1) {
226 if (mode != -1 && IS_ON(mode, S_ISGID)) {
227 message(MT_NOTICE,
228 "%s: unknown group \"%s\", clearing setgid",
229 target, group);
230 mode &= ~S_ISGID;
231 } else
232 message(MT_NOTICE,
233 "%s: unknown group \"%s\"",
234 target, group);
235 }
236
237 if (userid && gid != (gid_t)-1 && gid != primegid) {
238 for (i = 0; i < gidsetlen; i++) {
239 if (gid == gidset[i])
240 goto ok;
241 }
242 if (mode != -1 && IS_ON(mode, S_ISGID)) {
243 message(MT_NOTICE,
244 "%s: user %s not in group %s, clearing setgid",
245 target, locuser, group);
246 mode &= ~S_ISGID;
247 }
248 gid = (gid_t)-1;
249 }
250 ok:
251 if (stat(file, &st) == -1) {
252 error("%s: Stat failed %s", file, SYSERR);
253 return -1;
254 }
255 /*
256 * Set uid and gid ownership. If that fails, strip setuid and
257 * setgid bits from mode. Once ownership is set, successful
258 * or otherwise, set the new file mode.
259 */
260 if (setownership(file, fd, uid, gid, S_ISLNK(st.st_mode)) < 0) {
261 if (mode != -1 && IS_ON(mode, S_ISUID)) {
262 message(MT_NOTICE,
263 "%s: chown failed, clearing setuid", target);
264 mode &= ~S_ISUID;
265 }
266 if (mode != -1 && IS_ON(mode, S_ISGID)) {
267 message(MT_NOTICE,
268 "%s: chown failed, clearing setgid", target);
269 mode &= ~S_ISGID;
270 }
271 }
272 (void) setfilemode(file, fd, mode, S_ISLNK(st.st_mode));
273
274
275 return(0);
276 }
277
278 /*
279 * Remove a file or directory (recursively) and send back an acknowledge
280 * or an error message.
281 */
282 static int
removefile(struct stat * statb,int silent)283 removefile(struct stat *statb, int silent)
284 {
285 DIR *d;
286 static struct dirent *dp;
287 char *cp;
288 struct stat stb;
289 char *optarget;
290 int len, failures = 0;
291
292 switch (statb->st_mode & S_IFMT) {
293 case S_IFREG:
294 case S_IFLNK:
295 case S_IFCHR:
296 case S_IFBLK:
297 case S_IFSOCK:
298 case S_IFIFO:
299 if (unlink(target) == -1) {
300 if (errno == ETXTBSY) {
301 if (!silent)
302 message(MT_REMOTE|MT_NOTICE,
303 "%s: unlink failed: %s",
304 target, SYSERR);
305 return(0);
306 } else {
307 error("%s: unlink failed: %s", target, SYSERR);
308 return(-1);
309 }
310 }
311 goto removed;
312
313 case S_IFDIR:
314 break;
315
316 default:
317 error("%s: not a plain file", target);
318 return(-1);
319 }
320
321 errno = 0;
322 if ((d = opendir(target)) == NULL) {
323 error("%s: opendir failed: %s", target, SYSERR);
324 return(-1);
325 }
326
327 optarget = ptarget;
328 len = ptarget - target;
329 while ((dp = readdir(d)) != NULL) {
330 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
331 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
332 continue;
333
334 if (len + 1 + (int)strlen(dp->d_name) >= PATH_MAX - 1) {
335 if (!silent)
336 message(MT_REMOTE|MT_WARNING,
337 "%s/%s: Name too long",
338 target, dp->d_name);
339 continue;
340 }
341 ptarget = optarget;
342 *ptarget++ = '/';
343 cp = dp->d_name;
344 while ((*ptarget++ = *cp++) != '\0')
345 continue;
346 ptarget--;
347 if (lstat(target, &stb) == -1) {
348 if (!silent)
349 message(MT_REMOTE|MT_WARNING,
350 "%s: lstat failed: %s",
351 target, SYSERR);
352 continue;
353 }
354 if (removefile(&stb, 0) < 0)
355 ++failures;
356 }
357 (void) closedir(d);
358 ptarget = optarget;
359 *ptarget = CNULL;
360
361 if (failures)
362 return(-1);
363
364 if (rmdir(target) == -1) {
365 error("%s: rmdir failed: %s", target, SYSERR);
366 return(-1);
367 }
368 removed:
369 #if NEWWAY
370 if (!silent)
371 message(MT_CHANGE|MT_REMOTE, "%s: removed", target);
372 #else
373 /*
374 * We use MT_NOTICE instead of MT_CHANGE because this function is
375 * sometimes called by other functions that are suppose to return a
376 * single ack() back to the client (rdist). This is a kludge until
377 * the Rdist protocol is re-done. Sigh.
378 */
379 message(MT_NOTICE|MT_REMOTE, "%s: removed", target);
380 #endif
381 return(0);
382 }
383
384 /*
385 * Check the current directory (initialized by the 'T' command to server())
386 * for extraneous files and remove them.
387 */
388 static void
doclean(char * cp)389 doclean(char *cp)
390 {
391 DIR *d;
392 struct dirent *dp;
393 struct stat stb;
394 char *optarget, *ep;
395 int len;
396 opt_t opts;
397 char targ[PATH_MAX*4];
398
399 opts = strtol(cp, &ep, 8);
400 if (*ep != CNULL) {
401 error("clean: options not delimited");
402 return;
403 }
404 if ((d = opendir(target)) == NULL) {
405 error("%s: opendir failed: %s", target, SYSERR);
406 return;
407 }
408 ack();
409
410 optarget = ptarget;
411 len = ptarget - target;
412 while ((dp = readdir(d)) != NULL) {
413 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
414 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
415 continue;
416
417 if (len + 1 + (int)strlen(dp->d_name) >= PATH_MAX - 1) {
418 message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long",
419 target, dp->d_name);
420 continue;
421 }
422 ptarget = optarget;
423 *ptarget++ = '/';
424 cp = dp->d_name;
425 while ((*ptarget++ = *cp++) != '\0')
426 continue;
427 ptarget--;
428 if (lstat(target, &stb) == -1) {
429 message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s",
430 target, SYSERR);
431 continue;
432 }
433
434 ENCODE(targ, dp->d_name);
435 (void) sendcmd(CC_QUERY, "%s", targ);
436 (void) remline(cp = buf, sizeof(buf), TRUE);
437
438 if (*cp != CC_YES)
439 continue;
440
441 if (IS_ON(opts, DO_VERIFY))
442 message(MT_REMOTE|MT_INFO, "%s: need to remove",
443 target);
444 else
445 (void) removefile(&stb, 0);
446 }
447 (void) closedir(d);
448
449 ptarget = optarget;
450 *ptarget = CNULL;
451 }
452
453 /*
454 * Frontend to doclean().
455 */
456 static void
clean(char * cp)457 clean(char *cp)
458 {
459 doclean(cp);
460 (void) sendcmd(CC_END, NULL);
461 (void) response();
462 }
463
464 /*
465 * Execute a shell command to handle special cases.
466 * We can't really set an alarm timeout here since we
467 * have no idea how long the command should take.
468 */
469 static void
dospecial(char * xcmd)470 dospecial(char *xcmd)
471 {
472 char cmd[BUFSIZ];
473 if (DECODE(cmd, xcmd) == -1) {
474 error("dospecial: Cannot decode command.");
475 return;
476 }
477 runcommand(cmd);
478 }
479
480 /*
481 * Do a special cmd command. This differs from normal special
482 * commands in that it's done after an entire command has been updated.
483 * The list of updated target files is sent one at a time with RC_FILE
484 * commands. Each one is added to an environment variable defined by
485 * E_FILES. When an RC_COMMAND is finally received, the E_FILES variable
486 * is stuffed into our environment and a normal dospecial() command is run.
487 */
488 static void
docmdspecial(void)489 docmdspecial(void)
490 {
491 char *cp;
492 char *cmd, *env = NULL;
493 int n;
494 size_t len;
495
496 /* We're ready */
497 ack();
498
499 for ( ; ; ) {
500 n = remline(cp = buf, sizeof(buf), FALSE);
501 if (n <= 0) {
502 error("cmdspecial: premature end of input.");
503 return;
504 }
505
506 switch (*cp++) {
507 case RC_FILE:
508 if (env == NULL) {
509 len = (2 * sizeof(E_FILES)) + strlen(cp) + 10;
510 env = xmalloc(len);
511 (void) snprintf(env, len, "export %s;%s=%s",
512 E_FILES, E_FILES, cp);
513 } else {
514 len = strlen(env) + 1 + strlen(cp) + 1;
515 env = xrealloc(env, len);
516 (void) strlcat(env, ":", len);
517 (void) strlcat(env, cp, len);
518 }
519 ack();
520 break;
521
522 case RC_COMMAND:
523 if (env) {
524 len = strlen(env) + 1 + strlen(cp) + 1;
525 env = xrealloc(env, len);
526 (void) strlcat(env, ";", len);
527 (void) strlcat(env, cp, len);
528 cmd = env;
529 } else
530 cmd = cp;
531
532 dospecial(cmd);
533 if (env)
534 (void) free(env);
535 return;
536
537 default:
538 error("Unknown cmdspecial command '%s'.", cp);
539 return;
540 }
541 }
542 }
543
544 /*
545 * Query. Check to see if file exists. Return one of the following:
546 *
547 * QC_ONNFS - resides on a NFS
548 * QC_ONRO - resides on a Read-Only filesystem
549 * QC_NO - doesn't exist
550 * QC_YESsize mtime - exists and its a regular file (size & mtime of file)
551 * QC_YES - exists and its a directory or symbolic link
552 * QC_ERRMSGmessage - error message
553 */
554 static void
query(char * xname)555 query(char *xname)
556 {
557 static struct stat stb;
558 int s = -1, stbvalid = 0;
559 char name[PATH_MAX];
560
561 if (DECODE(name, xname) == -1) {
562 error("query: Cannot decode filename");
563 return;
564 }
565
566 if (catname && cattarget(name) < 0)
567 return;
568
569 if (IS_ON(options, DO_CHKNFS)) {
570 s = is_nfs_mounted(target, &stb, &stbvalid);
571 if (s > 0)
572 (void) sendcmd(QC_ONNFS, NULL);
573
574 /* Either the above check was true or an error occurred */
575 /* and is_nfs_mounted sent the error message */
576 if (s != 0) {
577 *ptarget = CNULL;
578 return;
579 }
580 }
581
582 if (IS_ON(options, DO_CHKREADONLY)) {
583 s = is_ro_mounted(target, &stb, &stbvalid);
584 if (s > 0)
585 (void) sendcmd(QC_ONRO, NULL);
586
587 /* Either the above check was true or an error occurred */
588 /* and is_ro_mounted sent the error message */
589 if (s != 0) {
590 *ptarget = CNULL;
591 return;
592 }
593 }
594
595 if (IS_ON(options, DO_CHKSYM)) {
596 if (is_symlinked(target, &stb, &stbvalid) > 0) {
597 (void) sendcmd(QC_SYM, NULL);
598 return;
599 }
600 }
601
602 /*
603 * If stbvalid is false, "stb" is not valid because the stat()
604 * by is_*_mounted() either failed or does not match "target".
605 */
606 if (!stbvalid && lstat(target, &stb) == -1) {
607 if (errno == ENOENT)
608 (void) sendcmd(QC_NO, NULL);
609 else
610 error("%s: lstat failed: %s", target, SYSERR);
611 *ptarget = CNULL;
612 return;
613 }
614
615 switch (stb.st_mode & S_IFMT) {
616 case S_IFLNK:
617 case S_IFDIR:
618 case S_IFREG:
619 (void) sendcmd(QC_YES, "%lld %lld %o %s %s",
620 (long long) stb.st_size,
621 (long long) stb.st_mtime,
622 stb.st_mode & 07777,
623 getusername(stb.st_uid, target, options),
624 getgroupname(stb.st_gid, target, options));
625 break;
626
627 default:
628 error("%s: not a file or directory", target);
629 break;
630 }
631 *ptarget = CNULL;
632 }
633
634 /*
635 * Check to see if parent directory exists and create one if not.
636 */
637 static int
chkparent(char * name,opt_t opts)638 chkparent(char *name, opt_t opts)
639 {
640 char *cp;
641 struct stat stb;
642 int r = -1;
643
644 debugmsg(DM_CALL, "chkparent(%s, %#x) start\n", name, opts);
645
646 cp = strrchr(name, '/');
647 if (cp == NULL || cp == name)
648 return(0);
649
650 *cp = CNULL;
651
652 if (lstat(name, &stb) == -1) {
653 if (errno == ENOENT && chkparent(name, opts) >= 0) {
654 if (mkdir(name, 0777 & ~oumask) == 0) {
655 message(MT_NOTICE, "%s: mkdir", name);
656 r = 0;
657 } else
658 debugmsg(DM_MISC,
659 "chkparent(%s, %#04o) mkdir fail: %s\n",
660 name, opts, SYSERR);
661 }
662 } else /* It exists */
663 r = 0;
664
665 /* Put back what we took away */
666 *cp = '/';
667
668 return(r);
669 }
670
671 /*
672 * Save a copy of 'file' by renaming it.
673 */
674 static char *
savetarget(char * file,opt_t opts)675 savetarget(char *file, opt_t opts)
676 {
677 static char savefile[PATH_MAX];
678
679 if (strlen(file) + sizeof(SAVE_SUFFIX) + 1 > PATH_MAX) {
680 error("%s: Cannot save: Save name too long", file);
681 return(NULL);
682 }
683
684 if (IS_ON(opts, DO_HISTORY)) {
685 int i;
686 struct stat st;
687 /*
688 * There is a race here, but the worst that can happen
689 * is to lose a version of the file
690 */
691 for (i = 1; i < 1000; i++) {
692 (void) snprintf(savefile, sizeof(savefile),
693 "%s;%.3d", file, i);
694 if (lstat(savefile, &st) == -1 && errno == ENOENT)
695 break;
696
697 }
698 if (i == 1000) {
699 message(MT_NOTICE,
700 "%s: More than 1000 versions for %s; reusing 1\n",
701 savefile, SYSERR);
702 i = 1;
703 (void) snprintf(savefile, sizeof(savefile),
704 "%s;%.3d", file, i);
705 }
706 }
707 else {
708 (void) snprintf(savefile, sizeof(savefile), "%s%s",
709 file, SAVE_SUFFIX);
710
711 if (unlink(savefile) != 0 && errno != ENOENT) {
712 message(MT_NOTICE, "%s: remove failed: %s",
713 savefile, SYSERR);
714 return(NULL);
715 }
716 }
717
718 if (rename(file, savefile) != 0 && errno != ENOENT) {
719 error("%s -> %s: rename failed: %s",
720 file, savefile, SYSERR);
721 return(NULL);
722 }
723
724 return(savefile);
725 }
726
727 /*
728 * Receive a file
729 */
730 static void
recvfile(char * new,opt_t opts,int mode,char * owner,char * group,time_t mtime,time_t atime,off_t size)731 recvfile(char *new, opt_t opts, int mode, char *owner, char *group,
732 time_t mtime, time_t atime, off_t size)
733 {
734 int f, wrerr, olderrno;
735 off_t i;
736 char *cp;
737 char *savefile = NULL;
738 static struct stat statbuff;
739
740 /*
741 * Create temporary file
742 */
743 if (chkparent(new, opts) < 0 || (f = mkstemp(new)) == -1) {
744 error("%s: create failed: %s", new, SYSERR);
745 return;
746 }
747
748 /*
749 * Receive the file itself
750 */
751 ack();
752 wrerr = 0;
753 olderrno = 0;
754 for (i = 0; i < size; i += BUFSIZ) {
755 off_t amt = BUFSIZ;
756
757 cp = buf;
758 if (i + amt > size)
759 amt = size - i;
760 do {
761 ssize_t j;
762
763 j = readrem(cp, amt);
764 if (j <= 0) {
765 (void) close(f);
766 (void) unlink(new);
767 fatalerr(
768 "Read error occurred while receiving file.");
769 finish();
770 }
771 amt -= j;
772 cp += j;
773 } while (amt > 0);
774 amt = BUFSIZ;
775 if (i + amt > size)
776 amt = size - i;
777 if (wrerr == 0 && xwrite(f, buf, amt) != amt) {
778 olderrno = errno;
779 wrerr++;
780 }
781 }
782
783 if (response() < 0) {
784 (void) close(f);
785 (void) unlink(new);
786 return;
787 }
788
789 if (wrerr) {
790 error("%s: Write error: %s", new, strerror(olderrno));
791 (void) close(f);
792 (void) unlink(new);
793 return;
794 }
795
796 /*
797 * Do file comparison if enabled
798 */
799 if (IS_ON(opts, DO_COMPARE)) {
800 FILE *f1, *f2;
801 int c;
802
803 errno = 0; /* fopen is not a syscall */
804 if ((f1 = fopen(target, "r")) == NULL) {
805 error("%s: open for read failed: %s", target, SYSERR);
806 (void) close(f);
807 (void) unlink(new);
808 return;
809 }
810 errno = 0;
811 if ((f2 = fopen(new, "r")) == NULL) {
812 error("%s: open for read failed: %s", new, SYSERR);
813 (void) fclose(f1);
814 (void) close(f);
815 (void) unlink(new);
816 return;
817 }
818 while ((c = getc(f1)) == getc(f2))
819 if (c == EOF) {
820 debugmsg(DM_MISC,
821 "Files are the same '%s' '%s'.",
822 target, new);
823 (void) fclose(f1);
824 (void) fclose(f2);
825 (void) close(f);
826 (void) unlink(new);
827 /*
828 * This isn't an error per-se, but we
829 * need to indicate to the master that
830 * the file was not updated.
831 */
832 error(NULL);
833 return;
834 }
835 debugmsg(DM_MISC, "Files are different '%s' '%s'.",
836 target, new);
837 (void) fclose(f1);
838 (void) fclose(f2);
839 if (IS_ON(opts, DO_VERIFY)) {
840 message(MT_REMOTE|MT_INFO, "%s: need to update",
841 target);
842 (void) close(f);
843 (void) unlink(new);
844 return;
845 }
846 }
847
848 /*
849 * Set owner, group, and file mode
850 */
851 if (fchog(f, new, owner, group, mode) < 0) {
852 (void) close(f);
853 (void) unlink(new);
854 return;
855 }
856 (void) close(f);
857
858 /*
859 * Perform utimes() after file is closed to make
860 * certain OS's, such as NeXT 2.1, happy.
861 */
862 if (setfiletime(new, time(NULL), mtime) < 0)
863 message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR);
864
865 /*
866 * Try to save target file from being over-written
867 */
868 if (IS_ON(opts, DO_SAVETARGETS))
869 if ((savefile = savetarget(target, opts)) == NULL) {
870 (void) unlink(new);
871 return;
872 }
873
874 /*
875 * If the target is a directory, we need to remove it first
876 * before we can rename the new file.
877 */
878 if ((stat(target, &statbuff) == 0) && S_ISDIR(statbuff.st_mode)) {
879 char *saveptr = ptarget;
880
881 ptarget = &target[strlen(target)];
882 removefile(&statbuff, 0);
883 ptarget = saveptr;
884 }
885
886 /*
887 * Install new (temporary) file as the actual target
888 */
889 if (rename(new, target) == -1) {
890 static const char fmt[] = "%s -> %s: rename failed: %s";
891 struct stat stb;
892 /*
893 * If the rename failed due to "Text file busy", then
894 * try to rename the target file and retry the rename.
895 */
896 switch (errno) {
897 case ETXTBSY:
898 /* Save the target */
899 if ((savefile = savetarget(target, opts)) != NULL) {
900 /* Retry installing new file as target */
901 if (rename(new, target) == -1) {
902 error(fmt, new, target, SYSERR);
903 /* Try to put back save file */
904 if (rename(savefile, target) == -1)
905 error(fmt,
906 savefile, target, SYSERR);
907 (void) unlink(new);
908 } else
909 message(MT_NOTICE, "%s: renamed to %s",
910 target, savefile);
911 /*
912 * XXX: We should remove the savefile here.
913 * But we are nice to nfs clients and
914 * we keep it.
915 */
916 }
917 break;
918 case EISDIR:
919 /*
920 * See if target is a directory and remove it if it is
921 */
922 if (lstat(target, &stb) == 0) {
923 if (S_ISDIR(stb.st_mode)) {
924 char *optarget = ptarget;
925 for (ptarget = target; *ptarget;
926 ptarget++);
927 /* If we failed to remove, we'll catch
928 it later */
929 (void) removefile(&stb, 1);
930 ptarget = optarget;
931 }
932 }
933 if (rename(new, target) >= 0)
934 break;
935 /*FALLTHROUGH*/
936
937 default:
938 error(fmt, new, target, SYSERR);
939 (void) unlink(new);
940 break;
941 }
942 }
943
944 if (IS_ON(opts, DO_COMPARE))
945 message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
946 else
947 ack();
948 }
949
950 /*
951 * Receive a directory
952 */
953 static void
recvdir(opt_t opts,int mode,char * owner,char * group)954 recvdir(opt_t opts, int mode, char *owner, char *group)
955 {
956 static char lowner[100], lgroup[100];
957 char *cp;
958 struct stat stb;
959 int s;
960
961 s = lstat(target, &stb);
962 if (s == 0) {
963 /*
964 * If target is not a directory, remove it
965 */
966 if (!S_ISDIR(stb.st_mode)) {
967 if (IS_ON(opts, DO_VERIFY))
968 message(MT_NOTICE, "%s: need to remove",
969 target);
970 else {
971 if (unlink(target) == -1) {
972 error("%s: remove failed: %s",
973 target, SYSERR);
974 return;
975 }
976 }
977 s = -1;
978 errno = ENOENT;
979 } else {
980 if (!IS_ON(opts, DO_NOCHKMODE) &&
981 (stb.st_mode & 07777) != mode) {
982 if (IS_ON(opts, DO_VERIFY))
983 message(MT_NOTICE,
984 "%s: need to chmod to %#04o",
985 target, mode);
986 else if (chmod(target, mode) != 0)
987 message(MT_NOTICE,
988 "%s: chmod from %#04o to %#04o failed: %s",
989 target,
990 stb.st_mode & 07777,
991 mode,
992 SYSERR);
993 else
994 message(MT_NOTICE,
995 "%s: chmod from %#04o to %#04o",
996 target,
997 stb.st_mode & 07777,
998 mode);
999 }
1000
1001 /*
1002 * Check ownership and set if necessary
1003 */
1004 lowner[0] = CNULL;
1005 lgroup[0] = CNULL;
1006
1007 if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
1008 int o;
1009
1010 o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER :
1011 opts;
1012 if ((cp = getusername(stb.st_uid, target, o))
1013 != NULL)
1014 if (strcmp(owner, cp))
1015 (void) strlcpy(lowner, cp,
1016 sizeof(lowner));
1017 }
1018 if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
1019 int o;
1020
1021 o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
1022 opts;
1023 if ((cp = getgroupname(stb.st_gid, target, o))
1024 != NULL)
1025 if (strcmp(group, cp))
1026 (void) strlcpy(lgroup, cp,
1027 sizeof(lgroup));
1028 }
1029
1030 /*
1031 * Need to set owner and/or group
1032 */
1033 #define PRN(n) ((n[0] == ':') ? n+1 : n)
1034 if (lowner[0] != CNULL || lgroup[0] != CNULL) {
1035 if (lowner[0] == CNULL &&
1036 (cp = getusername(stb.st_uid,
1037 target, opts)))
1038 (void) strlcpy(lowner, cp,
1039 sizeof(lowner));
1040 if (lgroup[0] == CNULL &&
1041 (cp = getgroupname(stb.st_gid,
1042 target, opts)))
1043 (void) strlcpy(lgroup, cp,
1044 sizeof(lgroup));
1045
1046 if (IS_ON(opts, DO_VERIFY))
1047 message(MT_NOTICE,
1048 "%s: need to chown from %s:%s to %s:%s",
1049 target,
1050 PRN(lowner), PRN(lgroup),
1051 PRN(owner), PRN(group));
1052 else {
1053 if (fchog(-1, target, owner,
1054 group, -1) == 0)
1055 message(MT_NOTICE,
1056 "%s: chown from %s:%s to %s:%s",
1057 target,
1058 PRN(lowner),
1059 PRN(lgroup),
1060 PRN(owner),
1061 PRN(group));
1062 }
1063 }
1064 #undef PRN
1065 ack();
1066 return;
1067 }
1068 }
1069
1070 if (IS_ON(opts, DO_VERIFY)) {
1071 ack();
1072 return;
1073 }
1074
1075 /*
1076 * Create the directory
1077 */
1078 if (s < 0) {
1079 if (errno == ENOENT) {
1080 if (mkdir(target, mode) == 0 ||
1081 (chkparent(target, opts) == 0 &&
1082 mkdir(target, mode) == 0)) {
1083 message(MT_NOTICE, "%s: mkdir", target);
1084 (void) fchog(-1, target, owner, group, mode);
1085 ack();
1086 } else {
1087 error("%s: mkdir failed: %s", target, SYSERR);
1088 ptarget = sptarget[--catname];
1089 *ptarget = CNULL;
1090 }
1091 return;
1092 }
1093 }
1094 error("%s: lstat failed: %s", target, SYSERR);
1095 ptarget = sptarget[--catname];
1096 *ptarget = CNULL;
1097 }
1098
1099 /*
1100 * Receive a link
1101 */
1102 static void
recvlink(char * new,opt_t opts,int mode,off_t size)1103 recvlink(char *new, opt_t opts, int mode, off_t size)
1104 {
1105 char tbuf[PATH_MAX], dbuf[BUFSIZ];
1106 struct stat stb;
1107 char *optarget;
1108 int uptodate;
1109 off_t i;
1110
1111 /*
1112 * Read basic link info
1113 */
1114 ack();
1115 (void) remline(buf, sizeof(buf), TRUE);
1116
1117 if (response() < 0) {
1118 err();
1119 return;
1120 }
1121
1122 if (DECODE(dbuf, buf) == -1) {
1123 error("recvlink: cannot decode symlink target");
1124 return;
1125 }
1126
1127 uptodate = 0;
1128 if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1) {
1129 tbuf[i] = '\0';
1130 if (i == size && strncmp(dbuf, tbuf, (int) size) == 0)
1131 uptodate = 1;
1132 }
1133 mode &= 0777;
1134
1135 if (IS_ON(opts, DO_VERIFY) || uptodate) {
1136 if (uptodate)
1137 message(MT_REMOTE|MT_INFO, NULL);
1138 else
1139 message(MT_REMOTE|MT_INFO, "%s: need to update",
1140 target);
1141 if (IS_ON(opts, DO_COMPARE))
1142 return;
1143 (void) sendcmd(C_END, NULL);
1144 (void) response();
1145 return;
1146 }
1147
1148 /*
1149 * Make new symlink using a temporary name
1150 */
1151 if (chkparent(new, opts) < 0 || mktemp(new) == NULL ||
1152 symlink(dbuf, new) == -1) {
1153 error("%s -> %s: symlink failed: %s", new, dbuf, SYSERR);
1154 return;
1155 }
1156
1157 /*
1158 * See if target is a directory and remove it if it is
1159 */
1160 if (lstat(target, &stb) == 0) {
1161 if (S_ISDIR(stb.st_mode)) {
1162 optarget = ptarget;
1163 for (ptarget = target; *ptarget; ptarget++);
1164 if (removefile(&stb, 0) < 0) {
1165 ptarget = optarget;
1166 (void) unlink(new);
1167 (void) sendcmd(C_END, NULL);
1168 (void) response();
1169 return;
1170 }
1171 ptarget = optarget;
1172 }
1173 }
1174
1175 /*
1176 * Install link as the target
1177 */
1178 if (rename(new, target) == -1) {
1179 error("%s -> %s: symlink rename failed: %s",
1180 new, target, SYSERR);
1181 (void) unlink(new);
1182 (void) sendcmd(C_END, NULL);
1183 (void) response();
1184 return;
1185 }
1186
1187 message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1188
1189 /*
1190 * Indicate end of receive operation
1191 */
1192 (void) sendcmd(C_END, NULL);
1193 (void) response();
1194 }
1195
1196 /*
1197 * Creat a hard link to existing file.
1198 */
1199 static void
hardlink(char * cmd)1200 hardlink(char *cmd)
1201 {
1202 struct stat stb;
1203 int exists = 0;
1204 char *xoldname, *xnewname;
1205 char *cp = cmd;
1206 static char expbuf[BUFSIZ];
1207 char oldname[BUFSIZ], newname[BUFSIZ];
1208
1209 /* Skip over opts */
1210 (void) strtol(cp, &cp, 8);
1211 if (*cp++ != ' ') {
1212 error("hardlink: options not delimited");
1213 return;
1214 }
1215
1216 xoldname = strtok(cp, " ");
1217 if (xoldname == NULL) {
1218 error("hardlink: oldname name not delimited");
1219 return;
1220 }
1221
1222 if (DECODE(oldname, xoldname) == -1) {
1223 error("hardlink: Cannot decode oldname");
1224 return;
1225 }
1226
1227 xnewname = strtok(NULL, " ");
1228 if (xnewname == NULL) {
1229 error("hardlink: new name not specified");
1230 return;
1231 }
1232
1233 if (DECODE(newname, xnewname) == -1) {
1234 error("hardlink: Cannot decode newname");
1235 return;
1236 }
1237
1238 if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) {
1239 error("hardlink: tilde expansion failed");
1240 return;
1241 }
1242
1243 if (catname && cattarget(newname) < 0) {
1244 error("Cannot set newname target.");
1245 return;
1246 }
1247
1248 if (lstat(target, &stb) == 0) {
1249 int mode = stb.st_mode & S_IFMT;
1250
1251 if (mode != S_IFREG && mode != S_IFLNK) {
1252 error("%s: not a regular file", target);
1253 return;
1254 }
1255 exists = 1;
1256 }
1257
1258 if (chkparent(target, options) < 0 ) {
1259 error("%s: no parent: %s ", target, SYSERR);
1260 return;
1261 }
1262 if (exists && (unlink(target) == -1)) {
1263 error("%s: unlink failed: %s", target, SYSERR);
1264 return;
1265 }
1266 if (linkat(AT_FDCWD, expbuf, AT_FDCWD, target, 0) == -1) {
1267 error("%s: cannot link to %s: %s", target, oldname, SYSERR);
1268 return;
1269 }
1270 ack();
1271 }
1272
1273 /*
1274 * Set configuration information.
1275 *
1276 * A key letter is followed immediately by the value
1277 * to set. The keys are:
1278 * SC_FREESPACE - Set minimum free space of filesystem
1279 * SC_FREEFILES - Set minimum free number of files of filesystem
1280 */
1281 static void
setconfig(char * cmd)1282 setconfig(char *cmd)
1283 {
1284 char *cp = cmd;
1285 char *estr;
1286 const char *errstr;
1287
1288 switch (*cp++) {
1289 case SC_HOSTNAME: /* Set hostname */
1290 /*
1291 * Only use info if we don't know who this is.
1292 */
1293 if (!fromhost) {
1294 fromhost = xstrdup(cp);
1295 message(MT_SYSLOG, "startup for %s", fromhost);
1296 setproctitle("serving %s", cp);
1297 }
1298 break;
1299
1300 case SC_FREESPACE: /* Minimum free space */
1301 min_freespace = (int64_t)strtonum(cp, 0, LLONG_MAX, &errstr);
1302 if (errstr)
1303 fatalerr("Minimum free space is %s: '%s'", errstr,
1304 optarg);
1305 break;
1306
1307 case SC_FREEFILES: /* Minimum free files */
1308 min_freefiles = (int64_t)strtonum(cp, 0, LLONG_MAX, &errstr);
1309 if (errstr)
1310 fatalerr("Minimum free files is %s: '%s'", errstr,
1311 optarg);
1312 break;
1313
1314 case SC_LOGGING: /* Logging options */
1315 if ((estr = msgparseopts(cp, TRUE)) != NULL) {
1316 fatalerr("Bad message option string (%s): %s",
1317 cp, estr);
1318 return;
1319 }
1320 break;
1321
1322 case SC_DEFOWNER:
1323 (void) strlcpy(defowner, cp, sizeof(defowner));
1324 break;
1325
1326 case SC_DEFGROUP:
1327 (void) strlcpy(defgroup, cp, sizeof(defgroup));
1328 break;
1329
1330 default:
1331 message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
1332 return;
1333 }
1334 }
1335
1336 /*
1337 * Receive something
1338 */
1339 static void
recvit(char * cmd,int type)1340 recvit(char *cmd, int type)
1341 {
1342 int mode;
1343 opt_t opts;
1344 off_t size;
1345 time_t mtime, atime;
1346 char *owner, *group, *file;
1347 char new[PATH_MAX];
1348 char fileb[PATH_MAX];
1349 int64_t freespace = -1, freefiles = -1;
1350 char *cp = cmd;
1351
1352 /*
1353 * Get rdist option flags
1354 */
1355 opts = strtol(cp, &cp, 8);
1356 if (*cp++ != ' ') {
1357 error("recvit: options not delimited");
1358 return;
1359 }
1360
1361 /*
1362 * Get file mode
1363 */
1364 mode = strtol(cp, &cp, 8);
1365 if (*cp++ != ' ') {
1366 error("recvit: mode not delimited");
1367 return;
1368 }
1369
1370 /*
1371 * Get file size
1372 */
1373 size = (off_t) strtoll(cp, &cp, 10);
1374 if (*cp++ != ' ') {
1375 error("recvit: size not delimited");
1376 return;
1377 }
1378
1379 /*
1380 * Get modification time
1381 */
1382 mtime = (time_t) strtoll(cp, &cp, 10);
1383 if (*cp++ != ' ') {
1384 error("recvit: mtime not delimited");
1385 return;
1386 }
1387
1388 /*
1389 * Get access time
1390 */
1391 atime = (time_t) strtoll(cp, &cp, 10);
1392 if (*cp++ != ' ') {
1393 error("recvit: atime not delimited");
1394 return;
1395 }
1396
1397 /*
1398 * Get file owner name
1399 */
1400 owner = strtok(cp, " ");
1401 if (owner == NULL) {
1402 error("recvit: owner name not delimited");
1403 return;
1404 }
1405
1406 /*
1407 * Get file group name
1408 */
1409 group = strtok(NULL, " ");
1410 if (group == NULL) {
1411 error("recvit: group name not delimited");
1412 return;
1413 }
1414
1415 /*
1416 * Get file name. Can't use strtok() since there could
1417 * be white space in the file name.
1418 */
1419 if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1420 error("recvit: Cannot decode file name");
1421 return;
1422 }
1423
1424 if (fileb[0] == '\0') {
1425 error("recvit: no file name");
1426 return;
1427 }
1428 file = fileb;
1429
1430 debugmsg(DM_MISC,
1431 "recvit: opts = %#x mode = %#04o size = %lld mtime = %lld",
1432 opts, mode, (long long) size, (long long)mtime);
1433 debugmsg(DM_MISC,
1434 "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
1435 owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
1436
1437 if (type == S_IFDIR) {
1438 if ((size_t) catname >= sizeof(sptarget)) {
1439 error("%s: too many directory levels", target);
1440 return;
1441 }
1442 sptarget[catname] = ptarget;
1443 if (catname++) {
1444 *ptarget++ = '/';
1445 while ((*ptarget++ = *file++) != '\0')
1446 continue;
1447 ptarget--;
1448 }
1449 } else {
1450 /*
1451 * Create name of temporary file
1452 */
1453 if (catname && cattarget(file) < 0) {
1454 error("Cannot set file name.");
1455 return;
1456 }
1457 file = strrchr(target, '/');
1458 if (file == NULL)
1459 (void) strlcpy(new, tempname, sizeof(new));
1460 else if (file == target)
1461 (void) snprintf(new, sizeof(new), "/%s", tempname);
1462 else {
1463 *file = CNULL;
1464 (void) snprintf(new, sizeof(new), "%s/%s", target,
1465 tempname);
1466 *file = '/';
1467 }
1468 }
1469
1470 /*
1471 * Check to see if there is enough free space and inodes
1472 * to install this file.
1473 */
1474 if (min_freespace || min_freefiles) {
1475 /* Convert file size to kilobytes */
1476 int64_t fsize = (int64_t)size / 1024;
1477
1478 if (getfilesysinfo(target, &freespace, &freefiles) != 0)
1479 return;
1480
1481 /*
1482 * filesystem values < 0 indicate unsupported or unavailable
1483 * information.
1484 */
1485 if (min_freespace && (freespace >= 0) &&
1486 (freespace - fsize < min_freespace)) {
1487 error(
1488 "%s: Not enough free space on filesystem: min %lld "
1489 "free %lld", target, min_freespace, freespace);
1490 return;
1491 }
1492 if (min_freefiles && (freefiles >= 0) &&
1493 (freefiles - 1 < min_freefiles)) {
1494 error(
1495 "%s: Not enough free files on filesystem: min %lld free "
1496 "%lld", target, min_freefiles, freefiles);
1497 return;
1498 }
1499 }
1500
1501 /*
1502 * Call appropriate receive function to receive file
1503 */
1504 switch (type) {
1505 case S_IFDIR:
1506 recvdir(opts, mode, owner, group);
1507 break;
1508
1509 case S_IFLNK:
1510 recvlink(new, opts, mode, size);
1511 break;
1512
1513 case S_IFREG:
1514 recvfile(new, opts, mode, owner, group, mtime, atime, size);
1515 break;
1516
1517 default:
1518 error("%d: unknown file type", type);
1519 break;
1520 }
1521 }
1522
1523 /*
1524 * Chmog something
1525 */
1526 static void
dochmog(char * cmd)1527 dochmog(char *cmd)
1528 {
1529 int mode;
1530 opt_t opts;
1531 char *owner, *group, *file;
1532 char *cp = cmd;
1533 char fileb[PATH_MAX];
1534
1535 /*
1536 * Get rdist option flags
1537 */
1538 opts = strtol(cp, &cp, 8);
1539 if (*cp++ != ' ') {
1540 error("dochmog: options not delimited");
1541 return;
1542 }
1543
1544 /*
1545 * Get file mode
1546 */
1547 mode = strtol(cp, &cp, 8);
1548 if (*cp++ != ' ') {
1549 error("dochmog: mode not delimited");
1550 return;
1551 }
1552
1553 /*
1554 * Get file owner name
1555 */
1556 owner = strtok(cp, " ");
1557 if (owner == NULL) {
1558 error("dochmog: owner name not delimited");
1559 return;
1560 }
1561
1562 /*
1563 * Get file group name
1564 */
1565 group = strtok(NULL, " ");
1566 if (group == NULL) {
1567 error("dochmog: group name not delimited");
1568 return;
1569 }
1570
1571 /*
1572 * Get file name. Can't use strtok() since there could
1573 * be white space in the file name.
1574 */
1575 if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1576 error("dochmog: Cannot decode file name");
1577 return;
1578 }
1579
1580 if (fileb[0] == '\0') {
1581 error("dochmog: no file name");
1582 return;
1583 }
1584 file = fileb;
1585
1586 debugmsg(DM_MISC,
1587 "dochmog: opts = %#x mode = %#04o", opts, mode);
1588 debugmsg(DM_MISC,
1589 "dochmog: owner = '%s' group = '%s' file = '%s' catname = %d",
1590 owner, group, file, catname);
1591
1592 if (catname && cattarget(file) < 0) {
1593 error("Cannot set newname target.");
1594 return;
1595 }
1596
1597 (void) fchog(-1, target, owner, group, mode);
1598
1599 ack();
1600 }
1601
1602 /*
1603 * Set target information
1604 */
1605 static void
settarget(char * cmd,int isdir)1606 settarget(char *cmd, int isdir)
1607 {
1608 char *cp = cmd;
1609 opt_t opts;
1610 char file[BUFSIZ];
1611
1612 catname = isdir;
1613
1614 /*
1615 * Parse options for this target
1616 */
1617 opts = strtol(cp, &cp, 8);
1618 if (*cp++ != ' ') {
1619 error("settarget: options not delimited");
1620 return;
1621 }
1622 options = opts;
1623
1624 if (DECODE(file, cp) == -1) {
1625 error("settarget: Cannot decode target name");
1626 return;
1627 }
1628
1629 /*
1630 * Handle target
1631 */
1632 if (exptilde(target, cp, sizeof(target)) == NULL)
1633 return;
1634 ptarget = target;
1635 while (*ptarget)
1636 ptarget++;
1637
1638 ack();
1639 }
1640
1641 /*
1642 * Cleanup in preparation for exiting.
1643 */
1644 void
cleanup(int dummy)1645 cleanup(int dummy)
1646 {
1647 /* We don't need to do anything */
1648 }
1649
1650 /*
1651 * Server routine to read requests and process them.
1652 */
1653 void
server(void)1654 server(void)
1655 {
1656 static char cmdbuf[BUFSIZ];
1657 char *cp;
1658 int n, proto_version;
1659
1660 if (setjmp(finish_jmpbuf))
1661 return;
1662 (void) signal(SIGHUP, sighandler);
1663 (void) signal(SIGINT, sighandler);
1664 (void) signal(SIGQUIT, sighandler);
1665 (void) signal(SIGTERM, sighandler);
1666 (void) signal(SIGPIPE, sighandler);
1667 (void) umask(oumask = umask(0));
1668 (void) strlcpy(tempname, _RDIST_TMP, sizeof(tempname));
1669 if (fromhost) {
1670 message(MT_SYSLOG, "Startup for %s", fromhost);
1671 #if defined(SETARGS)
1672 setproctitle("Serving %s", fromhost);
1673 #endif /* SETARGS */
1674 }
1675
1676 /*
1677 * Let client know we want it to send its version number
1678 */
1679 (void) sendcmd(S_VERSION, NULL);
1680
1681 if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
1682 error("server: expected control record");
1683 return;
1684 }
1685
1686 if (cmdbuf[0] != S_VERSION || !isdigit((unsigned char)cmdbuf[1])) {
1687 error("Expected version command, received: \"%s\".", cmdbuf);
1688 return;
1689 }
1690
1691 proto_version = atoi(&cmdbuf[1]);
1692 if (proto_version != VERSION) {
1693 error("Protocol version %d is not supported.", proto_version);
1694 return;
1695 }
1696
1697 /* Version number is okay */
1698 ack();
1699
1700 /*
1701 * Main command loop
1702 */
1703 for ( ; ; ) {
1704 n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
1705 if (n == -1) /* EOF */
1706 return;
1707 if (n == 0) {
1708 error("server: expected control record");
1709 continue;
1710 }
1711
1712 switch (*cp++) {
1713 case C_SETCONFIG: /* Configuration info */
1714 setconfig(cp);
1715 ack();
1716 continue;
1717
1718 case C_DIRTARGET: /* init target file/directory name */
1719 settarget(cp, TRUE);
1720 continue;
1721
1722 case C_TARGET: /* init target file/directory name */
1723 settarget(cp, FALSE);
1724 continue;
1725
1726 case C_RECVREG: /* Transfer a regular file. */
1727 recvit(cp, S_IFREG);
1728 continue;
1729
1730 case C_RECVDIR: /* Transfer a directory. */
1731 recvit(cp, S_IFDIR);
1732 continue;
1733
1734 case C_RECVSYMLINK: /* Transfer symbolic link. */
1735 recvit(cp, S_IFLNK);
1736 continue;
1737
1738 case C_RECVHARDLINK: /* Transfer hard link. */
1739 hardlink(cp);
1740 continue;
1741
1742 case C_END: /* End of transfer */
1743 *ptarget = CNULL;
1744 if (catname <= 0) {
1745 error("server: too many '%c's", C_END);
1746 continue;
1747 }
1748 ptarget = sptarget[--catname];
1749 *ptarget = CNULL;
1750 ack();
1751 continue;
1752
1753 case C_CLEAN: /* Clean. Cleanup a directory */
1754 clean(cp);
1755 continue;
1756
1757 case C_QUERY: /* Query file/directory */
1758 query(cp);
1759 continue;
1760
1761 case C_SPECIAL: /* Special. Execute commands */
1762 dospecial(cp);
1763 continue;
1764
1765 case C_CMDSPECIAL: /* Cmd Special. Execute commands */
1766 docmdspecial();
1767 continue;
1768
1769 case C_CHMOG: /* Set owner, group, mode */
1770 dochmog(cp);
1771 continue;
1772
1773 case C_ERRMSG: /* Normal error message */
1774 if (cp && *cp)
1775 message(MT_NERROR|MT_NOREMOTE, "%s", cp);
1776 continue;
1777
1778 case C_FERRMSG: /* Fatal error message */
1779 if (cp && *cp)
1780 message(MT_FERROR|MT_NOREMOTE, "%s", cp);
1781 return;
1782
1783 default:
1784 error("server: unknown command '%s'", cp - 1);
1785 case CNULL:
1786 continue;
1787 }
1788 }
1789 }
1790