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