/* * Server routine to read requests and process them. * Commands are: * Tname - Transmit file if out of date * Vname - Verify if file out of date or not * Qname - Query if file exists. Return mtime & size if it does. */ void server() { char cmdbuf[BUFSIZ]; register char *cp; signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); signal(SIGPIPE, cleanup); rem = 0; oumask = umask(0); (void) sprintf(buf, "V%d\n", VERSION); (void) write(rem, buf, strlen(buf)); for (;;) { cp = cmdbuf; if (read(rem, cp, 1) <= 0) return; if (*cp++ == '\n') { error("server: expected control record\n"); continue; } do { if (read(rem, cp, 1) != 1) cleanup(0); } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]); *--cp = '\0'; cp = cmdbuf; switch (*cp++) { case 'T': /* init target file/directory name */ catname = 1; /* target should be directory */ goto dotarget; case 't': /* init target file/directory name */ catname = 0; dotarget: if (exptilde(target, cp) == NULL) continue; tp = target; while (*tp) tp++; ack(); continue; case 'R': /* Transfer a regular file. */ recvf(cp, S_IFREG); continue; case 'D': /* Transfer a directory. */ recvf(cp, S_IFDIR); continue; case 'K': /* Transfer symbolic link. */ recvf(cp, S_IFLNK); continue; case 'k': /* Transfer hard link. */ hardlink(cp); continue; case 'E': /* End. (of directory) */ *tp = '\0'; if (catname <= 0) { error("server: too many 'E's\n"); continue; } tp = stp[--catname]; *tp = '\0'; ack(); continue; case 'C': /* Clean. Cleanup a directory */ clean(cp); continue; case 'Q': /* Query. Does the file/directory exist? */ query(cp); continue; case 'S': /* Special. Execute commands */ dospecial(cp); continue; #ifdef notdef /* * These entries are reserved but not currently used. * The intent is to allow remote hosts to have master copies. * Currently, only the host rdist runs on can have masters. */ case 'X': /* start a new list of files to exclude */ except = bp = NULL; case 'x': /* add name to list of files to exclude */ if (*cp == '\0') { ack(); continue; } if (*cp == '~') { if (exptilde(buf, cp) == NULL) continue; cp = buf; } if (bp == NULL) except = bp = expand(makeblock(NAME, cp), E_VARS); else bp->b_next = expand(makeblock(NAME, cp), E_VARS); while (bp->b_next != NULL) bp = bp->b_next; ack(); continue; case 'I': /* Install. Transfer file if out of date. */ opts = 0; while (*cp >= '0' && *cp <= '7') opts = (opts << 3) | (*cp++ - '0'); if (*cp++ != ' ') { error("server: options not delimited\n"); return; } install(cp, opts); continue; case 'L': /* Log. save message in log file */ log(lfp, cp); continue; #endif case '\1': nerrs++; continue; case '\2': return; default: error("server: unknown command '%s'\n", cp); case '\0': continue; } } }
/* * Perform parameter expansion, command substitution and arithmetic * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. * This is used to expand word in ${var+word} etc. * If EXP_FULL or EXP_CASE are set, keep and/or generate CTLESC * characters to allow for further processing. * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. */ static char * argstr(char *p, int flag) { char c; int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ int firsteq = 1; int split_lit; int lit_quoted; split_lit = flag & EXP_SPLIT_LIT; lit_quoted = flag & EXP_LIT_QUOTED; flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { CHECKSTRSPACE(2, expdest); switch (c = *p++) { case '\0': return (p - 1); case CTLENDVAR: case CTLENDARI: return (p); case CTLQUOTEMARK: lit_quoted = 1; /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) USTPUTC(c, expdest); break; case CTLQUOTEEND: lit_quoted = 0; break; case CTLESC: if (quotes) USTPUTC(c, expdest); c = *p++; USTPUTC(c, expdest); if (split_lit && !lit_quoted) recordregion(expdest - stackblock() - (quotes ? 2 : 1), expdest - stackblock(), 0); break; case CTLVAR: p = evalvar(p, flag); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c & CTLQUOTE, flag); argbackq = argbackq->next; break; case CTLARI: p = expari(p); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ USTPUTC(c, expdest); if (split_lit && !lit_quoted) recordregion(expdest - stackblock() - 1, expdest - stackblock(), 0); if (flag & EXP_VARTILDE && *p == '~' && (c != '=' || firsteq)) { if (c == '=') firsteq = 0; p = exptilde(p, flag); } break; default: USTPUTC(c, expdest); if (split_lit && !lit_quoted) recordregion(expdest - stackblock() - 1, expdest - stackblock(), 0); } } }
STATIC void argstr(shinstance *psh, char *p, int flag) { char c; int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ int firsteq = 1; const char *ifs = NULL; int ifs_split = EXP_IFS_SPLIT; if (flag & EXP_IFS_SPLIT) ifs = ifsset(psh) ? ifsval(psh) : " \t\n"; if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(psh, p, flag); for (;;) { switch (c = *p++) { case '\0': case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ return; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(psh, c, psh->expdest); ifs_split = 0; break; case CTLQUOTEEND: ifs_split = EXP_IFS_SPLIT; break; case CTLESC: if (quotes) STPUTC(psh, c, psh->expdest); c = *p++; STPUTC(psh, c, psh->expdest); break; case CTLVAR: p = evalvar(psh, p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(psh, psh->argbackq->n, c & CTLQUOTE, flag); psh->argbackq = psh->argbackq->next; break; case CTLENDARI: expari(psh, flag); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ STPUTC(psh, c, psh->expdest); if (flag & EXP_VARTILDE && *p == '~') { if (c == '=') { if (firsteq) firsteq = 0; else break; } p = exptilde(psh, p, flag); } break; default: STPUTC(psh, c, psh->expdest); if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) { /* We need to get the output split here... */ recordregion(psh, (int)(psh->expdest - stackblock(psh) - 1), (int)(psh->expdest - stackblock(psh)), 0); } break; } } }
/* * Update the file(s) if they are different. * destdir = 1 if destination should be a directory * (i.e., more than one source is being copied to the same destination). */ int install (char *src, char *dest, int destdir, int opts) { char *rname; char destcopy[BUFSIZ]; if (dest == NULL) { opts &= ~WHOLE; /* WHOLE mode only useful if renaming */ dest = src; } if (nflag || debug) { printf("%s%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install", opts & WHOLE ? " -w" : "", opts & YOUNGER ? " -y" : "", opts & COMPARE ? " -b" : "", opts & REMOVE ? " -R" : "", opts & QUERYM ? " -Q" : "", src, dest); if (nflag) return; } rname = exptilde(target, src); if (rname == NULL) return; tp = target; strcpy (basename, target); while (*tp) tp++; /* * If we are renaming a directory and we want to preserve * the directory heirarchy (-w), we must strip off the leading * directory name and preserve the rest. */ if (opts & WHOLE) { rname = target; while (*rname == '/') rname++; destdir = 1; } else { rname = rindex(target, '/'); if (rname == NULL) rname = target; else rname++; } if (debug) printf("target = %s, rname = %s\n", target, rname); /* * Pass the destination file/directory name to remote. */ if (initdir (destdir, dest) < 0) return; strcpy(destcopy, dest); Tdest = destcopy; sendf(rname, opts); Tdest = 0; }
/* * Compare the mtime of file to the list of time stamps. */ static void cmptime(char *name, struct subcmd *sbcmds, char **env) { struct subcmd *sc; struct stat stb; debugmsg(DM_CALL, "cmptime(%s)", name); if (except(name)) return; if (nflag) { (void) printf("comparing dates: %s\n", name); return; } /* * first time cmptime() is called? */ if (ptarget == NULL) { if (exptilde(target, name, sizeof(target)) == NULL) return; ptarget = name = target; while (*ptarget) ptarget++; } if (access(name, R_OK) < 0 || stat(name, &stb) < 0) { error("%s: cannot access file: %s", name, SYSERR); return; } if (S_ISDIR(stb.st_mode)) { rcmptime(&stb, sbcmds, env); return; } else if (!S_ISREG(stb.st_mode)) { error("%s: not a plain file", name); return; } if (stb.st_mtime > lastmod) { message(MT_INFO, "%s: file is newer", name); for (sc = sbcmds; sc != NULL; sc = sc->sc_next) { char buf[BUFSIZ]; if (sc->sc_type != SPECIAL) continue; if (sc->sc_args != NULL && !inlist(sc->sc_args, name)) continue; (void) snprintf(buf, sizeof(buf), "%s=%s;%s", E_LOCFILE, name, sc->sc_name); message(MT_CHANGE, "special \"%s\"", buf); if (*env) { size_t len = strlen(*env) + strlen(name) + 2; *env = (char *) xrealloc(*env, len); (void) strlcat(*env, name, len); (void) strlcat(*env, ":", len); } if (IS_ON(options, DO_VERIFY)) continue; runcommand(buf); } } }
/* * Perform parameter expansion, command substitution and arithmetic * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. * This is used to expand word in ${var+word} etc. * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC * characters to allow for further processing. * * If EXP_SPLIT is set, dst receives any complete words produced. */ static char * argstr(char *p, int flag, struct worddest *dst) { char c; int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */ int firsteq = 1; int split_lit; int lit_quoted; split_lit = flag & EXP_SPLIT_LIT; lit_quoted = flag & EXP_LIT_QUOTED; flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { CHECKSTRSPACE(2, expdest); switch (c = *p++) { case '\0': return (p - 1); case CTLENDVAR: case CTLENDARI: return (p); case CTLQUOTEMARK: lit_quoted = 1; /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_SPLIT) != 0 && expdest == stackblock()) dst->state = WORD_QUOTEMARK; break; case CTLQUOTEEND: lit_quoted = 0; break; case CTLESC: c = *p++; if (split_lit && !lit_quoted && strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { NEXTWORD(c, flag, expdest, dst); break; } if (quotes) USTPUTC(CTLESC, expdest); USTPUTC(c, expdest); break; case CTLVAR: p = evalvar(p, flag, dst); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c & CTLQUOTE, flag, dst); argbackq = argbackq->next; break; case CTLARI: p = expari(p, flag, dst); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ if (split_lit && !lit_quoted && strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { NEXTWORD(c, flag, expdest, dst); break; } USTPUTC(c, expdest); if (flag & EXP_VARTILDE && *p == '~' && (c != '=' || firsteq)) { if (c == '=') firsteq = 0; p = exptilde(p, flag); } break; default: if (split_lit && !lit_quoted && strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { NEXTWORD(c, flag, expdest, dst); break; } USTPUTC(c, expdest); } } }
STATIC void argstr(char *p, int flag) { char c; int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ int firsteq = 1; if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { switch (c = *p++) { case '\0': case CTLENDVAR: /* ??? */ goto breakloop; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(c, expdest); break; case CTLESC: if (quotes) STPUTC(c, expdest); c = *p++; STPUTC(c, expdest); break; case CTLVAR: p = evalvar(p, flag); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c & CTLQUOTE, flag); argbackq = argbackq->next; break; case CTLENDARI: expari(flag); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ STPUTC(c, expdest); if (flag & EXP_VARTILDE && *p == '~') { if (c == '=') { if (firsteq) firsteq = 0; else break; } p = exptilde(p, flag); } break; default: STPUTC(c, expdest); } } breakloop:; }