/* $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; }