NetBSD/gnu/libexec/uucp/sys1.unx
1993-03-21 09:45:37 +00:00

2544 lines
58 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* sys1.unx
The basic system dependent routines for UNIX.
Copyright (C) 1991, 1992 Ian Lance Taylor
This file is part of the Taylor UUCP package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author of the program may be contacted at ian@airs.com or
c/o AIRS, P.O. Box 520, Waltham, MA 02254.
$Log: sys1.unx,v $
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
initial import of 386bsd-0.1 sources
# Revision 1.2 92/05/13 05:42:07 rich
# ported to 386bsd
#
# Revision 1.1 1992/05/10 17:36:33 rich
# Initial revision
#
Revision 1.68 1992/04/03 05:37:11 ian
Minor cleanups for gcc 2.1
Revision 1.67 1992/03/31 23:53:34 ian
Use $PWD to get the current directory if it's defined and correct
Revision 1.66 1992/03/31 23:42:59 ian
Brian W. Antoine: use name from getpwnam rather than getlogin
Revision 1.65 1992/03/26 17:17:25 ian
Gerben Wierda: various cleanups
Revision 1.64 1992/03/18 06:00:25 ian
Open the controlling terminal in non delay mode
Revision 1.63 1992/03/17 15:35:28 ian
Log signals when they happen, even if we continue looping
Revision 1.62 1992/03/17 01:28:18 ian
Undefine remove in uucp.h if ! HAVE_REMOVE
Revision 1.61 1992/03/16 22:40:01 ian
Undefine remove before function definition
Revision 1.60 1992/03/16 22:22:35 ian
Adjusted external declarations
Revision 1.59 1992/03/16 22:01:58 ian
Don't declare sigemptyset
Revision 1.58 1992/03/16 01:23:08 ian
Make blocking writes optional
Revision 1.57 1992/03/15 04:51:17 ian
Keep an array of signals we've received rather than a single variable
Revision 1.56 1992/03/15 01:54:46 ian
All execs are now done in isspawn, all waits are done in iswait
Revision 1.55 1992/03/12 19:54:43 ian
Debugging based on types rather than number
Revision 1.54 1992/03/11 02:09:57 ian
Franc,ois Pinard: retry fork several times before giving up
Revision 1.53 1992/03/11 00:18:50 ian
Save temporary file if file send fails
Revision 1.52 1992/03/08 02:06:28 ian
Let setpgrp fail silently
Revision 1.51 1992/03/04 01:40:51 ian
Thomas Fischer: tweaked a bit for the NeXT
Revision 1.50 1992/03/03 21:01:20 ian
Use strict timeout in fsserial_read, eliminate all race conditions
Revision 1.49 1992/02/29 01:06:59 ian
Chip Salzenberg: recheck file permissions before sending
Revision 1.48 1992/02/28 05:06:15 ian
T. William Wells: fsysdep_catch must be a macro
Revision 1.47 1992/02/27 19:51:09 ian
Added some new extern definitions
Revision 1.46 1992/02/27 05:40:54 ian
T. William Wells: detach from controlling terminal, handle signals safely
Revision 1.45 1992/02/24 20:07:43 ian
John Theus: some systems don't have <fcntl.h>
Revision 1.44 1992/02/24 04:58:47 ian
Only permit files to be received into directories that are world-writeable
Revision 1.43 1992/02/23 03:26:51 ian
Overhaul to use automatic configure shell script
Revision 1.42 1992/02/19 19:36:07 ian
Rearranged time functions
Revision 1.41 1992/02/09 05:10:50 ian
Added HAVE_MKDIR configuration parameter and mkdir emulation
Revision 1.40 1992/02/09 03:14:48 ian
Added HAVE_OLD_DIRECTORIES for systems without readdir routines
Revision 1.39 1992/02/09 02:41:58 ian
Added HAVE_DUP2 configuration parameter and dup2 emulation function
Revision 1.38 1992/02/08 23:38:17 ian
Put utsname on stack rather than making it static
Revision 1.37 1992/02/08 23:34:41 ian
If we have neither getcwd nor getwd, fork /bin/pwd to get the cwd
Revision 1.36 1992/02/08 22:33:32 ian
Only get the current working directory if it's going to be needed
Revision 1.35 1992/02/08 03:54:18 ian
Include <string.h> only in <uucp.h>, added 1992 copyright
Revision 1.34 1992/01/29 04:27:11 ian
Jay Vassos-Libove: removed some conflicting declarations
Revision 1.33 1992/01/22 05:08:21 ian
Call execl with correct first argument
Revision 1.32 1992/01/21 19:39:12 ian
Chip Salzenberg: uucp and uux start uucico for right system, not any
Revision 1.31 1992/01/21 00:30:48 ian
Don't try to create a directory with no name
Revision 1.30 1992/01/16 03:38:20 ian
Put \n at end of fsysdep_run error message
Revision 1.29 1992/01/15 21:06:11 ian
Mike Park: some systems can't include <sys/time.h> and <time.h> together
Revision 1.28 1992/01/13 19:38:16 ian
Chip Salzenberg: can't declare execl, since it is varadic
Revision 1.27 1992/01/13 06:11:39 ian
David Nugent: can't declare open or fcntl
Revision 1.26 1992/01/11 17:30:10 ian
John Antypas: use memcpy instead of relying on structure assignment
Revision 1.25 1992/01/04 23:23:57 ian
usysdep_localtime can't use usysdep_full_time if HAVE_TIMES
Revision 1.24 1992/01/04 22:56:22 ian
Added extern definition
Revision 1.23 1991/12/29 15:45:46 ian
Don't take the address of a cast value
Revision 1.22 1991/12/29 04:04:18 ian
Added a bunch of extern definitions
Revision 1.21 1991/12/29 02:59:50 ian
Lele Gaifax: put full year in log file
Revision 1.20 1991/12/28 17:08:47 ian
John Theus: offer HAVE_GETWD as an alternative to using getcwd
Revision 1.19 1991/12/28 07:01:15 ian
Added HAVE_FTIME configuration option
Revision 1.18 1991/12/22 22:14:19 ian
Monty Solomon: added HAVE_UNISTD_H configuration parameter
Revision 1.17 1991/12/21 21:34:14 ian
Moved fsysdep_file_exists from sys5.unx to sys1.unx
Revision 1.16 1991/12/21 21:04:42 ian
Use real program name in fsysdep_run error messages
Revision 1.15 1991/12/17 07:09:58 ian
Record statistics in fractions of a second
Revision 1.14 1991/12/12 18:35:47 ian
Do locking with link to avoid races and to permit running as root
Revision 1.13 1991/12/12 17:39:40 ian
Set the GID as well as the UID for extra safety
Revision 1.12 1991/12/11 03:59:19 ian
Create directories when necessary; don't just assume they exist
Revision 1.11 1991/12/06 22:50:01 ian
Franc,ois Pinard: getcwd may legitimately fail in usysdep_initialize
Revision 1.10 1991/12/01 02:23:12 ian
Niels Baggesen: don't multiply include <unistd.h>
Revision 1.9 1991/11/21 20:59:32 ian
Brian Campbell: ttyname takes an argument
Revision 1.8 1991/11/14 19:11:25 ian
Add extern for ttyname
Revision 1.7 1991/11/14 03:40:10 ian
Try to figure out whether stdin is a TCP port
Revision 1.6 1991/11/11 18:55:52 ian
Get protocol parameters from port and dialer for incoming calls
Revision 1.5 1991/09/19 17:49:39 ian
Chip Salzenberg: the log file has been closed before calling fsysdep_run
Revision 1.4 1991/09/19 15:46:48 ian
Chip Salzenberg: Make sure getlogin () uid matches process uid
Revision 1.3 1991/09/19 03:23:34 ian
Chip Salzenberg: append to private debugging file, don't overwrite it
Revision 1.2 1991/09/11 02:33:14 ian
Added ffork argument to fsysdep_run
Revision 1.1 1991/09/10 19:45:50 ian
Initial revision
*/
#include "uucp.h"
#if USE_RCS_ID
char sys1_unx_rcsid[] = "$Id: sys1.unx,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
#endif
#include <errno.h>
#if USE_STDIO && HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "system.h"
#include "sysdep.h"
#include <pwd.h>
#if HAVE_GETGRENT
#include <grp.h>
extern struct group *getgrent ();
#endif
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef O_APPEND
#ifdef FAPPEND
#define O_APPEND FAPPEND
#endif
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
#if ! HAVE_GETHOSTNAME && HAVE_UNAME
#include <sys/utsname.h>
extern int uname ();
#endif
#if HAVE_TIME_H && (HAVE_SYS_TIME_AND_TIME_H || ! HAVE_GETTIMEOFDAY)
#include <time.h>
#endif
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
/* If we have getcwd, forget about getwd. */
#if HAVE_GETCWD
#undef HAVE_GETWD
#define HAVE_GETWD 0
#endif
#if HAVE_GETWD
/* If we didn't get MAXPATHLEN, make it up. */
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
extern char *getwd ();
#endif /* HAVE_GETWD */
/* Prefer gettimeofday to ftime to times. */
#if HAVE_GETTIMEOFDAY || HAVE_FTIME
#undef HAVE_TIMES
#define HAVE_TIMES 0
#endif
#if HAVE_GETTIMEOFDAY
#undef HAVE_FTIME
#define HAVE_FTIME 0
#endif
#if HAVE_GETTIMEOFDAY
#include <sys/time.h>
extern int gettimeofday ();
#endif
#if HAVE_FTIME
#include <sys/timeb.h>
extern int ftime ();
#endif
#if HAVE_TIMES
#include <sys/times.h>
#if TIMES_DECLARATION_OK
/* We use a macro to protect this because times really returns clock_t
and on some systems, such as Ultrix 4.0, clock_t is int. We don't
leave it out entirely because on some systems, such as System III,
the declaration is necessary for correct compilation. */
extern long times ();
#endif
#if TIMES_TICK == 0
/* We don't have a value for TIMES_TICK. Look for one. */
#ifdef CLK_TCK
#undef TIMES_TICK
#define TIMES_TICK CLK_TCK
#else /* ! defined (CLK_TCK) */
#ifdef HZ
#undef TIMES_TICK
#define TIMES_TICK HZ
#endif /* defined (HZ) */
#endif /* ! defined (CLK_TCK) */
#endif /* TIMES_TICK == 0 */
#endif /* HAVE_TIMES */
/* We need the access macros. */
#ifndef R_OK
#define R_OK 4
#define W_OK 2
#define X_OK 1
#define F_OK 0
#endif /* ! defined (R_OK) */
/* We need wait status information. */
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
/* We use a typedef wait_status for wait and related functions to put
results into. We define the POSIX examination functions we need if
they are not already defined (if they aren't defined, I assume that
we have a standard wait status). */
#if HAVE_UNION_WAIT
typedef union wait wait_status;
#ifndef WIFEXITED
#define WIFEXITED(u) ((u).w_termsig == 0)
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(u) ((u).w_retcode)
#endif
#ifndef WTERMSIG
#define WTERMSIG(u) ((u).w_termsig)
#endif
#else /* ! HAVE_UNION_WAIT */
typedef int wait_status;
#ifndef WIFEXITED
#define WIFEXITED(i) (((i) & 0xff) == 0)
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(i) (((i) >> 8) & 0xff)
#endif
#ifndef WTERMSIG
#define WTERMSIG(i) ((i) & 0x7f)
#endif
#endif /* ! HAVE_UNION_WAIT */
/* External variables. */
extern char **environ;
/* External functions. */
#ifndef __386BSD__
extern int kill ();
#endif __386BSD__
extern int chdir (), access (), stat (), unlink (), execve ();
extern int close (), pipe (), dup2 ();
extern int fputs ();
extern void _exit ();
extern time_t time ();
extern char *getlogin (), *ttyname ();
extern pid_t getpid (), getppid (), fork (), getpgrp ();
extern uid_t getuid (), geteuid (), getgid (), getegid ();
extern struct tm *localtime ();
#ifndef __386BSD__
extern struct passwd *getpwuid ();
#endif __386BSD__
extern struct passwd *getpwnam ();
#if HAVE_GETHOSTNAME
extern int gethostname ();
#endif
#if HAVE_GETCWD
extern char *getcwd ();
#else
#if ! HAVE_GETWD
static char *getcwd P((char *zbuf, int cbuf));
#endif /* ! HAVE_GETWD */
#endif /* ! HAVE_GETCWD */
#if HAVE_GETDTABLESIZE
extern int getdtablesize ();
#endif
#if HAVE_SYSCONF
extern long sysconf ();
#endif
#if HAVE_SETPGRP
#ifndef __386BSD__
extern int setpgrp ();
#endif __386BSD__
#endif
#if HAVE_SETSID
#ifndef __386BSD__
extern int setsid ();
#endif __386BSD__
#endif
#if HAVE_SIGACTION
extern int sigaction ();
#endif
#if HAVE_SIGVEC
extern int sigvec ();
#endif
#if HAVE_TCP
extern int getsockname ();
#endif
/* Initialize the system dependent routines. We will probably be running
suid to uucp, so we make sure that nothing is obviously wrong. We
save the login name since we will be losing the real uid. */
static char *zSlogin;
/* We save the current directory since we will do a chdir to the
spool directory. */
char *zScwd;
/* The maximum length of a system name is controlled by the type of spool
directory we use. */
#if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX
int cSysdep_max_name_len = 7;
#endif /* SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX */
#if SPOOLDIR_BNU
int cSysdep_max_name_len = 14;
#endif /* SPOOLDIR_BNU */
#if SPOOLDIR_TAYLOR
#if HAVE_LONG_NAMES
int cSysdep_max_name_len = 255;
#else /* ! HAVE_LONG_NAMES */
int cSysdep_max_name_len = 14;
#endif /* ! HAVE_LONG_NAMES */
#endif /* SPOOLDIR_TAYLOR */
/* The number of available file descriptors. */
static int cSdescriptors;
/* Local functions. */
static void xmkdir P((const char *zdir));
static void usmake_spool_dir P((void));
void
usysdep_initialize (fdaemon, fgetcwd)
boolean fdaemon;
boolean fgetcwd;
{
int o;
char *z;
struct passwd *q;
ulog_id (getpid ());
#if HAVE_GETDTABLESIZE
cSdescriptors = getdtablesize ();
#else
#if HAVE_SYSCONF
cSdescriptors = sysconf (_SC_OPEN_MAX);
#else
#ifdef OPEN_MAX
cSdescriptors = OPEN_MAX;
#else
#ifdef NOFILE
cSdescriptors = NOFILE;
#else
cSdescriptors = 20;
#endif /* ! defined (NOFILE) */
#endif /* ! defined (OPEN_MAX) */
#endif /* ! HAVE_SYSCONF */
#endif /* ! HAVE_GETDTABLESIZE */
/* Close everything but stdin, stdout and stderr. */
for (o = 3; o < cSdescriptors; o++)
(void) close (o);
/* Make sure stdin, stdout and stderr are open. Otherwise, newly
opened files will appear to be them and confusion will result. */
if (fcntl (0, F_GETFD, 0) == -1
&& open ("/dev/null", O_RDONLY, 0) != 0)
exit (EXIT_FAILURE);
if (fcntl (1, F_GETFD, 0) == -1
&& open ("/dev/null", O_WRONLY, 0) != 1)
exit (EXIT_FAILURE);
if (fcntl (2, F_GETFD, 0) == -1
&& open ("/dev/null", O_WRONLY, 0) != 2)
exit (EXIT_FAILURE);
/* We always set our file modes to exactly what we want. */
umask (0);
/* Get the login name, making sure that it matches the uid. Many
systems truncate the getlogin return value to 8 characters, but
keep the full name in the password file, so we prefer the name in
the password file. */
z = getlogin ();
if (z == NULL)
q = NULL;
else
{
q = getpwnam (z);
if (q != NULL)
z = q->pw_name;
}
if (q == NULL || q->pw_uid != getuid ())
{
q = getpwuid (getuid ());
if (q == NULL)
ulog (LOG_FATAL, "Can't get login name");
z = q->pw_name;
}
zSlogin = xstrdup (z);
if (fdaemon)
{
/* Set our uid to our effective uid. There is no point in
remembering who originally ran the program. This won't work
on System V, but there's nothing to be done about that and it
doesn't make all that much difference. */
(void) setuid (geteuid ());
(void) setgid (getegid ());
}
if (fgetcwd)
{
const char *zenv;
struct stat senv, sdot;
/* Get the current working directory. We have to get it now,
since we're about to do a chdir. We use PWD if it's defined
and if it really names the working directory, since if it's
not the same as whatever getcwd returns it's probably more
appropriate. */
zenv = getenv ("PWD");
if (zenv != NULL
&& stat (zenv, &senv) == 0
&& stat (".", &sdot) == 0
&& senv.st_ino == sdot.st_ino
&& senv.st_dev == sdot.st_dev)
zScwd = xstrdup (zenv);
else
{
#if HAVE_GETCWD || ! HAVE_GETWD
{
int c;
c = 128;
while (TRUE)
{
zScwd = (char *) xmalloc (c);
if (getcwd (zScwd, c) != NULL)
break;
if (errno != ERANGE)
ulog (LOG_FATAL, "getcwd: %s", strerror (errno));
xfree ((pointer) zScwd);
c <<= 1;
}
}
#endif /* HAVE_GETCWD */
#if HAVE_GETWD
zScwd = (char *) xmalloc (MAXPATHLEN);
/* The getwd function puts in an error message in the
buffer, rather than setting errno. */
if (getwd (zScwd) == NULL)
ulog (LOG_FATAL, "getwd: %s", zScwd);
#endif /* HAVE_GETWD */
zScwd = (char *) xrealloc ((pointer) zScwd, strlen (zScwd) + 1);
}
}
/* Connect to the spool directory, and create it if is doesn't
exist. */
if (chdir (zSpooldir) < 0)
{
if (errno != ENOENT)
ulog (LOG_FATAL, "chdir (%s): %s", zSpooldir,
strerror (errno));
usmake_spool_dir ();
}
}
/* Exit the program. */
void
usysdep_exit (fsuccess)
boolean fsuccess;
{
exit (fsuccess ? EXIT_SUCCESS : EXIT_FAILURE);
}
/* This is called when a non-standard configuration file is used, to
make sure the program doesn't hand out privileged file access.
This means that to test non-standard configuration files, you
should be logged in as uucp. This is called before
usysdep_initialize. It ensures that someone can't simply use an
alternate configuration file to steal UUCP transfers from other
systems. This will still permit people to set up their own
configuration file and pretend to be whatever system they choose.
The only real security is to use a high level of protection on the
modem ports. */
/*ARGSUSED*/
boolean fsysdep_other_config (z)
const char *z;
{
(void) setuid (getuid ());
(void) setgid (getgid ());
return TRUE;
}
/* Detach from the controlling terminal. This is called by uucico if
it is calling out to another system, so that it can receive SIGHUP
signals from the port it calls out on. It is also called by uucico
just before it starts uuxqt, so that uuxqt is completely
independent of the terminal. */
#ifdef TIOCNOTTY
#define HAVE_TIOCNOTTY 1
#else
#define HAVE_TIOCNOTTY 0
#endif
void
usysdep_detach ()
{
int o;
#if ! HAVE_BSD_PGRP || ! HAVE_TIOCNOTTY
pid_t igrp;
/* First make sure we are not a process group leader. If we have
TIOCNOTTY, this doesn't matter, since TIOCNOTTY sets our process
group to 0 anyhow. */
#if HAVE_BSD_PGRP
igrp = getpgrp (0);
#else
igrp = getpgrp ();
#endif
if (igrp == getpid ())
{
boolean fignored;
pid_t ipid;
/* Ignore SIGHUP, since our process group leader is about to
die. */
usset_signal (SIGHUP, SIG_IGN, FALSE, &fignored);
ipid = isfork ();
if (ipid < 0)
ulog (LOG_FATAL, "fork: %s", strerror (errno));
if (ipid != 0)
_exit (EXIT_SUCCESS);
/* We'll always wind up as a child of process number 1, right?
Right? We have to wait for our parent to die before
reenabling SIGHUP. */
while (getppid () != 1)
sleep (1);
/* Restore SIGHUP catcher if it wasn't being ignored. */
if (! fignored)
usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL);
}
#endif /* ! HAVE_BSD_PGRP || ! HAVE_TIOCNOTTY */
/* Close all open files. */
ulog_close ();
for (o = 0; o < cSdescriptors; o++)
(void) close (o);
/* Reopen stdin, stdout and stderr. */
if (open ("/dev/null", O_RDONLY) != 0
|| open ("/dev/null", O_WRONLY) != 1
|| open ("/dev/null", O_WRONLY) != 2)
ulog (LOG_FATAL, "open (/dev/null): %s", strerror (errno));
#if HAVE_BSD_PGRP
#if HAVE_TIOCNOTTY
/* Lose our controlling terminal. */
#ifndef O_NDELAY
#define O_NDELAY FNDELAY
#endif
o = open ("/dev/tty", O_RDWR | O_NDELAY, 0);
if (o >= 0)
{
(void) ioctl (o, TIOCNOTTY, (char *) NULL);
(void) close (o);
}
#endif /* HAVE_TIOCNOTTY */
/* Make sure our process group ID is set to 0. On BSD TIOCNOTTY
should already have set it 0, so this will do no harm. On System
V we presumably did not execute the TIOCNOTTY call, but the
System V setpgrp will detach the controlling terminal anyhow.
This lets us use the same code on both BSD and System V, provided
it compiles correctly, which life easier for the configure
script. We don't output an error if we got EPERM because some
BSD variants don't permit this usage of setpgrp (which means they
don't provide any way to pick up a new controlling terminal). */
if (setpgrp (0, 0) < 0)
{
if (errno != EPERM)
ulog (LOG_ERROR, "setpgrp: %s", strerror (errno));
}
#else /* ! HAVE_BSD_PGRP */
#if HAVE_SETSID
/* Under POSIX the setsid call creates a new session for which we
are the process group leader. It also detaches us from our
controlling terminal. I'm using the BSD setpgrp call first
because they should be equivalent for my purposes, but it turns
out that on Ultrix 4.0 setsid prevents us from ever acquiring
another controlling terminal (it does not change our process
group, and Ultrix 4.0 prevents us from setting our process group
to 0). */
if (setsid () < 0)
ulog (LOG_ERROR, "setsid: %s", strerror (errno));
#else /* ! HAVE_SETSID */
#if HAVE_SETPGRP
/* Now we assume we have the System V setpgrp, which takes no
arguments, and we couldn't compile the HAVE_BSD_PGRP code above
because there was a prototype somewhere in scope. On System V
setpgrp makes us the leader of a new process group and also
detaches the controlling terminal. */
if (setpgrp () < 0)
ulog (LOG_ERROR, "setpgrp: %s", strerror (errno));
#else /* ! HAVE_SETPGRP */
#error Must detach from controlling terminal
#endif /* HAVE_SETPGRP */
#endif /* ! HAVE_SETSID */
#endif /* ! HAVE_BSD_PGRP */
/* At this point we have completely detached from our controlling
terminal. The next terminal device we open will probably become
our controlling terminal. */
}
/* Get the node name to use if it was not specified in the configuration
file. */
const char *
zsysdep_local_name ()
{
#if HAVE_GETHOSTNAME
char ab[256];
if (gethostname (ab, sizeof ab) < 0)
{
ulog (LOG_ERROR, "gethostname: %s", strerror (errno));
return NULL;
}
ab[sizeof ab - 1] = '\0';
ab[strcspn (ab, ".")] = '\0';
return xstrdup (ab);
#else /* ! HAVE_GETHOSTNAME */
#if HAVE_UNAME
struct utsname s;
if (uname (&s) < 0)
{
ulog (LOG_ERROR, "uname: %s", strerror (errno));
return NULL;
}
return xstrdup (s.nodename);
#else /* ! HAVE_UNAME */
return NULL;
#endif /* ! HAVE_UNAME */
#endif /* ! HAVE_GETHOSTNAME */
}
/* Get the login name. We actually get the login name in
usysdep_initialize, because after that we will lost the real uid. */
const char *
zsysdep_login_name ()
{
return zSlogin;
}
/* Get the port name of standard input. I assume that Unix systems
generally support ttyname. If they don't, this function can just
return NULL. It uses getsockname to see whether standard input is
a TCP connection. */
const char *
zsysdep_port_name (ftcp_port)
boolean *ftcp_port;
{
const char *z;
*ftcp_port = FALSE;
#if HAVE_TCP
{
int clen;
clen = 0;
if (getsockname (0, (struct sockaddr *) NULL, &clen) == 0)
*ftcp_port = TRUE;
}
#endif /* HAVE_TCP */
z = ttyname (0);
if (z == NULL)
return NULL;
if (strncmp (z, "/dev/", 5) == 0)
return z + 5;
else
return z;
}
/* Signal handling routines. When we catch a signal, we want to set
the appropriate elements of afSignal and afLog_signal to TRUE. If
we are on a system which restarts system calls, we may also want to
longjmp out. On a system which does not restart system calls,
these signal handling routines are well-defined by ANSI C. */
#if HAVE_RESTARTABLE_SYSCALLS
volatile sig_atomic_t fSjmp;
volatile jmp_buf sSjmp_buf;
#endif /* HAVE_RESTARTABLE_SYSCALLS */
/* The SVR3 sigset function can be called just like signal, unless
system calls are restarted which is extremely unlikely; we prevent
this case in sysh.unx. */
#if HAVE_SIGSET && ! HAVE_SIGACTION && ! HAVE_SIGVEC
#define signal sigset
#endif
/* Catch a signal. Reinstall the signal handler if necessary, set the
appropriate variables, and do a longjmp if necessary. */
SIGtype
ussignal (isig)
int isig;
{
int iindex;
#if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET
(void) signal (isig, ussignal);
#endif
switch (isig)
{
default: iindex = INDEXSIG_SIGHUP; break;
#ifdef SIGINT
case SIGINT: iindex = INDEXSIG_SIGINT; break;
#endif
#ifdef SIGQUIT
case SIGQUIT: iindex = INDEXSIG_SIGQUIT; break;
#endif
#ifdef SIGTERM
case SIGTERM: iindex = INDEXSIG_SIGTERM; break;
#endif
#ifdef SIGPIPE
case SIGPIPE: iindex = INDEXSIG_SIGPIPE; break;
#endif
}
afSignal[iindex] = TRUE;
afLog_signal[iindex] = TRUE;
#if HAVE_RESTARTABLE_SYSCALLS
if (fSjmp)
longjmp (sSjmp_buf, 1);
#endif /* HAVE_RESTARTABLE_SYSCALLS */
}
/* Prepare to catch a signal. This is basically the ANSI C routine
signal, but it uses sigaction or sigvec instead if they are
available. If fforce is FALSE, we do not set the signal if it is
currently being ignored. If pfignored is not NULL and fforce is
FALSE, then *pfignored will be set to TRUE if the signal was
previously being ignored (if fforce is TRUE the value of *pfignored
is meaningless). If we can't change the signal handler we give a
fatal error. */
void
usset_signal (isig, pfn, fforce, pfignored)
int isig;
SIGtype (*pfn) P((int));
boolean fforce;
boolean *pfignored;
{
#if HAVE_SIGACTION
struct sigaction s;
if (! fforce)
{
sigemptyset (&s.sa_mask);
if (sigaction (isig, (struct sigaction *) NULL, &s) != 0)
ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno));
if (s.sa_handler == SIG_IGN)
{
if (pfignored != NULL)
*pfignored = TRUE;
return;
}
if (pfignored != NULL)
*pfignored = FALSE;
}
s.sa_handler = pfn;
sigemptyset (&s.sa_mask);
s.sa_flags = 0;
if (sigaction (isig, &s, (struct sigaction *) NULL) != 0)
ulog (LOG_FATAL, "sigaction (%d): %s", isig, strerror (errno));
#else /* ! HAVE_SIGACTION */
#if HAVE_SIGVEC
struct sigvec s;
if (! fforce)
{
if (sigvec (isig, (struct sigvec *) NULL, &s) != 0)
ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno));
if (s.sv_handler == SIG_IGN)
{
if (pfignored != NULL)
*pfignored = TRUE;
return;
}
if (pfignored != NULL)
*pfignored = FALSE;
}
s.sv_handler = pfn;
s.sv_mask = 0;
#ifdef SV_INTERRUPT
s.sv_flags = SV_INTERRUPT;
#else
s.sv_flags = 0;
#endif
if (sigvec (isig, &s, (struct sigvec *) NULL) != 0)
ulog (LOG_FATAL, "sigvec (%d): %s", isig, strerror (errno));
#else /* ! HAVE_SIGVEC */
if (! fforce)
{
if (signal (isig, SIG_IGN) == SIG_IGN)
{
if (pfignored != NULL)
*pfignored = TRUE;
return;
}
if (pfignored != NULL)
*pfignored = FALSE;
}
(void) signal (isig, pfn);
#endif /* ! HAVE_SIGVEC */
#endif /* ! HAVE_SIGACTION */
}
/* The routine called by the system independent code, which always
uses the same signal handler. */
void
usysdep_signal (isig)
int isig;
{
usset_signal (isig, ussignal, FALSE, (boolean *) NULL);
}
/* Get the time in seconds since the epoch, with optional
microseconds. We use usysdep_process_time to get the microseconds
if it will work (it won't is it uses times, since that returns a
time based only on the process). */
long
isysdep_time (pimicros)
long *pimicros;
{
#if HAVE_GETTIMEOFDAY || HAVE_FTIME
return isysdep_process_time (pimicros);
#else
if (pimicros != NULL)
*pimicros = 0;
return time ((time_t *) NULL);
#endif
}
/* Get the time in seconds and microseconds; this need only work
within the process when called from the system independent code.
It is also called by isysdep_time, above. */
long
isysdep_process_time (pimicros)
long *pimicros;
{
#if HAVE_GETTIMEOFDAY
struct timeval stime;
struct timezone stz;
(void) gettimeofday (&stime, &stz);
if (pimicros != NULL)
*pimicros = stime.tv_usec;
return stime.tv_sec;
#endif /* HAVE_GETTIMEOFDAY */
#if HAVE_FTIME
struct timeb stime;
(void) ftime (&stime);
if (pimicros != NULL)
*pimicros = stime.millitm * 1000;
return stime.time;
#endif /* HAVE_FTIME */
#if HAVE_TIMES
struct tms s;
long i;
static int itick;
if (itick == 0)
{
#if TIMES_TICK != 0
itick = TIMES_TICK;
#else /* TIMES_TICK == 0 */
const char *z;
z = getenv ("HZ");
if (z != NULL)
itick = atoi (z);
/* If we really couldn't get anything, just use 60. */
if (itick == 0)
itick = 60;
#endif /* TIMES_TICK == 0 */
}
i = (long) times (&s);
if (pimicros != NULL)
*pimicros = (i % (long) itick) * ((long) 1000000 / (long) itick);
return i / (long) itick;
#endif /* HAVE_TIMES */
#if ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES
if (pimicros != NULL)
*pimicros = 0;
return time ((time_t *) NULL);
#endif /* ! HAVE_GETTIMEOFDAY && ! HAVE_FTIME && ! HAVE_TIMES */
}
/* Fill in a struct tm. */
void
usysdep_localtime (itime, q)
long itime;
struct tm *q;
{
time_t i;
i = (time_t) itime;
memcpy (q, localtime (&i), sizeof (struct tm));
}
/* Sleep for a number of seconds. */
void
usysdep_sleep (c)
int c;
{
sleep (c);
}
/* Check whether a file exists. */
boolean
fsysdep_file_exists (zfile)
const char *zfile;
{
struct stat s;
return stat (zfile, &s) == 0;
}
/* Open a stdio file with appropriate permissions. */
FILE *
esysdep_fopen (zfile, fpublic, fappend, fmkdirs)
const char *zfile;
boolean fpublic;
boolean fappend;
boolean fmkdirs;
{
int imode;
int o;
FILE *e;
if (fpublic)
imode = IPUBLIC_FILE_MODE;
else
imode = IPRIVATE_FILE_MODE;
if (! fappend)
o = creat (zfile, imode);
else
{
#ifdef O_CREAT
o = open (zfile, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, imode);
#else
o = open (zfile, O_WRONLY | O_NOCTTY);
if (o < 0 && errno == ENOENT)
o = creat (zfile, imode);
#endif /* ! defined (O_CREAT) */
}
if (o < 0)
{
if (errno == ENOENT && fmkdirs)
{
if (! fsysdep_make_dirs (zfile, fpublic))
return NULL;
if (! fappend)
o = creat (zfile, imode);
else
{
#ifdef O_CREAT
o = open (zfile, O_WRONLY | O_APPEND | O_CREAT, imode);
#else
o = creat (zfile, imode);
#endif
}
}
if (o < 0)
{
ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno));
return NULL;
}
}
#ifndef O_CREAT
#ifdef O_APPEND
if (fappend)
{
if (fcntl (o, F_SETFL, O_APPEND) != 0)
{
ulog (LOG_ERROR, "fcntl (%s, O_APPEND): %s", zfile,
strerror (errno));
(void) close (o);
return NULL;
}
}
#endif /* defined (O_APPEND) */
#endif /* ! defined (O_CREAT) */
if (fappend)
e = fdopen (o, (char *) "a");
else
e = fdopen (o, (char *) "w");
if (e == NULL)
{
ulog (LOG_ERROR, "fdopen: %s", strerror (errno));
(void) close (o);
}
return e;
}
/* See whether a directory exists. */
boolean
fsdirectory_exists (z)
const char *z;
{
struct stat s;
if (stat (z, &s) < 0)
return FALSE;
return S_ISDIR (s.st_mode);
}
/* Create any directories needed for a file name. */
boolean
fsysdep_make_dirs (zfile, fpublic)
const char *zfile;
boolean fpublic;
{
char *zcopy, *z;
int imode;
zcopy = (char *) alloca (strlen (zfile) + 1);
strcpy (zcopy, zfile);
if (fpublic)
imode = IPUBLIC_DIRECTORY_MODE;
else
imode = IDIRECTORY_MODE;
for (z = zcopy; *z != '\0'; z++)
{
if (*z == '/' && z != zcopy)
{
*z = '\0';
if (! fsdirectory_exists (zcopy))
{
if (mkdir (zcopy, imode) != 0)
{
ulog (LOG_ERROR, "mkdir (%s): %s", zcopy,
strerror (errno));
return FALSE;
}
}
*z = '/';
}
}
return TRUE;
}
/* Tilde expand a file or directory name. */
/*ARGSUSED*/
const char *
zstilde_expand (qsys, zfile)
const struct ssysteminfo *qsys;
const char *zfile;
{
static int calc;
static char *zalc;
const char *zdir;
int clen;
if (zfile[0] != '~')
return zfile;
else if (zfile[1] == '\0' || zfile[1] == '/')
{
const char *zpub;
if (qsys->zpubdir == NULL)
zpub = zPubdir;
else
zpub = qsys->zpubdir;
if (zfile[1] == '\0')
return zpub;
else
{
zdir = zpub;
zfile += 2;
}
}
else
{
int cuserlen;
char *zcopy;
struct passwd *q;
++zfile;
cuserlen = strcspn (zfile, "/");
zcopy = (char *) alloca (cuserlen + 1);
strncpy (zcopy, zfile, cuserlen);
zcopy[cuserlen] = '\0';
q = getpwnam (zcopy);
if (q == NULL)
{
ulog (LOG_ERROR, "User %s not found", zcopy);
return NULL;
}
if (zfile[cuserlen] == '\0')
return q->pw_dir;
else
{
zdir = q->pw_dir;
zfile += cuserlen + 1;
}
}
clen = strlen (zdir) + strlen (zfile) + 2;
if (clen > calc)
{
zalc = (char *) xrealloc ((pointer) zalc, clen);
calc = clen;
}
sprintf (zalc, "%s/%s", zdir, zfile);
return zalc;
}
/* Do access(2) on a stat structure, except that the user name is
provided. If the user name in zuser is NULL, require the file to
be accessible to the world. Return TRUE if access is permitted,
FALSE otherwise. This does not log an error message. */
boolean
fsuser_access (q, imode, zuser)
const struct stat *q;
int imode;
const char *zuser;
{
static char *zuser_hold;
static uid_t iuid_hold;
static gid_t igid_hold;
static int cgroups_hold;
static gid_t *paigroups_hold;
int ir, iw, ix, iand;
if (imode == F_OK)
return TRUE;
if (zuser != NULL)
{
/* We keep static variables around for the last user we did, to
avoid looking up a user multiple times. */
if (zuser_hold == NULL || strcmp (zuser_hold, zuser) != 0)
{
struct passwd *qpwd;
if (zuser_hold != NULL)
{
xfree ((pointer) zuser_hold);
zuser_hold = NULL;
cgroups_hold = 0;
xfree ((pointer) paigroups_hold);
paigroups_hold = NULL;
}
qpwd = getpwnam ((char *) zuser);
if (qpwd == NULL)
{
/* Check this as a remote request. */
zuser = NULL;
}
else
{
#if HAVE_GETGRENT
struct group *qg;
#endif
zuser_hold = xstrdup (zuser);
iuid_hold = qpwd->pw_uid;
igid_hold = qpwd->pw_gid;
#if HAVE_GETGRENT
/* Get the list of groups for this user. This is
definitely more appropriate for BSD than for System
V. It may just be a waste of time, and perhaps it
should be configurable. */
setgrent ();
while ((qg = getgrent ()) != NULL)
{
const char **pz;
if (qg->gr_gid == igid_hold)
continue;
for (pz = (const char **) qg->gr_mem; *pz != NULL; pz++)
{
if ((*pz)[0] == *zuser
&& strcmp (*pz, zuser) == 0)
{
paigroups_hold = ((gid_t *)
(xrealloc
((pointer) paigroups_hold,
((cgroups_hold + 1)
* sizeof (gid_t)))));
paigroups_hold[cgroups_hold] = qg->gr_gid;
++cgroups_hold;
break;
}
}
}
endgrent ();
#endif
}
}
}
/* Now do the actual access check. */
if (zuser != NULL)
{
/* The superuser can do anything. */
if (iuid_hold == 0)
return TRUE;
/* If this is the uid we're running under, there's no point to
checking access further, because when we actually try the
operation the system will do the checking for us. */
if (iuid_hold == geteuid ())
return TRUE;
}
ir = S_IROTH;
iw = S_IWOTH;
ix = S_IXOTH;
if (zuser != NULL)
{
if (iuid_hold == q->st_uid)
{
ir = S_IRUSR;
iw = S_IWUSR;
ix = S_IXUSR;
}
else
{
boolean fgroup;
fgroup = FALSE;
if (igid_hold == q->st_gid)
fgroup = TRUE;
else
{
int i;
for (i = 0; i < cgroups_hold; i++)
{
if (paigroups_hold[i] == q->st_gid)
{
fgroup = TRUE;
break;
}
}
}
if (fgroup)
{
ir = S_IRGRP;
iw = S_IWGRP;
ix = S_IXGRP;
}
}
}
iand = 0;
if ((imode & R_OK) != 0)
iand |= ir;
if ((imode & W_OK) != 0)
iand |= iw;
if ((imode & X_OK) != 0)
iand |= ix;
return (q->st_mode & iand) == iand;
}
/* See whether a file is in a directory, and optionally check access. */
boolean
fsysdep_in_directory (qsys, zfile, zdir, fcheck, freadable, zuser)
const struct ssysteminfo *qsys;
const char *zfile;
const char *zdir;
boolean fcheck;
boolean freadable;
const char *zuser;
{
int c;
char *zcopy, *zslash;
struct stat s;
if (*zdir == '~')
{
zdir = zstilde_expand (qsys, zdir);
if (zdir == NULL)
return FALSE;
}
c = strlen (zdir);
if (zdir[c - 1] == '/')
c--;
if (strncmp (zfile, zdir, c) != 0
|| (zfile[c] != '/' && zfile[c] != '\0'))
return FALSE;
if (strstr (zfile + c, "/../") != NULL)
return FALSE;
/* If we're not checking access, get out now. */
if (! fcheck)
return TRUE;
zcopy = (char *) alloca (strlen (zfile) + 1);
strcpy (zcopy, zfile);
/* Start checking directories after zdir. Otherwise, we would
require that all directories down to /usr/spool/uucppublic be
publically searchable; they probably are but it should not be
requirement. */
zslash = zcopy + c;
do
{
char b;
struct stat shold;
b = *zslash;
*zslash = '\0';
shold = s;
if (stat (zcopy, &s) != 0)
{
if (errno != ENOENT)
{
ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno));
return FALSE;
}
/* If this is the top directory, any problems will be caught
later when we try to open it. */
if (zslash == zcopy + c)
return TRUE;
/* Go back and check the last directory for read or write
access. */
s = shold;
break;
}
/* If this is not a directory, get out of the loop. */
if (! S_ISDIR (s.st_mode))
break;
/* Make sure the directory is searchable. */
if (! fsuser_access (&s, X_OK, zuser))
{
ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES));
return FALSE;
}
/* If we've reached the end of the string, get out. */
if (b == '\0')
break;
*zslash = b;
}
while ((zslash = strchr (zslash + 1, '/')) != NULL);
/* At this point s holds a stat on the last component of the path.
We must check it for readability or writeability. */
if (! fsuser_access (&s, freadable ? R_OK : W_OK, zuser))
{
ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES));
return FALSE;
}
return TRUE;
}
/* Start up a new program and end the current one. We always go
through isspawn, and never exec directly. We don't have to worry
about SIGHUP because the current process is either not a process
group leader (uucp, uux) or it does not have a controlling terminal
(uucico). */
boolean
fsysdep_run (ffork, zprogram, zarg1, zarg2)
boolean ffork;
const char *zprogram;
const char *zarg1;
const char *zarg2;
{
char *zlib;
const char *azargs[4];
int aidescs[3];
pid_t ipid;
zlib = (char *) alloca (sizeof LIBDIR + sizeof "/" + strlen (zprogram));
sprintf (zlib, "%s/%s", LIBDIR, zprogram);
azargs[0] = zlib;
azargs[1] = zarg1;
azargs[2] = zarg2;
azargs[3] = NULL;
aidescs[0] = SPAWN_NULL;
aidescs[1] = SPAWN_NULL;
aidescs[2] = SPAWN_NULL;
/* We pass fshell as TRUE, which permits uucico and uuxqt to be
replaced by shell scripts. */
ipid = isspawn (azargs, aidescs, FALSE, FALSE, (const char *) NULL,
FALSE, TRUE, (const char *) NULL,
(const char *) NULL, (const char *) NULL);
if (ipid < 0)
{
ulog (LOG_ERROR, "isspawn: %s", strerror (errno));
return FALSE;
}
if (ffork)
return TRUE;
exit (EXIT_SUCCESS);
}
/* Mail a message to a user. */
boolean
fsysdep_mail (zto, zsubject, cstrs, paz)
const char *zto;
const char *zsubject;
int cstrs;
const char **paz;
{
const char *az[3];
FILE *e;
pid_t ipid;
time_t itime;
int i;
az[0] = MAIL_PROGRAM;
az[1] = zto;
az[2] = NULL;
e = espopen (az, FALSE, &ipid);
if (e == NULL)
{
ulog (LOG_ERROR, "espopen (%s): %s", MAIL_PROGRAM,
strerror (errno));
return FALSE;
}
fprintf (e, "Subject: %s\n", zsubject);
fprintf (e, "To: %s\n", zto);
/* We should probably put in a Date: header as well. */
fprintf (e, "\n");
(void) time (&itime);
/* Remember that ctime includes a \n, so this skips a line. */
fprintf (e, "Message from UUCP on %s %s\n", zLocalname,
ctime (&itime));
for (i = 0; i < cstrs; i++)
fputs (paz[i], e);
(void) fclose (e);
return iswait ((unsigned long) ipid, MAIL_PROGRAM) == 0;
}
/* Make a directory with error checking. */
static void
xmkdir (zdir)
const char *zdir;
{
if (mkdir ((char *) zdir, IDIRECTORY_MODE) < 0)
ulog (LOG_FATAL, "mkdir (%s): %s", zdir, strerror (errno));
}
/* Make the spool directory. */
static void
usmake_spool_dir ()
{
xmkdir (zSpooldir);
if (chdir (zSpooldir) < 0)
ulog (LOG_FATAL, "chdir (%s): %s", zSpooldir, strerror (errno));
#if SPOOLDIR_BSD42 | SPOOLDIR_BSD43
xmkdir ("C.");
xmkdir ("D.");
#if SPOOLDIR_BSD43
xmkdir ("X.");
#endif /* SPOOLDIR_BSD43 */
{
char ab[sizeof "D.1234567X"];
sprintf (ab, "D.%.7s", zLocalname);
xmkdir (ab);
#if SPOOLDIR_BSD43
strcat (ab, "X");
xmkdir (ab);
#endif /* SPOOLDIR_BSD43 */
}
#endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */
#if SPOOLDIR_ULTRIX
xmkdir ("sys");
xmkdir ("sys/DEFAULT");
xmkdir ("sys/DEFAULT/C.");
xmkdir ("sys/DEFAULT/D.");
xmkdir ("sys/DEFAULT/X.");
{
char ab[sizeof "sys/DEFAULT/D.1234567X"];
sprintf (ab, "sys/DEFAULT/D.%.7s", zLocalname);
xmkdir (ab);
strcat (ab, "X");
xmkdir (ab);
}
#endif /* SPOOLDIR_ULTRIX */
#if SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
xmkdir (".Temp");
#endif /* SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR */
xmkdir (".Status");
xmkdir (".Sequence");
xmkdir (XQTDIR);
xmkdir (PRESERVEDIR);
}
/* Retry fork several times before giving up. */
pid_t
isfork ()
{
int i;
pid_t iret;
for (i = 0; i < 10; i++)
{
iret = fork ();
if (iret >= 0 || errno != EAGAIN)
return iret;
sleep (5);
}
return iret;
}
/* Spawn a child in a fairly secure fashion. This returns the process
ID of the child or -1 on error. It takes far too many arguments:
pazargs -- arguments (element 0 is command)
aidescs -- file descriptors for stdin, stdout and stderr
fkeepuid -- TRUE if euid should be left unchanged
fkeepenv -- TRUE if environment should be left unmodified
zchdir -- directory to chdir to
fnosigs -- TRUE if child should ignore SIGHUP, SIGINT and SIGQUIT
fshell -- TRUE if should try /bin/sh if execve gets ENOEXEC
zpath -- value for environment variable PATH
zuu_machine -- value for environment variable UU_MACHINE
zuu_user -- value for environment variable UU_USER
The aidescs array is three elements long. 0 is stdin, 1 is stdout
and 2 is stderr. The array may contain either file descriptor
numbers to dup appropriately, or one of the following:
SPAWN_NULL -- set descriptor to /dev/null
SPAWN_READ_PIPE -- set aidescs element to pipe for parent to read
SPAWN_WRITE_PIPE -- set aidescs element to pipe for parent to write
If fkeepenv is FALSE, a standard environment is created. The
environment arguments (zpath, zuu_machine and zuu_user) are only
used if fkeepenv is FALSE; any of them may be NULL. */
pid_t
isspawn (pazargs, aidescs, fkeepuid, fkeepenv, zchdir, fnosigs, fshell,
zpath, zuu_machine, zuu_user)
const char **pazargs;
int aidescs[3];
boolean fkeepuid;
boolean fkeepenv;
const char *zchdir;
boolean fnosigs;
boolean fshell;
const char *zpath;
const char *zuu_machine;
const char *zuu_user;
{
char *zshcmd = NULL;
int i;
char *azenv[9];
char **pazenv;
boolean ferr;
int ierr = 0;
int onull;
int aichild_descs[3];
int cpar_close;
int aipar_close[4];
int cchild_close;
int aichild_close[3];
pid_t iret = 0;
const char *zcmd;
/* If we might have to use the shell, allocate enough space for the
quoted command before forking. Otherwise the allocation might
modify the data segment and we could not safely use vfork. */
if (fshell)
{
int clen;
clen = 0;
for (i = 0; pazargs[i] != NULL; i++)
clen += strlen (pazargs[i]);
zshcmd = (char *) alloca (2 * clen + i);
}
/* Set up a standard environment. This is again done before forking
because it might modify the data segment. */
if (fkeepenv)
pazenv = environ;
else
{
const char *zterm, *ztz;
char *zspace;
int ienv;
if (zpath == NULL)
zpath = CMDPATH;
azenv[0] = (char *) alloca (sizeof "PATH=" + strlen (zpath));
sprintf (azenv[0], "PATH=%s", zpath);
zspace = azenv[0] + sizeof "PATH=" - 1;
while ((zspace = strchr (zspace, ' ')) != NULL)
*zspace = ':';
azenv[1] = (char *) alloca (sizeof "HOME=" + strlen (zSpooldir));
sprintf (azenv[1], "HOME=%s", zSpooldir);
zterm = getenv ("TERM");
if (zterm == NULL)
zterm = "unknown";
azenv[2] = (char *) alloca (sizeof "TERM=" + strlen (zterm));
sprintf (azenv[2], "TERM=%s", zterm);
azenv[3] = (char *) "SHELL=/bin/sh";
azenv[4] = (char *) alloca (sizeof "USER=" + strlen (OWNER));
sprintf (azenv[4], "USER=%s", OWNER);
ienv = 5;
ztz = getenv ("TZ");
if (ztz != NULL)
{
azenv[ienv] = (char *) alloca (sizeof "TZ=" + strlen (ztz));
sprintf (azenv[ienv], "TZ=%s", ztz);
++ienv;
}
if (zuu_machine != NULL)
{
azenv[ienv] = (char *) alloca (sizeof "UU_MACHINE="
+ strlen (zuu_machine));
sprintf (azenv[ienv], "UU_MACHINE=%s", zuu_machine);
++ienv;
}
if (zuu_user != NULL)
{
azenv[ienv] = (char *) alloca (sizeof "UU_USER="
+ strlen (zuu_user));
sprintf (azenv[ienv], "UU_USER=%s", zuu_user);
++ienv;
}
azenv[ienv] = NULL;
pazenv = azenv;
}
/* Set up any needed pipes. */
ferr = FALSE;
onull = -1;
cpar_close = 0;
cchild_close = 0;
for (i = 0; i < 3; i++)
{
if (aidescs[i] == SPAWN_NULL)
{
if (onull < 0)
{
onull = open ("/dev/null", O_RDWR);
if (onull < 0)
{
ierr = errno;
ferr = TRUE;
break;
}
aipar_close[cpar_close] = onull;
++cpar_close;
}
aichild_descs[i] = onull;
}
else if (aidescs[i] != SPAWN_READ_PIPE
&& aidescs[i] != SPAWN_WRITE_PIPE)
aichild_descs[i] = aidescs[i];
else
{
int aipipe[2];
if (pipe (aipipe) < 0)
{
ierr = errno;
ferr = TRUE;
break;
}
if (aidescs[i] == SPAWN_READ_PIPE)
{
aidescs[i] = aipipe[0];
aichild_close[cchild_close] = aipipe[0];
aichild_descs[i] = aipipe[1];
aipar_close[cpar_close] = aipipe[1];
}
else
{
aidescs[i] = aipipe[1];
aichild_close[cchild_close] = aipipe[1];
aichild_descs[i] = aipipe[0];
aipar_close[cpar_close] = aipipe[0];
}
++cpar_close;
++cchild_close;
}
}
#if DEBUG > 1
if (! ferr && FDEBUGGING (DEBUG_EXECUTE))
{
ulog (LOG_DEBUG_START, "Forking %s", pazargs[0]);
for (i = 1; pazargs[i] != NULL; i++)
ulog (LOG_DEBUG_CONTINUE, " %s", pazargs[i]);
ulog (LOG_DEBUG_END, "%s", "");
}
#endif
if (! ferr)
{
/* This should really be vfork if available. */
iret = isfork ();
if (iret < 0)
{
ferr = TRUE;
ierr = errno;
}
}
if (ferr)
{
for (i = 0; i < cpar_close; i++)
(void) close (aipar_close[i]);
for (i = 0; i < cchild_close; i++)
(void) close (aichild_close[i]);
errno = ierr;
return -1;
}
/* Here the fork has succeeded and all the pipes have been done. */
if (iret != 0)
{
/* The parent. Close the child's ends of the pipes and return
the process ID. */
for (i = 0; i < cpar_close; i++)
(void) close (aipar_close[i]);
return iret;
}
/* The child. */
#ifdef STDIN_FILENO
#if STDIN_FILENO != 0 || STDOUT_FILENO != 1 || STDERR_FILENO != 2
#error The following code makes invalid assumptions
#endif
#endif
for (i = 0; i < 3; i++)
if (aichild_descs[i] != i)
(void) dup2 (aichild_descs[i], i);
for (i = 3; i < cSdescriptors; i++)
(void) close (i);
zcmd = pazargs[0];
pazargs[0] = strrchr (zcmd, '/');
if (pazargs[0] == NULL)
pazargs[0] = zcmd;
else
++pazargs[0];
if (! fkeepuid)
{
(void) setuid (getuid ());
(void) setgid (getgid ());
}
if (zchdir != NULL)
(void) chdir (zchdir);
if (fnosigs)
{
#ifdef SIGHUP
(void) signal (SIGHUP, SIG_IGN);
#endif
#ifdef SIGINT
(void) signal (SIGINT, SIG_IGN);
#endif
#ifdef SIGQUIT
(void) signal (SIGQUIT, SIG_IGN);
#endif
}
(void) execve ((char *) zcmd, (char **) pazargs, pazenv);
/* The exec failed. If permitted, try using /bin/sh to execute a
shell script. */
if (errno == ENOEXEC && fshell)
{
char *zto;
const char *azshargs[4];
pazargs[0] = zcmd;
zto = zshcmd;
for (i = 0; pazargs[i] != NULL; i++)
{
const char *zfrom;
for (zfrom = pazargs[i]; *zfrom != '\0'; zfrom++)
{
/* Some versions of /bin/sh appear to have a bug such
that quoting a '/' sometimes causes an error. I
don't know exactly when this happens (I can recreate
it on Ultrix 4.0), but in any case it is harmless to
not quote a '/'. */
if (*zfrom != '/')
*zto++ = '\\';
*zto++ = *zfrom;
}
*zto++ = ' ';
}
*(zto - 1) = '\0';
azshargs[0] = "sh";
azshargs[1] = "-c";
azshargs[2] = zshcmd;
azshargs[3] = NULL;
(void) execve ((char *) "/bin/sh", (char **) azshargs, pazenv);
}
_exit (EXIT_FAILURE);
/* Avoid compiler warning. */
return -1;
}
/* A version of popen that goes through isspawn. This actually takes
an array of arguments rather than a string, and takes a boolean
read/write value rather than a string. It sets *pipid to the
process ID of the child. */
FILE *
espopen (pazargs, frd, pipid)
const char **pazargs;
boolean frd;
pid_t *pipid;
{
int aidescs[3];
pid_t ipid;
FILE *eret;
if (frd)
{
aidescs[0] = SPAWN_NULL;
aidescs[1] = SPAWN_READ_PIPE;
}
else
{
aidescs[0] = SPAWN_WRITE_PIPE;
aidescs[1] = SPAWN_NULL;
}
aidescs[2] = SPAWN_NULL;
ipid = isspawn (pazargs, aidescs, FALSE, FALSE,
(const char *) NULL, FALSE, TRUE,
(const char *) NULL, (const char *) NULL,
(const char *) NULL);
if (ipid < 0)
return NULL;
if (frd)
eret = fdopen (aidescs[1], (char *) "r");
else
eret = fdopen (aidescs[0], (char *) "w");
if (eret == NULL)
{
int ierr;
ierr = errno;
(void) close (frd ? aidescs[1] : aidescs[0]);
(void) kill (ipid, SIGKILL);
(void) iswait ((unsigned long) ipid, (const char *) NULL);
errno = ierr;
return NULL;
}
*pipid = ipid;
return eret;
}
/* Wait for a particular process to finish. The ipid argument should
be pid_t, but then we couldn't have a prototype. If the zreport
argument is not NULL, then a wait error will be logged, and if the
exit status is non-zero it will be logged with zreport as the
header of the log message. If the zreport argument is NULL, no
errors will be logged. This function returns the exit status if
the process exited normally, or -1 on error or if the process was
killed by a signal (I don't just always return the exit status
because then the calling code would have to prepared to handle
union wait status vs. int status, and none of the callers care
which signal killed the program anyhow).
This functions keeps waiting until the process finished, even if it
is interrupted by a signal. I think this is right for all uses.
The controversial one would be when called from uuxqt to wait for a
requested process. Hitting uuxqt with SIGKILL will approximate the
actions taken if we return from here with an error anyhow. If we
do get a signal, we call ulog with a NULL argument to get it in the
log file at about the right time. */
int
iswait (ipid, zreport)
unsigned long ipid;
const char *zreport;
{
wait_status istat;
#if HAVE_WAITPID
while (waitpid ((pid_t) ipid, &istat, 0) < 0)
{
if (errno != EINTR)
{
if (zreport != NULL)
ulog (LOG_ERROR, "waitpid: %s", strerror (errno));
return -1;
}
ulog (LOG_ERROR, (const char *) NULL);
}
#else /* ! HAVE_WAITPID */
#if HAVE_WAIT4
while (wait4 ((pid_t) ipid, &istat, 0, (struct rusage *) NULL) < 0)
{
if (errno != EINTR)
{
if (zreport != NULL)
ulog (LOG_ERROR, "wait4: %s", strerror (errno));
return -1;
}
ulog (LOG_ERROR, (const char *) NULL);
}
#else /* ! HAVE_WAIT4 */
pid_t igot;
/* We could theoretically get the wrong child here if we're in some
kind of weird pipeline, so we don't give any error messages for
it. */
while ((igot = wait (&istat)) != (pid_t) ipid)
{
if (igot < 0)
{
if (errno != EINTR)
{
if (zreport != NULL)
ulog (LOG_ERROR, "wait: %s", strerror (errno));
return -1;
}
ulog (LOG_ERROR, (const char *) NULL);
}
}
#endif /* ! HAVE_WAIT4 */
#endif /* ! HAVE_WAITPID */
DEBUG_MESSAGE2 (DEBUG_EXECUTE, "%s %d",
WIFEXITED (istat) ? "Exit status" : "Signal",
WIFEXITED (istat) ? WEXITSTATUS (istat) : WTERMSIG (istat));
if (WIFEXITED (istat) && WEXITSTATUS (istat) == 0)
return 0;
if (zreport != NULL)
{
if (! WIFEXITED (istat))
ulog (LOG_ERROR, "%s: Got signal %d", zreport, WTERMSIG (istat));
else
ulog (LOG_ERROR, "%s: Exit status %d", zreport,
WEXITSTATUS (istat));
}
if (WIFEXITED (istat))
return WEXITSTATUS (istat);
else
return -1;
}
#if ! HAVE_REMOVE
/* Remove a file. */
int
remove (z)
const char *z;
{
return unlink (z);
}
#endif /* ! HAVE_REMOVE */
#if ! HAVE_STRERROR
/* Some systems don't have a strerror definition, so we provide one.
This function is, of course, system dependent. */
char *
strerror (ierr)
int ierr;
{
extern int sys_nerr;
extern char *sys_errlist[];
if (ierr >= 0 && ierr < sys_nerr)
return sys_errlist[ierr];
return (char *) "unknown error";
}
#endif /* ! HAVE_STRERROR */
#if ! HAVE_GETCWD && ! HAVE_GETWD
/* Implement a simple getcwd that just calls /bin/pwd. I probably
should include Roland McGrath's getcwd implementation here, since
it doesn't fork, but it requires readdir support that I don't feel
like contemplating just now. */
static char *
getcwd (zbuf, cbuf)
char *zbuf;
int cbuf;
{
const char *azargs[2];
FILE *e;
pid_t ipid;
int cread;
int ierr;
azargs[0] = PWD_PROGRAM;
azargs[1] = NULL;
e = espopen (azargs, TRUE, &ipid);
if (e == NULL)
return NULL;
ierr = 0;
cread = fread (zbuf, sizeof (char), cbuf, e);
if (cread == 0)
ierr = errno;
(void) fclose (e);
if (iswait ((unsigned long) ipid, (const char *) NULL) != 0)
{
ierr = EACCES;
cread = 0;
}
if (cread != 0)
{
if (zbuf[cread - 1] == '\n')
zbuf[cread - 1] = '\0';
else
{
ierr = ERANGE;
cread = 0;
}
}
if (cread == 0)
{
errno = ierr;
return NULL;
}
return zbuf;
}
#endif /* ! HAVE_GETCWD && ! HAVE_GETWD */
#if ! HAVE_DUP2
/* Emulate the dup2 call. I basically took this from the emacs 18.57
distribution, although I cleaned it up a bit and made it POSIX
compliant. */
int
dup2 (oold, onew)
int oold;
int onew;
{
if (oold == onew)
return onew;
(void) close (onew);
#ifdef F_DUPFD
return fcntl (oold, F_DUPFD, onew);
#else
{
int onext, oret, isave;
onext = dup (oold);
if (onext == onew)
return onext;
if (onext < 0)
return -1;
oret = dup2 (oold, onew);
isave = errno;
(void) close (onext);
errno = isave;
return oret;
}
#endif
}
#endif /* ! HAVE_DUP2 */
#if ! HAVE_OPENDIR
/* Simple emulations of opendir/readdir/closedir for systems which
have the original format of Unix directories. It's probably better
to get Doug Gwyn's public domain set of emulation functions. */
DIR *
opendir (zdir)
const char *zdir;
{
int o;
struct stat s;
DIR *qret;
o = open (zdir, O_RDONLY | O_NOCTTY, 0);
if (o == -1)
return NULL;
if (fstat (o, &s) < 0)
{
(void) close (o);
return NULL;
}
if (! S_ISDIR (s.st_mode))
{
(void) close (o);
errno = ENOTDIR;
return NULL;
}
qret = (DIR *) xmalloc (sizeof (DIR));
qret->o = o;
return qret;
}
struct dirent *
readdir (q)
DIR *q;
{
struct direct sdir;
int cgot;
do
{
cgot = read (q->o, &sdir, sizeof (struct direct));
if (cgot <= 0)
return NULL;
if (cgot != sizeof (struct direct))
{
errno = ENOENT;
return NULL;
}
}
while (sdir.d_ino == 0);
strncpy (q->s.d_name, sdir.d_name, DIRSIZ);
q->s.d_name[DIRSIZ] = '\0';
return &q->s;
}
int
closedir (q)
DIR *q;
{
int iret, isave;
iret = close (q->o);
isave = errno;
xfree (q);
errno = isave;
return iret;
}
#endif /* ! HAVE_OPENDIR */
#if ! HAVE_MKDIR
/* We don't have the mkdir system call, so we invoke the suid program
uudir to create the directory with the correct owner. */
int
mkdir (zdir, imode)
const char *zdir;
int imode;
{
const char *azargs[2];
int aidescs[3];
pid_t ipid;
/* /bin/mkdir will create the directory with mode 777, so we set our
umask to get the mode we want. */
(void) umask ((~ imode) & (S_IRWXU | S_IRWXG | S_IRWXO));
azargs[0] = UUDIR_PROGRAM;
azargs[1] = NULL;
aidescs[0] = SPAWN_NULL;
aidescs[1] = SPAWN_NULL;
aidescs[2] = SPAWN_NULL;
ipid = isspawn (azargs, aidescs, FALSE, FALSE, (const char *) NULL,
TRUE, FALSE, (const char *) NULL,
(const char *) NULL, (const char *) NULL);
(void) umask (0);
if (ipid < 0)
return -1;
if (iswait ((unsigned long) ipid, (const char *) NULL) != 0)
{
/* Make up an errno value. */
errno = EACCES;
return -1;
}
return 0;
}
#endif /* ! HAVE_MKDIR */
/*
Local variables:
mode:c
End:
*/