/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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. funcp 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. */ ASSERT(funcp != NULL); /* * 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) || (prp->p_stat == SIDL || prp->p_stat == SZOMB || prp->p_tlist == NULL || prp->p_flag & SSYS)) { /* * Specified proc doesn't exist or should * not be operated on. * Don't need to make HASZONEACCESS check * here since prfind() takes care of that. */ mutex_exit(&pidlock); return (ESRCH); } /* * Operate only on the specified proc. It's okay * if it's init. */ 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; /* * Ignore this process if it's coming or going, * if it's a system process or if it's not in * the given procset_t. */ if (prp->p_stat == SIDL || prp->p_stat == SZOMB) continue; mutex_enter(&prp->p_lock); if (prp->p_flag & SSYS || prp->p_tlist == NULL || procinset(prp, psp) == 0) { mutex_exit(&prp->p_lock); } else { mutex_exit(&prp->p_lock); nfound++; lastprp = prp; if (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) 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. If either process set operand * has type P_CID and pp refers to a process that is exiting, by which we mean * that its p_tlist is NULL, then procinset will return 0. pp's p_lock must be * held across the call to this function. The caller should ensure that the * process does not belong to 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; ASSERT(MUTEX_HELD(&pp->p_lock)); 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: mutex_enter(&pp->p_splock); if (pp->p_sessp->s_sid == psp->p_lid) loperand++; mutex_exit(&pp->p_splock); break; case P_CID: tp = proctot(pp); if (tp == NULL) return (0); if (tp->t_cid == 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: mutex_enter(&pp->p_splock); if (pp->p_sessp->s_sid == psp->p_rid) roperand++; mutex_exit(&pp->p_splock); break; case P_TASKID: if (pp->p_task->tk_tkid == psp->p_rid) roperand++; break; case P_CID: tp = proctot(pp); if (tp == NULL) return (0); 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, "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: mutex_enter(&pp->p_splock); if (pp->p_sessp->s_sid == psp->p_lid) loperand++; mutex_exit(&pp->p_splock); 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: mutex_enter(&pp->p_splock); if (pp->p_sessp->s_sid == psp->p_rid) roperand++; mutex_exit(&pp->p_splock); 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; pid_t sid; 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: mutex_enter(&pp->p_splock); sid = pp->p_sessp->s_sid; mutex_exit(&pp->p_splock); return (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; ASSERT(MUTEX_HELD(&(ttoproc(curthread)->p_lock))); if (id == P_MYID) t = curthread; else { p = ttoproc(curthread); t = idtot(p, id); } 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); mutex_enter(&pp->p_lock); if (procinset(pp, psp) || (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); }