Annotation of src/usr.bin/rdistd/server.c, Revision 1.8
1.8 ! millert 1: /* $OpenBSD: server.c,v 1.7 1998/06/26 21:20:53 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.8 ! millert 41: "$OpenBSD: server.c,v 1.7 1998/06/26 21:20:53 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:
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) {
113: message(MT_NOTICE, "%s: chown %d.%d failed: %s",
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;
199: register int i;
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.1 dm 224: strcpy(last_owner, owner);
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;
256: strcpy(last_group, gr->gr_name);
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;
327: register char *cp;
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)
416: register char *cp;
417: {
418: DIR *d;
419: register DIRENTRY *dp;
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)
483: register char *cp;
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: {
511: register char *cp;
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);
531: (void) sprintf(env, "export %s;%s=%s",
532: E_FILES, E_FILES, cp);
533: } else {
534: len = strlen(env);
535: env = (char *) xrealloc(env,
536: len + strlen(cp) + 2);
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:
597: /* Either the above check was true or an error occured */
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:
612: /* Either the above check was true or an error occured */
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: {
669: register char *cp;
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:
713: (void) sprintf(savefile, "%s%s", file, SAVE_SUFFIX);
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;
762: register char *cp;
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(
798: "Read error occured while receiving file.");
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];
989: register char *cp;
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))
1048: (void) strcpy(lowner, cp);
1049: }
1050: if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
1051: int o;
1052:
1053: o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
1054: opts;
1.7 millert 1055: if ((cp = getgroupname(stb.st_gid, target, o)))
1.1 dm 1056: if (strcmp(group, cp))
1057: (void) strcpy(lgroup, cp);
1058: }
1059:
1060: /*
1061: * Need to set owner and/or group
1062: */
1063: #define PRN(n) ((n[0] == ':') ? n+1 : n)
1064: if (lowner[0] != CNULL || lgroup[0] != CNULL) {
1065: if (lowner[0] == CNULL &&
1066: (cp = getusername(stb.st_uid,
1067: target, opts)))
1068: (void) strcpy(lowner, cp);
1069: if (lgroup[0] == CNULL &&
1070: (cp = getgroupname(stb.st_gid,
1071: target, opts)))
1072: (void) strcpy(lgroup, cp);
1073:
1074: if (IS_ON(opts, DO_VERIFY))
1075: message(MT_NOTICE,
1076: "%s: need to chown from %s.%s to %s.%s",
1077: target,
1078: PRN(lowner), PRN(lgroup),
1079: PRN(owner), PRN(group));
1080: else {
1081: if (fchog(-1, target, owner,
1082: group, -1) == 0)
1083: message(MT_NOTICE,
1084: "%s: chown from %s.%s to %s.%s",
1085: target,
1086: PRN(lowner),
1087: PRN(lgroup),
1088: PRN(owner),
1089: PRN(group));
1090: }
1091: }
1092: #undef PRN
1093: ack();
1094: return;
1095: }
1096: }
1097:
1098: if (IS_ON(opts, DO_VERIFY)) {
1099: ack();
1100: return;
1101: }
1102:
1103: /*
1104: * Create the directory
1105: */
1106: if (s < 0) {
1107: if (errno == ENOENT) {
1108: if (mkdir(target, mode) == 0 ||
1.7 millert 1109: (chkparent(target, opts) == 0 &&
1110: mkdir(target, mode) == 0)) {
1.1 dm 1111: message(MT_NOTICE, "%s: mkdir", target);
1112: (void) fchog(-1, target, owner, group, mode);
1113: ack();
1114: } else {
1115: error("%s: mkdir failed: %s", target, SYSERR);
1116: ptarget = sptarget[--catname];
1117: *ptarget = CNULL;
1118: }
1119: return;
1120: }
1121: }
1122: error("%s: lstat failed: %s", target, SYSERR);
1123: ptarget = sptarget[--catname];
1124: *ptarget = CNULL;
1125: }
1126:
1127: /*
1128: * Receive a link
1129: */
1130: static void recvlink(new, opts, mode, size)
1131: char *new;
1132: opt_t opts;
1133: int mode;
1134: off_t size;
1135: {
1136: struct stat stb;
1137: char *optarget;
1138: off_t i;
1139:
1140: /*
1141: * Read basic link info
1142: */
1143: ack();
1144: (void) remline(buf, sizeof(buf), TRUE);
1145:
1146: if (response() < 0) {
1147: err();
1148: return;
1149: }
1150:
1151: /*
1152: * Make new symlink using a temporary name
1153: */
1154: if (symlink(buf, new) < 0) {
1155: if (errno != ENOENT || chkparent(new, opts) < 0 ||
1156: symlink(buf, new) < 0) {
1157: error("%s -> %s: symlink failed: %s", new, buf,SYSERR);
1158: (void) unlink(new);
1159: return;
1160: }
1161: }
1162:
1163: /*
1164: * Do comparison of what link is pointing to if enabled
1165: */
1166: mode &= 0777;
1167: if (IS_ON(opts, DO_COMPARE)) {
1168: char tbuf[MAXPATHLEN];
1169:
1.7 millert 1170: if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1)
1171: tbuf[i] = '\0';
1172: if (i != -1 && i == size && strncmp(buf, tbuf, (size_t) size) == 0) {
1.1 dm 1173: (void) unlink(new);
1174: ack();
1175: return;
1176: }
1177: if (IS_ON(opts, DO_VERIFY)) {
1178: (void) unlink(new);
1179: message(MT_REMOTE|MT_INFO, "%s: need to update",
1180: target);
1181: (void) sendcmd(C_END, NULL);
1182: (void) response();
1183: return;
1184: }
1185: }
1186:
1187: /*
1188: * See if target is a directory and remove it if it is
1189: */
1190: if (lstat(target, &stb) == 0) {
1191: if (S_ISDIR(stb.st_mode)) {
1192: optarget = ptarget;
1193: for (ptarget = target; *ptarget; ptarget++);
1194: if (removefile(&stb) < 0) {
1195: ptarget = optarget;
1196: (void) unlink(new);
1197: (void) sendcmd(C_END, NULL);
1198: (void) response();
1199: return;
1200: }
1201: ptarget = optarget;
1202: }
1203: }
1204:
1205: /*
1206: * Install link as the target
1207: */
1208: if (rename(new, target) < 0) {
1209: error("%s -> %s: symlink rename failed: %s",
1210: new, target, SYSERR);
1211: (void) unlink(new);
1212: (void) sendcmd(C_END, NULL);
1213: (void) response();
1214: return;
1215: }
1216:
1217: if (IS_ON(opts, DO_COMPARE))
1218: message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1219: else
1220: ack();
1221:
1222: /*
1223: * Indicate end of receive operation
1224: */
1225: (void) sendcmd(C_END, NULL);
1226: (void) response();
1227: }
1228:
1229: /*
1230: * Creat a hard link to existing file.
1231: */
1232: static void hardlink(cmd)
1233: char *cmd;
1234: {
1235: struct stat stb;
1236: int exists = 0;
1237: char *oldname, *newname;
1238: char *cp = cmd;
1239: static char expbuf[BUFSIZ];
1240:
1241: /* Skip over opts */
1242: (void) strtol(cp, &cp, 8);
1243: if (*cp++ != ' ') {
1244: error("hardlink: options not delimited");
1245: return;
1246: }
1247:
1248: oldname = strtok(cp, " ");
1249: if (oldname == NULL) {
1250: error("hardlink: oldname name not delimited");
1251: return;
1252: }
1253:
1.7 millert 1254: newname = strtok(NULL, " ");
1.1 dm 1255: if (newname == NULL) {
1256: error("hardlink: new name not specified");
1257: return;
1258: }
1259:
1260: if (exptilde(expbuf, oldname) == NULL) {
1261: error("hardlink: tilde expansion failed");
1262: return;
1263: }
1264: oldname = expbuf;
1265:
1266: if (catname && cattarget(newname) < 0) {
1267: error("Cannot set newname target.");
1268: return;
1269: }
1270:
1271: if (lstat(target, &stb) == 0) {
1272: int mode = stb.st_mode & S_IFMT;
1273:
1274: if (mode != S_IFREG && mode != S_IFLNK) {
1275: error("%s: not a regular file", target);
1276: return;
1277: }
1278: exists = 1;
1279: }
1280:
1281: if (chkparent(target, options) < 0 ) {
1282: error("%s: no parent: %s ", target, SYSERR);
1283: return;
1284: }
1285: if (exists && (unlink(target) < 0)) {
1286: error("%s: unlink failed: %s", target, SYSERR);
1287: return;
1288: }
1289: if (link(oldname, target) < 0) {
1290: error("%s: cannot link to %s: %s", target, oldname, SYSERR);
1291: return;
1292: }
1293: ack();
1294: }
1295:
1296: /*
1297: * Set configuration information.
1298: *
1299: * A key letter is followed immediately by the value
1300: * to set. The keys are:
1301: * SC_FREESPACE - Set minimium free space of filesystem
1302: * SC_FREEFILES - Set minimium free number of files of filesystem
1303: */
1304: static void setconfig(cmd)
1305: char *cmd;
1306: {
1307: register char *cp = cmd;
1308: char *estr;
1309:
1310: switch (*cp++) {
1311: case SC_HOSTNAME: /* Set hostname */
1312: /*
1313: * Only use info if we don't know who this is.
1314: */
1315: if (!fromhost) {
1.8 ! millert 1316: fromhost = xstrdup(cp);
1.1 dm 1317: message(MT_SYSLOG, "startup for %s", fromhost);
1.7 millert 1318: #if defined(SETARGS) || defined(HAVE_SETPROCTITLE)
1.1 dm 1319: setproctitle("serving %s", cp);
1.7 millert 1320: #endif /* SETARGS || HAVE_SETPROCTITLE */
1.1 dm 1321: }
1322: break;
1323:
1324: case SC_FREESPACE: /* Minimium free space */
1325: if (!isdigit(*cp)) {
1326: fatalerr("Expected digit, got '%s'.", cp);
1327: return;
1328: }
1329: min_freespace = (unsigned long) atoi(cp);
1330: break;
1331:
1332: case SC_FREEFILES: /* Minimium free files */
1333: if (!isdigit(*cp)) {
1334: fatalerr("Expected digit, got '%s'.", cp);
1335: return;
1336: }
1337: min_freefiles = (unsigned long) atoi(cp);
1338: break;
1339:
1340: case SC_LOGGING: /* Logging options */
1.7 millert 1341: if ((estr = msgparseopts(cp, TRUE))) {
1.1 dm 1342: fatalerr("Bad message option string (%s): %s",
1343: cp, estr);
1344: return;
1345: }
1346: break;
1347:
1348: default:
1349: message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
1350: return;
1351: }
1352: }
1353:
1354: /*
1355: * Receive something
1356: */
1357: static void recvit(cmd, type)
1358: char *cmd;
1359: int type;
1360: {
1361: int mode;
1362: opt_t opts;
1363: off_t size;
1364: time_t mtime, atime;
1365: char *owner, *group, *file;
1366: char new[MAXPATHLEN];
1.5 millert 1367: long freespace = -1, freefiles = -1;
1.1 dm 1368: char *cp = cmd;
1369:
1370: /*
1371: * Get rdist option flags
1372: */
1373: opts = strtol(cp, &cp, 8);
1374: if (*cp++ != ' ') {
1375: error("recvit: options not delimited");
1376: return;
1377: }
1378:
1379: /*
1380: * Get file mode
1381: */
1382: mode = strtol(cp, &cp, 8);
1383: if (*cp++ != ' ') {
1384: error("recvit: mode not delimited");
1385: return;
1386: }
1387:
1388: /*
1389: * Get file size
1390: */
1391: size = strtol(cp, &cp, 10);
1392: if (*cp++ != ' ') {
1393: error("recvit: size not delimited");
1394: return;
1395: }
1396:
1397: /*
1398: * Get modification time
1399: */
1400: mtime = strtol(cp, &cp, 10);
1401: if (*cp++ != ' ') {
1402: error("recvit: mtime not delimited");
1403: return;
1404: }
1405:
1406: /*
1407: * Get access time
1408: */
1409: atime = strtol(cp, &cp, 10);
1410: if (*cp++ != ' ') {
1411: error("recvit: atime not delimited");
1412: return;
1413: }
1414:
1415: /*
1416: * Get file owner name
1417: */
1418: owner = strtok(cp, " ");
1419: if (owner == NULL) {
1420: error("recvit: owner name not delimited");
1421: return;
1422: }
1423:
1424: /*
1425: * Get file group name
1426: */
1.7 millert 1427: group = strtok(NULL, " ");
1.1 dm 1428: if (group == NULL) {
1429: error("recvit: group name not delimited");
1430: return;
1431: }
1432:
1433: /*
1434: * Get file name. Can't use strtok() since there could
1435: * be white space in the file name.
1436: */
1437: file = group + strlen(group) + 1;
1438: if (file == NULL) {
1439: error("recvit: no file name");
1440: return;
1441: }
1442:
1443: debugmsg(DM_MISC,
1444: "recvit: opts = %04o mode = %04o size = %d mtime = %d",
1445: opts, mode, size, mtime);
1446: debugmsg(DM_MISC,
1447: "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
1448: owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
1449:
1450: if (type == S_IFDIR) {
1451: if (catname >= sizeof(sptarget)) {
1452: error("%s: too many directory levels", target);
1453: return;
1454: }
1455: sptarget[catname] = ptarget;
1456: if (catname++) {
1457: *ptarget++ = '/';
1.7 millert 1458: while ((*ptarget++ = *file++))
1.1 dm 1459: ;
1460: ptarget--;
1461: }
1462: } else {
1463: /*
1464: * Create name of temporary file
1465: */
1466: if (catname && cattarget(file) < 0) {
1467: error("Cannot set file name.");
1468: return;
1469: }
1470: file = strrchr(target, '/');
1471: if (file == NULL)
1472: (void) strcpy(new, tempname);
1473: else if (file == target)
1474: (void) sprintf(new, "/%s", tempname);
1475: else {
1476: *file = CNULL;
1477: (void) sprintf(new, "%s/%s", target, tempname);
1478: *file = '/';
1479: }
1480: (void) mktemp(new);
1481: }
1482:
1483: /*
1484: * Check to see if there is enough free space and inodes
1485: * to install this file.
1486: */
1487: if (min_freespace || min_freefiles) {
1488: /* Convert file size to kilobytes */
1.5 millert 1489: long fsize = (long) (size / 1024);
1.1 dm 1490:
1491: if (getfilesysinfo(target, &freespace, &freefiles) != 0)
1492: return;
1493:
1494: /*
1495: * filesystem values < 0 indicate unsupported or unavailable
1496: * information.
1497: */
1498: if (min_freespace && (freespace >= 0) &&
1499: (freespace - fsize < min_freespace)) {
1500: error(
1501: "%s: Not enough free space on filesystem: min %d free %d",
1502: target, min_freespace, freespace);
1503: return;
1504: }
1505: if (min_freefiles && (freefiles >= 0) &&
1506: (freefiles - 1 < min_freefiles)) {
1507: error(
1508: "%s: Not enough free files on filesystem: min %d free %d",
1509: target, min_freefiles, freefiles);
1510: return;
1511: }
1512: }
1513:
1514: /*
1515: * Call appropriate receive function to receive file
1516: */
1517: switch (type) {
1518: case S_IFDIR:
1519: recvdir(opts, mode, owner, group);
1520: break;
1521:
1522: case S_IFLNK:
1523: recvlink(new, opts, mode, size);
1524: break;
1525:
1526: case S_IFREG:
1527: recvfile(new, opts, mode, owner, group, mtime, atime, size);
1528: break;
1529:
1530: default:
1531: error("%d: unknown file type", type);
1532: break;
1533: }
1534: }
1535:
1536: /*
1537: * Set target information
1538: */
1539: static void settarget(cmd, isdir)
1540: char *cmd;
1541: int isdir;
1542: {
1543: char *cp = cmd;
1544: opt_t opts;
1545:
1546: catname = isdir;
1547:
1548: /*
1549: * Parse options for this target
1550: */
1551: opts = strtol(cp, &cp, 8);
1552: if (*cp++ != ' ') {
1553: error("settarget: options not delimited");
1554: return;
1555: }
1556: options = opts;
1557:
1558: /*
1559: * Handle target
1560: */
1561: if (exptilde(target, cp) == NULL)
1562: return;
1563: ptarget = target;
1564: while (*ptarget)
1565: ptarget++;
1566:
1567: ack();
1568: }
1569:
1570: /*
1571: * Cleanup in preparation for exiting.
1572: */
1573: extern void cleanup()
1574: {
1575: /* We don't need to do anything */
1576: }
1577:
1578: /*
1579: * Server routine to read requests and process them.
1580: */
1581: extern void server()
1582: {
1583: static char cmdbuf[BUFSIZ];
1584: register char *cp;
1585: register int n;
1586: extern jmp_buf finish_jmpbuf;
1587:
1.2 dm 1588: if (setjmp(finish_jmpbuf)) {
1589: setjmp_ok = FALSE;
1.1 dm 1590: return;
1.2 dm 1591: }
1592: setjmp_ok = TRUE;
1.1 dm 1593: (void) signal(SIGHUP, sighandler);
1594: (void) signal(SIGINT, sighandler);
1595: (void) signal(SIGQUIT, sighandler);
1596: (void) signal(SIGTERM, sighandler);
1597: (void) signal(SIGPIPE, sighandler);
1598: (void) umask(oumask = umask(0));
1599: (void) strcpy(tempname, _RDIST_TMP);
1600: if (fromhost) {
1601: message(MT_SYSLOG, "Startup for %s", fromhost);
1602: #if defined(SETARGS)
1603: setproctitle("Serving %s", fromhost);
1604: #endif /* SETARGS */
1605: }
1606:
1607: /*
1608: * Let client know we want it to send it's version number
1609: */
1610: (void) sendcmd(S_VERSION, NULL);
1611:
1612: if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
1.2 dm 1613: setjmp_ok = FALSE;
1.1 dm 1614: error("server: expected control record");
1615: return;
1616: }
1617:
1618: if (cmdbuf[0] != S_VERSION || !isdigit(cmdbuf[1])) {
1.2 dm 1619: setjmp_ok = FALSE;
1.1 dm 1620: error("Expected version command, received: \"%s\".", cmdbuf);
1621: return;
1622: }
1623:
1624: proto_version = atoi(&cmdbuf[1]);
1625: if (proto_version != VERSION) {
1.2 dm 1626: setjmp_ok = FALSE;
1.1 dm 1627: error("Protocol version %d is not supported.", proto_version);
1628: return;
1629: }
1630:
1631: /* Version number is okay */
1632: ack();
1633:
1634: /*
1635: * Main command loop
1636: */
1637: for ( ; ; ) {
1638: n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
1.2 dm 1639: if (n == -1) { /* EOF */
1640: setjmp_ok = FALSE;
1.1 dm 1641: return;
1.2 dm 1642: }
1.1 dm 1643: if (n == 0) {
1644: error("server: expected control record");
1645: continue;
1646: }
1647:
1648: switch (*cp++) {
1649: case C_SETCONFIG: /* Configuration info */
1650: setconfig(cp);
1651: ack();
1652: continue;
1653:
1654: case C_DIRTARGET: /* init target file/directory name */
1655: settarget(cp, TRUE);
1656: continue;
1657:
1658: case C_TARGET: /* init target file/directory name */
1659: settarget(cp, FALSE);
1660: continue;
1661:
1662: case C_RECVREG: /* Transfer a regular file. */
1663: recvit(cp, S_IFREG);
1664: continue;
1665:
1666: case C_RECVDIR: /* Transfer a directory. */
1667: recvit(cp, S_IFDIR);
1668: continue;
1669:
1670: case C_RECVSYMLINK: /* Transfer symbolic link. */
1671: recvit(cp, S_IFLNK);
1672: continue;
1673:
1674: case C_RECVHARDLINK: /* Transfer hard link. */
1675: hardlink(cp);
1676: continue;
1677:
1678: case C_END: /* End of transfer */
1679: *ptarget = CNULL;
1680: if (catname <= 0) {
1681: error("server: too many '%c's", C_END);
1682: continue;
1683: }
1684: ptarget = sptarget[--catname];
1685: *ptarget = CNULL;
1686: ack();
1687: continue;
1688:
1689: case C_CLEAN: /* Clean. Cleanup a directory */
1690: clean(cp);
1691: continue;
1692:
1693: case C_QUERY: /* Query file/directory */
1694: query(cp);
1695: continue;
1696:
1697: case C_SPECIAL: /* Special. Execute commands */
1698: dospecial(cp);
1699: continue;
1700:
1701: case C_CMDSPECIAL: /* Cmd Special. Execute commands */
1702: docmdspecial();
1703: continue;
1704:
1705: #ifdef DOCHMOD
1706: case C_CHMOD: /* Set mode */
1707: dochmod(cp);
1708: continue;
1709: #endif /* DOCHMOD */
1710:
1711: case C_ERRMSG: /* Normal error message */
1712: if (cp && *cp)
1713: message(MT_NERROR|MT_NOREMOTE, "%s", cp);
1714: continue;
1715:
1716: case C_FERRMSG: /* Fatal error message */
1717: if (cp && *cp)
1718: message(MT_FERROR|MT_NOREMOTE, "%s", cp);
1.2 dm 1719: setjmp_ok = FALSE;
1.1 dm 1720: return;
1721:
1722: default:
1723: error("server: unknown command '%s'", cp - 1);
1724: case CNULL:
1725: continue;
1726: }
1727: }
1728: }