1babdf84bSdist /* 2babdf84bSdist * Copyright (c) 1983 Regents of the University of California. 37a864411Sbostic * All rights reserved. 47a864411Sbostic * 54ec22e22Sbostic * %sccs.include.redist.c% 6babdf84bSdist */ 7babdf84bSdist 8978e44c9Sralph #ifndef lint 9*bdfadfffSbostic static char sccsid[] = "@(#)server.c 5.17 (Berkeley) 07/06/92"; 107a864411Sbostic #endif /* not lint */ 11978e44c9Sralph 12*bdfadfffSbostic #include <sys/wait.h> 13978e44c9Sralph #include "defs.h" 14978e44c9Sralph 156d401060Sralph #define ack() (void) write(rem, "\0\n", 2) 166d401060Sralph #define err() (void) write(rem, "\1\n", 2) 17978e44c9Sralph 182cb25675Sralph struct linkbuf *ihead; /* list of files with more than one link */ 19d49851feSralph char buf[BUFSIZ]; /* general purpose buffer */ 20978e44c9Sralph char target[BUFSIZ]; /* target/source directory name */ 21978e44c9Sralph char *tp; /* pointer to end of target name */ 2226ce117eSsklower char *Tdest; /* pointer to last T dest*/ 23978e44c9Sralph int catname; /* cat name to target name */ 2465a0d230Sralph char *stp[32]; /* stack of saved tp's for directories */ 2501de96ecSralph int oumask; /* old umask for creating files */ 26d49851feSralph 27d49851feSralph extern FILE *lfp; /* log file for mailing changes */ 28d49851feSralph 29*bdfadfffSbostic static int chkparent __P((char *)); 30*bdfadfffSbostic static void clean __P((char *)); 31*bdfadfffSbostic static void comment __P((char *)); 32*bdfadfffSbostic static void dospecial __P((char *)); 33*bdfadfffSbostic static int fchog __P((int, char *, char *, char *, int)); 34*bdfadfffSbostic static void hardlink __P((char *)); 35*bdfadfffSbostic static void note __P((const char *, ...)); 36*bdfadfffSbostic static void query __P((char *)); 37*bdfadfffSbostic static void recvf __P((char *, int)); 38*bdfadfffSbostic static void removeit __P((struct stat *)); 39*bdfadfffSbostic static int response __P((void)); 40*bdfadfffSbostic static void rmchk __P((int)); 41*bdfadfffSbostic static struct linkbuf * 42*bdfadfffSbostic savelink __P((struct stat *)); 43*bdfadfffSbostic static void sendf __P((char *, int)); 44*bdfadfffSbostic static int update __P((char *, int, struct stat *)); 45aca7acf4Sralph 46978e44c9Sralph /* 47978e44c9Sralph * Server routine to read requests and process them. 48978e44c9Sralph * Commands are: 49978e44c9Sralph * Tname - Transmit file if out of date 50978e44c9Sralph * Vname - Verify if file out of date or not 51978e44c9Sralph * Qname - Query if file exists. Return mtime & size if it does. 52978e44c9Sralph */ 53*bdfadfffSbostic void 54978e44c9Sralph server() 55978e44c9Sralph { 56978e44c9Sralph char cmdbuf[BUFSIZ]; 57978e44c9Sralph register char *cp; 58978e44c9Sralph 59aca7acf4Sralph signal(SIGHUP, cleanup); 60aca7acf4Sralph signal(SIGINT, cleanup); 61aca7acf4Sralph signal(SIGQUIT, cleanup); 62aca7acf4Sralph signal(SIGTERM, cleanup); 63aca7acf4Sralph signal(SIGPIPE, cleanup); 64aca7acf4Sralph 65aca7acf4Sralph rem = 0; 6601de96ecSralph oumask = umask(0); 6736a9f5b1Sralph (void) sprintf(buf, "V%d\n", VERSION); 6836a9f5b1Sralph (void) write(rem, buf, strlen(buf)); 69978e44c9Sralph 70978e44c9Sralph for (;;) { 71978e44c9Sralph cp = cmdbuf; 72978e44c9Sralph if (read(rem, cp, 1) <= 0) 73978e44c9Sralph return; 74978e44c9Sralph if (*cp++ == '\n') { 7501de96ecSralph error("server: expected control record\n"); 76978e44c9Sralph continue; 77978e44c9Sralph } 78978e44c9Sralph do { 79978e44c9Sralph if (read(rem, cp, 1) != 1) 80*bdfadfffSbostic cleanup(0); 81d49851feSralph } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]); 82978e44c9Sralph *--cp = '\0'; 83978e44c9Sralph cp = cmdbuf; 84978e44c9Sralph switch (*cp++) { 85978e44c9Sralph case 'T': /* init target file/directory name */ 86d49851feSralph catname = 1; /* target should be directory */ 87d49851feSralph goto dotarget; 88d49851feSralph 89d49851feSralph case 't': /* init target file/directory name */ 90978e44c9Sralph catname = 0; 91d49851feSralph dotarget: 9201de96ecSralph if (exptilde(target, cp) == NULL) 9301de96ecSralph continue; 94978e44c9Sralph tp = target; 95978e44c9Sralph while (*tp) 96978e44c9Sralph tp++; 976d401060Sralph ack(); 98978e44c9Sralph continue; 99978e44c9Sralph 1002cb25675Sralph case 'R': /* Transfer a regular file. */ 1012cb25675Sralph recvf(cp, S_IFREG); 102978e44c9Sralph continue; 103978e44c9Sralph 1042cb25675Sralph case 'D': /* Transfer a directory. */ 1052cb25675Sralph recvf(cp, S_IFDIR); 1062cb25675Sralph continue; 1072cb25675Sralph 1082cb25675Sralph case 'K': /* Transfer symbolic link. */ 1092cb25675Sralph recvf(cp, S_IFLNK); 1102cb25675Sralph continue; 1112cb25675Sralph 1122cb25675Sralph case 'k': /* Transfer hard link. */ 1132cb25675Sralph hardlink(cp); 114978e44c9Sralph continue; 115978e44c9Sralph 116978e44c9Sralph case 'E': /* End. (of directory) */ 117978e44c9Sralph *tp = '\0'; 11801de96ecSralph if (catname <= 0) { 11901de96ecSralph error("server: too many 'E's\n"); 120978e44c9Sralph continue; 121978e44c9Sralph } 12201de96ecSralph tp = stp[--catname]; 12365a0d230Sralph *tp = '\0'; 1246d401060Sralph ack(); 125978e44c9Sralph continue; 126978e44c9Sralph 127c27256c0Sralph case 'C': /* Clean. Cleanup a directory */ 1286d401060Sralph clean(cp); 129c27256c0Sralph continue; 130c27256c0Sralph 13101de96ecSralph case 'Q': /* Query. Does the file/directory exist? */ 13201de96ecSralph query(cp); 13365a0d230Sralph continue; 13465a0d230Sralph 1356d401060Sralph case 'S': /* Special. Execute commands */ 1366d401060Sralph dospecial(cp); 1376d401060Sralph continue; 1386d401060Sralph 13901de96ecSralph #ifdef notdef 14001de96ecSralph /* 14101de96ecSralph * These entries are reserved but not currently used. 14201de96ecSralph * The intent is to allow remote hosts to have master copies. 14301de96ecSralph * Currently, only the host rdist runs on can have masters. 14401de96ecSralph */ 14501de96ecSralph case 'X': /* start a new list of files to exclude */ 14601de96ecSralph except = bp = NULL; 14701de96ecSralph case 'x': /* add name to list of files to exclude */ 14801de96ecSralph if (*cp == '\0') { 1496d401060Sralph ack(); 15001de96ecSralph continue; 15101de96ecSralph } 15201de96ecSralph if (*cp == '~') { 15301de96ecSralph if (exptilde(buf, cp) == NULL) 15401de96ecSralph continue; 15501de96ecSralph cp = buf; 15601de96ecSralph } 15701de96ecSralph if (bp == NULL) 1586d401060Sralph except = bp = expand(makeblock(NAME, cp), E_VARS); 15901de96ecSralph else 1606d401060Sralph bp->b_next = expand(makeblock(NAME, cp), E_VARS); 16101de96ecSralph while (bp->b_next != NULL) 16201de96ecSralph bp = bp->b_next; 1636d401060Sralph ack(); 16401de96ecSralph continue; 16501de96ecSralph 1666d401060Sralph case 'I': /* Install. Transfer file if out of date. */ 16701de96ecSralph opts = 0; 16801de96ecSralph while (*cp >= '0' && *cp <= '7') 16901de96ecSralph opts = (opts << 3) | (*cp++ - '0'); 17001de96ecSralph if (*cp++ != ' ') { 17101de96ecSralph error("server: options not delimited\n"); 17201de96ecSralph return; 17301de96ecSralph } 1746d401060Sralph install(cp, opts); 175978e44c9Sralph continue; 176978e44c9Sralph 177978e44c9Sralph case 'L': /* Log. save message in log file */ 17865a0d230Sralph log(lfp, cp); 179978e44c9Sralph continue; 18001de96ecSralph #endif 181978e44c9Sralph 182d49851feSralph case '\1': 183aca7acf4Sralph nerrs++; 184d49851feSralph continue; 185d49851feSralph 186d49851feSralph case '\2': 187d49851feSralph return; 188d49851feSralph 189978e44c9Sralph default: 19001de96ecSralph error("server: unknown command '%s'\n", cp); 191978e44c9Sralph case '\0': 192978e44c9Sralph continue; 193978e44c9Sralph } 194978e44c9Sralph } 195978e44c9Sralph } 196978e44c9Sralph 197978e44c9Sralph /* 19801de96ecSralph * Update the file(s) if they are different. 19901de96ecSralph * destdir = 1 if destination should be a directory 20001de96ecSralph * (i.e., more than one source is being copied to the same destination). 201978e44c9Sralph */ 202*bdfadfffSbostic void 20301de96ecSralph install(src, dest, destdir, opts) 20401de96ecSralph char *src, *dest; 20501de96ecSralph int destdir, opts; 20601de96ecSralph { 20701de96ecSralph char *rname; 20826ce117eSsklower char destcopy[BUFSIZ]; 20901de96ecSralph 21001de96ecSralph if (dest == NULL) { 21101de96ecSralph opts &= ~WHOLE; /* WHOLE mode only useful if renaming */ 21201de96ecSralph dest = src; 21301de96ecSralph } 21401de96ecSralph 21501de96ecSralph if (nflag || debug) { 21601de96ecSralph printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install", 21701de96ecSralph opts & WHOLE ? " -w" : "", 21801de96ecSralph opts & YOUNGER ? " -y" : "", 21901de96ecSralph opts & COMPARE ? " -b" : "", 2206d401060Sralph opts & REMOVE ? " -R" : "", src, dest); 22101de96ecSralph if (nflag) 22201de96ecSralph return; 22301de96ecSralph } 22401de96ecSralph 22501de96ecSralph rname = exptilde(target, src); 22601de96ecSralph if (rname == NULL) 22701de96ecSralph return; 22801de96ecSralph tp = target; 22901de96ecSralph while (*tp) 23001de96ecSralph tp++; 23101de96ecSralph /* 23201de96ecSralph * If we are renaming a directory and we want to preserve 2336d401060Sralph * the directory heirarchy (-w), we must strip off the leading 23401de96ecSralph * directory name and preserve the rest. 23501de96ecSralph */ 23601de96ecSralph if (opts & WHOLE) { 2376d401060Sralph while (*rname == '/') 23801de96ecSralph rname++; 2396d401060Sralph destdir = 1; 24001de96ecSralph } else { 24101de96ecSralph rname = rindex(target, '/'); 24201de96ecSralph if (rname == NULL) 24301de96ecSralph rname = target; 24401de96ecSralph else 24501de96ecSralph rname++; 24601de96ecSralph } 24701de96ecSralph if (debug) 24801de96ecSralph printf("target = %s, rname = %s\n", target, rname); 24901de96ecSralph /* 25001de96ecSralph * Pass the destination file/directory name to remote. 25101de96ecSralph */ 25201de96ecSralph (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest); 25301de96ecSralph if (debug) 25401de96ecSralph printf("buf = %s", buf); 25501de96ecSralph (void) write(rem, buf, strlen(buf)); 25601de96ecSralph if (response() < 0) 25701de96ecSralph return; 25801de96ecSralph 25926ce117eSsklower if (destdir) { 26026ce117eSsklower strcpy(destcopy, dest); 26126ce117eSsklower Tdest = destcopy; 26226ce117eSsklower } 26301de96ecSralph sendf(rname, opts); 26426ce117eSsklower Tdest = 0; 26501de96ecSralph } 26601de96ecSralph 26726ce117eSsklower #define protoname() (pw ? pw->pw_name : user) 26826ce117eSsklower #define protogroup() (gr ? gr->gr_name : group) 26901de96ecSralph /* 27001de96ecSralph * Transfer the file or directory in target[]. 27101de96ecSralph * rname is the name of the file on the remote host. 27201de96ecSralph */ 273*bdfadfffSbostic static void 27401de96ecSralph sendf(rname, opts) 27501de96ecSralph char *rname; 276ad3d87d9Sralph int opts; 277978e44c9Sralph { 278aca7acf4Sralph register struct subcmd *sc; 279978e44c9Sralph struct stat stb; 2802cb25675Sralph int sizerr, f, u, len; 281978e44c9Sralph off_t i; 2822cb25675Sralph DIR *d; 2832cb25675Sralph struct direct *dp; 2842cb25675Sralph char *otp, *cp; 28558fc31f7Sralph extern struct subcmd *subcmds; 28626ce117eSsklower static char user[15], group[15]; 287978e44c9Sralph 288978e44c9Sralph if (debug) 28901de96ecSralph printf("sendf(%s, %x)\n", rname, opts); 290978e44c9Sralph 29158fc31f7Sralph if (except(target)) 29265a0d230Sralph return; 293e980046fSralph if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) { 29490bde559Sbostic error("%s: %s\n", target, strerror(errno)); 295978e44c9Sralph return; 296978e44c9Sralph } 2972cb25675Sralph if ((u = update(rname, opts, &stb)) == 0) { 2982cb25675Sralph if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1) 2992cb25675Sralph (void) savelink(&stb); 300978e44c9Sralph return; 3012cb25675Sralph } 302978e44c9Sralph 30301de96ecSralph if (pw == NULL || pw->pw_uid != stb.st_uid) 30401de96ecSralph if ((pw = getpwuid(stb.st_uid)) == NULL) { 3056dfcab80Ssklower log(lfp, "%s: no password entry for uid %d \n", 3066dfcab80Ssklower target, stb.st_uid); 30726ce117eSsklower pw = NULL; 308*bdfadfffSbostic (void)sprintf(user, ":%lu", stb.st_uid); 309978e44c9Sralph } 31001de96ecSralph if (gr == NULL || gr->gr_gid != stb.st_gid) 31101de96ecSralph if ((gr = getgrgid(stb.st_gid)) == NULL) { 3126dfcab80Ssklower log(lfp, "%s: no name for group %d\n", 3136dfcab80Ssklower target, stb.st_gid); 31426ce117eSsklower gr = NULL; 315*bdfadfffSbostic (void)sprintf(group, ":%lu", stb.st_gid); 316978e44c9Sralph } 317156664fbSralph if (u == 1) { 3186d401060Sralph if (opts & VERIFY) { 3196d401060Sralph log(lfp, "need to install: %s\n", target); 3206d401060Sralph goto dospecial; 3216d401060Sralph } 32201de96ecSralph log(lfp, "installing: %s\n", target); 3236d401060Sralph opts &= ~(COMPARE|REMOVE); 324156664fbSralph } 325978e44c9Sralph 326978e44c9Sralph switch (stb.st_mode & S_IFMT) { 3272cb25675Sralph case S_IFDIR: 3282cb25675Sralph if ((d = opendir(target)) == NULL) { 32990bde559Sbostic error("%s: %s\n", target, strerror(errno)); 3302cb25675Sralph return; 3312cb25675Sralph } 3322cb25675Sralph (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts, 33326ce117eSsklower stb.st_mode & 07777, protoname(), protogroup(), rname); 3342cb25675Sralph if (debug) 33515867ad7Sralph printf("buf = %s", buf); 3362cb25675Sralph (void) write(rem, buf, strlen(buf)); 3372cb25675Sralph if (response() < 0) { 3382cb25675Sralph closedir(d); 3392cb25675Sralph return; 3402cb25675Sralph } 3412cb25675Sralph 3422cb25675Sralph if (opts & REMOVE) 3432cb25675Sralph rmchk(opts); 3442cb25675Sralph 3452cb25675Sralph otp = tp; 3462cb25675Sralph len = tp - target; 3472cb25675Sralph while (dp = readdir(d)) { 3482cb25675Sralph if (!strcmp(dp->d_name, ".") || 3492cb25675Sralph !strcmp(dp->d_name, "..")) 3502cb25675Sralph continue; 3512cb25675Sralph if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 3522cb25675Sralph error("%s/%s: Name too long\n", target, 3532cb25675Sralph dp->d_name); 3542cb25675Sralph continue; 3552cb25675Sralph } 3562cb25675Sralph tp = otp; 3572cb25675Sralph *tp++ = '/'; 3582cb25675Sralph cp = dp->d_name; 3592cb25675Sralph while (*tp++ = *cp++) 3602cb25675Sralph ; 3612cb25675Sralph tp--; 3622cb25675Sralph sendf(dp->d_name, opts); 3632cb25675Sralph } 3642cb25675Sralph closedir(d); 3652cb25675Sralph (void) write(rem, "E\n", 2); 3662cb25675Sralph (void) response(); 3672cb25675Sralph tp = otp; 3682cb25675Sralph *tp = '\0'; 3692cb25675Sralph return; 3702cb25675Sralph 3712cb25675Sralph case S_IFLNK: 3722cb25675Sralph if (u != 1) 3732cb25675Sralph opts |= COMPARE; 37426ce117eSsklower if (stb.st_nlink > 1) { 37526ce117eSsklower struct linkbuf *lp; 37626ce117eSsklower 37726ce117eSsklower if ((lp = savelink(&stb)) != NULL) { 37826ce117eSsklower /* install link */ 37926ce117eSsklower if (*lp->target == 0) 38026ce117eSsklower (void) sprintf(buf, "k%o %s %s\n", opts, 38126ce117eSsklower lp->pathname, rname); 38226ce117eSsklower else 38326ce117eSsklower (void) sprintf(buf, "k%o %s/%s %s\n", opts, 38426ce117eSsklower lp->target, lp->pathname, rname); 38526ce117eSsklower if (debug) 38626ce117eSsklower printf("buf = %s", buf); 38726ce117eSsklower (void) write(rem, buf, strlen(buf)); 38826ce117eSsklower (void) response(); 38926ce117eSsklower return; 39026ce117eSsklower } 39126ce117eSsklower } 392*bdfadfffSbostic (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts, 3932cb25675Sralph stb.st_mode & 07777, stb.st_size, stb.st_mtime, 39426ce117eSsklower protoname(), protogroup(), rname); 3952cb25675Sralph if (debug) 3962cb25675Sralph printf("buf = %s", buf); 3972cb25675Sralph (void) write(rem, buf, strlen(buf)); 3982cb25675Sralph if (response() < 0) 3992cb25675Sralph return; 4002cb25675Sralph sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size); 4012cb25675Sralph (void) write(rem, buf, stb.st_size); 4022cb25675Sralph if (debug) 403e157bfacSbostic printf("readlink = %.*s\n", (int)stb.st_size, buf); 4042cb25675Sralph goto done; 4052cb25675Sralph 406978e44c9Sralph case S_IFREG: 407978e44c9Sralph break; 408978e44c9Sralph 409978e44c9Sralph default: 4102cb25675Sralph error("%s: not a file or directory\n", target); 411978e44c9Sralph return; 412978e44c9Sralph } 413978e44c9Sralph 414156664fbSralph if (u == 2) { 4156d401060Sralph if (opts & VERIFY) { 4166d401060Sralph log(lfp, "need to update: %s\n", target); 4176d401060Sralph goto dospecial; 418156664fbSralph } 4196d401060Sralph log(lfp, "updating: %s\n", target); 4206d401060Sralph } 4212cb25675Sralph 4222cb25675Sralph if (stb.st_nlink > 1) { 4232cb25675Sralph struct linkbuf *lp; 4242cb25675Sralph 4252cb25675Sralph if ((lp = savelink(&stb)) != NULL) { 4262cb25675Sralph /* install link */ 42726ce117eSsklower if (*lp->target == 0) 4282cb25675Sralph (void) sprintf(buf, "k%o %s %s\n", opts, 4292cb25675Sralph lp->pathname, rname); 43026ce117eSsklower else 43126ce117eSsklower (void) sprintf(buf, "k%o %s/%s %s\n", opts, 43226ce117eSsklower lp->target, lp->pathname, rname); 4332cb25675Sralph if (debug) 4342cb25675Sralph printf("buf = %s", buf); 4352cb25675Sralph (void) write(rem, buf, strlen(buf)); 4362cb25675Sralph (void) response(); 4372cb25675Sralph return; 4382cb25675Sralph } 4392cb25675Sralph } 440978e44c9Sralph 441*bdfadfffSbostic if ((f = open(target, O_RDONLY, 0)) < 0) { 44290bde559Sbostic error("%s: %s\n", target, strerror(errno)); 443d49851feSralph return; 444d49851feSralph } 445*bdfadfffSbostic (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts, 446ad3d87d9Sralph stb.st_mode & 07777, stb.st_size, stb.st_mtime, 44726ce117eSsklower protoname(), protogroup(), rname); 448978e44c9Sralph if (debug) 449978e44c9Sralph printf("buf = %s", buf); 450978e44c9Sralph (void) write(rem, buf, strlen(buf)); 451978e44c9Sralph if (response() < 0) { 452978e44c9Sralph (void) close(f); 453978e44c9Sralph return; 454978e44c9Sralph } 455978e44c9Sralph sizerr = 0; 456978e44c9Sralph for (i = 0; i < stb.st_size; i += BUFSIZ) { 457978e44c9Sralph int amt = BUFSIZ; 458978e44c9Sralph if (i + amt > stb.st_size) 459978e44c9Sralph amt = stb.st_size - i; 460978e44c9Sralph if (sizerr == 0 && read(f, buf, amt) != amt) 461978e44c9Sralph sizerr = 1; 462978e44c9Sralph (void) write(rem, buf, amt); 463978e44c9Sralph } 464978e44c9Sralph (void) close(f); 4652cb25675Sralph done: 4666d401060Sralph if (sizerr) { 46701de96ecSralph error("%s: file changed size\n", target); 4686d401060Sralph err(); 4696d401060Sralph } else 4706d401060Sralph ack(); 4712cb25675Sralph f = response(); 4722cb25675Sralph if (f < 0 || f == 0 && (opts & COMPARE)) 4736d401060Sralph return; 4746d401060Sralph dospecial: 47558fc31f7Sralph for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 476aca7acf4Sralph if (sc->sc_type != SPECIAL) 4776d401060Sralph continue; 478e980046fSralph if (sc->sc_args != NULL && !inlist(sc->sc_args, target)) 4796d401060Sralph continue; 480aca7acf4Sralph log(lfp, "special \"%s\"\n", sc->sc_name); 4816d401060Sralph if (opts & VERIFY) 4826d401060Sralph continue; 483e980046fSralph (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name); 4846d401060Sralph if (debug) 4856d401060Sralph printf("buf = %s", buf); 4866d401060Sralph (void) write(rem, buf, strlen(buf)); 4876d401060Sralph while (response() > 0) 4886d401060Sralph ; 4896d401060Sralph } 490978e44c9Sralph } 491978e44c9Sralph 492*bdfadfffSbostic static struct linkbuf * 4932cb25675Sralph savelink(stp) 4942cb25675Sralph struct stat *stp; 495978e44c9Sralph { 4962cb25675Sralph struct linkbuf *lp; 497978e44c9Sralph 4982cb25675Sralph for (lp = ihead; lp != NULL; lp = lp->nextp) 4992cb25675Sralph if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) { 5002cb25675Sralph lp->count--; 5012cb25675Sralph return(lp); 502978e44c9Sralph } 5032cb25675Sralph lp = (struct linkbuf *) malloc(sizeof(*lp)); 5042cb25675Sralph if (lp == NULL) 5052cb25675Sralph log(lfp, "out of memory, link information lost\n"); 5062cb25675Sralph else { 5072cb25675Sralph lp->nextp = ihead; 5082cb25675Sralph ihead = lp; 5092cb25675Sralph lp->inum = stp->st_ino; 5102cb25675Sralph lp->devnum = stp->st_dev; 5112cb25675Sralph lp->count = stp->st_nlink - 1; 5122cb25675Sralph strcpy(lp->pathname, target); 51326ce117eSsklower if (Tdest) 51426ce117eSsklower strcpy(lp->target, Tdest); 51526ce117eSsklower else 51626ce117eSsklower *lp->target = 0; 517978e44c9Sralph } 5182cb25675Sralph return(NULL); 519978e44c9Sralph } 520978e44c9Sralph 521978e44c9Sralph /* 522978e44c9Sralph * Check to see if file needs to be updated on the remote machine. 52301de96ecSralph * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date 52401de96ecSralph * and 3 if comparing binaries to determine if out of date. 525978e44c9Sralph */ 526*bdfadfffSbostic static int 5272cb25675Sralph update(rname, opts, stp) 52801de96ecSralph char *rname; 529ad3d87d9Sralph int opts; 5302cb25675Sralph struct stat *stp; 531978e44c9Sralph { 53201de96ecSralph register char *cp, *s; 533978e44c9Sralph register off_t size; 534978e44c9Sralph register time_t mtime; 535978e44c9Sralph 536978e44c9Sralph if (debug) 5372cb25675Sralph printf("update(%s, %x, %x)\n", rname, opts, stp); 538978e44c9Sralph 539978e44c9Sralph /* 540978e44c9Sralph * Check to see if the file exists on the remote machine. 541978e44c9Sralph */ 54201de96ecSralph (void) sprintf(buf, "Q%s\n", rname); 543978e44c9Sralph if (debug) 544978e44c9Sralph printf("buf = %s", buf); 545978e44c9Sralph (void) write(rem, buf, strlen(buf)); 54626ce117eSsklower again: 54701de96ecSralph cp = s = buf; 548978e44c9Sralph do { 549978e44c9Sralph if (read(rem, cp, 1) != 1) 550*bdfadfffSbostic lostconn(0); 551d49851feSralph } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 552978e44c9Sralph 55301de96ecSralph switch (*s++) { 554978e44c9Sralph case 'Y': 555978e44c9Sralph break; 556978e44c9Sralph 557d49851feSralph case 'N': /* file doesn't exist so install it */ 558978e44c9Sralph return(1); 559978e44c9Sralph 560978e44c9Sralph case '\1': 561aca7acf4Sralph nerrs++; 56201de96ecSralph if (*s != '\n') { 563d49851feSralph if (!iamremote) { 564d49851feSralph fflush(stdout); 56501de96ecSralph (void) write(2, s, cp - s); 566d49851feSralph } 567d49851feSralph if (lfp != NULL) 56801de96ecSralph (void) fwrite(s, 1, cp - s, lfp); 569d49851feSralph } 570978e44c9Sralph return(0); 571978e44c9Sralph 57226ce117eSsklower case '\3': 57326ce117eSsklower *--cp = '\0'; 57426ce117eSsklower if (lfp != NULL) 57526ce117eSsklower log(lfp, "update: note: %s\n", s); 57626ce117eSsklower goto again; 57726ce117eSsklower 578978e44c9Sralph default: 57915867ad7Sralph *--cp = '\0'; 58026ce117eSsklower error("update: unexpected response '%s'\n", s); 581978e44c9Sralph return(0); 582978e44c9Sralph } 583978e44c9Sralph 58401de96ecSralph if (*s == '\n') 585d49851feSralph return(2); 586978e44c9Sralph 587156664fbSralph if (opts & COMPARE) 588156664fbSralph return(3); 589156664fbSralph 590978e44c9Sralph size = 0; 59101de96ecSralph while (isdigit(*s)) 59201de96ecSralph size = size * 10 + (*s++ - '0'); 59301de96ecSralph if (*s++ != ' ') { 59401de96ecSralph error("update: size not delimited\n"); 595978e44c9Sralph return(0); 596978e44c9Sralph } 597978e44c9Sralph mtime = 0; 59801de96ecSralph while (isdigit(*s)) 59901de96ecSralph mtime = mtime * 10 + (*s++ - '0'); 60001de96ecSralph if (*s != '\n') { 60101de96ecSralph error("update: mtime not delimited\n"); 602978e44c9Sralph return(0); 603978e44c9Sralph } 604978e44c9Sralph /* 605978e44c9Sralph * File needs to be updated? 606978e44c9Sralph */ 607ad3d87d9Sralph if (opts & YOUNGER) { 6082cb25675Sralph if (stp->st_mtime == mtime) 60965a0d230Sralph return(0); 6102cb25675Sralph if (stp->st_mtime < mtime) { 61101de96ecSralph log(lfp, "Warning: %s: remote copy is newer\n", target); 61265a0d230Sralph return(0); 61365a0d230Sralph } 6142cb25675Sralph } else if (stp->st_mtime == mtime && stp->st_size == size) 615978e44c9Sralph return(0); 616d49851feSralph return(2); 617978e44c9Sralph } 618978e44c9Sralph 619978e44c9Sralph /* 620978e44c9Sralph * Query. Check to see if file exists. Return one of the following: 621978e44c9Sralph * N\n - doesn't exist 622978e44c9Sralph * Ysize mtime\n - exists and its a regular file (size & mtime of file) 6232cb25675Sralph * Y\n - exists and its a directory or symbolic link 624978e44c9Sralph * ^Aerror message\n 625978e44c9Sralph */ 626*bdfadfffSbostic static void 62701de96ecSralph query(name) 628978e44c9Sralph char *name; 629978e44c9Sralph { 630978e44c9Sralph struct stat stb; 631978e44c9Sralph 632978e44c9Sralph if (catname) 633978e44c9Sralph (void) sprintf(tp, "/%s", name); 63465a0d230Sralph 6352cb25675Sralph if (lstat(target, &stb) < 0) { 636e980046fSralph if (errno == ENOENT) 637978e44c9Sralph (void) write(rem, "N\n", 2); 638e980046fSralph else 63990bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno)); 640978e44c9Sralph *tp = '\0'; 641978e44c9Sralph return; 642978e44c9Sralph } 643978e44c9Sralph 644978e44c9Sralph switch (stb.st_mode & S_IFMT) { 645978e44c9Sralph case S_IFREG: 646*bdfadfffSbostic (void) sprintf(buf, "Y%qd %ld\n", stb.st_size, stb.st_mtime); 647978e44c9Sralph (void) write(rem, buf, strlen(buf)); 648978e44c9Sralph break; 649978e44c9Sralph 6502cb25675Sralph case S_IFLNK: 651978e44c9Sralph case S_IFDIR: 652978e44c9Sralph (void) write(rem, "Y\n", 2); 653978e44c9Sralph break; 654978e44c9Sralph 655978e44c9Sralph default: 6562cb25675Sralph error("%s: not a file or directory\n", name); 657978e44c9Sralph break; 658978e44c9Sralph } 659978e44c9Sralph *tp = '\0'; 660978e44c9Sralph } 661978e44c9Sralph 662*bdfadfffSbostic static void 6632cb25675Sralph recvf(cmd, type) 664978e44c9Sralph char *cmd; 6652cb25675Sralph int type; 666978e44c9Sralph { 667978e44c9Sralph register char *cp; 66844f1d1c2Sralph int f, mode, opts, wrerr, olderrno; 669978e44c9Sralph off_t i, size; 670978e44c9Sralph time_t mtime; 671978e44c9Sralph struct stat stb; 672978e44c9Sralph struct timeval tvp[2]; 673e980046fSralph char *owner, *group; 674978e44c9Sralph char new[BUFSIZ]; 67516580127Seric extern char *tempname; 676978e44c9Sralph 677ad3d87d9Sralph cp = cmd; 678156664fbSralph opts = 0; 679156664fbSralph while (*cp >= '0' && *cp <= '7') 680156664fbSralph opts = (opts << 3) | (*cp++ - '0'); 681ad3d87d9Sralph if (*cp++ != ' ') { 68201de96ecSralph error("recvf: options not delimited\n"); 683ad3d87d9Sralph return; 684ad3d87d9Sralph } 685978e44c9Sralph mode = 0; 686156664fbSralph while (*cp >= '0' && *cp <= '7') 687ad3d87d9Sralph mode = (mode << 3) | (*cp++ - '0'); 688978e44c9Sralph if (*cp++ != ' ') { 68901de96ecSralph error("recvf: mode not delimited\n"); 690978e44c9Sralph return; 691978e44c9Sralph } 692978e44c9Sralph size = 0; 693978e44c9Sralph while (isdigit(*cp)) 694978e44c9Sralph size = size * 10 + (*cp++ - '0'); 695978e44c9Sralph if (*cp++ != ' ') { 69601de96ecSralph error("recvf: size not delimited\n"); 697978e44c9Sralph return; 698978e44c9Sralph } 699978e44c9Sralph mtime = 0; 700978e44c9Sralph while (isdigit(*cp)) 701978e44c9Sralph mtime = mtime * 10 + (*cp++ - '0'); 702978e44c9Sralph if (*cp++ != ' ') { 70301de96ecSralph error("recvf: mtime not delimited\n"); 704978e44c9Sralph return; 705978e44c9Sralph } 706978e44c9Sralph owner = cp; 707978e44c9Sralph while (*cp && *cp != ' ') 708978e44c9Sralph cp++; 709978e44c9Sralph if (*cp != ' ') { 71001de96ecSralph error("recvf: owner name not delimited\n"); 711978e44c9Sralph return; 712978e44c9Sralph } 713978e44c9Sralph *cp++ = '\0'; 714978e44c9Sralph group = cp; 715978e44c9Sralph while (*cp && *cp != ' ') 716978e44c9Sralph cp++; 717978e44c9Sralph if (*cp != ' ') { 71801de96ecSralph error("recvf: group name not delimited\n"); 719978e44c9Sralph return; 720978e44c9Sralph } 721978e44c9Sralph *cp++ = '\0'; 722978e44c9Sralph 7232cb25675Sralph if (type == S_IFDIR) { 72465a0d230Sralph if (catname >= sizeof(stp)) { 7252cb25675Sralph error("%s:%s: too many directory levels\n", 7262cb25675Sralph host, target); 72765a0d230Sralph return; 72865a0d230Sralph } 72965a0d230Sralph stp[catname] = tp; 730978e44c9Sralph if (catname++) { 731978e44c9Sralph *tp++ = '/'; 732978e44c9Sralph while (*tp++ = *cp++) 733978e44c9Sralph ; 734978e44c9Sralph tp--; 735978e44c9Sralph } 736ad3d87d9Sralph if (opts & VERIFY) { 7376d401060Sralph ack(); 738d49851feSralph return; 739d49851feSralph } 7402cb25675Sralph if (lstat(target, &stb) == 0) { 7416d401060Sralph if (ISDIR(stb.st_mode)) { 742e980046fSralph if ((stb.st_mode & 07777) == mode) { 7436d401060Sralph ack(); 744978e44c9Sralph return; 745d49851feSralph } 7463e1cad88Sralph buf[0] = '\0'; 7473e1cad88Sralph (void) sprintf(buf + 1, 748d66beb1aSlepreau "%s: Warning: remote mode %o != local mode %o\n", 749d66beb1aSlepreau target, stb.st_mode & 07777, mode); 7503e1cad88Sralph (void) write(rem, buf, strlen(buf + 1) + 1); 7513e1cad88Sralph return; 7523e1cad88Sralph } 753e980046fSralph errno = ENOTDIR; 754e980046fSralph } else if (errno == ENOENT && (mkdir(target, mode) == 0 || 755e980046fSralph chkparent(target) == 0 && mkdir(target, mode) == 0)) { 7561b11f3a3Sbostic if (fchog(-1, target, owner, group, mode) == 0) 7576d401060Sralph ack(); 7586d401060Sralph return; 7596d401060Sralph } 76090bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno)); 7616d401060Sralph tp = stp[--catname]; 7626d401060Sralph *tp = '\0'; 763978e44c9Sralph return; 764978e44c9Sralph } 765978e44c9Sralph 766978e44c9Sralph if (catname) 767978e44c9Sralph (void) sprintf(tp, "/%s", cp); 768978e44c9Sralph cp = rindex(target, '/'); 769978e44c9Sralph if (cp == NULL) 77016580127Seric strcpy(new, tempname); 771e980046fSralph else if (cp == target) 77216580127Seric (void) sprintf(new, "/%s", tempname); 773e980046fSralph else { 774978e44c9Sralph *cp = '\0'; 77516580127Seric (void) sprintf(new, "%s/%s", target, tempname); 776d49851feSralph *cp = '/'; 777e980046fSralph } 7782cb25675Sralph 7792cb25675Sralph if (type == S_IFLNK) { 7802cb25675Sralph int j; 7812cb25675Sralph 7822cb25675Sralph ack(); 7832cb25675Sralph cp = buf; 7842cb25675Sralph for (i = 0; i < size; i += j) { 7852cb25675Sralph if ((j = read(rem, cp, size - i)) <= 0) 786*bdfadfffSbostic cleanup(0); 7872cb25675Sralph cp += j; 7882cb25675Sralph } 7892cb25675Sralph *cp = '\0'; 790e980046fSralph if (response() < 0) { 791e980046fSralph err(); 792e980046fSralph return; 793e980046fSralph } 79475527c89Sralph if (symlink(buf, new) < 0) { 79575527c89Sralph if (errno != ENOENT || chkparent(new) < 0 || 79675527c89Sralph symlink(buf, new) < 0) 7971b11f3a3Sbostic goto badnew1; 79875527c89Sralph } 7992cb25675Sralph mode &= 0777; 8002cb25675Sralph if (opts & COMPARE) { 8012cb25675Sralph char tbuf[BUFSIZ]; 8022cb25675Sralph 80375527c89Sralph if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 && 80475527c89Sralph i == size && strncmp(buf, tbuf, size) == 0) { 8052cb25675Sralph (void) unlink(new); 8062cb25675Sralph ack(); 8072cb25675Sralph return; 8082cb25675Sralph } 80915867ad7Sralph if (opts & VERIFY) 81015867ad7Sralph goto differ; 8112cb25675Sralph } 8122cb25675Sralph goto fixup; 8132cb25675Sralph } 8142cb25675Sralph 815e980046fSralph if ((f = creat(new, mode)) < 0) { 816e980046fSralph if (errno != ENOENT || chkparent(new) < 0 || 817e980046fSralph (f = creat(new, mode)) < 0) 8181b11f3a3Sbostic goto badnew1; 819e980046fSralph } 820e980046fSralph 8216d401060Sralph ack(); 822978e44c9Sralph wrerr = 0; 823978e44c9Sralph for (i = 0; i < size; i += BUFSIZ) { 824978e44c9Sralph int amt = BUFSIZ; 825978e44c9Sralph 82665a0d230Sralph cp = buf; 827978e44c9Sralph if (i + amt > size) 828978e44c9Sralph amt = size - i; 829978e44c9Sralph do { 830978e44c9Sralph int j = read(rem, cp, amt); 831978e44c9Sralph 832d49851feSralph if (j <= 0) { 833d49851feSralph (void) close(f); 834d49851feSralph (void) unlink(new); 835*bdfadfffSbostic cleanup(0); 836d49851feSralph } 837978e44c9Sralph amt -= j; 838978e44c9Sralph cp += j; 839978e44c9Sralph } while (amt > 0); 840978e44c9Sralph amt = BUFSIZ; 841978e44c9Sralph if (i + amt > size) 842978e44c9Sralph amt = size - i; 843978e44c9Sralph if (wrerr == 0 && write(f, buf, amt) != amt) { 844978e44c9Sralph olderrno = errno; 845978e44c9Sralph wrerr++; 846978e44c9Sralph } 847978e44c9Sralph } 848e980046fSralph if (response() < 0) { 849e980046fSralph err(); 8501b11f3a3Sbostic goto badnew2; 851e980046fSralph } 8521b11f3a3Sbostic if (wrerr) 8531b11f3a3Sbostic goto badnew1; 854156664fbSralph if (opts & COMPARE) { 855156664fbSralph FILE *f1, *f2; 856156664fbSralph int c; 857156664fbSralph 858156664fbSralph if ((f1 = fopen(target, "r")) == NULL) 8591b11f3a3Sbostic goto badtarget; 86026ce117eSsklower if ((f2 = fopen(new, "r")) == NULL) { 8611b11f3a3Sbostic badnew1: error("%s:%s: %s\n", host, new, strerror(errno)); 8621b11f3a3Sbostic goto badnew2; 86326ce117eSsklower } 864156664fbSralph while ((c = getc(f1)) == getc(f2)) 865156664fbSralph if (c == EOF) { 866156664fbSralph (void) fclose(f1); 867156664fbSralph (void) fclose(f2); 8686d401060Sralph ack(); 8691b11f3a3Sbostic goto badnew2; 870156664fbSralph } 871156664fbSralph (void) fclose(f1); 872156664fbSralph (void) fclose(f2); 873156664fbSralph if (opts & VERIFY) { 8741b11f3a3Sbostic differ: buf[0] = '\0'; 875e980046fSralph (void) sprintf(buf + 1, "need to update: %s\n",target); 876156664fbSralph (void) write(rem, buf, strlen(buf + 1) + 1); 8771b11f3a3Sbostic goto badnew2; 878156664fbSralph } 879156664fbSralph } 880978e44c9Sralph 881978e44c9Sralph /* 882978e44c9Sralph * Set last modified time 883978e44c9Sralph */ 8841b11f3a3Sbostic tvp[0].tv_sec = time(0); 885978e44c9Sralph tvp[0].tv_usec = 0; 886978e44c9Sralph tvp[1].tv_sec = mtime; 887978e44c9Sralph tvp[1].tv_usec = 0; 8881b11f3a3Sbostic if (utimes(new, tvp) < 0) 88990bde559Sbostic note("%s: utimes failed %s: %s\n", host, new, strerror(errno)); 8901b11f3a3Sbostic 8911b11f3a3Sbostic if (fchog(f, new, owner, group, mode) < 0) { 8921b11f3a3Sbostic badnew2: (void) close(f); 893d49851feSralph (void) unlink(new); 894978e44c9Sralph return; 895978e44c9Sralph } 8961b11f3a3Sbostic (void) close(f); 8971b11f3a3Sbostic 8981b11f3a3Sbostic fixup: if (rename(new, target) < 0) { 8991b11f3a3Sbostic badtarget: error("%s:%s: %s\n", host, target, strerror(errno)); 900d49851feSralph (void) unlink(new); 901978e44c9Sralph return; 902978e44c9Sralph } 9031b11f3a3Sbostic 904156664fbSralph if (opts & COMPARE) { 905156664fbSralph buf[0] = '\0'; 906d66beb1aSlepreau (void) sprintf(buf + 1, "updated %s\n", target); 907156664fbSralph (void) write(rem, buf, strlen(buf + 1) + 1); 908156664fbSralph } else 9096d401060Sralph ack(); 910978e44c9Sralph } 911978e44c9Sralph 912978e44c9Sralph /* 9132cb25675Sralph * Creat a hard link to existing file. 9142cb25675Sralph */ 915*bdfadfffSbostic static void 9162cb25675Sralph hardlink(cmd) 9172cb25675Sralph char *cmd; 9182cb25675Sralph { 9192cb25675Sralph register char *cp; 9202cb25675Sralph struct stat stb; 9212cb25675Sralph char *oldname; 9222cb25675Sralph int opts, exists = 0; 9232cb25675Sralph 9242cb25675Sralph cp = cmd; 9252cb25675Sralph opts = 0; 9262cb25675Sralph while (*cp >= '0' && *cp <= '7') 9272cb25675Sralph opts = (opts << 3) | (*cp++ - '0'); 9282cb25675Sralph if (*cp++ != ' ') { 9292cb25675Sralph error("hardlink: options not delimited\n"); 9302cb25675Sralph return; 9312cb25675Sralph } 9322cb25675Sralph oldname = cp; 9332cb25675Sralph while (*cp && *cp != ' ') 9342cb25675Sralph cp++; 9352cb25675Sralph if (*cp != ' ') { 9362cb25675Sralph error("hardlink: oldname name not delimited\n"); 9372cb25675Sralph return; 9382cb25675Sralph } 9392cb25675Sralph *cp++ = '\0'; 9402cb25675Sralph 94126ce117eSsklower if (catname) { 9422cb25675Sralph (void) sprintf(tp, "/%s", cp); 94326ce117eSsklower } 9442cb25675Sralph if (lstat(target, &stb) == 0) { 94526ce117eSsklower int mode = stb.st_mode & S_IFMT; 94626ce117eSsklower if (mode != S_IFREG && mode != S_IFLNK) { 9472cb25675Sralph error("%s:%s: not a regular file\n", host, target); 9482cb25675Sralph return; 9492cb25675Sralph } 9502cb25675Sralph exists = 1; 9512cb25675Sralph } 95226ce117eSsklower if (chkparent(target) < 0 ) { 95326ce117eSsklower error("%s:%s: %s (no parent)\n", 95490bde559Sbostic host, target, strerror(errno)); 95526ce117eSsklower return; 95626ce117eSsklower } 95726ce117eSsklower if (exists && (unlink(target) < 0)) { 95826ce117eSsklower error("%s:%s: %s (unlink)\n", 95990bde559Sbostic host, target, strerror(errno)); 96026ce117eSsklower return; 96126ce117eSsklower } 96226ce117eSsklower if (link(oldname, target) < 0) { 96326ce117eSsklower error("%s:can't link %s to %s\n", 96426ce117eSsklower host, target, oldname); 9652cb25675Sralph return; 9662cb25675Sralph } 9672cb25675Sralph ack(); 9682cb25675Sralph } 9692cb25675Sralph 9702cb25675Sralph /* 971e980046fSralph * Check to see if parent directory exists and create one if not. 97265a0d230Sralph */ 973*bdfadfffSbostic static int 97465a0d230Sralph chkparent(name) 97565a0d230Sralph char *name; 97665a0d230Sralph { 977e980046fSralph register char *cp; 978e980046fSralph struct stat stb; 97965a0d230Sralph 98065a0d230Sralph cp = rindex(name, '/'); 981e980046fSralph if (cp == NULL || cp == name) 982e980046fSralph return(0); 98365a0d230Sralph *cp = '\0'; 984e980046fSralph if (lstat(name, &stb) < 0) { 985e980046fSralph if (errno == ENOENT && chkparent(name) >= 0 && 986e980046fSralph mkdir(name, 0777 & ~oumask) >= 0) { 98765a0d230Sralph *cp = '/'; 98865a0d230Sralph return(0); 98965a0d230Sralph } 990e980046fSralph } else if (ISDIR(stb.st_mode)) { 99165a0d230Sralph *cp = '/'; 99265a0d230Sralph return(0); 99365a0d230Sralph } 99465a0d230Sralph *cp = '/'; 99565a0d230Sralph return(-1); 99665a0d230Sralph } 99765a0d230Sralph 99865a0d230Sralph /* 9996d401060Sralph * Change owner, group and mode of file. 1000978e44c9Sralph */ 1001*bdfadfffSbostic static int 10021b11f3a3Sbostic fchog(fd, file, owner, group, mode) 10031b11f3a3Sbostic int fd; 1004978e44c9Sralph char *file, *owner, *group; 1005d49851feSralph int mode; 1006978e44c9Sralph { 1007978e44c9Sralph register int i; 1008978e44c9Sralph int uid, gid; 1009e980046fSralph extern char user[]; 1010e980046fSralph extern int userid; 1011978e44c9Sralph 1012978e44c9Sralph uid = userid; 1013978e44c9Sralph if (userid == 0) { 101426ce117eSsklower if (*owner == ':') { 101526ce117eSsklower uid = atoi(owner + 1); 101626ce117eSsklower } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) { 101701de96ecSralph if ((pw = getpwnam(owner)) == NULL) { 1018d49851feSralph if (mode & 04000) { 101926ce117eSsklower note("%s:%s: unknown login name, clearing setuid", 10202cb25675Sralph host, owner); 102126ce117eSsklower mode &= ~04000; 102226ce117eSsklower uid = 0; 1023978e44c9Sralph } 1024d49851feSralph } else 102501de96ecSralph uid = pw->pw_uid; 1026d49851feSralph } else 102701de96ecSralph uid = pw->pw_uid; 102826ce117eSsklower if (*group == ':') { 102926ce117eSsklower gid = atoi(group + 1); 103026ce117eSsklower goto ok; 103126ce117eSsklower } 10326d401060Sralph } else if ((mode & 04000) && strcmp(user, owner) != 0) 10336d401060Sralph mode &= ~04000; 1034e980046fSralph gid = -1; 103501de96ecSralph if (gr == NULL || strcmp(group, gr->gr_name) != 0) { 103626ce117eSsklower if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL)) 103726ce117eSsklower || ((gr = getgrnam(group)) == NULL)) { 1038d49851feSralph if (mode & 02000) { 103926ce117eSsklower note("%s:%s: unknown group", host, group); 104026ce117eSsklower mode &= ~02000; 1041978e44c9Sralph } 1042d49851feSralph } else 104301de96ecSralph gid = gr->gr_gid; 1044d49851feSralph } else 104501de96ecSralph gid = gr->gr_gid; 1046e980046fSralph if (userid && gid >= 0) { 104726ce117eSsklower if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++) 104801de96ecSralph if (!(strcmp(user, gr->gr_mem[i]))) 1049978e44c9Sralph goto ok; 10506d401060Sralph mode &= ~02000; 1051e980046fSralph gid = -1; 1052978e44c9Sralph } 10531b11f3a3Sbostic ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0) 10541b11f3a3Sbostic note("%s: %s chown: %s", host, file, strerror(errno)); 10551b11f3a3Sbostic else if (mode & 07000 && 10561b11f3a3Sbostic (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0)) 10571b11f3a3Sbostic note("%s: %s chmod: %s", host, file, strerror(errno)); 1058978e44c9Sralph return(0); 1059978e44c9Sralph } 1060978e44c9Sralph 1061c27256c0Sralph /* 1062c27256c0Sralph * Check for files on the machine being updated that are not on the master 1063c27256c0Sralph * machine and remove them. 1064c27256c0Sralph */ 1065*bdfadfffSbostic static void 10666d401060Sralph rmchk(opts) 1067c27256c0Sralph int opts; 1068c27256c0Sralph { 106901de96ecSralph register char *cp, *s; 1070c27256c0Sralph struct stat stb; 1071c27256c0Sralph 1072c27256c0Sralph if (debug) 10736d401060Sralph printf("rmchk()\n"); 1074156664fbSralph 1075c27256c0Sralph /* 1076c27256c0Sralph * Tell the remote to clean the files from the last directory sent. 1077c27256c0Sralph */ 10786d401060Sralph (void) sprintf(buf, "C%o\n", opts & VERIFY); 1079c27256c0Sralph if (debug) 1080c27256c0Sralph printf("buf = %s", buf); 1081c27256c0Sralph (void) write(rem, buf, strlen(buf)); 1082c27256c0Sralph if (response() < 0) 1083c27256c0Sralph return; 1084c27256c0Sralph for (;;) { 108501de96ecSralph cp = s = buf; 1086c27256c0Sralph do { 1087c27256c0Sralph if (read(rem, cp, 1) != 1) 1088*bdfadfffSbostic lostconn(0); 1089c27256c0Sralph } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 1090c27256c0Sralph 109101de96ecSralph switch (*s++) { 10926d401060Sralph case 'Q': /* Query if file should be removed */ 1093156664fbSralph /* 1094156664fbSralph * Return the following codes to remove query. 10956d401060Sralph * N\n -- file exists - DON'T remove. 10966d401060Sralph * Y\n -- file doesn't exist - REMOVE. 1097156664fbSralph */ 1098c27256c0Sralph *--cp = '\0'; 109901de96ecSralph (void) sprintf(tp, "/%s", s); 1100c27256c0Sralph if (debug) 1101c27256c0Sralph printf("check %s\n", target); 110258fc31f7Sralph if (except(target)) 1103c27256c0Sralph (void) write(rem, "N\n", 2); 11042cb25675Sralph else if (lstat(target, &stb) < 0) 1105c27256c0Sralph (void) write(rem, "Y\n", 2); 11066d401060Sralph else 11076d401060Sralph (void) write(rem, "N\n", 2); 1108c27256c0Sralph break; 1109c27256c0Sralph 1110c27256c0Sralph case '\0': 1111c27256c0Sralph *--cp = '\0'; 111201de96ecSralph if (*s != '\0') 111301de96ecSralph log(lfp, "%s\n", s); 1114c27256c0Sralph break; 1115c27256c0Sralph 11166d401060Sralph case 'E': 11176d401060Sralph *tp = '\0'; 11186d401060Sralph ack(); 11196d401060Sralph return; 11206d401060Sralph 1121c27256c0Sralph case '\1': 1122c27256c0Sralph case '\2': 1123aca7acf4Sralph nerrs++; 112401de96ecSralph if (*s != '\n') { 1125c27256c0Sralph if (!iamremote) { 1126c27256c0Sralph fflush(stdout); 112701de96ecSralph (void) write(2, s, cp - s); 1128c27256c0Sralph } 1129c27256c0Sralph if (lfp != NULL) 113001de96ecSralph (void) fwrite(s, 1, cp - s, lfp); 1131c27256c0Sralph } 1132c27256c0Sralph if (buf[0] == '\2') 1133*bdfadfffSbostic lostconn(0); 1134c27256c0Sralph break; 1135c27256c0Sralph 1136c27256c0Sralph default: 11376d401060Sralph error("rmchk: unexpected response '%s'\n", buf); 11386d401060Sralph err(); 1139c27256c0Sralph } 1140c27256c0Sralph } 1141c27256c0Sralph } 1142c27256c0Sralph 1143c27256c0Sralph /* 11446d401060Sralph * Check the current directory (initialized by the 'T' command to server()) 1145156664fbSralph * for extraneous files and remove them. 1146c27256c0Sralph */ 1147*bdfadfffSbostic static void 114800fd8e74Sralph clean(cp) 114900fd8e74Sralph register char *cp; 1150c27256c0Sralph { 1151c27256c0Sralph DIR *d; 11526d401060Sralph register struct direct *dp; 1153c27256c0Sralph struct stat stb; 11546d401060Sralph char *otp; 11556d401060Sralph int len, opts; 1156c27256c0Sralph 11576d401060Sralph opts = 0; 11586d401060Sralph while (*cp >= '0' && *cp <= '7') 11596d401060Sralph opts = (opts << 3) | (*cp++ - '0'); 11606d401060Sralph if (*cp != '\0') { 11616d401060Sralph error("clean: options not delimited\n"); 1162c27256c0Sralph return; 1163c27256c0Sralph } 1164e980046fSralph if ((d = opendir(target)) == NULL) { 116590bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno)); 116601de96ecSralph return; 116701de96ecSralph } 11686d401060Sralph ack(); 1169c27256c0Sralph 1170c27256c0Sralph otp = tp; 1171c27256c0Sralph len = tp - target; 1172c27256c0Sralph while (dp = readdir(d)) { 1173c27256c0Sralph if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 1174c27256c0Sralph continue; 1175c27256c0Sralph if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 11762cb25675Sralph error("%s:%s/%s: Name too long\n", 11772cb25675Sralph host, target, dp->d_name); 1178c27256c0Sralph continue; 1179c27256c0Sralph } 1180c27256c0Sralph tp = otp; 1181c27256c0Sralph *tp++ = '/'; 1182c27256c0Sralph cp = dp->d_name;; 1183c27256c0Sralph while (*tp++ = *cp++) 1184c27256c0Sralph ; 1185c27256c0Sralph tp--; 11862cb25675Sralph if (lstat(target, &stb) < 0) { 118790bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno)); 1188c27256c0Sralph continue; 1189c27256c0Sralph } 11906d401060Sralph (void) sprintf(buf, "Q%s\n", dp->d_name); 1191c27256c0Sralph (void) write(rem, buf, strlen(buf)); 1192c27256c0Sralph cp = buf; 1193c27256c0Sralph do { 1194c27256c0Sralph if (read(rem, cp, 1) != 1) 1195*bdfadfffSbostic cleanup(0); 1196c27256c0Sralph } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); 1197c27256c0Sralph *--cp = '\0'; 1198c27256c0Sralph cp = buf; 11996d401060Sralph if (*cp != 'Y') 1200c27256c0Sralph continue; 120101de96ecSralph if (opts & VERIFY) { 120201de96ecSralph cp = buf; 120301de96ecSralph *cp++ = '\0'; 1204e980046fSralph (void) sprintf(cp, "need to remove: %s\n", target); 120501de96ecSralph (void) write(rem, buf, strlen(cp) + 1); 120601de96ecSralph } else 12076d57652cStorek removeit(&stb); 1208c27256c0Sralph } 1209c27256c0Sralph closedir(d); 1210c27256c0Sralph (void) write(rem, "E\n", 2); 1211c27256c0Sralph (void) response(); 12126d401060Sralph tp = otp; 121301de96ecSralph *tp = '\0'; 1214c27256c0Sralph } 1215c27256c0Sralph 121601de96ecSralph /* 121701de96ecSralph * Remove a file or directory (recursively) and send back an acknowledge 121801de96ecSralph * or an error message. 121901de96ecSralph */ 1220*bdfadfffSbostic static void 12216d57652cStorek removeit(stp) 12222cb25675Sralph struct stat *stp; 1223c27256c0Sralph { 1224c27256c0Sralph DIR *d; 1225c27256c0Sralph struct direct *dp; 1226c27256c0Sralph register char *cp; 1227c27256c0Sralph struct stat stb; 1228c27256c0Sralph char *otp; 1229c27256c0Sralph int len; 1230c27256c0Sralph 12312cb25675Sralph switch (stp->st_mode & S_IFMT) { 1232c27256c0Sralph case S_IFREG: 12332cb25675Sralph case S_IFLNK: 1234c27256c0Sralph if (unlink(target) < 0) 1235c27256c0Sralph goto bad; 1236c27256c0Sralph goto removed; 1237c27256c0Sralph 1238c27256c0Sralph case S_IFDIR: 1239c27256c0Sralph break; 1240c27256c0Sralph 1241c27256c0Sralph default: 12422cb25675Sralph error("%s:%s: not a plain file\n", host, target); 1243c27256c0Sralph return; 1244c27256c0Sralph } 1245c27256c0Sralph 1246e980046fSralph if ((d = opendir(target)) == NULL) 1247c27256c0Sralph goto bad; 1248c27256c0Sralph 1249c27256c0Sralph otp = tp; 1250c27256c0Sralph len = tp - target; 1251c27256c0Sralph while (dp = readdir(d)) { 1252c27256c0Sralph if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 1253c27256c0Sralph continue; 1254c27256c0Sralph if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { 12552cb25675Sralph error("%s:%s/%s: Name too long\n", 12562cb25675Sralph host, target, dp->d_name); 1257c27256c0Sralph continue; 1258c27256c0Sralph } 1259c27256c0Sralph tp = otp; 1260c27256c0Sralph *tp++ = '/'; 1261c27256c0Sralph cp = dp->d_name;; 1262c27256c0Sralph while (*tp++ = *cp++) 1263c27256c0Sralph ; 1264c27256c0Sralph tp--; 12652cb25675Sralph if (lstat(target, &stb) < 0) { 126690bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno)); 1267c27256c0Sralph continue; 1268c27256c0Sralph } 12696d57652cStorek removeit(&stb); 1270c27256c0Sralph } 1271c27256c0Sralph closedir(d); 1272c27256c0Sralph tp = otp; 1273c27256c0Sralph *tp = '\0'; 1274c27256c0Sralph if (rmdir(target) < 0) { 1275c27256c0Sralph bad: 127690bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno)); 1277c27256c0Sralph return; 1278c27256c0Sralph } 1279c27256c0Sralph removed: 1280c27256c0Sralph cp = buf; 1281c27256c0Sralph *cp++ = '\0'; 1282c27256c0Sralph (void) sprintf(cp, "removed %s\n", target); 1283c27256c0Sralph (void) write(rem, buf, strlen(cp) + 1); 1284c27256c0Sralph } 1285c27256c0Sralph 12866d401060Sralph /* 12876d401060Sralph * Execute a shell command to handle special cases. 12886d401060Sralph */ 1289*bdfadfffSbostic static void 12906d401060Sralph dospecial(cmd) 12916d401060Sralph char *cmd; 12926d401060Sralph { 12936d401060Sralph int fd[2], status, pid, i; 12946d401060Sralph register char *cp, *s; 12956d401060Sralph char sbuf[BUFSIZ]; 1296e980046fSralph extern int userid, groupid; 12976d401060Sralph 12986d401060Sralph if (pipe(fd) < 0) { 129990bde559Sbostic error("%s\n", strerror(errno)); 13006d401060Sralph return; 13016d401060Sralph } 13026d401060Sralph if ((pid = fork()) == 0) { 13036d401060Sralph /* 13046d401060Sralph * Return everything the shell commands print. 13056d401060Sralph */ 13066d401060Sralph (void) close(0); 13076d401060Sralph (void) close(1); 13086d401060Sralph (void) close(2); 13090af019deSbostic (void) open(_PATH_DEVNULL, O_RDONLY); 13106d401060Sralph (void) dup(fd[1]); 13116d401060Sralph (void) dup(fd[1]); 13126d401060Sralph (void) close(fd[0]); 13136d401060Sralph (void) close(fd[1]); 1314e980046fSralph setgid(groupid); 1315a5437550Sralph setuid(userid); 13160af019deSbostic execl(_PATH_BSHELL, "sh", "-c", cmd, 0); 13176d401060Sralph _exit(127); 13186d401060Sralph } 13196d401060Sralph (void) close(fd[1]); 13206d401060Sralph s = sbuf; 13216d401060Sralph *s++ = '\0'; 13226d401060Sralph while ((i = read(fd[0], buf, sizeof(buf))) > 0) { 13236d401060Sralph cp = buf; 13246d401060Sralph do { 13256d401060Sralph *s++ = *cp++; 13266d401060Sralph if (cp[-1] != '\n') { 13276d401060Sralph if (s < &sbuf[sizeof(sbuf)-1]) 13286d401060Sralph continue; 13296d401060Sralph *s++ = '\n'; 13306d401060Sralph } 13316d401060Sralph /* 13326d401060Sralph * Throw away blank lines. 13336d401060Sralph */ 13346d401060Sralph if (s == &sbuf[2]) { 13356d401060Sralph s--; 13366d401060Sralph continue; 13376d401060Sralph } 13386d401060Sralph (void) write(rem, sbuf, s - sbuf); 13396d401060Sralph s = &sbuf[1]; 13406d401060Sralph } while (--i); 13416d401060Sralph } 13426d401060Sralph if (s > &sbuf[1]) { 13436d401060Sralph *s++ = '\n'; 13446d401060Sralph (void) write(rem, sbuf, s - sbuf); 13456d401060Sralph } 13466d401060Sralph while ((i = wait(&status)) != pid && i != -1) 13476d401060Sralph ; 13486d401060Sralph if (i == -1) 13496d401060Sralph status = -1; 1350e980046fSralph (void) close(fd[0]); 13516d401060Sralph if (status) 13526d401060Sralph error("shell returned %d\n", status); 13536d401060Sralph else 13546d401060Sralph ack(); 13556d401060Sralph } 13566d401060Sralph 1357*bdfadfffSbostic #if __STDC__ 1358*bdfadfffSbostic #include <stdarg.h> 1359*bdfadfffSbostic #else 1360*bdfadfffSbostic #include <varargs.h> 1361*bdfadfffSbostic #endif 1362*bdfadfffSbostic 1363*bdfadfffSbostic void 1364*bdfadfffSbostic #if __STDC__ 1365*bdfadfffSbostic log(FILE *fp, const char *fmt, ...) 1366*bdfadfffSbostic #else 1367*bdfadfffSbostic log(fp, fmt, va_alist) 1368d49851feSralph FILE *fp; 1369978e44c9Sralph char *fmt; 1370*bdfadfffSbostic va_dcl 1371*bdfadfffSbostic #endif 1372978e44c9Sralph { 1373*bdfadfffSbostic va_list ap; 1374*bdfadfffSbostic #if __STDC__ 1375*bdfadfffSbostic va_start(ap, fmt); 1376*bdfadfffSbostic #else 1377*bdfadfffSbostic va_start(ap); 1378*bdfadfffSbostic #endif 1379978e44c9Sralph /* Print changes locally if not quiet mode */ 1380978e44c9Sralph if (!qflag) 1381*bdfadfffSbostic (void)vprintf(fmt, ap); 1382978e44c9Sralph 1383978e44c9Sralph /* Save changes (for mailing) if really updating files */ 1384ad3d87d9Sralph if (!(options & VERIFY) && fp != NULL) 1385*bdfadfffSbostic (void)vfprintf(fp, fmt, ap); 1386*bdfadfffSbostic va_end(ap); 1387978e44c9Sralph } 1388978e44c9Sralph 1389*bdfadfffSbostic void 1390*bdfadfffSbostic #if __STDC__ 1391*bdfadfffSbostic error(const char *fmt, ...) 1392*bdfadfffSbostic #else 1393*bdfadfffSbostic error(fmt, va_alist) 1394978e44c9Sralph char *fmt; 1395*bdfadfffSbostic va_dcl 1396*bdfadfffSbostic #endif 1397978e44c9Sralph { 139852d94563Sbostic static FILE *fp; 1399*bdfadfffSbostic va_list ap; 1400*bdfadfffSbostic #if __STDC__ 1401*bdfadfffSbostic va_start(ap, fmt); 1402*bdfadfffSbostic #else 1403*bdfadfffSbostic va_start(ap); 1404*bdfadfffSbostic #endif 140552d94563Sbostic 140652d94563Sbostic ++nerrs; 140752d94563Sbostic if (!fp && !(fp = fdopen(rem, "w"))) 140852d94563Sbostic return; 140952d94563Sbostic if (iamremote) { 141052d94563Sbostic (void)fprintf(fp, "%crdist: ", 0x01); 1411*bdfadfffSbostic (void)vfprintf(fp, fmt, ap); 141252d94563Sbostic fflush(fp); 141352d94563Sbostic } 141452d94563Sbostic else { 1415d49851feSralph fflush(stdout); 141652d94563Sbostic (void)fprintf(stderr, "rdist: "); 1417*bdfadfffSbostic (void)vfprintf(stderr, fmt, ap); 141852d94563Sbostic fflush(stderr); 141952d94563Sbostic } 142052d94563Sbostic if (lfp != NULL) { 142152d94563Sbostic (void)fprintf(lfp, "rdist: "); 1422*bdfadfffSbostic (void)vfprintf(lfp, fmt, ap); 142352d94563Sbostic fflush(lfp); 142452d94563Sbostic } 1425*bdfadfffSbostic va_end(ap); 1426978e44c9Sralph } 1427978e44c9Sralph 1428*bdfadfffSbostic void 1429*bdfadfffSbostic #if __STDC__ 1430*bdfadfffSbostic fatal(const char *fmt, ...) 1431*bdfadfffSbostic #else 1432*bdfadfffSbostic fatal(fmt, va_alist) 1433978e44c9Sralph char *fmt; 1434*bdfadfffSbostic va_dcl 1435*bdfadfffSbostic #endif 1436978e44c9Sralph { 143752d94563Sbostic static FILE *fp; 1438*bdfadfffSbostic va_list ap; 1439*bdfadfffSbostic #if __STDC__ 1440*bdfadfffSbostic va_start(ap, fmt); 1441*bdfadfffSbostic #else 1442*bdfadfffSbostic va_start(ap); 1443*bdfadfffSbostic #endif 144452d94563Sbostic 144552d94563Sbostic ++nerrs; 144652d94563Sbostic if (!fp && !(fp = fdopen(rem, "w"))) 144752d94563Sbostic return; 144852d94563Sbostic if (iamremote) { 144952d94563Sbostic (void)fprintf(fp, "%crdist: ", 0x02); 1450*bdfadfffSbostic (void)vfprintf(fp, fmt, ap); 145152d94563Sbostic fflush(fp); 145252d94563Sbostic } 145352d94563Sbostic else { 1454d49851feSralph fflush(stdout); 145552d94563Sbostic (void)fprintf(stderr, "rdist: "); 1456*bdfadfffSbostic (void)vfprintf(stderr, fmt, ap); 145752d94563Sbostic fflush(stderr); 145852d94563Sbostic } 145952d94563Sbostic if (lfp != NULL) { 146052d94563Sbostic (void)fprintf(lfp, "rdist: "); 1461*bdfadfffSbostic (void)vfprintf(lfp, fmt, ap); 146252d94563Sbostic fflush(lfp); 146352d94563Sbostic } 1464*bdfadfffSbostic cleanup(0); 1465978e44c9Sralph } 1466978e44c9Sralph 1467*bdfadfffSbostic static int 1468978e44c9Sralph response() 1469978e44c9Sralph { 1470156664fbSralph char *cp, *s; 147115867ad7Sralph char resp[BUFSIZ]; 1472978e44c9Sralph 1473978e44c9Sralph if (debug) 1474978e44c9Sralph printf("response()\n"); 1475978e44c9Sralph 147615867ad7Sralph cp = s = resp; 1477978e44c9Sralph do { 1478978e44c9Sralph if (read(rem, cp, 1) != 1) 1479*bdfadfffSbostic lostconn(0); 148015867ad7Sralph } while (*cp++ != '\n' && cp < &resp[BUFSIZ]); 1481156664fbSralph 1482156664fbSralph switch (*s++) { 1483156664fbSralph case '\0': 1484156664fbSralph *--cp = '\0'; 14856d401060Sralph if (*s != '\0') { 1486156664fbSralph log(lfp, "%s\n", s); 14876d401060Sralph return(1); 14886d401060Sralph } 1489156664fbSralph return(0); 149026ce117eSsklower case '\3': 149126ce117eSsklower *--cp = '\0'; 149226ce117eSsklower log(lfp, "Note: %s\n",s); 149326ce117eSsklower return(response()); 1494156664fbSralph 1495156664fbSralph default: 1496156664fbSralph s--; 1497156664fbSralph /* fall into... */ 1498156664fbSralph case '\1': 1499156664fbSralph case '\2': 1500aca7acf4Sralph nerrs++; 1501156664fbSralph if (*s != '\n') { 1502d49851feSralph if (!iamremote) { 1503d49851feSralph fflush(stdout); 1504156664fbSralph (void) write(2, s, cp - s); 1505d49851feSralph } 1506978e44c9Sralph if (lfp != NULL) 1507156664fbSralph (void) fwrite(s, 1, cp - s, lfp); 1508978e44c9Sralph } 150915867ad7Sralph if (resp[0] == '\2') 1510*bdfadfffSbostic lostconn(0); 151101de96ecSralph return(-1); 1512978e44c9Sralph } 1513978e44c9Sralph } 1514978e44c9Sralph 1515aca7acf4Sralph /* 1516aca7acf4Sralph * Remove temporary files and do any cleanup operations before exiting. 1517aca7acf4Sralph */ 151821439bbcSbostic void 1519*bdfadfffSbostic cleanup(signo) 1520*bdfadfffSbostic int signo; 1521978e44c9Sralph { 152216580127Seric (void) unlink(tempfile); 1523aca7acf4Sralph exit(1); 1524978e44c9Sralph } 152526ce117eSsklower 1526*bdfadfffSbostic static void 1527*bdfadfffSbostic #if __STDC__ 1528*bdfadfffSbostic note(const char *fmt, ...) 1529*bdfadfffSbostic #else 1530*bdfadfffSbostic note(fmt, va_alist) 15316d57652cStorek char *fmt; 1532*bdfadfffSbostic va_dcl 1533*bdfadfffSbostic #endif 153426ce117eSsklower { 153526ce117eSsklower static char buf[BUFSIZ]; 1536*bdfadfffSbostic va_list ap; 1537*bdfadfffSbostic #if __STDC__ 1538*bdfadfffSbostic va_start(ap, fmt); 1539*bdfadfffSbostic #else 1540*bdfadfffSbostic va_start(ap); 1541*bdfadfffSbostic #endif 1542*bdfadfffSbostic (void)vsnprintf(buf, sizeof(buf), fmt, ap); 1543*bdfadfffSbostic va_end(ap); 154426ce117eSsklower comment(buf); 154526ce117eSsklower } 154626ce117eSsklower 1547*bdfadfffSbostic static void 154826ce117eSsklower comment(s) 154926ce117eSsklower char *s; 155026ce117eSsklower { 1551*bdfadfffSbostic char c; 1552*bdfadfffSbostic 1553*bdfadfffSbostic c = '\3'; 155426ce117eSsklower write(rem, &c, 1); 155526ce117eSsklower write(rem, s, strlen(s)); 155626ce117eSsklower c = '\n'; 155726ce117eSsklower write(rem, &c, 1); 155826ce117eSsklower } 1559