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