836 lines
17 KiB
C
836 lines
17 KiB
C
/* $OpenBSD: common.c,v 1.40 2019/06/28 13:35:03 deraadt Exp $ */
|
|
|
|
/*
|
|
* 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. 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.
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <grp.h>
|
|
#include <limits.h>
|
|
#include <paths.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "defs.h"
|
|
|
|
/*
|
|
* Things common to both the client and server.
|
|
*/
|
|
|
|
/*
|
|
* Variables common to both client and server
|
|
*/
|
|
char host[HOST_NAME_MAX+1]; /* Name of this host */
|
|
uid_t userid = (uid_t)-1; /* User's UID */
|
|
gid_t groupid = (gid_t)-1; /* User's GID */
|
|
gid_t gidset[NGROUPS_MAX]; /* User's GID list */
|
|
int gidsetlen = 0; /* Number of GIDS in list */
|
|
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 */
|
|
volatile sig_atomic_t contimedout = FALSE; /* Connection timed out */
|
|
int rtimeout = RTIMEOUT; /* Response time out */
|
|
jmp_buf finish_jmpbuf; /* Finish() jmp buffer */
|
|
int setjmp_ok = FALSE; /* setjmp()/longjmp() status */
|
|
char **realargv; /* Real main() argv */
|
|
int realargc; /* Real main() argc */
|
|
opt_t options = 0; /* Global install options */
|
|
char defowner[64] = "bin"; /* Default owner */
|
|
char defgroup[64] = "bin"; /* Default group */
|
|
|
|
static int sendcmdmsg(int, char *, size_t);
|
|
static ssize_t remread(int, u_char *, size_t);
|
|
static int remmore(void);
|
|
|
|
/*
|
|
* Front end to write() that handles partial write() requests.
|
|
*/
|
|
ssize_t
|
|
xwrite(int fd, void *buf, size_t len)
|
|
{
|
|
size_t nleft = len;
|
|
ssize_t nwritten;
|
|
char *ptr = buf;
|
|
|
|
while (nleft > 0) {
|
|
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
|
|
return nwritten;
|
|
}
|
|
nleft -= nwritten;
|
|
ptr += nwritten;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* Do run-time initialization
|
|
*/
|
|
int
|
|
init(int argc, char **argv, char **envp)
|
|
{
|
|
struct passwd *pw;
|
|
int i;
|
|
|
|
/*
|
|
* Save a copy of our argc and argv before setargs() overwrites them
|
|
*/
|
|
realargc = argc;
|
|
realargv = xmalloc(sizeof(char *) * (argc+1));
|
|
for (i = 0; i < argc; i++)
|
|
realargv[i] = xstrdup(argv[i]);
|
|
|
|
pw = getpwuid(userid = getuid());
|
|
if (pw == NULL) {
|
|
error("Your user id (%u) is not known to this system.",
|
|
getuid());
|
|
return(-1);
|
|
}
|
|
|
|
debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
|
|
userid, pw->pw_name, pw->pw_dir);
|
|
homedir = xstrdup(pw->pw_dir);
|
|
locuser = xstrdup(pw->pw_name);
|
|
groupid = pw->pw_gid;
|
|
gidsetlen = getgroups(NGROUPS_MAX, gidset);
|
|
gethostname(host, sizeof(host));
|
|
#if 0
|
|
if ((cp = strchr(host, '.')) != NULL)
|
|
*cp = CNULL;
|
|
#endif
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
void
|
|
finish(void)
|
|
{
|
|
debugmsg(DM_CALL,
|
|
"finish() called: do_fork = %d amchild = %d isserver = %d",
|
|
do_fork, amchild, isserver);
|
|
cleanup(0);
|
|
|
|
/*
|
|
* There's no valid finish_jmpbuf for the rdist master parent.
|
|
*/
|
|
if (!do_fork || amchild || isserver) {
|
|
|
|
if (!setjmp_ok) {
|
|
#ifdef DEBUG_SETJMP
|
|
error("attemping longjmp() without target");
|
|
abort();
|
|
#else
|
|
exit(1);
|
|
#endif
|
|
}
|
|
|
|
longjmp(finish_jmpbuf, 1);
|
|
/*NOTREACHED*/
|
|
error("Unexpected failure of longjmp() in finish()");
|
|
exit(2);
|
|
} else
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Handle lost connections
|
|
*/
|
|
void
|
|
lostconn(void)
|
|
{
|
|
/* 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();
|
|
}
|
|
|
|
/*
|
|
* General signal handler
|
|
*/
|
|
void
|
|
sighandler(int sig)
|
|
{
|
|
int save_errno = errno;
|
|
|
|
/* XXX signal race */
|
|
debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
|
|
|
|
switch (sig) {
|
|
case SIGALRM:
|
|
contimedout = TRUE;
|
|
/* XXX signal race */
|
|
checkhostname();
|
|
error("Response time out");
|
|
finish();
|
|
break;
|
|
|
|
case SIGPIPE:
|
|
/* XXX signal race */
|
|
lostconn();
|
|
break;
|
|
|
|
case SIGFPE:
|
|
debug = !debug;
|
|
break;
|
|
|
|
case SIGHUP:
|
|
case SIGINT:
|
|
case SIGQUIT:
|
|
case SIGTERM:
|
|
/* XXX signal race */
|
|
finish();
|
|
break;
|
|
|
|
default:
|
|
/* XXX signal race */
|
|
fatalerr("No signal handler defined for signal %d.", sig);
|
|
}
|
|
errno = save_errno;
|
|
}
|
|
|
|
/*
|
|
* Function to actually send the command char and message to the
|
|
* remote host.
|
|
*/
|
|
static int
|
|
sendcmdmsg(int cmd, char *msg, size_t msgsize)
|
|
{
|
|
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) strlcat(msg + 1, "\n", msgsize - 1);
|
|
|
|
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 may be NULL, in which case there are no args.
|
|
*/
|
|
int
|
|
sendcmd(char cmd, const char *fmt, ...)
|
|
{
|
|
static char buf[BUFSIZ];
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
if (fmt)
|
|
(void) vsnprintf(buf + (cmd != C_NONE),
|
|
sizeof(buf) - (cmd != C_NONE), fmt, args);
|
|
else
|
|
buf[1] = CNULL;
|
|
va_end(args);
|
|
|
|
return(sendcmdmsg(cmd, buf, sizeof(buf)));
|
|
}
|
|
|
|
/*
|
|
* Internal variables and routines for reading lines from the remote.
|
|
*/
|
|
static u_char rembuf[BUFSIZ];
|
|
static u_char *remptr;
|
|
static ssize_t remleft;
|
|
|
|
#define remc() (--remleft < 0 ? remmore() : *remptr++)
|
|
|
|
/*
|
|
* Back end to remote read()
|
|
*/
|
|
static ssize_t
|
|
remread(int fd, u_char *buf, size_t bufsiz)
|
|
{
|
|
return(read(fd, (char *)buf, bufsiz));
|
|
}
|
|
|
|
static int
|
|
remmore(void)
|
|
{
|
|
(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.
|
|
*/
|
|
int
|
|
remline(u_char *buffer, int space, int doclean)
|
|
{
|
|
int c, left = space;
|
|
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) snprintf(mbuf, sizeof(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.
|
|
*/
|
|
ssize_t
|
|
readrem(char *p, ssize_t 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;
|
|
|
|
memcpy(p, remptr, space);
|
|
|
|
remptr += space;
|
|
remleft -= space;
|
|
|
|
return (space);
|
|
}
|
|
|
|
/*
|
|
* Get the user name for the uid.
|
|
*/
|
|
char *
|
|
getusername(uid_t uid, char *file, opt_t opts)
|
|
{
|
|
static char buf[100];
|
|
static uid_t lastuid = (uid_t)-1;
|
|
const char *name;
|
|
|
|
/*
|
|
* The value of opts may have changed so we always
|
|
* do the opts check.
|
|
*/
|
|
if (IS_ON(opts, DO_NUMCHKOWNER)) {
|
|
(void) snprintf(buf, sizeof(buf), ":%u", uid);
|
|
return(buf);
|
|
}
|
|
|
|
/*
|
|
* Try to avoid passwd lookup.
|
|
*/
|
|
if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
|
|
return(buf);
|
|
|
|
lastuid = uid;
|
|
|
|
if ((name = user_from_uid(uid, 1)) == NULL) {
|
|
if (IS_ON(opts, DO_DEFOWNER) && !isserver)
|
|
(void) strlcpy(buf, defowner, sizeof(buf));
|
|
else {
|
|
message(MT_WARNING,
|
|
"%s: No password entry for uid %u", file, uid);
|
|
(void) snprintf(buf, sizeof(buf), ":%u", uid);
|
|
}
|
|
} else {
|
|
(void) strlcpy(buf, name, sizeof(buf));
|
|
}
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/*
|
|
* Get the group name for the gid.
|
|
*/
|
|
char *
|
|
getgroupname(gid_t gid, char *file, opt_t opts)
|
|
{
|
|
static char buf[100];
|
|
static gid_t lastgid = (gid_t)-1;
|
|
const char *name;
|
|
|
|
/*
|
|
* The value of opts may have changed so we always
|
|
* do the opts check.
|
|
*/
|
|
if (IS_ON(opts, DO_NUMCHKGROUP)) {
|
|
(void) snprintf(buf, sizeof(buf), ":%u", gid);
|
|
return(buf);
|
|
}
|
|
|
|
/*
|
|
* Try to avoid group lookup.
|
|
*/
|
|
if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
|
|
return(buf);
|
|
|
|
lastgid = gid;
|
|
|
|
if ((name = group_from_gid(gid, 1)) == NULL) {
|
|
if (IS_ON(opts, DO_DEFGROUP) && !isserver)
|
|
(void) strlcpy(buf, defgroup, sizeof(buf));
|
|
else {
|
|
message(MT_WARNING, "%s: No name for group %u",
|
|
file, gid);
|
|
(void) snprintf(buf, sizeof(buf), ":%u", gid);
|
|
}
|
|
} else
|
|
(void) strlcpy(buf, name, sizeof(buf));
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/*
|
|
* Read a response from the remote host.
|
|
*/
|
|
int
|
|
response(void)
|
|
{
|
|
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();
|
|
return(-1);
|
|
}
|
|
/*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'.
|
|
*/
|
|
char *
|
|
exptilde(char *ebuf, char *file, size_t ebufsize)
|
|
{
|
|
struct passwd *pw;
|
|
char *pw_dir, *rest;
|
|
static char lastuser[_PW_NAME_LEN + 1];
|
|
static char lastdir[PATH_MAX];
|
|
size_t len;
|
|
|
|
if (*file != '~') {
|
|
notilde:
|
|
(void) strlcpy(ebuf, file, ebufsize);
|
|
return(ebuf);
|
|
}
|
|
pw_dir = homedir;
|
|
if (*++file == CNULL) {
|
|
rest = NULL;
|
|
} else if (*file == '/') {
|
|
rest = file;
|
|
} else {
|
|
rest = file;
|
|
while (*rest && *rest != '/')
|
|
rest++;
|
|
if (*rest == '/')
|
|
*rest = CNULL;
|
|
else
|
|
rest = NULL;
|
|
if (strcmp(locuser, file) != 0) {
|
|
if (strcmp(lastuser, file) != 0) {
|
|
if ((pw = getpwnam(file)) == NULL) {
|
|
error("%s: unknown user name", file);
|
|
if (rest != NULL)
|
|
*rest = '/';
|
|
return(NULL);
|
|
}
|
|
strlcpy(lastuser, pw->pw_name, sizeof(lastuser));
|
|
strlcpy(lastdir, pw->pw_dir, sizeof(lastdir));
|
|
}
|
|
pw_dir = lastdir;
|
|
}
|
|
if (rest != NULL)
|
|
*rest = '/';
|
|
}
|
|
if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
|
|
goto notilde;
|
|
pw_dir = ebuf + len;
|
|
if (rest != NULL) {
|
|
pw_dir++;
|
|
if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
|
|
goto notilde;
|
|
}
|
|
return(pw_dir);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Set access and modify times of a given file
|
|
*/
|
|
int
|
|
setfiletime(char *file, time_t atime, time_t mtime)
|
|
{
|
|
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 = 0;
|
|
return (utimes(file, tv));
|
|
} else /* Set to current time */
|
|
return (utimes(file, NULL));
|
|
}
|
|
|
|
/*
|
|
* Get version info
|
|
*/
|
|
char *
|
|
getversion(void)
|
|
{
|
|
static char buff[BUFSIZ];
|
|
|
|
(void) snprintf(buff, sizeof(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(char *cmd)
|
|
{
|
|
ssize_t nread;
|
|
pid_t pid, wpid;
|
|
char *cp, *s;
|
|
char sbuf[BUFSIZ], buf[BUFSIZ];
|
|
int fd[2], status;
|
|
|
|
if (pipe(fd) == -1) {
|
|
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, (char *)NULL);
|
|
_exit(127);
|
|
}
|
|
(void) close(fd[PIPE_WRITE]);
|
|
s = sbuf;
|
|
*s++ = C_LOGMSG;
|
|
while ((nread = 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 (--nread);
|
|
}
|
|
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 ((wpid = wait(&status)) != pid && wpid != -1)
|
|
;
|
|
if (wpid == -1)
|
|
status = -1;
|
|
(void) close(fd[PIPE_READ]);
|
|
if (status)
|
|
error("shell returned %d", status);
|
|
else if (isserver)
|
|
ack();
|
|
}
|
|
|
|
/*
|
|
* Malloc with error checking
|
|
*/
|
|
void *
|
|
xmalloc(size_t amt)
|
|
{
|
|
void *ptr;
|
|
|
|
if ((ptr = malloc(amt)) == NULL)
|
|
fatalerr("Cannot malloc %zu bytes of memory.", amt);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
/*
|
|
* realloc with error checking
|
|
*/
|
|
void *
|
|
xrealloc(void *baseptr, size_t amt)
|
|
{
|
|
void *new;
|
|
|
|
if ((new = realloc(baseptr, amt)) == NULL)
|
|
fatalerr("Cannot realloc %zu bytes of memory.", amt);
|
|
|
|
return (new);
|
|
}
|
|
|
|
/*
|
|
* calloc with error checking
|
|
*/
|
|
void *
|
|
xcalloc(size_t num, size_t esize)
|
|
{
|
|
void *ptr;
|
|
|
|
if ((ptr = calloc(num, esize)) == NULL)
|
|
fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
|
|
num, esize, num * esize);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
/*
|
|
* Strdup with error checking
|
|
*/
|
|
char *
|
|
xstrdup(const char *str)
|
|
{
|
|
size_t len = strlen(str) + 1;
|
|
char *nstr = xmalloc(len);
|
|
|
|
return (memcpy(nstr, str, len));
|
|
}
|
|
|
|
/*
|
|
* Private version of basename()
|
|
*/
|
|
char *
|
|
xbasename(char *path)
|
|
{
|
|
char *cp;
|
|
|
|
if ((cp = strrchr(path, '/')) != NULL)
|
|
return(cp+1);
|
|
else
|
|
return(path);
|
|
}
|
|
|
|
/*
|
|
* Take a colon (':') separated path to a file and
|
|
* search until a component of that path is found and
|
|
* return the found file name.
|
|
*/
|
|
char *
|
|
searchpath(char *path)
|
|
{
|
|
char *file;
|
|
char *space;
|
|
int found;
|
|
struct stat statbuf;
|
|
|
|
for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
|
|
if ((space = strchr(file, ' ')) != NULL)
|
|
*space = CNULL;
|
|
found = stat(file, &statbuf) == 0;
|
|
if (space)
|
|
*space = ' '; /* Put back what we zapped */
|
|
}
|
|
return (file);
|
|
}
|