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