969 lines
20 KiB
C
969 lines
20 KiB
C
/*
|
|
* 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.3 1998/05/19 19:22:14 rwu 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 = -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 */
|
|
int setjmp_ok = FALSE; /* setjmp()/longjmp() status */
|
|
char **realargv; /* Real main() argv */
|
|
int realargc; /* Real main() argc */
|
|
opt_t options = 0; /* Global install options */
|
|
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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 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) {
|
|
if (!setjmp_ok) exit(1);
|
|
|
|
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 = -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 < 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(!(write(rem, 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, 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 < 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, 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 - Protocol Version %d, Release %s, Patch level %d",
|
|
DISTVERSION, PATCHLEVEL, 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) write(rem, 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) write(rem, 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);
|
|
}
|
|
|
|
extern char *basename(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 */
|
|
}
|