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