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