summaryrefslogtreecommitdiff
path: root/usr.bin/rdist/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/rdist/common.c')
-rw-r--r--usr.bin/rdist/common.c995
1 files changed, 995 insertions, 0 deletions
diff --git a/usr.bin/rdist/common.c b/usr.bin/rdist/common.c
new file mode 100644
index 00000000000..80c9b119f62
--- /dev/null
+++ b/usr.bin/rdist/common.c
@@ -0,0 +1,995 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char RCSid[] =
+"$Id: common.c,v 1.1 1996/02/03 12:12:12 dm Exp $";
+
+static char sccsid[] = "@(#)common.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* !lint */
+
+/*
+ * Things common to both the client and server.
+ */
+
+#include "defs.h"
+#if defined(NEED_UTIME_H)
+#include <utime.h>
+#endif /* defined(NEED_UTIME_H) */
+
+/*
+ * Variables common to both client and server
+ */
+char host[MAXHOSTNAMELEN]; /* Name of this host */
+UID_T userid = (UID_T)-1; /* User's UID */
+GID_T groupid = (GID_T)-1; /* User's GID */
+char *homedir = NULL; /* User's $HOME */
+char *locuser = NULL; /* Local User's name */
+int isserver = FALSE; /* We're the server */
+int amchild = 0; /* This PID is a child */
+int do_fork = 1; /* Fork child process */
+char *currenthost = NULL; /* Current client hostname */
+char *progname = NULL; /* Name of this program */
+int rem_r = -1; /* Client file descriptor */
+int rem_w = -1; /* Client file descriptor */
+struct passwd *pw = NULL; /* Local user's pwd entry */
+int contimedout = FALSE; /* Connection timed out */
+int proto_version = -1; /* Protocol version */
+int rtimeout = RTIMEOUT; /* Response time out */
+jmp_buf finish_jmpbuf; /* Finish() jmp buffer */
+char **realargv; /* Real main() argv */
+int realargc; /* Real main() argc */
+opt_t options = 0; /* Global install options */
+
+/*
+ * Front end to write() that handles partial write() requests.
+ */
+extern WRITE_RETURN_T xwrite(fd, buf, len)
+ int fd;
+ void *buf;
+ WRITE_AMT_T len;
+{
+ WRITE_AMT_T nleft = len;
+ WRITE_RETURN_T nwritten;
+ register char *ptr = buf;
+
+ while (nleft > 0) {
+ if ((nwritten = write(fd, ptr, nleft)) <= 0) {
+ return nwritten;
+ }
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+
+ return len;
+}
+
+/*
+ * Set program name
+ */
+extern void setprogname(argv)
+ char **argv;
+{
+ register char *cp;
+
+ if (!progname) {
+ progname = strdup(argv[0]);
+ if (cp = strrchr(progname, '/'))
+ progname = cp + 1;
+ }
+}
+
+/*
+ * Do run-time initialization
+ */
+extern int init(argc, argv, envp)
+ /*ARGSUSED*/
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register int i;
+ register char *cp;
+
+ if (!isserver)
+ (void) signal(SIGSEGV, sighandler);
+
+ setprogname(argv);
+
+ /*
+ * Save a copy of our argc and argv before setargs() overwrites them
+ */
+ realargc = argc;
+ realargv = (char **) xmalloc(sizeof(char *) * (argc+1));
+ for (i = 0; i < argc; i++)
+ realargv[i] = strdup(argv[i]);
+
+#if defined(SETARGS)
+ setargs_settup(argc, argv, envp);
+#endif /* SETARGS */
+
+ pw = getpwuid(userid = getuid());
+ if (pw == NULL) {
+ error("Your user id (%d) is not known to this system.",
+ getuid());
+ return(-1);
+ }
+
+ debugmsg(DM_MISC, "UserID = %d pwname = '%s' home = '%s'\n",
+ userid, pw->pw_name, pw->pw_dir);
+ homedir = strdup(pw->pw_dir);
+ locuser = strdup(pw->pw_name);
+ groupid = pw->pw_gid;
+ gethostname(host, sizeof(host));
+ if ((cp = strchr(host, '.')) != NULL)
+ *cp = CNULL;
+
+ /*
+ * If we're not root, disable paranoid ownership checks
+ * since normal users cannot chown() files.
+ */
+ if (!isserver && userid != 0) {
+ FLAG_ON(options, DO_NOCHKOWNER);
+ FLAG_ON(options, DO_NOCHKGROUP);
+ }
+
+ return(0);
+}
+
+/*
+ * Finish things up before ending.
+ */
+extern void finish()
+{
+ extern jmp_buf finish_jmpbuf;
+
+ debugmsg(DM_CALL,
+ "finish() called: do_fork = %d amchild = %d isserver = %d",
+ do_fork, amchild, isserver);
+ cleanup();
+
+ /*
+ * There's no valid finish_jmpbuf for the rdist master parent.
+ */
+ if (!do_fork || amchild || isserver) {
+ longjmp(finish_jmpbuf, 1);
+ /*NOTREACHED*/
+ error("Unexpected failure of longjmp() in finish()");
+ exit(2);
+ } else
+ exit(1);
+}
+
+/*
+ * Handle lost connections
+ */
+extern void lostconn()
+{
+ /* Prevent looping */
+ (void) signal(SIGPIPE, SIG_IGN);
+
+ rem_r = rem_w = -1; /* Ensure we don't try to send to server */
+ checkhostname();
+ error("Lost connection to %s",
+ (currenthost) ? currenthost : "(unknown)");
+
+ finish();
+}
+
+/*
+ * Do a core dump
+ */
+extern void coredump()
+{
+ error("Segmentation violation - dumping core [PID = %d, %s]",
+ getpid(),
+ (isserver) ? "isserver" : ((amchild) ? "amchild" : "parent"));
+ abort();
+ /*NOTREACHED*/
+ fatalerr("Abort failed - no core dump. Exiting...");
+}
+
+/*
+ * General signal handler
+ */
+extern void sighandler(sig)
+ int sig;
+{
+ debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
+
+ switch (sig) {
+ case SIGALRM:
+ contimedout = TRUE;
+ checkhostname();
+ error("Response time out");
+ finish();
+ break;
+
+ case SIGPIPE:
+ lostconn();
+ break;
+
+ case SIGFPE:
+ debug = !debug;
+ break;
+
+ case SIGSEGV:
+ coredump();
+ break;
+
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ finish();
+ break;
+
+ default:
+ fatalerr("No signal handler defined for signal %d.", sig);
+ }
+}
+
+/*
+ * Function to actually send the command char and message to the
+ * remote host.
+ */
+static int sendcmdmsg(cmd, msg)
+ char cmd;
+ char *msg;
+{
+ int len;
+
+ if (rem_w < 0)
+ return(-1);
+
+ /*
+ * All commands except C_NONE should have a newline
+ */
+ if (cmd != C_NONE && !strchr(msg + 1, '\n'))
+ (void) strcat(msg + 1, "\n");
+
+ if (cmd == C_NONE)
+ len = strlen(msg);
+ else {
+ len = strlen(msg + 1) + 1;
+ msg[0] = cmd;
+ }
+
+ debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
+ cmd, cmd,
+ (cmd == C_NONE) ? len-1 : len-2,
+ (cmd == C_NONE) ? msg : msg + 1);
+
+ return(!(xwrite(rem_w, msg, len) == len));
+}
+
+/*
+ * Send a command message to the remote host.
+ * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
+ * The fmt and arg? arguments are optional.
+ */
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_STDARG
+/*
+ * Stdarg frontend to sendcmdmsg()
+ */
+extern int sendcmd(char cmd, char *fmt, ...)
+{
+ static char buf[BUFSIZ];
+ va_list args;
+
+ va_start(args, fmt);
+ if (fmt)
+ (void) vsprintf((cmd == C_NONE) ? buf : buf + 1, fmt, args);
+ else
+ buf[1] = CNULL;
+ va_end(args);
+
+ return(sendcmdmsg(cmd, buf));
+}
+#endif /* ARG_TYPE == ARG_STDARG */
+
+#if defined(ARG_TYPE) && ARG_TYPE == ARG_VARARGS
+/*
+ * Varargs frontend to sendcmdmsg()
+ */
+extern int sendcmd(va_alist)
+ va_dcl
+{
+ static char buf[BUFSIZ];
+ va_list args;
+ char cmd;
+ char *fmt;
+
+ va_start(args);
+ /* XXX The "int" is necessary as a workaround for broken varargs */
+ cmd = (char) va_arg(args, int);
+ fmt = va_arg(args, char *);
+ if (fmt)
+ (void) vsprintf((cmd == C_NONE) ? buf : buf + 1, fmt, args);
+ else
+ buf[1] = CNULL;
+ va_end(args);
+
+ return(sendcmdmsg(cmd, buf));
+}
+#endif /* ARG_TYPE == ARG_VARARGS */
+
+#if !defined(ARG_TYPE)
+/*
+ * Stupid frontend to sendcmdmsg()
+ */
+/*VARARGS2*/
+extern int sendcmd(cmd, fmt, a1, a2, a3, a4, a5, a6, a7, a8)
+ char cmd;
+ char *fmt;
+{
+ static char buf[BUFSIZ];
+
+ if (fmt)
+ (void) sprintf((cmd == C_NONE) ? buf : buf + 1,
+ fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+ else
+ buf[1] = CNULL;
+
+ return(sendcmdmsg(cmd, buf));
+}
+#endif /* !ARG_TYPE */
+
+/*
+ * Internal variables and routines for reading lines from the remote.
+ */
+static u_char rembuf[BUFSIZ];
+static u_char *remptr;
+static int remleft;
+
+#define remc() (--remleft < 0 ? remmore() : *remptr++)
+
+/*
+ * Back end to remote read()
+ */
+static int remread(fd, buf, bufsiz)
+ int fd;
+ u_char *buf;
+ int bufsiz;
+{
+ return(read(fd, (char *)buf, bufsiz));
+}
+
+static int remmore()
+{
+ (void) signal(SIGALRM, sighandler);
+ (void) alarm(rtimeout);
+
+ remleft = remread(rem_r, rembuf, sizeof(rembuf));
+
+ (void) alarm(0);
+
+ if (remleft < 0)
+ return (-2); /* error */
+ if (remleft == 0)
+ return (-1); /* EOF */
+ remptr = rembuf;
+ remleft--;
+ return (*remptr++);
+}
+
+/*
+ * Read an input line from the remote. Return the number of bytes
+ * stored (equivalent to strlen(p)). If `cleanup' is set, EOF at
+ * the beginning of a line is returned as EOF (-1); other EOFs, or
+ * errors, call cleanup() or lostconn(). In other words, unless
+ * the third argument is nonzero, this routine never returns failure.
+ */
+extern int remline(buffer, space, doclean)
+ register u_char *buffer;
+ int space;
+ int doclean;
+{
+ register int c, left = space;
+ register u_char *p = buffer;
+
+ if (rem_r < 0) {
+ error("Cannot read remote input: Remote descriptor not open.");
+ return(-1);
+ }
+
+ while (left > 0) {
+ if ((c = remc()) < -1) { /* error */
+ if (doclean) {
+ finish();
+ /*NOTREACHED*/
+ }
+ lostconn();
+ /*NOTREACHED*/
+ }
+ if (c == -1) { /* got EOF */
+ if (doclean) {
+ if (left == space)
+ return (-1);/* signal proper EOF */
+ finish(); /* improper EOF */
+ /*NOTREACHED*/
+ }
+ lostconn();
+ /*NOTREACHED*/
+ }
+ if (c == '\n') {
+ *p = CNULL;
+
+ if (debug) {
+ static char mbuf[BUFSIZ];
+
+ (void) sprintf(mbuf,
+ "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
+ buffer[0], buffer[0],
+ buffer + 1);
+
+ debugmsg(DM_PROTO, "%s", mbuf);
+ }
+
+ return (space - left);
+ }
+ *p++ = c;
+ left--;
+ }
+
+ /* this will probably blow the entire session */
+ error("remote input line too long");
+ p[-1] = CNULL; /* truncate */
+ return (space);
+}
+
+/*
+ * Non-line-oriented remote read.
+ */
+readrem(p, space)
+ char *p;
+ register int space;
+{
+ if (remleft <= 0) {
+ /*
+ * Set remote time out alarm.
+ */
+ (void) signal(SIGALRM, sighandler);
+ (void) alarm(rtimeout);
+
+ remleft = remread(rem_r, rembuf, sizeof(rembuf));
+
+ (void) alarm(0);
+ remptr = rembuf;
+ }
+
+ if (remleft <= 0)
+ return (remleft);
+ if (remleft < space)
+ space = remleft;
+
+ bcopy((char *) remptr, p, space);
+
+ remptr += space;
+ remleft -= space;
+
+ return (space);
+}
+
+/*
+ * Get the user name for the uid.
+ */
+extern char *getusername(uid, file, opts)
+ UID_T uid;
+ char *file;
+ opt_t opts;
+{
+ static char buf[100];
+ static UID_T lastuid = (UID_T)-1;
+ struct passwd *pwd = NULL;
+
+ /*
+ * The value of opts may have changed so we always
+ * do the opts check.
+ */
+ if (IS_ON(opts, DO_NUMCHKOWNER)) {
+ (void) sprintf(buf, ":%d", uid);
+ return(buf);
+ }
+
+ /*
+ * Try to avoid getpwuid() call.
+ */
+ if (lastuid == uid && buf[0])
+ return(buf);
+
+ lastuid = uid;
+
+ if ((pwd = getpwuid(uid)) == NULL) {
+ message(MT_WARNING,
+ "%s: No password entry for uid %d", file, uid);
+ (void) sprintf(buf, ":%d", uid);
+ } else
+ (void) strcpy(buf, pwd->pw_name);
+
+ return(buf);
+}
+
+/*
+ * Get the group name for the gid.
+ */
+extern char *getgroupname(gid, file, opts)
+ GID_T gid;
+ char *file;
+ opt_t opts;
+{
+ static char buf[100];
+ static GID_T lastgid = (GID_T)-1;
+ struct group *grp = NULL;
+
+ /*
+ * The value of opts may have changed so we always
+ * do the opts check.
+ */
+ if (IS_ON(opts, DO_NUMCHKGROUP)) {
+ (void) sprintf(buf, ":%d", gid);
+ return(buf);
+ }
+
+ /*
+ * Try to avoid getgrgid() call.
+ */
+ if (lastgid == gid && buf[0])
+ return(buf);
+
+ lastgid = gid;
+
+ if ((grp = (struct group *)getgrgid(gid)) == NULL) {
+ message(MT_WARNING, "%s: No name for group %d", file, gid);
+ (void) sprintf(buf, ":%d", gid);
+ } else
+ (void) strcpy(buf, grp->gr_name);
+
+ return(buf);
+}
+
+/*
+ * Read a response from the remote host.
+ */
+extern int response()
+{
+ static u_char resp[BUFSIZ];
+ u_char *s;
+ int n;
+
+ debugmsg(DM_CALL, "response() start\n");
+
+ n = remline(s = resp, sizeof(resp), 0);
+
+ n--;
+ switch (*s++) {
+ case C_ACK:
+ debugmsg(DM_PROTO, "received ACK\n");
+ return(0);
+ case C_LOGMSG:
+ if (n > 0) {
+ message(MT_CHANGE, "%s", s);
+ return(1);
+ }
+ debugmsg(DM_PROTO, "received EMPTY logmsg\n");
+ return(0);
+ case C_NOTEMSG:
+ if (s)
+ message(MT_NOTICE, "%s", s);
+ return(response());
+
+ default:
+ s--;
+ n++;
+ /* fall into... */
+
+ case C_ERRMSG: /* Normal error message */
+ if (s)
+ message(MT_NERROR, "%s", s);
+ return(-1);
+
+ case C_FERRMSG: /* Fatal error message */
+ if (s)
+ message(MT_FERROR, "%s", s);
+ finish();
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * This should be in expand.c but the other routines call other modules
+ * that we don't want to load in.
+ *
+ * Expand file names beginning with `~' into the
+ * user's home directory path name. Return a pointer in buf to the
+ * part corresponding to `file'.
+ */
+extern char *exptilde(ebuf, file)
+ char *ebuf;
+ register char *file;
+{
+ register char *s1, *s2, *s3;
+ extern char *homedir;
+
+ if (*file != '~') {
+ (void) strcpy(ebuf, file);
+ return(ebuf);
+ }
+ if (*++file == CNULL) {
+ s2 = homedir;
+ s3 = NULL;
+ } else if (*file == '/') {
+ s2 = homedir;
+ s3 = file;
+ } else {
+ s3 = file;
+ while (*s3 && *s3 != '/')
+ s3++;
+ if (*s3 == '/')
+ *s3 = CNULL;
+ else
+ s3 = NULL;
+ if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
+ if ((pw = getpwnam(file)) == NULL) {
+ error("%s: unknown user name", file);
+ if (s3 != NULL)
+ *s3 = '/';
+ return(NULL);
+ }
+ }
+ if (s3 != NULL)
+ *s3 = '/';
+ s2 = pw->pw_dir;
+ }
+ for (s1 = ebuf; *s1++ = *s2++; )
+ ;
+ s2 = --s1;
+ if (s3 != NULL) {
+ s2++;
+ while (*s1++ = *s3++)
+ ;
+ }
+ return(s2);
+}
+
+#if defined(DIRECT_RCMD)
+/*
+ * Set our effective user id to the user running us.
+ * This should be the uid we do most of our work as.
+ */
+extern int becomeuser()
+{
+ int r = 0;
+
+#if defined(HAVE_SAVED_IDS)
+ r = seteuid(userid);
+#else
+ r = setreuid(0, userid);
+#endif /* HAVE_SAVED_IDS */
+
+ if (r < 0)
+ error("becomeuser %d failed: %s (ruid = %d euid = %d)",
+ userid, SYSERR, getuid(), geteuid());
+
+ return(r);
+}
+#endif /* DIRECT_RCMD */
+
+#if defined(DIRECT_RCMD)
+/*
+ * Set our effective user id to "root" (uid = 0)
+ */
+extern int becomeroot()
+{
+ int r = 0;
+
+#if defined(HAVE_SAVED_IDS)
+ r = seteuid(0);
+#else
+ r = setreuid(userid, 0);
+#endif /* HAVE_SAVED_IDS */
+
+ if (r < 0)
+ error("becomeroot failed: %s (ruid = %d euid = %d)",
+ SYSERR, getuid(), geteuid());
+
+ return(r);
+}
+#endif /* DIRECT_RCMD */
+
+/*
+ * Set access and modify times of a given file
+ */
+extern int setfiletime(file, atime, mtime)
+ char *file;
+ time_t atime;
+ time_t mtime;
+{
+#if SETFTIME_TYPE == SETFTIME_UTIMES
+ struct timeval tv[2];
+
+ if (atime != 0 && mtime != 0) {
+ tv[0].tv_sec = atime;
+ tv[1].tv_sec = mtime;
+ tv[0].tv_usec = tv[1].tv_usec = (time_t) 0;
+ return(utimes(file, tv));
+ } else /* Set to current time */
+ return(utimes(file, (struct timeval *) NULL));
+
+#endif /* SETFTIME_UTIMES */
+
+#if SETFTIME_TYPE == SETFTIME_UTIME
+ struct utimbuf utbuf;
+
+ if (atime != 0 && mtime != 0) {
+ utbuf.actime = atime;
+ utbuf.modtime = mtime;
+ return(utime(file, &utbuf));
+ } else /* Set to current time */
+ return(utime(file, (struct utimbuf *)NULL));
+#endif /* SETFTIME_UTIME */
+
+#if !defined(SETFTIME_TYPE)
+ There is no "SETFTIME_TYPE" defined!
+#endif /* SETFTIME_TYPE */
+}
+
+/*
+ * Get version info
+ */
+extern char *getversion()
+{
+ static char buff[BUFSIZ];
+
+ (void) sprintf(buff,
+ "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
+ DISTVERSION, PATCHLEVEL, DISTSTATUS,
+ VERSION, DISTVERSION, PATCHLEVEL);
+
+ return(buff);
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ * This is now common to both server and client
+ */
+void runcommand(cmd)
+ char *cmd;
+{
+ int fd[2], pid, i;
+ int status;
+ register char *cp, *s;
+ char sbuf[BUFSIZ], buf[BUFSIZ];
+
+ if (pipe(fd) < 0) {
+ error("pipe of %s failed: %s", cmd, SYSERR);
+ return;
+ }
+
+ if ((pid = fork()) == 0) {
+ /*
+ * Return everything the shell commands print.
+ */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open(_PATH_DEVNULL, O_RDONLY);
+ (void) dup(fd[PIPE_WRITE]);
+ (void) dup(fd[PIPE_WRITE]);
+ (void) close(fd[PIPE_READ]);
+ (void) close(fd[PIPE_WRITE]);
+ (void) execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ (void) close(fd[PIPE_WRITE]);
+ s = sbuf;
+ *s++ = C_LOGMSG;
+ while ((i = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
+ cp = buf;
+ do {
+ *s++ = *cp++;
+ if (cp[-1] != '\n') {
+ if (s < (char *) &sbuf[sizeof(sbuf)-1])
+ continue;
+ *s++ = '\n';
+ }
+ /*
+ * Throw away blank lines.
+ */
+ if (s == &sbuf[2]) {
+ s--;
+ continue;
+ }
+ if (isserver)
+ (void) xwrite(rem_w, sbuf, s - sbuf);
+ else {
+ *s = CNULL;
+ message(MT_INFO, "%s", sbuf+1);
+ }
+ s = &sbuf[1];
+ } while (--i);
+ }
+ if (s > (char *) &sbuf[1]) {
+ *s++ = '\n';
+ if (isserver)
+ (void) xwrite(rem_w, sbuf, s - sbuf);
+ else {
+ *s = CNULL;
+ message(MT_INFO, "%s", sbuf+1);
+ }
+ }
+ while ((i = wait(&status)) != pid && i != -1)
+ ;
+ if (i == -1)
+ status = -1;
+ (void) close(fd[PIPE_READ]);
+ if (status)
+ error("shell returned %d", status);
+ else if (isserver)
+ ack();
+}
+
+/*
+ * Malloc with error checking
+ */
+char *xmalloc(amt)
+ int amt;
+{
+ char *ptr;
+ extern POINTER *malloc();
+
+ if ((ptr = (char *)malloc(amt)) == NULL)
+ fatalerr("Cannot malloc %d bytes of memory.", amt);
+
+ return(ptr);
+}
+
+/*
+ * realloc with error checking
+ */
+char *xrealloc(baseptr, amt)
+ char *baseptr;
+ unsigned int amt;
+{
+ char *new;
+ extern POINTER *realloc();
+
+ if ((new = (char *)realloc(baseptr, amt)) == NULL)
+ fatalerr("Cannot realloc %d bytes of memory.", amt);
+
+ return(new);
+}
+
+/*
+ * calloc with error checking
+ */
+char *xcalloc(num, esize)
+ unsigned num;
+ unsigned esize;
+{
+ char *ptr;
+ extern POINTER *calloc();
+
+ if ((ptr = (char *)calloc(num, esize)) == NULL)
+ fatalerr("Cannot calloc %d * %d = %d bytes of memory.",
+ num, esize, num * esize);
+
+ return(ptr);
+}
+
+/*
+ * Private version of basename()
+ */
+extern char *xbasename(path)
+ char *path;
+{
+ register char *cp;
+
+ if (cp = strrchr(path, '/'))
+ return(cp+1);
+ else
+ return(path);
+}
+
+/*
+ * Take a colon (':') seperated path to a file and
+ * search until a component of that path is found and
+ * return the found file name.
+ */
+extern char *searchpath(path)
+ char *path;
+{
+ register char *cp;
+ register char *file;
+ struct stat statbuf;
+
+ for (; ;) {
+ if (!path)
+ return((char *) NULL);
+ file = path;
+ cp = strchr(path, ':');
+ if (cp) {
+ path = cp + 1;
+ *cp = CNULL;
+ } else
+ path = NULL;
+ if (stat(file, &statbuf) == 0)
+ return(file);
+ /* Put back what we zapped */
+ if (path)
+ *cp = ':';
+ }
+}
+
+/*
+ * Set line buffering.
+ */
+extern int
+mysetlinebuf(fp)
+ FILE *fp;
+{
+#if SETBUF_TYPE == SETBUF_SETLINEBUF
+ return(setlinebuf(fp));
+#endif /* SETBUF_SETLINEBUF */
+#if SETBUF_TYPE == SETBUF_SETVBUF
+ return(setvbuf(stdout, NULL, _IOLBF, BUFSIZ));
+#endif /* SETBUF_SETVBUF */
+#if !defined(SETBUF_TYPE)
+ No SETBUF_TYPE is defined!
+#endif /* SETBUF_TYPE */
+}
+
+/*
+ * Our interface to system call to get a socket pair.
+ */
+int
+getsocketpair(domain, type, protocol, sv)
+ int domain;
+ int type;
+ int protocol;
+ int sv[];
+{
+#if SOCKPAIR_TYPE == SOCKPAIR_SOCKETPAIR
+ return(socketpair(domain, type, protocol, sv));
+#endif /* SOCKPAIR_SOCKETPAIR */
+#if SOCKPAIR_TYPE == SOCKPAIR_SPIPE
+ return(spipe(sv));
+#endif /* SOCKPAIR_SPIPE */
+#if !defined(SOCKPAIR_TYPE)
+ No SOCKPAIR_TYPE is defined!
+#endif /* SOCKPAIR_TYPE */
+}