Coherent4.2.10/coh.386/sys6.c
/* $Header: /ker/coh.386/RCS/sys6.c,v 2.4 93/10/29 00:55:54 nigel Exp Locker: nigel $ */
/*
* POSIX.1-oriented system calls for Coherent.
*
* Conventions: as elsewhere, system call handlers have the same name as the
* user would use but prefixed by a 'u'. Internal data interfaces have the
* same name as a user function if they have the same signature, which is a
* good thing for testing, and can save on redundant prototypes. Wherever
* possible, we use such function-call interfaces rather than get involved in
* the disgusting mess that is the U area or process-table mechanisms.
*/
/*
* $Log: sys6.c,v $
* Revision 2.4 93/10/29 00:55:54 nigel
* R98 (aka 4.2 Beta) prior to removing System Global memory
*
* Revision 2.3 93/08/19 03:26:57 nigel
* Nigel's r83 (Stylistic cleanup)
*
* Revision 2.2 93/07/26 14:55:33 nigel
* Nigel's R80
*/
#define _SYSV3 1
#define _DDI_DKI 1
#define _DDI_DKI_IMPL 1
#include <common/ccompat.h>
#include <common/_tricks.h>
#include <common/_clktck.h>
#include <common/_gregset.h>
#include <kernel/sig_lib.h>
#include <kernel/proc_lib.h>
#include <kernel/cred_lib.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/cmn_err.h>
#include <sys/cred.h>
#include <unistd.h>
#include <limits.h>
#include <stddef.h>
#include <poll.h>
#include <sys/sched.h>
/*
* Dip into the filth...
*/
#define _KERNEL 1
#include <kernel/dir.h>
#include <sys/uproc.h> /* for pathconf () to use ftoi () */
#include <sys/proc.h>
#include <sys/inode.h>
#include <sys/io.h>
#include <sys/fd.h>
/*
* A brief note, before we begin: at the time of writing, the Coherent kernel
* is a little screwed-up w.r.t. checking validity of memory references. While
* in a sane system the user-kernel copying routines would perform all the
* necessary checking, apparently this is not in fact the case. Sadly, rather
* than do the right thing there is now an additional API for this which is
* used or not used apparently at random.
*
* Well, screw that. The only sane course of action is to make the user-kernel
* copy routines do everything they need to do to cope with checking user
* address validity; the routines below assume this, and when I rewrite all
* the MMU stuff I'll ensure that it is so.
*/
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* sigaction () Detailed signal management
*
*-SYNOPSIS:
* #include <signal.h>
*
* int sigaction (int sig, const struct sigaction * act,
* struct sigaction * oact);
*
*-DESCRIPTION:
* sigaction () allows the calling process to examine and/or specify the
* action to be taken on delivery of a specific signal.
*
* "sig" specifies the signal and can be assigned any of the signals
* specified in signal(5) except SIGKILL and SIGSTOP.
*
* If the argument "arg" is not NULL, it points to a structure specifying
* the new action to be taken when delivering "sig". If the argument
* "oact" is not NULL, it points to a structure where the action
* previously associated with "sig" is to be stored on return from
* sigaction ().
*
* The "sigaction" structure includes the following members:
* void (* sa_handler) ();
* sigset_t sa_mask;
* int sa_flags;
*
* "sa_handler" specifies the disposition of the signal and may take any
* of the values specified in signal (5).
*
* "sa_mask" specifies a set of signals to be blocked while the signal
* handler is active. On entry to the signal handler, that set of signals
* is added to the set of signals already being blocked when the signal
* is delivered. In addition, the signal that caused the handler to be
* executed will also be blocked, unless the SA_NODEFER flag has been
* specified. SIGSTOP and SIGKILL cannot be blocked (the system silently
* enforces this restriction).
*
* "sa_flags" specifies a set of flags used to modify the behaviour of
* the signal. It is formed by a logical OR of any of the following
* values (only SA_CLDSTOP is available to System V, Release 3
* applications):
*
* SA_NOCLDSTOP If set and "sig" equals SIGCHLD, "sig" will not be
* sent to the calling process when its child processes
* stop or continue.
*
* SA_NOCLDWAIT If set and "sig" equals SIGCHLD, the system will not
* create zombie processes when children of the calling
* process exit. If the calling process subsequently
* issues a wait (), it blocks until all of the calling
* process's child processes terminate, and then returns
* a value of -1 with "errno" set to ECHILD.
*
* SA_ONSTACK If set and the signal is caught and an alternate
* signal stack has been declared with sigaltstack (),
* the signal is delivered to the calling process on that
* stack. Otherwise, the signal is delivered on the
* same stack as the main program.
*
* SA_RESETHAND If set and the signal is caught, the disposition of
* the signal is reset to SIG_DFL, and the signal will
* not be blocked on entry to the signal handler (SIGILL,
* SIGTRAP, and SIGPWR cannot be automatically reset when
* delivered; the system silently enforces this
* restriction).
*
* SA_NODEFER If set and the signal is caught, the signal will not
* be automatically blocked by the kernel while it is
* being caught.
*
* SA_RESTART If set and the signal is caught, a system call that
* is interrupted by the execution of this signal's
* handler is transparently restarted by the system.
* Otherwise, the system call returns an EINTR error.
*
* SA_SIGINFO If cleared and the signal is caught, "sig" is passed
* as the only argument to the signal-catching function.
* If set and the signal is caught, pending signals of
* type "sig" are reliably queued to the calling process
* and two additional arguments are passed to the signal-
* catching function. If the second argument is not equal
* to NULL, it points to a "siginfo_t" structure
* containing the reason why the signal was generated;
* the third argument points to a "ucontext_t" structure
* containing the receiving process's context when the
* signal was delivered.
*
* sigaction () fails if any of the following is true:
*
* EINVAL The value of the "sig" argument is not a valid signal
* number or is equal to SIGKILL or SIGSTOP.
*
* EFAULT "act" or "oact" points outside the process's allocated
* address space.
*
*-DIAGNOSTICS:
* On success, sigaction () returns zero. On failure, it returns -1 and
* sets "errno" to indicate the error.
*/
#if __USE_PROTO__
int usigaction (int sig, __CONST__ struct sigaction * act,
struct sigaction * oact, gregset_t * regsetp)
#else
int
usigaction (sig, act, oact, regsetp)
int sig;
__CONST__ struct sigaction
* act;
struct sigaction * oact;
gregset_t * regsetp;
#endif
{
__sigaction_t action;
__sigmiscfl_t flags = curr_signal_misc (0, 0);
struct sigaction temp;
if (! __IN_RANGE (1, sig, _SIGNAL_MAX) || sig == SIGKILL ||
sig == SIGSTOP) {
set_user_error (EINVAL);
return -1;
}
u.u_sigreturn = (__sighand_t *) regsetp->_i386._edx;
if (act != NULL) {
if (ukcopy (act, & temp, sizeof (temp)) != sizeof (temp))
return -1;
if (sig == SIGCHLD) {
/*
* Deal with the child-related signal flags.
*/
(void) curr_signal_misc
(__SF_NOCLDSTOP,
(temp.sa_flags & SA_NOCLDSTOP) == 0 ? 0 :
__SF_NOCLDSTOP);
}
action.sa_flags = 0;
action.sa_handler = temp.sa_handler;
action.sa_mask._sigbits [0] = temp.sa_mask._sigbits [0];
#if ___SIGSET_LEN == 2
action.sa_mask._sigbits [1] = 0;
#endif
/*
* This needs to be done automatically unless SA_RESETHANDLER
* is true; so, for an SVR3 interface, we always do this...
*/
__SIGSET_ADDBIT (action.sa_mask, sig);
}
curr_signal_action (sig, act == NULL ? NULL : & action, & action);
if (oact == NULL)
return 0;
temp.sa_handler = action.sa_handler;
temp.sa_mask._sigbits [0] = action.sa_mask._sigbits [0];
temp.sa_flags = (flags & __SF_NOCLDSTOP) != 0 ? SA_NOCLDSTOP : 0;
if (kucopy (& temp, oact, sizeof (temp)) != sizeof (temp)) {
/*
* If the user requested a change in the signal disposition,
* undo the change before returning in error.
*/
if (act != NULL) {
curr_signal_action (sig, & action, NULL);
curr_signal_misc (-1, flags);
}
return -1;
}
return 0;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* sigpending () Examine signals that are blocked and pending
*
*-SYNOPSIS:
* #include <signal.h>
*
* int sigpending (sigset_t * set);
*
*-DESCRIPTION:
* The sigpending () function retrieves those signals that have been sent
* to the calling process but are being blocked from delivery by the
* calling process's signal mask. The signals are stored in the space
* pointed to by the argument "set".
*
* sigpending () fails if any of the following are true:
*
* EFAULT The "set" argument points outside the process's
* allocated address space.
*
*-DIAGNOSTICS:
* On success, sigpending () returns zero. On failure, it returns -1 and
* sets "errno" to indicate the error.
*/
#if __USE_PROTO__
int usigpending (o_sigset_t * set)
#else
int
usigpending (set)
o_sigset_t * set;
#endif
{
o_sigset_t temp;
temp._sigbits [0] = SELF->p_pending_signals._sigbits [0];
return kucopy (& temp, set, sizeof (* set)) != sizeof (* set) ? -1 : 0;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* sigprocmask () Change or examine signal mask
*
*-SYNOPSIS:
* #include <signal.h>
*
* int sigprocmask (int how, const sigset_t * set, sigset_t * oset);
*
*-DESCRIPTION:
* The sigprocmask () function is used to examine and/or change the
* calling process's signal mask. If the value of "how" is SIG_BLOCK, the
* set pointed to by the argument "set" is added to the current signal
* mask. If "how" is SIG_UNBLOCK, the set pointed to by the argument
* "set" is removed from the current signal mask. If "how" is
* SIG_SETMASK, the current signal mask is replaced by the set pointed
* to by the argument "set". If the argument "oset" is not NULL, the
* previous mask is stored in the space pointed to by "oset". If the
* value of the argument "set" is NULL, the value "how" is not
* significant and the process's signal mask is unchanged; thus, the call
* can be used to enquire about currently blocked signals.
*
* If there are any pending unblocked signals after the call to
* sigprocmask (), at least one of those signals will be delivered before
* the call to sigprocmask () returns.
*
* It is not possible to block those signals that cannot be ignored [see
* sigaction ()]; this restriction is silently imposed by the system.
*
* If sigprocmask () fails, the process's signal mask is not changed.
*
* sigprocmask () fails if any of the following are true:
*
* EINVAL The value of the "how" argument is not equal to one of
* the defined values.
*
* EFAULT The value of "set" or "oset" points outside the
* process's allocated address space.
*
*-DIAGNOSTICS:
* On success, sigprocmask () returns zero. On failure, it returns -1 and
* sets "errno" to indicate the error.
*/
#if __USE_PROTO__
int usigprocmask (int how, __CONST__ o_sigset_t * set, o_sigset_t * oset)
#else
int
usigprocmask (how, set, oset)
int how;
__CONST__ o_sigset_t
* set;
o_sigset_t * oset;
#endif
{
o_sigset_t temp;
__sigset_t mask;
if (set != NULL) {
if (ukcopy (set, & temp, sizeof (temp)) != sizeof (temp))
return -1;
curr_signal_mask (NULL, & mask);
switch (how) {
case SIG_UNBLOCK:
mask._sigbits [0] &= ~ temp._sigbits [0];
break;
case SIG_BLOCK:
mask._sigbits [0] |= temp._sigbits [0];
break;
case SIG_SETMASK:
mask._sigbits [0] = temp._sigbits [0];
break;
default:
set_user_error (EINVAL);
return -1;
}
}
curr_signal_mask (set == NULL ? NULL : & mask, & mask);
temp._sigbits [0] = mask._sigbits [0];
if (oset == NULL)
return 0;
if (kucopy (& temp, oset, sizeof (* oset)) != sizeof (* oset)) {
/*
* If the user requested a change to the signal mask, we undo
* it before returning in error.
*/
if (set != NULL)
curr_signal_mask (& mask, NULL);
return -1;
}
return 0;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* sigsuspend () install a signal mask and suspend process until signal
*
*-SYNOPSIS:
* #include <signal.h>
*
* int sigsuspend (const sigset_t * set);
*
*-DESCRIPTION:
* sigsuspend () replaces the process's signal mask with the set of
* signals pointed to by the argument "set" and then suspends the process
* until delivery of a signal whose action is either the execute a signal
* catching function or to terminate the process.
*
* If the action is to terminate the process, sigsuspend () does not
* return. If the action is to execute a signal catching function,
* sigsuspend () returns are the signal catching function returns. On
* return, the signal mask is restored to the set the existed before the
* call to sigsuspend ().
*
* It is not possible to block those signals that cannot be ignored; this
* restriction is silently imposed by the system.
*
* sigsuspend () fails if either of the following is true:
*
* EINTR A signal is caught by the calling process and control
* is returned from the signal catching function.
*
* EFAULT The "set" argument points outside the process's
* allocated address space.
*
*-DIAGNOSTICS:
* Since sigsuspend () suspends process execution indefinitely, there is
* no successful completion return value. On failure, it returns -1 and
* sets "errno" to indicate the error.
*/
#if __USE_PROTO__
int usigsuspend (__CONST__ o_sigset_t * set, gregset_t * regsetp)
#else
int
usigsuspend (set, regsetp)
__CONST__ o_sigset_t
* set;
gregset_t * regsetp;
#endif
{
__sigset_t mask;
if (ukcopy (set, & mask, sizeof (* set)) != sizeof (* set))
return - 1;
curr_signal_mask (& mask, & mask);
/*
* Like pause (), sleep such that we will only be woken by a signal.
*/
x_sleep ((caddr_t) & u, prilo, slpriSigCatch, "sigsuspend ()");
/*
* Set up the signal context now rather than in the return-to-user-
* mode code so that the signal dispatch is based on the "set" mask,
* but ensure that the old mask is put into the return-from-signal
* context.
*
* Note also that because the user signal frame is constructed here
* rather than on the way out, we get to set up the user return value
* from the sigsuspend () system call right here - and since we must
* return an error indication from sigsuspend (), we have to do the
* magic for the user-level library stub.
*/
/* Fix from Nigel 94/07/03 */
regsetp->_i386._eax = EINTR;
__FLAG_REG (regsetp) = __FLAG_SET_FLAG (__FLAG_REG (regsetp), __CARRY);
curr_check_signals (regsetp, & mask);
curr_signal_mask (& mask, NULL);
/* Fix from Nigel 94/07/03 */
return 0; /* return any damn thing... */
}
/*
* Local support routine for [f]pathconf (), defined below.
*/
#if __USE_PROTO__
__LOCAL__ int _pathconf (INODE * ip, int name)
#else
__LOCAL__ int
_pathconf (ip, name)
INODE * ip;
int name;
#endif
{
switch (name) {
case _PC_LINK_MAX:
return LINK_MAX;
case _PC_MAX_CANON:
case _PC_MAX_INPUT:
return 255; /* CANON_MAX */
case _PC_NAME_MAX:
return DIRSIZ;
case _PC_PATH_MAX:
return PATH_MAX;
case _PC_PIPE_BUF:
return PIPE_MAX;
case _PC_CHOWN_RESTRICTED:
return 1; /* always true for Coh 4.x */
case _PC_NO_TRUNC:
return 0; /* never true for Coh 4.x */
case _PC_VDISABLE:
#ifndef _POSIX_VDISABLE
return -1; /* never true for Coh 4.x */
#else
return _POSIX_VDISABLE;
#endif
default:
set_user_error (EINVAL);
return -1;
}
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* fpathconf ()
* pathconf () get configurable pathname variables
*
*-SYNOPSIS:
* #include <unistd.h>
*
* long fpathconf (int fildes, int name);
* long pathconf (const char * path, int name);
*
*-DESCRIPTION:
* The functions fpathconf () and pathconf () return the current value of
* a configurable limit or option associated with a file or directory.
* The "path" argument points to the pathname of a file or directory;
* "fildes" is an open file descriptor; and "name" is the symbolic
* constant (defined in <unistd.h>) representing the configurable system
* limit or option to be returned.
*
* The values returned by pathconf () or fpathconf () depend on the type
* of file specified by "path" or "fildes". The following table contains
* the symbolic constants supported by pathconf () and fpathconf ()
* along with the POSIX defined return value. The return value is based
* on the type of file specified by "path" or "fildes".
*
* _PC_LINK_MAX The maximum value of a file's link count. If "path" or
* "fildes" refers to a directory, the value returned
* applies to the directory itself.
*
* _PC_MAX_CANON The number of bytes in a terminal canonical input
* queue. The behaviour is undefined if "path" or
* "fildes" does not refer to a terminal file.
*
* _PC_MAX_INPUT The number of bytes for which space will be available
* in a terminal input queue. The behaviour is undefined
* "path" or "fildes" does not refer to a terminal file.
*
* _PC_NAME_MAX The number of bytes in a filename. The behaviour is
* undefined if "path" or "fildes" does not refer to a
* directory. The value returned applies to the filenames
* within the directory.
*
* _PC_PATH_MAX The number of bytes in a pathname. The behaviour is
* undefined if "path" or "fildes" does not refer to a
* directory. The value returned is the maximum length of
* a relative pathname when the specified directory is
* the working directory.
*
* _PC_PIPE_BUF The number of bytes that can be written atomically
* when writing to a pipe. If "path" or "files" refers to
* a pipe or FIFO, the value returned applies to the FIFO
* itself. If "path" or "fildes" refers to a directory,
* the value returned applies to any FIFOs that exist or
* can be created within the directory. If "path" or
* "fildes" refer to any other type of file, the
* behaviour is undefined.
*
* _PC_CHOWN_RESTRICTED The use of the chown () function is restricted
* to a process with appropriate priveleges, and to
* changing the group ID of a file only to the effective
* group ID of the process or to one of its supplementary
* group IDs. If "path" or "fildes" refers to a
* directory, the value returned applies to any files,
* other than directories, that exist or can be created
* within the directory.
*
* _PC_NO_TRUNC Pathname components longer than NAME_MAX generate an
* error. The behaviour is undefined if "path" or
* "fildes" does not refer to a directory. The value
* returned applies to the filenames within the
* directory.
* _PC_VDISABLE Terminal special characters can be disabled using this
* character value, if it is defined. The behaviour is
* undefined if "path" or "filedes" does not refer to a
* terminal file.
*
* The value of the configurable system limit or option specified by
* "name" does not change during the lifetime of the calling process.
*
* fpathconf () fails if the following is true:
*
* EBADF "fildes" is not a valid file descriptor.
*
* pathconf () fails if any of the following are true:
*
* EACCES Search permission is denied for a component of the
* path prefix.
*
* ELOOP Too many symbolic links are encountered while
* translating "path".
*
* EMULTIHOP Components of "path" require hopping to multiple
* remote machines and the file system type does not
* allow it.
*
* ENAMETOOLONG The length of a pathname component exceeds PATH_MAX,
* or a pathname component is longer than NAME_MAX while
* _POSIX_NO_TRUNC is in effect.
*
* ENOENT "path" is needed for the command specified and the
* named file does not exist or if the "path" argument
* points to an empty string.
*
* ENOLINK "path" points to a remote machine and the link to that
* machine is no longer active.
*
* ENOTDIR A component of the path prefix is not a directory.
*
* Both fpathconf () and pathconf () fail if the following is true:
*
* EINVAL if "name" is an invalid value.
*
*-DIAGNOSTICS:
* If fpathconf () or pathconf () is invoked with an invalid symbolic
* constant or the symbolic constant corresponds to a configurable system
* limit or option not supported on the system, a value of -1 is returned
* to the invoking process. If the function fails because the
* configurable system limit or option corresponding to "name" is not
* supported on the system the value of "errno" is not changed.
*/
#if __USE_PROTO__
int ufpathconf (int fildes, int name)
#else
int
ufpathconf (fildes, name)
int fildes;
int name;
#endif
{
__fd_t * fdp;
if ((fdp = fd_get (fildes)) == NULL) {
set_user_error (EBADF);
return -1;
}
return _pathconf (fdp->f_ip, name);
}
#if __USE_PROTO__
int upathconf (__CONST__ char * path, int name)
#else
int
upathconf (path, name)
__CONST__ char * path;
int name;
#endif
{
struct direct dir;
int ret;
if (ftoi (path, 'r', IOUSR, NULL, & dir, SELF->p_credp))
return -1;
ret = _pathconf (u.u_cdiri, name);
idetach (u.u_cdiri);
return ret;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* sysconf () get configurable system variables
*
*-SYNOPSIS:
* #include <unistd.h>
*
* long sysconf (int name);
*
*-DESCRIPTION:
* The sysconf () function provides a method for the application to
* determine the current value of a configurable system limit or option.
*
* The "name" argument represents the system variable to be queried. The
* following table lists the minimal set of system variables from
* <limits.h> and <unistd.h> that can be returned by sysconf (), and the
* symbolic constants that are the corresponding values used for "name":
*
* NAME: RETURN VALUE:
* _SC_ARG_MAX ARG_MAX
* _SC_CHILD_MAX CHILD_MAX
* _SC_CLK_TCK CLK_TCK
* _SC_NGROUPS_MAX NGROUPS_MAX
* _SC_OPEN_MAX OPEN_MAX
* _SC_PASS_MAX PASS_MAX
* _SC_PAGESIZE PAGESIZE
* _SC_JOB_CONTROL _POSIX_JOB_CONTROL
* _SC_SAVED_IDS _POSIX_SAVED_IDS
* _SC_VERSION _POSIX_VERSION
* _SC_XOPEN_VERSION _XOPEN_VERSION
* _SC_LOGNAME_MAX LOGNAME_MAX
*
* The value of CLK_TCK may be variable and it should not be assumed that
* CLK_TCK is a compile-time constant. The value of CLK_TCK is the same
* as the value of sysconf (_SC_CLK_TCK).
*
*-DIAGNOSTICS:
* If "name" is an invalid value, sysconf () will return -1 and set
* "errno" to indicate the error. If sysconf () fails due to a value of
* "name" that is not defined on the system, the function will return
* a value of -1 without changing the value of "errno".
*
*-NOTES:
* A call to setrlimit () may cause the value of OPEN_MAX to change on
* System V, Release 4-compatible systems.
*/
#if __USE_PROTO__
long usysconf (int name)
#else
long
usysconf (name)
int name;
#endif
{
switch (name) {
case _SC_ARG_MAX:
/*
* Actually, Coherent has no limit here, but we say we do for
* iBCS2 compatibility.
*/
return ARG_MAX;
case _SC_CHILD_MAX:
return CHILD_MAX;
case _SC_CLK_TCK:
return CLK_TCK;
case _SC_NGROUPS_MAX:
return NGROUPS_MAX; /* set usetgroups () */
case _SC_OPEN_MAX:
return NOFILE;
#ifdef _SC_PASS_MAX
case _SC_PASS_MAX:
return PASS_MAX;
#endif
#ifdef _SC_PAGESIZE
case _SC_PAGESIZE:
return NBPC;
#endif
case _SC_JOB_CONTROL:
#if _POSIX_JOB_CONTROL
return 1;
#else
return 0;
#endif
case _SC_SAVED_IDS:
#if _POSIX_SAVED_IDS
return 1;
#else
return 0;
#endif
case _SC_VERSION:
return 199009L;
#ifdef _SC_LOGNAME_MAX
case _SC_LOGNAME_MAX:
return LOGNAME_MAX;
#endif
default:
set_user_error (EINVAL);
return -1;
}
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* getgroups () get supplementary groups access list IDs
*
*-SYNOPSIS:
* #include <unistd.h>
*
* int getgroups (int gidsetsize, gid_t * grouplist);
*
*-DESCRIPTION:
* getgroups () gets the current supplemental group access list of the
* calling process and stores the result into the array of group IDs
* specified by "grouplist". This array has "gidsetsize" entries and must
* be large enough to contain the entire list. This list cannot be
* greater than {NGROUPS_MAX}. If "gidsetsize" equals 0, getgroups ()
* will return the number of groups to which the calling process belongs
* without modifying the array pointed to by "grouplist".
*
* getgroups () will fail if:
*
* EINVAL The value of "gidsetsize" is non-zero and less than
* the number of supplementary groups IDs set for the
* calling process.
*
* EFAULT A referenced part of the array pointed to by
* "grouplist" is outside of the allocated address space
* of the process.
*
*-DIAGNOSTICS:
* Upon successful completion, getgroups () returns the number of
* supplementary groups IDs set for the calling process. Otherwise, a
* value of -1 is returned and "errno" is set to indicate the error.
*/
#if __USE_PROTO__
int ugetgroups (int gidsetsize, __gid_t * grouplist)
#else
int
ugetgroups (gidsetsize, grouplist)
int gidsetsize;
__gid_t * grouplist;
#endif
{
__gid_t groups [NGROUPS_MAX];
int i;
if (gidsetsize < 0) {
set_user_error (EINVAL);
return -1;
}
if (gidsetsize > SELF->p_credp->cr_ngroups)
gidsetsize = SELF->p_credp->cr_ngroups;
/*
* Convert group ID formats.
*/
for (i = 0 ; i < gidsetsize ; i ++)
groups [i] = SELF->p_credp->cr_groups [i];
kucopy (groups, grouplist, gidsetsize * sizeof (__gid_t));
return SELF->p_credp->cr_ngroups;
}
/*
*-STATUS:
* SVR4
*
*-NAME:
* setgroups () set supplementary groups access list IDs
*
*-SYNOPSIS:
* #include <unistd.h>
*
* int setgroups (int ngroups, const gid_t * grouplist);
*
*-DESCRIPTION:
* setgroups () sets the supplementary group access list of the calling
* process from the array of group IDs specified by "grouplist". The
* number of entries is specified by "ngroups" and can not be greater
* than {NGROUPS_MAX}. This function may be invoked only by the super-
* user.
*
* setgroups () will fail if:
*
* EINVAL The value of "ngroups" is greater than {NGROUPS_MAX}.
*
* EPERM The effective user ID is not the super-user.
*
* EFAULT A referenced part of the array pointed to by
* "grouplist" is outside of the allocated address space
* of the process.
*
*-DIAGNOSTICS:
* Upon successful completion, setgroups () returns the value 0.
* Otherwise, a value of -1 is returned and "errno" is set to indicate
* the error.
*/
#if __USE_PROTO__
int usetgroups (int ngroups, __gid_t * grouplist)
#else
int
usetgroups (ngroups, grouplist)
int ngroups;
__gid_t * grouplist;
#endif
{
cred_t * credp;
__gid_t groups [NGROUPS_MAX];
int i;
if (! __IN_RANGE (0, ngroups, NGROUPS_MAX)) {
set_user_error (EINVAL);
return -1;
}
if (! super ())
return -1;
if (ukcopy (grouplist, groups, ngroups * sizeof (__gid_t)) !=
ngroups * sizeof (__gid_t))
return -1;
if ((credp = cred_setgrp (SELF->p_credp, ngroups)) == NULL) {
set_user_error (EAGAIN);
return -1;
}
SELF->p_credp = credp;
/*
* Convert group ID formats.
*/
for (i = 0 ; i < ngroups ; i ++)
SELF->p_credp->cr_groups [i] = groups [i];
return 0;
}
/*
*-STATUS:
* ANSI
*
*-NAME:
* rename () change the name of a file
*
*-SYNOPSIS:
* #include <stdio.h>
*
* int rename (const char * old, const char * new);
*
*-DESCRIPTION:
* rename () renames a file. "old" is a pointer to the pathname of the
* file or directory to be ranamed. "new" is a pointer to the new
* pathname of the file or directory. Both "old" and "new" must be of the
* same type (either both files, or both directories) and must reside on
* the same file system.
*
* If "new" already exists, it is removed. Thus, if "new" names an
* existing directory, the directory must not have any entries other than
* possibly '.' and '..'. When renaming directories, the "new" pathname
* must not name a descendant of "old". The implementation of rename ()
* ensures that upon successful completion a link named "name" will
* always exist.
*
* If the final component of "old" is a symbolic link, the symbolic link
* is renamed, not the file or directory to which it points.
*
* Write permission is required for both the directory containing "old"
* and the directory containing "new".
*
* rename () fails, "old" is not changed, and no "new" file is created if
* one or more of the following are true:
*
* EACCES A component of either path prefix denies search
* permission; one of the directories containing "old"
* or "new" denies write permission; or one of the
* directories pointed to by "old" or "new" denies write
* permission.
*
* EBUSY "new" is a directory and the mount point for a mounted
* filesystem.
*
* EDQUOT The directory in which the entry for the new name is
* being placed cannot be extended because the user's
* quota of disk blocks on the file system containing the
* directory has been exhausted.
*
* EEXIST The link named by "new" is a directory containing
* entries other than '.' and '..'.
*
* EFAULT "old" or "new" points outside the process's allocated
* address space.
*
* EINVAL "old" is a parent directory of "new", or an attempt is
* made to rename '.' or '..'.
*
* EINTR A signal was caught during execution of the rename ()
* system call.
*
* EIO An I/O error occurred while making or updating a
* directory entry.
*
* EISDIR "new" points to a directory by "old" points to a file
* that is not a directory.
*
* ELOOP Too many symbolic links were encountered in translating
* "old" or "new".
*
* EMULTIHOP Components of pathnames require hopping to multiple
* remote machines and the filesystem type does not allow
* it.
*
* ENAMETOOLONG The length of the "old" or "new" argument exceeds
* {PATH_MAX}, or the length of an "old" or "new"
* component exceeds {NAME_MAX} while _POSIX_NO_TRUNC is
* in effect.
*
* ENOENT A component of either "old" or "new" does not exists,
* or the file referred to by either "old" or "new" does
* not exist.
*
* ENOLINK Pathnames point to a remote machine and the link to
* that machine is no longer active.
*
* ENOSPC The directory that would contain "new" is out of
* space.
*
* ENOTDIR A component of either path prefix is not a directory;
* or the "old" parameter names a directory and the "new"
* parameter names a file.
*
* EROFS The requested operation requires writing in a
* directory on a read-only file system.
*
* EXDEV The links named by "old" and "new" are on different
* file systems.
*
*-DIAGNOSTICS:
* Upon successful completion, a value of 0 is returned. Otherwise, a
* value of -1 is returned and "errno" is set to indicate the error.
*
*-NOTES:
* The system can deadlock if there is a loop in the file system graph.
* Such a loop takes the form of an entry in directory "a", say "a/foo",
* being a hard link to directory "b", and an entry in directory "b", say
* "b/bar", being a hard link to directory "a". When such a loop exists
* and two separate processes attempt to perform rename () "a/foo" to
* "b/bar" and rename () "b/bar" to "a/foo" respectively, the system may
* deadlock attempting to lock both directories for modification. The
* system administrator should replace hard links to directories with
* symbolic links.
*/
#if __USE_PROTO__
int urename (__CONST__ char * old, __CONST__ char * new)
#else
int
urename (old, new)
__CONST__ char * old;
__CONST__ char * new;
#endif
{
/*
* NIGEL: I am putting this in as a quick hack simply because Steve's
* C-library version of rename () is absolutely pathetic. We don't
* comply with the semantics above w.r.t. directories; only regular
* files can be renamed.
*
* Believe me, even this is a massive improvement, not to mention
* having been required for iBCS2 compliance. In theory, we should do
* all the permissions checks before doing any links...
*/
if (do_link (old, new, IOUSR, 1) != 0)
return -1;
(void) do_unlink (old, IOUSR);
if (get_user_error ()) {
/*
* Remove the link to "new"; the original C-library version
* didn't even do this much.
*/
set_user_error (do_unlink (new, IOUSR));
return -1;
}
return 0;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* getpgrp () Get process group
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <unistd.h>
*
* pid_t getpgrp (void);
*
*-DESCRIPTION:
* getpgrp () returns the process group ID of the calling process.
*
*-DIAGNOSTICS:
* getpgrp () is always successful, and no return value is reserved to
* indicate an error.
*/
#if __USE_PROTO__
__pid_t getpgrp (void)
#else
__pid_t
getpgrp ()
#endif
{
return SELF->p_group;
}
/*
*-STATUS:
* SVR4 deprecated
*
*-NAME:
* setpgrp () set process group ID
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <unistd.h>
*
* pid_t setpgrp (void);
*
*-DESCRIPTION:
* If the calling process is not already a session leader, setpgrp ()
* sets the process group ID and session ID of the calling process to the
* process ID of the calling process, and releases the calling process's
* controlling terminal.
*
*-DIAGNOSTICS:
* setpgrp () returns the value of the new process group ID.
*
*-NOTES:
* setpgrp () is being phased out on favour of the setsid () function.
*/
#if __USE_PROTO__
__pid_t setpgrp (void)
#else
__pid_t
setpgrp ()
#endif
{
if (SELF->p_group != SELF->p_pid)
SELF->p_ttdev = NODEV;
return SELF->p_sid = SELF->p_group = SELF->p_pid;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* setsid () set session ID
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <unistd.h>
*
* pid_t setsid (void);
*
*-DESCRIPTION:
* If the calling process is not already a process group leader,
* setsid () sets the process group ID and session ID of the calling
* process to the process ID of the calling process, and releases the
* process's controlling terminal.
*
* setsid () will fail and return an error if the following is true:
*
* EPERM The calling process is already a process group leader,
* or there are processes other than the calling process
* whose process group ID is equal to the process ID of
* the calling process.
*
*-WARNING:
* If the calling process is the last member of a pipeline started by a
* job control shell, the shell may make the calling process a process
* group leader. The other processes of the pipeline become members of
* that process group. In this case, the call to setsid () will fail. For
* this reason, a process that calls setsid () and expects to be part of
* a pipeline should first fork; the parent should exit and the child
* should call setsid (), thereby ensuring that the process will work
* reliably when started by both job control shells and non-jon control
* shells.
*
*-DIAGNOSTICS:
* Upon successful completion, setsid () returns the calling process's
* session ID. Otherwise, a value of -1 is returned and "errno" is set to
* indicate the error.
*/
#if __USE_PROTO__
__pid_t setsid (void)
#else
__pid_t
setsid ()
#endif
{
if (SELF->p_pid == SELF->p_group ||
process_find_pgid (SELF->p_pid, iter_find_any, NULL)) {
set_user_error (EPERM);
return -1;
}
SELF->p_ttdev = NODEV;
return SELF->p_sid = SELF->p_group = SELF->p_pid;
}
/*
*-STATUS:
* POSIX.1
*
*-NAME:
* setpgid () set process group ID
*
*-SYNOPSIS:
* #include <sys/types.h>
* #include <unistd.h>
*
* int setpgid (pid_t pid, pid_t pgid);
*
*-DESCRIPTION:
* setpgid () set the process group ID of the process with ID "pid" to
* "pgid". If "pgid" is equal to "pid", the process becomes a process
* group leader. If "pgid" is not equal to "pid", the process becomes a
* member of an existing process group.
*
* If "pid" is equal to 0, the process ID of the calling process is used.
* If "pgid" is qual to 0, the process specified by "pid" becomes a
* process group leader.
*
* setpgid () fails and returns an error if one or more of the following
* are true:
*
* EACCES "pid" matches the process ID of a child process of the
* calling process and the child process has successfully
* executed an exec () function.
*
* EINVAL "pgid" is less than "(pid_t) 0", or greater than or
* equal to {PID_MAX}.
*
* EINVAL The calling process has a controlling terminal that
* does not support job control.
*
* EPERM The process indicated by the "pid" argument is a
* session leader.
*
* EPERM "pid" matches the process ID of a child process of the
* calling process, and the child process is not in the
* same session as the calling process.
*
* EPERM "pgid" does not match the process ID of the process
* indicated by the "pid" argument and there is no
* process with a process group ID that matches "pgid" in
* the same session as the calling process.
*
* ESRCH "pid" does not match the process ID of the calling
* process or of a child process of the calling process.
*
*-DIAGNOSTICS:
* Upon successful completion, setpgid () returns a value of 0.
* Otherwise, a value of -1 is returned and "errno" is set to indicate
* the error.
*/
#if __USE_PROTO__
int setpgid (__pid_t pid, __pid_t pgid)
#else
int
setpgid (pid, pgid)
__pid_t pid;
__pid_t pgid;
#endif
{
__proc_t * procp;
if (! __IN_RANGE (0, pid, PID_MAX - 1)) {
set_user_error (EINVAL);
return -1;
}
if (pid == 0)
pid = SELF->p_pid;
if ((procp = process_find_pid (pid)) == NULL ||
(procp != SELF && procp->p_ppid != SELF->p_pid)) {
/*
* Either cannot find the process, or the process is not us
* or one of our children.
*/
set_user_error (ESRCH);
return -1;
}
if (procp != SELF && (procp->p_flags & PFEXEC) != 0) {
/*
* The process is a child that has performed an exec ().
*/
set_user_error (EACCES);
return -1;
}
if (pgid == 0)
pgid = procp->p_pid;
if (procp->p_sid == procp->p_pid || procp->p_sid != SELF->p_sid ||
(procp->p_pid != pgid &&
session_find_pgid (SELF, pgid, iter_find_any, NULL) == 0)) {
/*
* Ether the process is a session leader or the process is not
* in our session, or the process is trying to join a process
* group but there is no member of the group in our session.
*/
set_user_error (EPERM);
return -1;
}
/*
* POSIX.1 would allow us to proceed at this point; SVR4 says that we
* should give EINVAL because our controlling terminal does not
* support job control.
*/
#if 1
set_user_error (EINVAL);
return -1;
#else
procp->p_group = pgid;
return 0;
#endif
}
/*
* iBCS2 system-call entry point which dispatches to the process-group-related
* functions.
*/
#if __USE_PROTO__
int upgrpsys (int flag, __pid_t arg1, __pid_t arg2)
#else
int
upgrpsys (flag, arg1, arg2)
int flag;
__pid_t arg1;
__pid_t arg2;
#endif
{
switch (flag) {
case 0: return getpgrp ();
case 1: return setpgrp ();
case 2: return setpgid (arg1, arg2);
case 3: return setsid ();
case 4: /* reserved */
case 5: /* reserved */
default:
set_user_error (ENOSYS);
return -1;
}
}
/*
* Poll devices for input/output events.
*/
int
upoll(pollfds, npoll, msec)
struct pollfd * pollfds;
unsigned long npoll;
int msec;
{
struct pollfd * pollp; /* current poll pointer */
int i;
int ret;
/*
* Validate number of polls.
*/
if (! __IN_RANGE (0, npoll, NOFILE)) {
set_user_error (EINVAL);
return -1;
}
/*
* If there are any fd's to poll
* validate address of polling information.
* npoll of 0 is legal, allows user a short delay.
*/
if (npoll != 0 &&
(pollfds == NULL ||
! useracc (pollfds, npoll * sizeof (struct pollfd), 1))) {
set_user_error (EFAULT);
return -1;
}
if ((ret = pwalloc (npoll, msec)) != 0) {
set_user_error (ret);
return -1;
}
for (;;) {
/*
* Service each poll in turn.
*/
ret = 0;
for (i = npoll, pollp = pollfds; i > 0; -- i, pollp ++) {
fd_t fd;
__fd_t * fdp;
short events;
short revents;
/*
* Fetch file descriptor.
*/
fd = getuwd (& pollp->fd);
events = getusd (& pollp->events);
/*
* Ignore negative file descriptors.
*/
if (! __IN_RANGE (0, fd, NOFILE - 1)) {
revents = 0;
goto remember;
}
/*
* Validate file descriptor.
*/
if ((fdp = fd_get (fd)) == NULL) {
revents = POLLNVAL;
goto remember;
}
switch (fdp->f_ip->i_mode & __IFMT) {
case __IFCHR:
revents = dpoll (fdp->f_ip->i_rdev,
events, msec,
fdp->f_ip->i_private);
break;
case __IFPIP:
revents = ppoll (fdp->f_ip, events, msec);
break;
default:
revents = POLLNVAL;
break;
}
/*
* Remember reponses.
*/
remember:
putusd (& pollp->revents, revents);
/*
* Record number of non-zero responses.
*/
if (revents) {
msec = 0;
ret ++;
}
}
/*
* Non-blocking poll or poll response received.
*/
if (msec == 0)
break;
/*
* Wait for polled event, poll timeout, or signal.
*/
switch (pollsleep (msec)) {
case __POLL_SIGNALLED:
set_user_error (EINTR);
break;
case __POLL_NOMEM:
set_user_error (EAGAIN);
break;
case __POLL_TIMEOUT:
ret = 0;
break;
default:
continue;
}
break;
}
pwfree ();
return ret;
}