diff options
Diffstat (limited to 'usr/src/uts/common/os/procset.c')
-rw-r--r-- | usr/src/uts/common/os/procset.c | 914 |
1 files changed, 914 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/procset.c b/usr/src/uts/common/os/procset.c new file mode 100644 index 0000000000..7a675c604e --- /dev/null +++ b/usr/src/uts/common/os/procset.c @@ -0,0 +1,914 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +#pragma ident "%Z%%M% %I% %E% SMI" /* from SVr4.0 1.25 */ + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/vfs.h> +#include <sys/cred.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/kmem.h> +#include <sys/user.h> +#include <sys/buf.h> +#include <sys/var.h> +#include <sys/conf.h> +#include <sys/debug.h> +#include <sys/proc.h> +#include <sys/signal.h> +#include <sys/siginfo.h> +#include <sys/acct.h> +#include <sys/procset.h> +#include <sys/cmn_err.h> +#include <sys/fault.h> +#include <sys/syscall.h> +#include <sys/ucontext.h> +#include <sys/procfs.h> +#include <sys/session.h> +#include <sys/task.h> +#include <sys/project.h> +#include <sys/pool.h> +#include <sys/zone.h> +#include <sys/contract/process_impl.h> + +id_t getmyid(idtype_t); +int checkprocset(procset_t *); +static kthread_t *getlwpptr(id_t); +int procinset(proc_t *, procset_t *); +static int lwpinset(proc_t *, procset_t *, kthread_t *, int *); + +/* + * The dotoprocs function locates the process(es) specified + * by the procset structure pointed to by psp. If funcp + * is non-NULL then it points to a function which dotoprocs + * will call for each process in the specified set. The + * arguments to this function will be a pointer to the + * current process from the set and arg. + * If the called function returns -1, it means that processing of the + * procset should stop and a normal (non-error) return should be made + * to the caller of dotoprocs. + * If the called function returns any other non-zero value the search + * is terminated and the function's return value is returned to + * the caller of dotoprocs. This will normally be an error code. + * Otherwise, dotoprocs will return zero after processing the entire + * process set unless no processes were found in which case ESRCH will + * be returned. + */ +int +dotoprocs(procset_t *psp, int (*funcp)(), char *arg) +{ + proc_t *prp; /* A process from the set */ + int error; + int nfound; /* Nbr of processes found. */ + proc_t *lastprp; /* Last proc found. */ + + /* + * Check that the procset_t is valid. + */ + error = checkprocset(psp); + if (error) { + return (error); + } + /* + * Check for the special value P_MYID in either operand + * and replace it with the correct value. We don't check + * for an error return from getmyid() because the idtypes + * have been validated by the checkprocset() call above. + */ + mutex_enter(&pidlock); + if (psp->p_lid == P_MYID) { + psp->p_lid = getmyid(psp->p_lidtype); + } + if (psp->p_rid == P_MYID) { + psp->p_rid = getmyid(psp->p_ridtype); + } + + /* + * If psp only acts on a single proc, we can reduce pidlock hold time + * by avoiding a needless scan of the entire proc list. Although + * there are many procset_t combinations which might boil down to a + * single proc, the most common case is an AND operation where one + * side is a specific pid, and the other side is P_ALL, so that is + * the case for which we will provide a fast path. Other cases could + * be added in a similar fashion if they were to become significant + * pidlock bottlenecks. + * + * Perform the check symmetrically: either the left or right side may + * specify a pid, with the opposite side being 'all'. + */ + if (psp->p_op == POP_AND) { + if (((psp->p_lidtype == P_PID) && (psp->p_ridtype == P_ALL)) || + ((psp->p_ridtype == P_PID) && (psp->p_lidtype == P_ALL))) { + id_t pid; + + pid = (psp->p_lidtype == P_PID) ? + psp->p_lid : psp->p_rid; + if ((prp = prfind((pid_t)pid)) == NULL) { + /* specified proc doesn't exist */ + mutex_exit(&pidlock); + return (ESRCH); + } + /* operate only on the specified proc */ + error = (*funcp)(prp, arg); + mutex_exit(&pidlock); + if (error == -1) + error = 0; + return (error); + } + } + + nfound = 0; + error = 0; + + for (prp = practive; prp != NULL; prp = prp->p_next) { + /* + * If caller is in a non-global zone, skip processes + * in other zones. + */ + if (!HASZONEACCESS(curproc, prp->p_zone->zone_id)) + continue; + if (prp->p_stat == SIDL || prp->p_stat == SZOMB || + prp->p_tlist == NULL || prp->p_flag & SSYS) + continue; + if (procinset(prp, psp)) { + nfound++; + lastprp = prp; + if (funcp != NULL && prp != proc_init) { + error = (*funcp)(prp, arg); + if (error == -1) { + mutex_exit(&pidlock); + return (0); + } else if (error) { + mutex_exit(&pidlock); + return (error); + } + } + } + } + if (nfound == 0) { + mutex_exit(&pidlock); + return (ESRCH); + } + if (nfound == 1 && lastprp == proc_init && funcp != NULL) + error = (*funcp)(lastprp, arg); + if (error == -1) + error = 0; + mutex_exit(&pidlock); + return (error); +} + +/* + * Check if a procset_t is valid. Return zero or an errno. + */ +int +checkprocset(procset_t *psp) +{ + switch (psp->p_lidtype) { + case P_LWPID: + case P_PID: + case P_PPID: + case P_PGID: + case P_SID: + case P_TASKID: + case P_CID: + case P_UID: + case P_GID: + case P_PROJID: + case P_POOLID: + case P_ZONEID: + case P_CTID: + case P_ALL: + break; + default: + return (EINVAL); + } + + switch (psp->p_ridtype) { + case P_LWPID: + case P_PID: + case P_PPID: + case P_PGID: + case P_SID: + case P_TASKID: + case P_CID: + case P_UID: + case P_GID: + case P_PROJID: + case P_POOLID: + case P_ZONEID: + case P_CTID: + case P_ALL: + break; + default: + return (EINVAL); + } + + switch (psp->p_op) { + case POP_DIFF: + case POP_AND: + case POP_OR: + case POP_XOR: + break; + default: + return (EINVAL); + } + + return (0); +} + +/* + * procinset returns 1 if the process pointed to + * by pp is in the process set specified by psp, otherwise 0 is returned. + * The caller should check that the process is not exiting and is not + * in the SYS scheduling class. + * + * This function expects to be called with a valid procset_t. + * The set should be checked using checkprocset() before calling + * this function. + */ +int +procinset(proc_t *pp, procset_t *psp) +{ + int loperand = 0; + int roperand = 0; + int lwplinproc = 0; + int lwprinproc = 0; + kthread_t *tp = proctot(pp); + + switch (psp->p_lidtype) { + + case P_LWPID: + if (pp == ttoproc(curthread)) + if (getlwpptr(psp->p_lid) != NULL) + lwplinproc++; + break; + + case P_PID: + if (pp->p_pid == psp->p_lid) + loperand++; + break; + + case P_PPID: + if (pp->p_ppid == psp->p_lid) + loperand++; + break; + + case P_PGID: + if (pp->p_pgrp == psp->p_lid) + loperand++; + break; + + case P_SID: + if (pp->p_sessp->s_sid == psp->p_lid) + loperand++; + break; + + case P_CID: + ASSERT(tp != NULL); + /* This case is broken for now. Need to be fixed XXX */ + if (tp->t_cid == psp->p_lid) + /* + * if (checkcid(psp->p_lid)) + */ + loperand++; + break; + + case P_TASKID: + if (pp->p_task->tk_tkid == psp->p_lid) + loperand++; + break; + + case P_UID: + mutex_enter(&pp->p_crlock); + if (crgetuid(pp->p_cred) == psp->p_lid) + loperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_GID: + mutex_enter(&pp->p_crlock); + if (crgetgid(pp->p_cred) == psp->p_lid) + loperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_PROJID: + if (pp->p_task->tk_proj->kpj_id == psp->p_lid) + loperand++; + break; + + case P_POOLID: + if (pp->p_pool->pool_id == psp->p_lid) + loperand++; + break; + + case P_ZONEID: + if (pp->p_zone->zone_id == psp->p_lid) + loperand++; + break; + + case P_CTID: + if (PRCTID(pp) == psp->p_lid) + loperand++; + break; + + case P_ALL: + loperand++; + break; + + default: +#ifdef DEBUG + cmn_err(CE_WARN, "procinset called with bad set"); + return (0); +#else + return (0); +#endif + } + + switch (psp->p_ridtype) { + + case P_LWPID: + if (pp == ttoproc(curthread)) + if (getlwpptr(psp->p_rid) != NULL) + lwprinproc++; + break; + + case P_PID: + if (pp->p_pid == psp->p_rid) + roperand++; + break; + + case P_PPID: + if (pp->p_ppid == psp->p_rid) + roperand++; + break; + + case P_PGID: + if (pp->p_pgrp == psp->p_rid) + roperand++; + break; + + case P_SID: + if (pp->p_sessp->s_sid == psp->p_rid) + roperand++; + break; + + case P_TASKID: + if (pp->p_task->tk_tkid == psp->p_rid) + roperand++; + break; + + case P_CID: + ASSERT(tp != NULL); + /* This case is broken for now. Need to be fixed XXX */ + if (tp->t_cid == psp->p_rid) + /* + * if (checkcid(psp->p_rid)) + */ + roperand++; + break; + + case P_UID: + mutex_enter(&pp->p_crlock); + if (crgetuid(pp->p_cred) == psp->p_rid) + roperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_GID: + mutex_enter(&pp->p_crlock); + if (crgetgid(pp->p_cred) == psp->p_rid) + roperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_PROJID: + if (pp->p_task->tk_proj->kpj_id == psp->p_rid) + roperand++; + break; + + case P_POOLID: + if (pp->p_pool->pool_id == psp->p_rid) + roperand++; + break; + + case P_ZONEID: + if (pp->p_zone->zone_id == psp->p_rid) + roperand++; + break; + + case P_CTID: + if (PRCTID(pp) == psp->p_rid) + roperand++; + break; + + case P_ALL: + roperand++; + break; + + default: +#ifdef DEBUG + cmn_err(CE_WARN, "procinset called with bad set"); + return (0); +#else + return (0); +#endif + } + + switch (psp->p_op) { + + case POP_DIFF: + if (loperand && !lwprinproc && !roperand) + return (1); + else + return (0); + + case POP_AND: + if (loperand && roperand) + return (1); + else + return (0); + + case POP_OR: + if (loperand || roperand) + return (1); + else + return (0); + + case POP_XOR: + if ((loperand && !lwprinproc && !roperand) || + (roperand && !lwplinproc && !loperand)) + return (1); + else + return (0); + + default: +#ifdef DEBUG + cmn_err(CE_WARN, "procinset called with bad set"); + return (0); +#else + return (0); +#endif + } + /* NOTREACHED */ +} + +/* + * lwpinset returns 1 if the thread pointed to + * by tp is in the process set specified by psp and is not in + * the sys scheduling class - otherwise 0 is returned. + * + * This function expects to be called with a valid procset_t. + * The set should be checked using checkprocset() before calling + * this function. + */ +int +lwpinset(proc_t *pp, procset_t *psp, kthread_t *tp, int *done) +{ + int loperand = 0; + int roperand = 0; + int lwplinset = 0; + int lwprinset = 0; + + ASSERT(ttoproc(tp) == pp); + + /* + * If process is in the sys class return (0). + */ + if (proctot(pp)->t_cid == 0) { + return (0); + } + + switch (psp->p_lidtype) { + + case P_LWPID: + if (tp->t_tid == psp->p_lid) + lwplinset ++; + break; + + case P_PID: + if (pp->p_pid == psp->p_lid) + loperand++; + break; + + case P_PPID: + if (pp->p_ppid == psp->p_lid) + loperand++; + break; + + case P_PGID: + if (pp->p_pgrp == psp->p_lid) + loperand++; + break; + + case P_SID: + if (pp->p_sessp->s_sid == psp->p_lid) + loperand++; + break; + + case P_TASKID: + if (pp->p_task->tk_tkid == psp->p_lid) + loperand++; + break; + + case P_CID: + if (tp->t_cid == psp->p_lid) + loperand++; + break; + + case P_UID: + mutex_enter(&pp->p_crlock); + if (crgetuid(pp->p_cred) == psp->p_lid) + loperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_GID: + mutex_enter(&pp->p_crlock); + if (crgetgid(pp->p_cred) == psp->p_lid) + loperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_PROJID: + if (pp->p_task->tk_proj->kpj_id == psp->p_lid) + loperand++; + break; + + case P_POOLID: + if (pp->p_pool->pool_id == psp->p_lid) + loperand++; + break; + + case P_ZONEID: + if (pp->p_zone->zone_id == psp->p_lid) + loperand++; + break; + + case P_CTID: + if (PRCTID(pp) == psp->p_lid) + loperand++; + break; + + case P_ALL: + loperand++; + break; + + default: +#ifdef DEBUG + cmn_err(CE_WARN, "lwpinset called with bad set"); + return (0); +#else + return (0); +#endif + } + + switch (psp->p_ridtype) { + + case P_LWPID: + if (tp->t_tid == psp->p_rid) + lwprinset ++; + break; + + case P_PID: + if (pp->p_pid == psp->p_rid) + roperand++; + break; + + case P_PPID: + if (pp->p_ppid == psp->p_rid) + roperand++; + break; + + case P_PGID: + if (pp->p_pgrp == psp->p_rid) + roperand++; + break; + + case P_SID: + if (pp->p_sessp->s_sid == psp->p_rid) + roperand++; + break; + + case P_TASKID: + if (pp->p_task->tk_tkid == psp->p_rid) + roperand++; + break; + + case P_CID: + if (tp->t_cid == psp->p_rid) + roperand++; + break; + + case P_UID: + mutex_enter(&pp->p_crlock); + if (crgetuid(pp->p_cred) == psp->p_rid) + roperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_GID: + mutex_enter(&pp->p_crlock); + if (crgetgid(pp->p_cred) == psp->p_rid) + roperand++; + mutex_exit(&pp->p_crlock); + break; + + case P_PROJID: + if (pp->p_task->tk_proj->kpj_id == psp->p_rid) + roperand++; + break; + + case P_POOLID: + if (pp->p_pool->pool_id == psp->p_rid) + roperand++; + break; + + case P_ZONEID: + if (pp->p_zone->zone_id == psp->p_rid) + roperand++; + break; + + case P_CTID: + if (PRCTID(pp) == psp->p_rid) + roperand++; + break; + + case P_ALL: + roperand++; + break; + + default: +#ifdef DEBUG + cmn_err(CE_WARN, "lwpinset called with bad set"); + return (0); +#else + return (0); +#endif + } + + if (lwplinset && lwprinset) + *done = 1; + + switch (psp->p_op) { + + case POP_DIFF: + if ((loperand || lwplinset) && !(lwprinset || roperand)) + return (1); + else + return (0); + + case POP_AND: + if ((loperand || lwplinset) && (roperand || lwprinset)) + return (1); + else + return (0); + + case POP_OR: + if (loperand || roperand || lwplinset || lwprinset) + return (1); + else + return (0); + + case POP_XOR: + if (((loperand || lwplinset) && + !(lwprinset || roperand)) || + ((roperand || lwprinset) && + !(lwplinset || loperand))) + return (1); + else + return (0); + + default: +#ifdef DEBUG + cmn_err(CE_WARN, "lwpinset called with bad set"); + return (0); +#else + return (0); +#endif + } + /* NOTREACHED */ +} +/* + * Check for common cases of procsets which specify only the + * current process. cur_inset_only() returns B_TRUE when + * the current process is the only one in the set. B_FALSE + * is returned to indicate that this may not be the case. + */ +boolean_t +cur_inset_only(procset_t *psp) +{ + if (((psp->p_lidtype == P_PID && + (psp->p_lid == P_MYID || + psp->p_lid == ttoproc(curthread)->p_pid)) || + ((psp->p_lidtype == P_LWPID) && + (psp->p_lid == P_MYID || + psp->p_lid == curthread->t_tid))) && + psp->p_op == POP_AND && psp->p_ridtype == P_ALL) + return (B_TRUE); + + if (((psp->p_ridtype == P_PID && + (psp->p_rid == P_MYID || + psp->p_rid == ttoproc(curthread)->p_pid)) || + ((psp->p_ridtype == P_LWPID) && + (psp->p_rid == P_MYID || + psp->p_rid == curthread->t_tid))) && + psp->p_op == POP_AND && psp->p_lidtype == P_ALL) + return (B_TRUE); + + return (B_FALSE); +} + +id_t +getmyid(idtype_t idtype) +{ + proc_t *pp; + uid_t uid; + gid_t gid; + + pp = ttoproc(curthread); + + switch (idtype) { + case P_LWPID: + return (curthread->t_tid); + + case P_PID: + return (pp->p_pid); + + case P_PPID: + return (pp->p_ppid); + + case P_PGID: + return (pp->p_pgrp); + + case P_SID: + return (pp->p_sessp->s_sid); + + case P_TASKID: + return (pp->p_task->tk_tkid); + + case P_CID: + return (curthread->t_cid); + + case P_UID: + mutex_enter(&pp->p_crlock); + uid = crgetuid(pp->p_cred); + mutex_exit(&pp->p_crlock); + return (uid); + + case P_GID: + mutex_enter(&pp->p_crlock); + gid = crgetgid(pp->p_cred); + mutex_exit(&pp->p_crlock); + return (gid); + + case P_PROJID: + return (pp->p_task->tk_proj->kpj_id); + + case P_POOLID: + return (pp->p_pool->pool_id); + + case P_ZONEID: + return (pp->p_zone->zone_id); + + case P_CTID: + return (PRCTID(pp)); + + case P_ALL: + /* + * The value doesn't matter for P_ALL. + */ + return (0); + + default: + return (-1); + } +} + +static kthread_t * +getlwpptr(id_t id) +{ + proc_t *p; + kthread_t *t; + + if (id == P_MYID) + t = curthread; + else { + p = ttoproc(curthread); + mutex_enter(&p->p_lock); + t = idtot(p, id); + mutex_exit(&p->p_lock); + } + + return (t); +} + +/* + * The dotolwp function locates the LWP(s) specified by the procset structure + * pointed to by psp. If funcp is non-NULL then it points to a function + * which dotolwp will call for each LWP in the specified set. + * LWPIDs specified in the procset structure always refer to lwps in curproc. + * The arguments for this function must be "char *arg", and "kthread_t *tp", + * where tp is a pointer to the current thread from the set. + * Note that these arguments are passed to the function in reversed order + * than the order of arguments passed by dotoprocs() to its callback function. + * Also note that there are two separate cases where this routine returns zero. + * In the first case no mutex is grabbed, in the second the p_lock mutex + * is NOT RELEASED. The priocntl code is expecting this behaviour. + */ +int +dotolwp(procset_t *psp, int (*funcp)(), char *arg) +{ + int error = 0; + int nfound = 0; + kthread_t *tp; + proc_t *pp; + int done = 0; + + /* + * Check that the procset_t is valid. + */ + error = checkprocset(psp); + if (error) { + return (error); + } + + mutex_enter(&pidlock); + + /* + * Check for the special value P_MYID in either operand + * and replace it with the correct value. We don't check + * for an error return from getmyid() because the idtypes + * have been validated by the checkprocset() call above. + */ + if (psp->p_lid == P_MYID) { + psp->p_lid = getmyid(psp->p_lidtype); + } + if (psp->p_rid == P_MYID) { + psp->p_rid = getmyid(psp->p_ridtype); + } + + pp = ttoproc(curthread); + + if (procinset(pp, psp)) { + mutex_exit(&pidlock); + return (0); + } + mutex_enter(&pp->p_lock); + if ((tp = pp->p_tlist) == NULL) { + mutex_exit(&pp->p_lock); + mutex_exit(&pidlock); + return (0); + } + do { + if (lwpinset(pp, psp, tp, &done)) { + nfound ++; + error = (*funcp)(arg, tp); + if (error) { + mutex_exit(&pp->p_lock); + mutex_exit(&pidlock); + return (error); + } + } + } while (((tp = tp->t_forw) != pp->p_tlist) && !done); + + if (nfound == 0) { + mutex_exit(&pp->p_lock); + mutex_exit(&pidlock); + return (ESRCH); + } + + mutex_exit(&pidlock); + return (error); +} |