Merge branch 'vendor/GREP'
[dragonfly.git] / usr.bin / rdist / server.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)server.c 8.1 (Berkeley) 6/9/93
34  * $FreeBSD: src/usr.bin/rdist/server.c,v 1.10 1999/08/28 01:05:09 peter Exp $
35  */
36
37 #include <sys/wait.h>
38
39 #include <stdarg.h>
40
41 #include "defs.h"
42
43 #define ack() write(rem, "\0\n", 2)
44 #define err() write(rem, "\1\n", 2)
45 #define protoname() (pw ? pw->pw_name : user)
46 #define protogroup() (gr ? gr->gr_name : group)
47
48 struct  linkbuf *ihead = NULL;  /* list of files with more than one link */
49 char    buf[BUFSIZ];            /* general purpose buffer */
50 char    target[BUFSIZ];         /* target/source directory name */
51 char    source[BUFSIZ];         /* source directory name */
52 char    *tp;                    /* pointer to end of target name */
53 char    *Tdest;                 /* pointer to last T dest*/
54 int     catname;                /* cat name to target name */
55 char    *stp[32];               /* stack of saved tp's for directories */
56 int     oumask;                 /* old umask for creating files */
57
58 extern  FILE *lfp;              /* log file for mailing changes */
59
60 static int      chkparent(char *);
61 static void     clean(char *);
62 static void     comment(char *);
63 static void     dospecial(char *);
64 static int      fchog(int, char *, char *, char *, int);
65 static void     hardlink(char *);
66 static void     note(const char *, ...) __printflike(1, 2);
67 static void     query(char *);
68 static void     recvf(char *, int);
69 static void     removeit(struct stat *);
70 static int      response(void);
71 static void     rmchk(int);
72 static struct linkbuf *savelink(struct stat *);
73 static void     sendf(char *, int);
74 static int      update(char *, int, struct stat *);
75
76 /*
77  * Server routine to read requests and process them.
78  * Commands are:
79  *      Tname   - Transmit file if out of date
80  *      Vname   - Verify if file out of date or not
81  *      Qname   - Query if file exists. Return mtime & size if it does.
82  */
83 void
84 server(void)
85 {
86         char cmdbuf[BUFSIZ];
87         char *cp;
88
89         signal(SIGHUP, cleanup);
90         signal(SIGINT, cleanup);
91         signal(SIGQUIT, cleanup);
92         signal(SIGTERM, cleanup);
93         signal(SIGPIPE, cleanup);
94
95         rem = 0;
96         oumask = umask(0);
97         snprintf(buf, sizeof(buf), "V%d\n", VERSION);
98         write(rem, buf, strlen(buf));
99
100         for (;;) {
101                 cp = cmdbuf;
102                 if (read(rem, cp, 1) <= 0)
103                         return;
104                 if (*cp++ == '\n') {
105                         error("server: expected control record\n");
106                         continue;
107                 }
108                 do {
109                         if (read(rem, cp, 1) != 1)
110                                 cleanup(0);
111                 } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
112                 *--cp = '\0';
113                 cp = cmdbuf;
114                 switch (*cp++) {
115                 case 'T':  /* init target file/directory name */
116                         catname = 1;    /* target should be directory */
117                         goto dotarget;
118
119                 case 't':  /* init target file/directory name */
120                         catname = 0;
121                 dotarget:
122                         if (exptilde(target, cp, sizeof(target)) == NULL)
123                                 continue;
124                         tp = target;
125                         while (*tp)
126                                 tp++;
127                         ack();
128                         continue;
129
130                 case 'R':  /* Transfer a regular file. */
131                         recvf(cp, S_IFREG);
132                         continue;
133
134                 case 'D':  /* Transfer a directory. */
135                         recvf(cp, S_IFDIR);
136                         continue;
137
138                 case 'K':  /* Transfer symbolic link. */
139                         recvf(cp, S_IFLNK);
140                         continue;
141
142                 case 'k':  /* Transfer hard link. */
143                         hardlink(cp);
144                         continue;
145
146                 case 'E':  /* End. (of directory) */
147                         *tp = '\0';
148                         if (catname <= 0) {
149                                 error("server: too many 'E's\n");
150                                 continue;
151                         }
152                         tp = stp[--catname];
153                         *tp = '\0';
154                         ack();
155                         continue;
156
157                 case 'C':  /* Clean. Cleanup a directory */
158                         clean(cp);
159                         continue;
160
161                 case 'Q':  /* Query. Does the file/directory exist? */
162                         query(cp);
163                         continue;
164
165                 case 'S':  /* Special. Execute commands */
166                         dospecial(cp);
167                         continue;
168
169 #ifdef notdef
170                 /*
171                  * These entries are reserved but not currently used.
172                  * The intent is to allow remote hosts to have master copies.
173                  * Currently, only the host rdist runs on can have masters.
174                  */
175                 case 'X':  /* start a new list of files to exclude */
176                         except = bp = NULL;
177                 case 'x':  /* add name to list of files to exclude */
178                         if (*cp == '\0') {
179                                 ack();
180                                 continue;
181                         }
182                         if (*cp == '~') {
183                                 if (exptilde(buf, cp, sizeof(buf)) == NULL)
184                                         continue;
185                                 cp = buf;
186                         }
187                         if (bp == NULL)
188                                 except = bp = expand(makeblock(NAME, cp), E_VARS);
189                         else
190                                 bp->b_next = expand(makeblock(NAME, cp), E_VARS);
191                         while (bp->b_next != NULL)
192                                 bp = bp->b_next;
193                         ack();
194                         continue;
195
196                 case 'I':  /* Install. Transfer file if out of date. */
197                         opts = 0;
198                         while (*cp >= '0' && *cp <= '7')
199                                 opts = (opts << 3) | (*cp++ - '0');
200                         if (*cp++ != ' ') {
201                                 error("server: options not delimited\n");
202                                 return;
203                         }
204                         install(cp, opts);
205                         continue;
206
207                 case 'L':  /* Log. save message in log file */
208                         dolog(lfp, cp);
209                         continue;
210 #endif
211
212                 case '\1':
213                         nerrs++;
214                         continue;
215
216                 case '\2':
217                         return;
218
219                 default:
220                         error("server: unknown command '%s'\n", cp);
221                 case '\0':
222                         continue;
223                 }
224         }
225 }
226
227 /*
228  * Update the file(s) if they are different.
229  * destdir = 1 if destination should be a directory
230  * (i.e., more than one source is being copied to the same destination).
231  */
232 void
233 install(char *src, char *dest, int destdir, int opts)
234 {
235         char *rname;
236         char destcopy[BUFSIZ];
237
238         if (opts & WHOLE)
239                 source[0] = '\0';
240         else
241                 strcpy(source, src);
242
243         if (dest == NULL) {
244                 opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
245                 dest = src;
246         }
247
248         if (nflag || debug) {
249                 printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
250                         opts & WHOLE ? " -w" : "",
251                         opts & YOUNGER ? " -y" : "",
252                         opts & COMPARE ? " -b" : "",
253                         opts & REMOVE ? " -R" : "", src, dest);
254                 if (nflag)
255                         return;
256         }
257
258         rname = exptilde(target, src, sizeof(target));
259         if (rname == NULL)
260                 return;
261         tp = target;
262         while (*tp)
263                 tp++;
264         /*
265          * If we are renaming a directory and we want to preserve
266          * the directory heirarchy (-w), we must strip off the leading
267          * directory name and preserve the rest.
268          */
269         if (opts & WHOLE) {
270                 while (*rname == '/')
271                         rname++;
272                 destdir = 1;
273         } else {
274                 rname = rindex(target, '/');
275                 if (rname == NULL)
276                         rname = target;
277                 else
278                         rname++;
279         }
280         if (debug)
281                 printf("target = %s, rname = %s\n", target, rname);
282         /*
283          * Pass the destination file/directory name to remote.
284          */
285         snprintf(buf, sizeof(buf), "%c%s\n", destdir ? 'T' : 't', dest);
286         if (debug)
287                 printf("buf = %s", buf);
288         write(rem, buf, strlen(buf));
289         if (response() < 0)
290                 return;
291
292         /*                            
293          * Save the name of the remote target destination if we are     
294          * in WHOLE mode (destdir > 0) or if the source and destination 
295          * are not the same.  This info will be used later for maintaining
296          * hardlink info.
297          */
298         if (destdir || (src && dest && strcmp(src, dest))) {
299                 strcpy(destcopy, dest);
300                 Tdest = destcopy;
301         }
302         sendf(rname, opts);
303         Tdest = NULL;
304 }
305
306 static char *
307 remotename(char *pathname, char *src)
308 {
309         char *cp;
310         int len;
311
312         cp = pathname;
313         len = strlen(src);
314         if (0 == strncmp(pathname, src, len))
315                 cp += len;
316         if (*cp == '/')
317                 cp ++;
318         return(cp);
319 }
320
321 void
322 installlink(struct linkbuf *lp, char *rname, int opts)
323 {
324         if (*lp->target == 0)
325                 snprintf(buf, sizeof(buf), "k%o %s %s\n", opts, lp->pathname, rname);
326         else
327                 snprintf(buf, sizeof(buf), "k%o %s/%s %s\n", opts, lp->target,
328                         remotename(lp->pathname, lp->src), rname);
329
330         if (debug) {
331                 printf("lp->src      = %s\n", lp->src);
332                 printf("lp->target   = %s\n", lp->target);
333                 printf("lp->pathname = %s\n", lp->pathname);
334                 printf("rname        = %s\n", rname);
335                 printf("buf          = %s",   buf);
336         }
337         write(rem, buf, strlen(buf));
338         response();
339 }
340
341 /*
342  * Transfer the file or directory in target[].
343  * rname is the name of the file on the remote host.
344  */
345 static void
346 sendf(char *rname, int opts)
347 {
348         struct subcmd *sc;
349         struct stat stb;
350         int sizerr, f, u, len;
351         off_t i;
352         DIR *d;
353         struct dirent *dp;
354         char *otp, *cp;
355         extern struct subcmd *subcmds;
356         static char user[15], group[15];
357
358         if (debug)
359                 printf("sendf(%s, %x)\n", rname, opts);
360
361         if (except(target))
362                 return;
363         if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
364                 error("%s: %s\n", target, strerror(errno));
365                 return;
366         }
367         if ((u = update(rname, opts, &stb)) == 0) {
368                 if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
369                         savelink(&stb);
370                 return;
371         }
372
373         if (pw == NULL || pw->pw_uid != stb.st_uid)
374                 if ((pw = getpwuid(stb.st_uid)) == NULL) {
375                         dolog(lfp, "%s: no password entry for uid %d \n",
376                                 target, stb.st_uid);
377                         pw = NULL;
378                         snprintf(user, sizeof(user), ":%lu", stb.st_uid);
379                 }
380         if (gr == NULL || gr->gr_gid != stb.st_gid)
381                 if ((gr = getgrgid(stb.st_gid)) == NULL) {
382                         dolog(lfp, "%s: no name for group %d\n",
383                                 target, stb.st_gid);
384                         gr = NULL;
385                         snprintf(group, sizeof(group), ":%lu", stb.st_gid);
386                 }
387         if (u == 1) {
388                 if (opts & VERIFY) {
389                         dolog(lfp, "need to install: %s\n", target);
390                         goto dospecial;
391                 }
392                 dolog(lfp, "installing: %s\n", target);
393                 opts &= ~(COMPARE|REMOVE);
394         }
395
396         switch (stb.st_mode & S_IFMT) {
397         case S_IFDIR:
398                 if ((d = opendir(target)) == NULL) {
399                         error("%s: %s\n", target, strerror(errno));
400                         return;
401                 }
402                 snprintf(buf, sizeof(buf), "D%o %04o 0 0 %s %s %s\n",
403                     opts, stb.st_mode & 07777, protoname(), protogroup(),
404                     rname);
405                 if (debug)
406                         printf("buf = %s", buf);
407                 write(rem, buf, strlen(buf));
408                 if (response() < 0) {
409                         closedir(d);
410                         return;
411                 }
412
413                 if (opts & REMOVE)
414                         rmchk(opts);
415
416                 otp = tp;
417                 len = tp - target;
418                 while ((dp = readdir(d))) {
419                         if (!strcmp(dp->d_name, ".") ||
420                             !strcmp(dp->d_name, ".."))
421                                 continue;
422                         if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
423                                 error("%s/%s: Name too long\n", target,
424                                         dp->d_name);
425                                 continue;
426                         }
427                         tp = otp;
428                         *tp++ = '/';
429                         cp = dp->d_name;
430                         while ((*tp++ = *cp++))
431                                 ;
432                         tp--;
433                         sendf(dp->d_name, opts);
434                 }
435                 closedir(d);
436                 write(rem, "E\n", 2);
437                 response();
438                 tp = otp;
439                 *tp = '\0';
440                 return;
441
442         case S_IFLNK:
443                 if (u != 1)
444                         opts |= COMPARE;
445                 if (stb.st_nlink > 1) {
446                         struct linkbuf *lp;
447
448                         if ((lp = savelink(&stb)) != NULL) {
449                                 installlink(lp, rname, opts);
450                                 return;
451                         }
452                 }
453                 snprintf(buf, sizeof(buf),
454                         "K%o %o %qd %ld %s %s %s\n", opts,
455                         stb.st_mode & 07777, stb.st_size, stb.st_mtime,
456                         protoname(), protogroup(), rname);
457                 if (debug)
458                         printf("buf = %s", buf);
459                 write(rem, buf, strlen(buf));
460                 if (response() < 0)
461                         return;
462                 sizerr = (readlink(target, buf, BUFSIZ - 1) != stb.st_size);
463                 write(rem, buf, stb.st_size);
464                 if (debug)
465                         printf("readlink = %.*s\n", (int)stb.st_size, buf);
466                 goto done;
467
468         case S_IFREG:
469                 break;
470
471         default:
472                 error("%s: not a file or directory\n", target);
473                 return;
474         }
475
476         if (u == 2) {
477                 if (opts & VERIFY) {
478                         dolog(lfp, "need to update: %s\n", target);
479                         goto dospecial;
480                 }
481                 dolog(lfp, "updating: %s\n", target);
482         }
483
484         if (stb.st_nlink > 1) {
485                 struct linkbuf *lp;
486
487                 if ((lp = savelink(&stb)) != NULL) {
488                         installlink(lp, rname, opts);
489                         return;
490                 }
491         }
492
493         if ((f = open(target, O_RDONLY, 0)) < 0) {
494                 error("%s: %s\n", target, strerror(errno));
495                 return;
496         }
497         snprintf(buf, sizeof(buf), "R%o %o %qd %ld %s %s %s\n", opts,
498                 stb.st_mode & 07777, stb.st_size, stb.st_mtime,
499                 protoname(), protogroup(), rname);
500         if (debug)
501                 printf("buf = %s", buf);
502         write(rem, buf, strlen(buf));
503         if (response() < 0) {
504                 close(f);
505                 return;
506         }
507         sizerr = 0;
508         for (i = 0; i < stb.st_size; i += BUFSIZ) {
509                 int amt = BUFSIZ;
510                 if (i + amt > stb.st_size)
511                         amt = stb.st_size - i;
512                 if (sizerr == 0 && read(f, buf, amt) != amt)
513                         sizerr = 1;
514                 write(rem, buf, amt);
515         }
516         close(f);
517 done:
518         if (sizerr) {
519                 error("%s: file changed size\n", target);
520                 err();
521         } else
522                 ack();
523         f = response();
524         if (f < 0 || (f == 0 && (opts & COMPARE)))
525                 return;
526 dospecial:
527         for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
528                 if (sc->sc_type != SPECIAL)
529                         continue;
530                 if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
531                         continue;
532                 dolog(lfp, "special \"%s\"\n", sc->sc_name);
533                 if (opts & VERIFY)
534                         continue;
535                 snprintf(buf, sizeof(buf), "SFILE=%s;%s\n", target,
536                     sc->sc_name);
537                 if (debug)
538                         printf("buf = %s", buf);
539                 write(rem, buf, strlen(buf));
540                 while (response() > 0)
541                         ;
542         }
543 }
544
545 static struct linkbuf *
546 savelink(struct stat *stp)
547 {
548         struct linkbuf *lp;
549
550         for (lp = ihead; lp != NULL; lp = lp->nextp)
551                 if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
552                         lp->count--;
553                         return(lp);
554                 }
555         lp = (struct linkbuf *) malloc(sizeof(*lp));
556         if (lp == NULL)
557                 dolog(lfp, "out of memory, link information lost\n");
558         else {
559                 lp->nextp = ihead;
560                 ihead = lp;
561                 lp->inum = stp->st_ino;
562                 lp->devnum = stp->st_dev;
563                 lp->count = stp->st_nlink - 1;
564                 strcpy(lp->pathname, target);
565                 strcpy(lp->src, source);
566                 if (Tdest)
567                         strcpy(lp->target, Tdest);
568                 else
569                         *lp->target = 0;
570         }
571         return(NULL);
572 }
573
574 /*
575  * Check to see if file needs to be updated on the remote machine.
576  * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
577  * and 3 if comparing binaries to determine if out of date.
578  */
579 static int
580 update(char *rname, int opts, struct stat *stp)
581 {
582         char *cp, *s;
583         off_t size;
584         time_t mtime;
585
586         if (debug)
587                 printf("update(%s, %x, %p)\n", rname, opts, stp);
588
589         /*
590          * Check to see if the file exists on the remote machine.
591          */
592         snprintf(buf, sizeof(buf), "Q%s\n", rname);
593         if (debug)
594                 printf("buf = %s", buf);
595         write(rem, buf, strlen(buf));
596 again:
597         cp = s = buf;
598         do {
599                 if (read(rem, cp, 1) != 1)
600                         lostconn(0);
601         } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
602
603         switch (*s++) {
604         case 'Y':
605                 break;
606
607         case 'N':  /* file doesn't exist so install it */
608                 return(1);
609
610         case '\1':
611                 nerrs++;
612                 if (*s != '\n') {
613                         if (!iamremote) {
614                                 fflush(stdout);
615                                 write(2, s, cp - s);
616                         }
617                         if (lfp != NULL)
618                                 fwrite(s, 1, cp - s, lfp);
619                 }
620                 return(0);
621
622         case '\3':
623                 *--cp = '\0';
624                 if (lfp != NULL)
625                         dolog(lfp, "update: note: %s\n", s);
626                 goto again;
627
628         default:
629                 *--cp = '\0';
630                 error("update: unexpected response '%s'\n", s);
631                 return(0);
632         }
633
634         if (*s == '\n')
635                 return(2);
636
637         if (opts & COMPARE)
638                 return(3);
639
640         size = 0;
641         while (isdigit(*s))
642                 size = size * 10 + (*s++ - '0');
643         if (*s++ != ' ') {
644                 error("update: size not delimited\n");
645                 return(0);
646         }
647         mtime = 0;
648         while (isdigit(*s))
649                 mtime = mtime * 10 + (*s++ - '0');
650         if (*s != '\n') {
651                 error("update: mtime not delimited\n");
652                 return(0);
653         }
654         /*
655          * File needs to be updated?
656          */
657         if (opts & YOUNGER) {
658                 if (stp->st_mtime == mtime)
659                         return(0);
660                 if (stp->st_mtime < mtime) {
661                         dolog(lfp, "Warning: %s: remote copy is newer\n", target);
662                         return(0);
663                 }
664         } else if (stp->st_mtime == mtime && stp->st_size == size)
665                 return(0);
666         return(2);
667 }
668
669 /*
670  * Query. Check to see if file exists. Return one of the following:
671  *      N\n             - doesn't exist
672  *      Ysize mtime\n   - exists and its a regular file (size & mtime of file)
673  *      Y\n             - exists and its a directory or symbolic link
674  *      ^Aerror message\n
675  */
676 static void
677 query(char *name)
678 {
679         struct stat stb;
680
681         if (catname)
682                 snprintf(tp, sizeof(target) - (tp - target), "/%s",
683                     name);
684
685         if (lstat(target, &stb) < 0) {
686                 if (errno == ENOENT)
687                         write(rem, "N\n", 2);
688                 else
689                         error("%s:%s: %s\n", host, target, strerror(errno));
690                 *tp = '\0';
691                 return;
692         }
693
694         switch (stb.st_mode & S_IFMT) {
695         case S_IFREG:
696                 snprintf(buf, sizeof(buf), "Y%qd %ld\n", stb.st_size,
697                     stb.st_mtime);
698                 write(rem, buf, strlen(buf));
699                 break;
700
701         case S_IFLNK:
702         case S_IFDIR:
703                 write(rem, "Y\n", 2);
704                 break;
705
706         default:
707                 error("%s: not a file or directory\n", name);
708                 break;
709         }
710         *tp = '\0';
711 }
712
713 static void
714 recvf(char *cmd, int type)
715 {
716         char *cp;
717         int f, mode, opts, wrerr, olderrno;
718         off_t i, size;
719         time_t mtime;
720         struct stat stb;
721         struct timeval tvp[2];
722         char *owner, *group;
723         char new[BUFSIZ];
724         extern char *tempname;
725
726         cp = cmd;
727         opts = 0;
728         f = -1;
729         while (*cp >= '0' && *cp <= '7')
730                 opts = (opts << 3) | (*cp++ - '0');
731         if (*cp++ != ' ') {
732                 error("recvf: options not delimited\n");
733                 return;
734         }
735         mode = 0;
736         while (*cp >= '0' && *cp <= '7')
737                 mode = (mode << 3) | (*cp++ - '0');
738         if (*cp++ != ' ') {
739                 error("recvf: mode not delimited\n");
740                 return;
741         }
742         size = 0;
743         while (isdigit(*cp))
744                 size = size * 10 + (*cp++ - '0');
745         if (*cp++ != ' ') {
746                 error("recvf: size not delimited\n");
747                 return;
748         }
749         mtime = 0;
750         while (isdigit(*cp))
751                 mtime = mtime * 10 + (*cp++ - '0');
752         if (*cp++ != ' ') {
753                 error("recvf: mtime not delimited\n");
754                 return;
755         }
756         owner = cp;
757         while (*cp && *cp != ' ')
758                 cp++;
759         if (*cp != ' ') {
760                 error("recvf: owner name not delimited\n");
761                 return;
762         }
763         *cp++ = '\0';
764         group = cp;
765         while (*cp && *cp != ' ')
766                 cp++;
767         if (*cp != ' ') {
768                 error("recvf: group name not delimited\n");
769                 return;
770         }
771         *cp++ = '\0';
772
773         if (type == S_IFDIR) {
774                 if (catname >= sizeof(stp)) {
775                         error("%s:%s: too many directory levels\n",
776                                 host, target);
777                         return;
778                 }
779                 stp[catname] = tp;
780                 if (catname++) {
781                         *tp++ = '/';
782                         while ((*tp++ = *cp++))
783                                 ;
784                         tp--;
785                 }
786                 if (opts & VERIFY) {
787                         ack();
788                         return;
789                 }
790                 if (lstat(target, &stb) == 0) {
791                         if (ISDIR(stb.st_mode)) {
792                                 if ((stb.st_mode & 07777) == mode) {
793                                         ack();
794                                         return;
795                                 }
796                                 buf[0] = '\0';
797                                 snprintf(buf + 1, sizeof(buf) - 1,
798                                         "%s: Warning: remote mode %o != local mode %o\n",
799                                         target, stb.st_mode & 07777, mode);
800                                 write(rem, buf, strlen(buf + 1) + 1);
801                                 return;
802                         }
803                         errno = ENOTDIR;
804                 } else if ((errno == ENOENT && mkdir(target, mode) == 0) ||
805                     (chkparent(target) == 0 && mkdir(target, mode) == 0)) {
806                         if (fchog(-1, target, owner, group, mode) == 0)
807                                 ack();
808                         return;
809                 }
810                 error("%s:%s: %s\n", host, target, strerror(errno));
811                 tp = stp[--catname];
812                 *tp = '\0';
813                 return;
814         }
815
816         if (catname)
817                 snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
818         cp = rindex(target, '/');
819         if (cp == NULL)
820                 strcpy(new, tempname);
821         else if (cp == target)
822                 snprintf(new, sizeof(new), "/%s", tempname);
823         else {
824                 *cp = '\0';
825                 snprintf(new, sizeof(new), "%s/%s", target, tempname);
826                 *cp = '/';
827         }
828
829         if (type == S_IFLNK) {
830                 int j;
831
832                 ack();
833                 cp = buf;
834                 for (i = 0; i < size; i += j) {
835                         if ((j = read(rem, cp, size - i)) <= 0)
836                                 cleanup(0);
837                         cp += j;
838                 }
839                 *cp = '\0';
840                 if (response() < 0) {
841                         err();
842                         return;
843                 }
844                 if (symlink(buf, new) < 0) {
845                         if (errno != ENOENT || chkparent(new) < 0 ||
846                             symlink(buf, new) < 0)
847                                 goto badnew1;
848                 }
849                 mode &= 0777;
850                 if (opts & COMPARE) {
851                         char tbuf[BUFSIZ];
852
853                         if ((i = readlink(target, tbuf, BUFSIZ - 1)) >= 0 &&
854                             i == size && strncmp(buf, tbuf, size) == 0) {
855                                 unlink(new);
856                                 ack();
857                                 return;
858                         }
859                         if (opts & VERIFY)
860                                 goto differ;
861                 }
862                 goto fixup;
863         }
864
865         if ((f = creat(new, mode)) < 0) {
866                 if (errno != ENOENT || chkparent(new) < 0 ||
867                     (f = creat(new, mode)) < 0)
868                         goto badnew1;
869         }
870
871         ack();
872         wrerr = 0;
873         for (i = 0; i < size; i += BUFSIZ) {
874                 int amt;
875
876                 amt = BUFSIZ;
877                 cp = buf;
878                 if (i + amt > size)
879                         amt = size - i;
880                 do {
881                         int j = read(rem, cp, amt);
882
883                         if (j <= 0) {
884                                 close(f);
885                                 unlink(new);
886                                 cleanup(0);
887                         }
888                         amt -= j;
889                         cp += j;
890                 } while (amt > 0);
891                 amt = BUFSIZ;
892                 if (i + amt > size)
893                         amt = size - i;
894                 if (wrerr == 0 && write(f, buf, amt) != amt) {
895                         olderrno = errno;
896                         wrerr++;
897                 }
898         }
899         if (response() < 0) {
900                 err();
901                 goto badnew2;
902         }
903         if (wrerr)
904                 goto badnew1;
905         if (opts & COMPARE) {
906                 FILE *f1, *f2;
907                 int c;
908
909                 if ((f1 = fopen(target, "r")) == NULL)
910                         goto badtarget;
911                 if ((f2 = fopen(new, "r")) == NULL) {
912 badnew1:                error("%s:%s: %s\n", host, new, strerror(errno));
913                         goto badnew2;
914                 }
915                 while ((c = getc(f1)) == getc(f2))
916                         if (c == EOF) {
917                                 fclose(f1);
918                                 fclose(f2);
919                                 ack();
920                                 goto badnew2;
921                         }
922                 fclose(f1);
923                 fclose(f2);
924                 if (opts & VERIFY) {
925 differ:                 buf[0] = '\0';
926                         snprintf(buf + 1, sizeof(buf) - 1, 
927                             "need to update: %s\n",target);
928                         write(rem, buf, strlen(buf + 1) + 1);
929                         goto badnew2;
930                 }
931         }
932
933         /*
934          * Set last modified time
935          */
936         tvp[0].tv_sec = time(0);
937         tvp[0].tv_usec = 0;
938         tvp[1].tv_sec = mtime;
939         tvp[1].tv_usec = 0;
940         if (utimes(new, tvp) < 0)
941                 note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
942
943         if (fchog(f, new, owner, group, mode) < 0) {
944 badnew2:        if (f != -1)
945                         close(f);
946                 unlink(new);
947                 return;
948         }
949         close(f);
950
951 fixup:  if (rename(new, target) < 0) {
952 badtarget:      error("%s:%s: %s\n", host, target, strerror(errno));
953                 unlink(new);
954                 return;
955         }
956
957         if (opts & COMPARE) {
958                 buf[0] = '\0';
959                 snprintf(buf + 1, sizeof(buf) - 1,
960                     "updated %s\n", target);
961                 write(rem, buf, strlen(buf + 1) + 1);
962         } else
963                 ack();
964 }
965
966 /*
967  * Creat a hard link to existing file.
968  */
969 static void
970 hardlink(char *cmd)
971 {
972         char *cp;
973         struct stat stb;
974         char *oldname;
975         int opts, exists;
976
977         cp = cmd;
978         opts = 0;
979         exists = 0;
980         while (*cp >= '0' && *cp <= '7')
981                 opts = (opts << 3) | (*cp++ - '0');
982         if (*cp++ != ' ') {
983                 error("hardlink: options not delimited\n");
984                 return;
985         }
986         oldname = cp;
987         while (*cp && *cp != ' ')
988                 cp++;
989         if (*cp != ' ') {
990                 error("hardlink: oldname name not delimited\n");
991                 return;
992         }
993         *cp++ = '\0';
994
995         if (catname) {
996                 snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
997         }
998         if (lstat(target, &stb) == 0) {
999                 int mode = stb.st_mode & S_IFMT;
1000                 if (mode != S_IFREG && mode != S_IFLNK) {
1001                         error("%s:%s: not a regular file\n", host, target);
1002                         return;
1003                 }
1004                 exists = 1;
1005         }
1006         if (chkparent(target) < 0 ) {
1007                 error("%s:%s: %s (no parent)\n",
1008                         host, target, strerror(errno));
1009                 return;
1010         }
1011         if (exists && (unlink(target) < 0)) {
1012                 error("%s:%s: %s (unlink)\n",
1013                         host, target, strerror(errno));
1014                 return;
1015         }
1016         if (link(oldname, target) < 0) {
1017                 error("%s:can't link %s to %s\n",
1018                         host, target, oldname);
1019                 return;
1020         }
1021         ack();
1022 }
1023
1024 /*
1025  * Check to see if parent directory exists and create one if not.
1026  */
1027 static int
1028 chkparent(char *name)
1029 {
1030         char *cp;
1031         struct stat stb;
1032
1033         cp = rindex(name, '/');
1034         if (cp == NULL || cp == name)
1035                 return(0);
1036         *cp = '\0';
1037         if (lstat(name, &stb) < 0) {
1038                 if (errno == ENOENT && chkparent(name) >= 0 &&
1039                     mkdir(name, 0777 & ~oumask) >= 0) {
1040                         *cp = '/';
1041                         return(0);
1042                 }
1043         } else if (ISDIR(stb.st_mode)) {
1044                 *cp = '/';
1045                 return(0);
1046         }
1047         *cp = '/';
1048         return(-1);
1049 }
1050
1051 /*
1052  * Change owner, group and mode of file.
1053  */
1054 static int
1055 fchog(int fd, char *file, char *owner, char *group, int mode)
1056 {
1057         int i;
1058         int uid, gid;
1059         extern char user[];
1060         extern int userid;
1061
1062         uid = userid;
1063         if (userid == 0) {
1064                 if (*owner == ':') {
1065                         uid = atoi(owner + 1);
1066                 } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1067                         if ((pw = getpwnam(owner)) == NULL) {
1068                                 if (mode & 04000) {
1069                                         note("%s:%s: unknown login name, clearing setuid",
1070                                                 host, owner);
1071                                         mode &= ~04000;
1072                                         uid = 0;
1073                                 }
1074                         } else
1075                                 uid = pw->pw_uid;
1076                 } else
1077                         uid = pw->pw_uid;
1078                 if (*group == ':') {
1079                         gid = atoi(group + 1);
1080                         goto ok;
1081                 }
1082         } else if ((mode & 04000) && strcmp(user, owner) != 0)
1083                 mode &= ~04000;
1084         gid = -1;
1085         if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1086                 if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
1087                    || ((gr = getgrnam(group)) == NULL)) {
1088                         if (mode & 02000) {
1089                                 note("%s:%s: unknown group", host, group);
1090                                 mode &= ~02000;
1091                         }
1092                 } else
1093                         gid = gr->gr_gid;
1094         } else
1095                 gid = gr->gr_gid;
1096         if (userid && gid >= 0) {
1097                 if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
1098                         if (!(strcmp(user, gr->gr_mem[i])))
1099                                 goto ok;
1100                 mode &= ~02000;
1101                 gid = -1;
1102         }
1103 ok:     if ((fd != -1 && fchown(fd, uid, gid) < 0) || chown(file, uid, gid) < 0)
1104                 note("%s: %s chown: %s", host, file, strerror(errno));
1105         else if (mode & 07000 &&
1106            ((fd != -1 && fchmod(fd, mode) < 0) || chmod(file, mode) < 0))
1107                 note("%s: %s chmod: %s", host, file, strerror(errno));
1108         return(0);
1109 }
1110
1111 /*
1112  * Check for files on the machine being updated that are not on the master
1113  * machine and remove them.
1114  */
1115 static void
1116 rmchk(int opts)
1117 {
1118         char *cp, *s;
1119         struct stat stb;
1120
1121         if (debug)
1122                 printf("rmchk()\n");
1123
1124         /*
1125          * Tell the remote to clean the files from the last directory sent.
1126          */
1127         snprintf(buf, sizeof(buf), "C%o\n", opts & VERIFY);
1128         if (debug)
1129                 printf("buf = %s", buf);
1130         write(rem, buf, strlen(buf));
1131         if (response() < 0)
1132                 return;
1133         for (;;) {
1134                 cp = s = buf;
1135                 do {
1136                         if (read(rem, cp, 1) != 1)
1137                                 lostconn(0);
1138                 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1139
1140                 switch (*s++) {
1141                 case 'Q': /* Query if file should be removed */
1142                         /*
1143                          * Return the following codes to remove query.
1144                          * N\n -- file exists - DON'T remove.
1145                          * Y\n -- file doesn't exist - REMOVE.
1146                          */
1147                         *--cp = '\0';
1148                         snprintf(tp, sizeof(target) - (tp - target), "/%s", s);
1149                         if (debug)
1150                                 printf("check %s\n", target);
1151                         if (except(target))
1152                                 write(rem, "N\n", 2);
1153                         else if (lstat(target, &stb) < 0)
1154                                 write(rem, "Y\n", 2);
1155                         else
1156                                 write(rem, "N\n", 2);
1157                         break;
1158
1159                 case '\0':
1160                         *--cp = '\0';
1161                         if (*s != '\0')
1162                                 dolog(lfp, "%s\n", s);
1163                         break;
1164
1165                 case 'E':
1166                         *tp = '\0';
1167                         ack();
1168                         return;
1169
1170                 case '\1':
1171                 case '\2':
1172                         nerrs++;
1173                         if (*s != '\n') {
1174                                 if (!iamremote) {
1175                                         fflush(stdout);
1176                                         write(2, s, cp - s);
1177                                 }
1178                                 if (lfp != NULL)
1179                                         fwrite(s, 1, cp - s, lfp);
1180                         }
1181                         if (buf[0] == '\2')
1182                                 lostconn(0);
1183                         break;
1184
1185                 default:
1186                         error("rmchk: unexpected response '%s'\n", buf);
1187                         err();
1188                 }
1189         }
1190 }
1191
1192 /*
1193  * Check the current directory (initialized by the 'T' command to server())
1194  * for extraneous files and remove them.
1195  */
1196 static void
1197 clean(char *cp)
1198 {
1199         DIR *d;
1200         struct dirent *dp;
1201         struct stat stb;
1202         char *otp;
1203         int len, opts;
1204
1205         opts = 0;
1206         while (*cp >= '0' && *cp <= '7')
1207                 opts = (opts << 3) | (*cp++ - '0');
1208         if (*cp != '\0') {
1209                 error("clean: options not delimited\n");
1210                 return;
1211         }
1212         if ((d = opendir(target)) == NULL) {
1213                 error("%s:%s: %s\n", host, target, strerror(errno));
1214                 return;
1215         }
1216         ack();
1217
1218         otp = tp;
1219         len = tp - target;
1220         while ((dp = readdir(d))) {
1221                 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1222                         continue;
1223                 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1224                         error("%s:%s/%s: Name too long\n",
1225                                 host, target, dp->d_name);
1226                         continue;
1227                 }
1228                 tp = otp;
1229                 *tp++ = '/';
1230                 cp = dp->d_name;
1231                 while ((*tp++ = *cp++))
1232                         ;
1233                 tp--;
1234                 if (lstat(target, &stb) < 0) {
1235                         error("%s:%s: %s\n", host, target, strerror(errno));
1236                         continue;
1237                 }
1238                 snprintf(buf, sizeof(buf), "Q%s\n", dp->d_name);
1239                 write(rem, buf, strlen(buf));
1240                 cp = buf;
1241                 do {
1242                         if (read(rem, cp, 1) != 1)
1243                                 cleanup(0);
1244                 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1245                 *--cp = '\0';
1246                 cp = buf;
1247                 if (*cp != 'Y')
1248                         continue;
1249                 if (opts & VERIFY) {
1250                         cp = buf;
1251                         *cp++ = '\0';
1252                         snprintf(cp, sizeof(buf) - 1, "need to remove: %s\n", target);
1253                         write(rem, buf, strlen(cp) + 1);
1254                 } else
1255                         removeit(&stb);
1256         }
1257         closedir(d);
1258         write(rem, "E\n", 2);
1259         response();
1260         tp = otp;
1261         *tp = '\0';
1262 }
1263
1264 /*
1265  * Remove a file or directory (recursively) and send back an acknowledge
1266  * or an error message.
1267  */
1268 static void
1269 removeit(struct stat *stp)
1270 {
1271         DIR *d;
1272         struct dirent *dp;
1273         char *cp;
1274         struct stat stb;
1275         char *otp;
1276         int len;
1277
1278         switch (stp->st_mode & S_IFMT) {
1279         case S_IFREG:
1280         case S_IFLNK:
1281                 if (unlink(target) < 0)
1282                         goto bad;
1283                 goto removed;
1284
1285         case S_IFDIR:
1286                 break;
1287
1288         default:
1289                 error("%s:%s: not a plain file\n", host, target);
1290                 return;
1291         }
1292
1293         if ((d = opendir(target)) == NULL)
1294                 goto bad;
1295
1296         otp = tp;
1297         len = tp - target;
1298         while ((dp = readdir(d))) {
1299                 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1300                         continue;
1301                 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
1302                         error("%s:%s/%s: Name too long\n",
1303                                 host, target, dp->d_name);
1304                         continue;
1305                 }
1306                 tp = otp;
1307                 *tp++ = '/';
1308                 cp = dp->d_name;
1309                 while ((*tp++ = *cp++))
1310                         ;
1311                 tp--;
1312                 if (lstat(target, &stb) < 0) {
1313                         error("%s:%s: %s\n", host, target, strerror(errno));
1314                         continue;
1315                 }
1316                 removeit(&stb);
1317         }
1318         closedir(d);
1319         tp = otp;
1320         *tp = '\0';
1321         if (rmdir(target) < 0) {
1322 bad:
1323                 error("%s:%s: %s\n", host, target, strerror(errno));
1324                 return;
1325         }
1326 removed:
1327         cp = buf;
1328         *cp++ = '\0';
1329         snprintf(cp, sizeof(buf) - 1, "removed %s\n", target);
1330         write(rem, buf, strlen(cp) + 1);
1331 }
1332
1333 /*
1334  * Execute a shell command to handle special cases.
1335  */
1336 static void
1337 dospecial(char *cmd)
1338 {
1339         int fd[2], status, pid, i;
1340         char *cp, *s;
1341         char sbuf[BUFSIZ];
1342         extern int userid, groupid;
1343
1344         if (pipe(fd) < 0) {
1345                 error("%s\n", strerror(errno));
1346                 return;
1347         }
1348         if ((pid = fork()) == 0) {
1349                 /*
1350                  * Return everything the shell commands print.
1351                  */
1352                 close(0);
1353                 close(1);
1354                 close(2);
1355                 open(_PATH_DEVNULL, O_RDONLY);
1356                 dup(fd[1]);
1357                 dup(fd[1]);
1358                 close(fd[0]);
1359                 close(fd[1]);
1360                 setgid(groupid);
1361                 setuid(userid);
1362                 execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
1363                 _exit(127);
1364         }
1365         close(fd[1]);
1366         s = sbuf;
1367         *s++ = '\0';
1368         while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
1369                 cp = buf;
1370                 do {
1371                         *s++ = *cp++;
1372                         if (cp[-1] != '\n') {
1373                                 if (s < &sbuf[sizeof(sbuf)-1])
1374                                         continue;
1375                                 *s++ = '\n';
1376                         }
1377                         /*
1378                          * Throw away blank lines.
1379                          */
1380                         if (s == &sbuf[2]) {
1381                                 s--;
1382                                 continue;
1383                         }
1384                         write(rem, sbuf, s - sbuf);
1385                         s = &sbuf[1];
1386                 } while (--i);
1387         }
1388         if (s > &sbuf[1]) {
1389                 *s++ = '\n';
1390                 write(rem, sbuf, s - sbuf);
1391         }
1392         while ((i = wait(&status)) != pid && i != -1)
1393                 ;
1394         if (i == -1)
1395                 status = -1;
1396         close(fd[0]);
1397         if (status)
1398                 error("shell returned %d\n", status);
1399         else
1400                 ack();
1401 }
1402
1403 void
1404 dolog(FILE *fp, const char *fmt, ...)
1405 {
1406         va_list ap;
1407
1408         va_start(ap, fmt);
1409         /* Print changes locally if not quiet mode */
1410         if (!qflag)
1411                 vprintf(fmt, ap);
1412
1413         /* Save changes (for mailing) if really updating files */
1414         if (!(options & VERIFY) && fp != NULL)
1415                 vfprintf(fp, fmt, ap);
1416         va_end(ap);
1417 }
1418
1419 void
1420 error(const char *fmt, ...)
1421 {
1422         static FILE *fp;
1423         va_list ap;
1424
1425         va_start(ap, fmt);
1426
1427         ++nerrs;
1428         if (!fp && !(fp = fdopen(rem, "w")))
1429                 return;
1430         if (iamremote) {
1431                 fprintf(fp, "%crdist: ", 0x01);
1432                 vfprintf(fp, fmt, ap);
1433                 fflush(fp);
1434         }
1435         else {
1436                 fflush(stdout);
1437                 fprintf(stderr, "rdist: ");
1438                 vfprintf(stderr, fmt, ap);
1439                 fflush(stderr);
1440         }
1441         if (lfp != NULL) {
1442                 fprintf(lfp, "rdist: ");
1443                 vfprintf(lfp, fmt, ap);
1444                 fflush(lfp);
1445         }
1446         va_end(ap);
1447 }
1448
1449 void
1450 fatal(const char *fmt, ...)
1451 {
1452         static FILE *fp;
1453         va_list ap;
1454
1455         va_start(ap, fmt);
1456
1457         ++nerrs;
1458         if (!fp && !(fp = fdopen(rem, "w")))
1459                 return;
1460         if (iamremote) {
1461                 fprintf(fp, "%crdist: ", 0x02);
1462                 vfprintf(fp, fmt, ap);
1463                 fflush(fp);
1464         }
1465         else {
1466                 fflush(stdout);
1467                 fprintf(stderr, "rdist: ");
1468                 vfprintf(stderr, fmt, ap);
1469                 fflush(stderr);
1470         }
1471         if (lfp != NULL) {
1472                 fprintf(lfp, "rdist: ");
1473                 vfprintf(lfp, fmt, ap);
1474                 fflush(lfp);
1475         }
1476         va_end(ap);
1477         cleanup(0);
1478 }
1479
1480 static int
1481 response(void)
1482 {
1483         char *cp, *s;
1484         char resp[BUFSIZ];
1485
1486         if (debug)
1487                 printf("response()\n");
1488
1489         cp = s = resp;
1490         do {
1491                 if (read(rem, cp, 1) != 1)
1492                         lostconn(0);
1493         } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1494
1495         switch (*s++) {
1496         case '\0':
1497                 *--cp = '\0';
1498                 if (*s != '\0') {
1499                         dolog(lfp, "%s\n", s);
1500                         return(1);
1501                 }
1502                 return(0);
1503         case '\3':
1504                 *--cp = '\0';
1505                 dolog(lfp, "Note: %s\n",s);
1506                 return(response());
1507
1508         default:
1509                 s--;
1510                 /* fall into... */
1511         case '\1':
1512         case '\2':
1513                 nerrs++;
1514                 if (*s != '\n') {
1515                         if (!iamremote) {
1516                                 fflush(stdout);
1517                                 write(2, s, cp - s);
1518                         }
1519                         if (lfp != NULL)
1520                                 fwrite(s, 1, cp - s, lfp);
1521                 }
1522                 if (resp[0] == '\2')
1523                         lostconn(0);
1524                 return(-1);
1525         }
1526 }
1527
1528 /*
1529  * Remove temporary files and do any cleanup operations before exiting.
1530  */
1531 void
1532 cleanup(int signo)
1533 {
1534         unlink(tempfile);
1535         exit(1);
1536 }
1537
1538 static void
1539 note(const char *fmt, ...)
1540 {
1541         static char buf[BUFSIZ];
1542         va_list ap;
1543
1544         va_start(ap, fmt);
1545         vsnprintf(buf, sizeof(buf), fmt, ap);
1546         va_end(ap);
1547         comment(buf);
1548 }
1549
1550 static void
1551 comment(char *s)
1552 {
1553         char c;
1554
1555         c = '\3';
1556         write(rem, &c, 1);
1557         write(rem, s, strlen(s));
1558         c = '\n';
1559         write(rem, &c, 1);
1560 }