Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_proc.c
Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | ||||||||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | |||||||||
#include <compat/freebsd32/freebsd32.h> | #include <compat/freebsd32/freebsd32.h> | |||||||||
#include <compat/freebsd32/freebsd32_util.h> | #include <compat/freebsd32/freebsd32_util.h> | |||||||||
#endif | #endif | |||||||||
SDT_PROVIDER_DEFINE(proc); | SDT_PROVIDER_DEFINE(proc); | |||||||||
MALLOC_DEFINE(M_PGRP, "pgrp", "process group header"); | ||||||||||
MALLOC_DEFINE(M_SESSION, "session", "session header"); | MALLOC_DEFINE(M_SESSION, "session", "session header"); | |||||||||
static MALLOC_DEFINE(M_PROC, "proc", "Proc structures"); | static MALLOC_DEFINE(M_PROC, "proc", "Proc structures"); | |||||||||
MALLOC_DEFINE(M_SUBPROC, "subproc", "Proc sub-structures"); | MALLOC_DEFINE(M_SUBPROC, "subproc", "Proc sub-structures"); | |||||||||
static void fixjobc_enterpgrp(struct proc *p, struct pgrp *pgrp); | ||||||||||
static void doenterpgrp(struct proc *, struct pgrp *); | static void doenterpgrp(struct proc *, struct pgrp *); | |||||||||
static void orphanpg(struct pgrp *pg); | static void orphanpg(struct pgrp *pg); | |||||||||
static void fill_kinfo_aggregate(struct proc *p, struct kinfo_proc *kp); | static void fill_kinfo_aggregate(struct proc *p, struct kinfo_proc *kp); | |||||||||
static void fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp); | static void fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp); | |||||||||
static void fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp, | static void fill_kinfo_thread(struct thread *td, struct kinfo_proc *kp, | |||||||||
int preferthread); | int preferthread); | |||||||||
static void pgadjustjobc(struct pgrp *pgrp, bool entering); | ||||||||||
static void pgdelete(struct pgrp *); | static void pgdelete(struct pgrp *); | |||||||||
static int pgrp_init(void *mem, int size, int flags); | ||||||||||
static int proc_ctor(void *mem, int size, void *arg, int flags); | static int proc_ctor(void *mem, int size, void *arg, int flags); | |||||||||
static void proc_dtor(void *mem, int size, void *arg); | static void proc_dtor(void *mem, int size, void *arg); | |||||||||
static int proc_init(void *mem, int size, int flags); | static int proc_init(void *mem, int size, int flags); | |||||||||
static void proc_fini(void *mem, int size); | static void proc_fini(void *mem, int size); | |||||||||
static void pargs_free(struct pargs *pa); | static void pargs_free(struct pargs *pa); | |||||||||
/* | /* | |||||||||
* Other process lists | * Other process lists | |||||||||
*/ | */ | |||||||||
struct pidhashhead *pidhashtbl; | struct pidhashhead *pidhashtbl; | |||||||||
struct sx *pidhashtbl_lock; | struct sx *pidhashtbl_lock; | |||||||||
u_long pidhash; | u_long pidhash; | |||||||||
u_long pidhashlock; | u_long pidhashlock; | |||||||||
struct pgrphashhead *pgrphashtbl; | struct pgrphashhead *pgrphashtbl; | |||||||||
u_long pgrphash; | u_long pgrphash; | |||||||||
struct proclist allproc; | struct proclist allproc; | |||||||||
struct sx __exclusive_cache_line allproc_lock; | struct sx __exclusive_cache_line allproc_lock; | |||||||||
struct sx __exclusive_cache_line proctree_lock; | struct sx __exclusive_cache_line proctree_lock; | |||||||||
struct mtx __exclusive_cache_line ppeers_lock; | struct mtx __exclusive_cache_line ppeers_lock; | |||||||||
struct mtx __exclusive_cache_line procid_lock; | struct mtx __exclusive_cache_line procid_lock; | |||||||||
uma_zone_t proc_zone; | uma_zone_t proc_zone; | |||||||||
uma_zone_t pgrp_zone; | ||||||||||
/* | /* | |||||||||
* The offset of various fields in struct proc and struct thread. | * The offset of various fields in struct proc and struct thread. | |||||||||
* These are used by kernel debuggers to enumerate kernel threads and | * These are used by kernel debuggers to enumerate kernel threads and | |||||||||
* processes. | * processes. | |||||||||
*/ | */ | |||||||||
const int proc_off_p_pid = offsetof(struct proc, p_pid); | const int proc_off_p_pid = offsetof(struct proc, p_pid); | |||||||||
const int proc_off_p_comm = offsetof(struct proc, p_comm); | const int proc_off_p_comm = offsetof(struct proc, p_comm); | |||||||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | procinit(void) | |||||||||
pidhashtbl_lock = malloc(sizeof(*pidhashtbl_lock) * (pidhashlock + 1), | pidhashtbl_lock = malloc(sizeof(*pidhashtbl_lock) * (pidhashlock + 1), | |||||||||
M_PROC, M_WAITOK | M_ZERO); | M_PROC, M_WAITOK | M_ZERO); | |||||||||
for (i = 0; i < pidhashlock + 1; i++) | for (i = 0; i < pidhashlock + 1; i++) | |||||||||
sx_init_flags(&pidhashtbl_lock[i], "pidhash", SX_DUPOK); | sx_init_flags(&pidhashtbl_lock[i], "pidhash", SX_DUPOK); | |||||||||
pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash); | pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash); | |||||||||
proc_zone = uma_zcreate("PROC", sched_sizeof_proc(), | proc_zone = uma_zcreate("PROC", sched_sizeof_proc(), | |||||||||
proc_ctor, proc_dtor, proc_init, proc_fini, | proc_ctor, proc_dtor, proc_init, proc_fini, | |||||||||
UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | |||||||||
pgrp_zone = uma_zcreate("PGRP", sizeof(struct pgrp), NULL, NULL, | ||||||||||
pgrp_init, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | ||||||||||
uihashinit(); | uihashinit(); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* Prepare a proc for use. | * Prepare a proc for use. | |||||||||
*/ | */ | |||||||||
static int | static int | |||||||||
proc_ctor(void *mem, int size, void *arg, int flags) | proc_ctor(void *mem, int size, void *arg, int flags) | |||||||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | #ifdef notnow | |||||||||
mtx_destroy(&p->p_mtx); | mtx_destroy(&p->p_mtx); | |||||||||
if (p->p_ksi != NULL) | if (p->p_ksi != NULL) | |||||||||
ksiginfo_free(p->p_ksi); | ksiginfo_free(p->p_ksi); | |||||||||
#else | #else | |||||||||
panic("proc reclaimed"); | panic("proc reclaimed"); | |||||||||
#endif | #endif | |||||||||
} | } | |||||||||
static int | ||||||||||
pgrp_init(void *mem, int size, int flags) | ||||||||||
{ | ||||||||||
struct pgrp *pg; | ||||||||||
pg = mem; | ||||||||||
mtx_init(&pg->pg_mtx, "process group", NULL, MTX_DEF | MTX_DUPOK); | ||||||||||
return (0); | ||||||||||
} | ||||||||||
/* | /* | |||||||||
* PID space management. | * PID space management. | |||||||||
* | * | |||||||||
* These bitmaps are used by fork_findpid. | * These bitmaps are used by fork_findpid. | |||||||||
*/ | */ | |||||||||
bitstr_t bit_decl(proc_id_pidmap, PID_MAX); | bitstr_t bit_decl(proc_id_pidmap, PID_MAX); | |||||||||
bitstr_t bit_decl(proc_id_grpidmap, PID_MAX); | bitstr_t bit_decl(proc_id_grpidmap, PID_MAX); | |||||||||
bitstr_t bit_decl(proc_id_sessidmap, PID_MAX); | bitstr_t bit_decl(proc_id_sessidmap, PID_MAX); | |||||||||
▲ Show 20 Lines • Show All 255 Lines • ▼ Show 20 Lines | enterpgrp(struct proc *p, pid_t pgid, struct pgrp *pgrp, struct session *sess) | |||||||||
KASSERT(pgrp != NULL, ("enterpgrp: pgrp == NULL")); | KASSERT(pgrp != NULL, ("enterpgrp: pgrp == NULL")); | |||||||||
KASSERT(p->p_pid == pgid, | KASSERT(p->p_pid == pgid, | |||||||||
("enterpgrp: new pgrp and pid != pgid")); | ("enterpgrp: new pgrp and pid != pgid")); | |||||||||
KASSERT(pgfind(pgid) == NULL, | KASSERT(pgfind(pgid) == NULL, | |||||||||
("enterpgrp: pgrp with pgid exists")); | ("enterpgrp: pgrp with pgid exists")); | |||||||||
KASSERT(!SESS_LEADER(p), | KASSERT(!SESS_LEADER(p), | |||||||||
("enterpgrp: session leader attempted setpgrp")); | ("enterpgrp: session leader attempted setpgrp")); | |||||||||
mtx_init(&pgrp->pg_mtx, "process group", NULL, MTX_DEF | MTX_DUPOK); | ||||||||||
if (sess != NULL) { | if (sess != NULL) { | |||||||||
/* | /* | |||||||||
* new session | * new session | |||||||||
*/ | */ | |||||||||
mtx_init(&sess->s_mtx, "session", NULL, MTX_DEF); | mtx_init(&sess->s_mtx, "session", NULL, MTX_DEF); | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
p->p_flag &= ~P_CONTROLT; | p->p_flag &= ~P_CONTROLT; | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
Show All 13 Lines | enterpgrp(struct proc *p, pid_t pgid, struct pgrp *pgrp, struct session *sess) | |||||||||
} else { | } else { | |||||||||
pgrp->pg_session = p->p_session; | pgrp->pg_session = p->p_session; | |||||||||
sess_hold(pgrp->pg_session); | sess_hold(pgrp->pg_session); | |||||||||
PGRP_LOCK(pgrp); | PGRP_LOCK(pgrp); | |||||||||
} | } | |||||||||
pgrp->pg_id = pgid; | pgrp->pg_id = pgid; | |||||||||
proc_id_set(PROC_ID_GROUP, p->p_pid); | proc_id_set(PROC_ID_GROUP, p->p_pid); | |||||||||
LIST_INIT(&pgrp->pg_members); | LIST_INIT(&pgrp->pg_members); | |||||||||
pgrp->pg_flags = 0; | ||||||||||
/* | /* | |||||||||
* As we have an exclusive lock of proctree_lock, | * As we have an exclusive lock of proctree_lock, | |||||||||
* this should not deadlock. | * this should not deadlock. | |||||||||
*/ | */ | |||||||||
LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash); | LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash); | |||||||||
pgrp->pg_jobc = 0; | ||||||||||
SLIST_INIT(&pgrp->pg_sigiolst); | SLIST_INIT(&pgrp->pg_sigiolst); | |||||||||
PGRP_UNLOCK(pgrp); | PGRP_UNLOCK(pgrp); | |||||||||
doenterpgrp(p, pgrp); | doenterpgrp(p, pgrp); | |||||||||
return (0); | return (0); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* Move p to an existing process group | * Move p to an existing process group | |||||||||
*/ | */ | |||||||||
int | int | |||||||||
enterthispgrp(struct proc *p, struct pgrp *pgrp) | enterthispgrp(struct proc *p, struct pgrp *pgrp) | |||||||||
{ | { | |||||||||
sx_assert(&proctree_lock, SX_XLOCKED); | sx_assert(&proctree_lock, SX_XLOCKED); | |||||||||
PROC_LOCK_ASSERT(p, MA_NOTOWNED); | PROC_LOCK_ASSERT(p, MA_NOTOWNED); | |||||||||
PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | |||||||||
PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); | PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); | |||||||||
SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); | SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); | |||||||||
KASSERT(pgrp->pg_session == p->p_session, | KASSERT(pgrp->pg_session == p->p_session, | |||||||||
("%s: pgrp's session %p, p->p_session %p.\n", | ("%s: pgrp's session %p, p->p_session %p proc %p\n", | |||||||||
__func__, | __func__, pgrp->pg_session, p->p_session, p)); | |||||||||
pgrp->pg_session, | ||||||||||
p->p_session)); | ||||||||||
KASSERT(pgrp != p->p_pgrp, | KASSERT(pgrp != p->p_pgrp, | |||||||||
("%s: p belongs to pgrp.", __func__)); | ("%s: p %p belongs to pgrp %p", __func__, p, pgrp)); | |||||||||
doenterpgrp(p, pgrp); | doenterpgrp(p, pgrp); | |||||||||
return (0); | return (0); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* If true, any child of q which belongs to group pgrp, qualifies the | * If true, any child of q which belongs to group pgrp, qualifies the | |||||||||
* process group pgrp as not orphaned. | * process group pgrp as not orphaned. | |||||||||
*/ | */ | |||||||||
static bool | static bool | |||||||||
isjobproc(struct proc *q, struct pgrp *pgrp) | isjobproc(struct proc *q, struct pgrp *pgrp) | |||||||||
{ | { | |||||||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&proctree_lock, SX_LOCKED); | |||||||||
return (q->p_pgrp != pgrp && | return (q->p_pgrp != pgrp && | |||||||||
q->p_pgrp->pg_session == pgrp->pg_session); | q->p_pgrp->pg_session == pgrp->pg_session); | |||||||||
} | } | |||||||||
static struct proc * | static struct proc * | |||||||||
jobc_reaper(struct proc *p) | jobc_reaper(struct proc *p) | |||||||||
{ | { | |||||||||
struct proc *pp; | struct proc *pp; | |||||||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&proctree_lock, SA_LOCKED); | |||||||||
for (pp = p;;) { | for (pp = p;;) { | |||||||||
pp = pp->p_reaper; | pp = pp->p_reaper; | |||||||||
if (pp->p_reaper == pp || | if (pp->p_reaper == pp || | |||||||||
(pp->p_treeflag & P_TREE_GRPEXITED) == 0) | (pp->p_treeflag & P_TREE_GRPEXITED) == 0) | |||||||||
return (pp); | return (pp); | |||||||||
} | } | |||||||||
} | } | |||||||||
static struct proc * | static struct proc * | |||||||||
jobc_parent(struct proc *p) | jobc_parent(struct proc *p, struct proc *p_exiting) | |||||||||
{ | { | |||||||||
struct proc *pp; | struct proc *pp; | |||||||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&proctree_lock, SA_LOCKED); | |||||||||
pp = proc_realparent(p); | pp = proc_realparent(p); | |||||||||
if (pp->p_pptr == NULL || | if (pp->p_pptr == NULL || pp == p_exiting || | |||||||||
(pp->p_treeflag & P_TREE_GRPEXITED) == 0) | (pp->p_treeflag & P_TREE_GRPEXITED) == 0) | |||||||||
return (pp); | return (pp); | |||||||||
return (jobc_reaper(pp)); | return (jobc_reaper(pp)); | |||||||||
} | } | |||||||||
#ifdef INVARIANTS | static int | |||||||||
static void | pgrp_calc_jobc(struct pgrp *pgrp) | |||||||||
check_pgrp_jobc(struct pgrp *pgrp) | ||||||||||
{ | { | |||||||||
struct proc *q; | struct proc *q; | |||||||||
int cnt; | int cnt; | |||||||||
sx_assert(&proctree_lock, SX_LOCKED); | #ifdef INVARIANTS | |||||||||
PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | if (!mtx_owned(&pgrp->pg_mtx)) | |||||||||
sx_assert(&proctree_lock, SA_LOCKED); | ||||||||||
#endif | ||||||||||
cnt = 0; | cnt = 0; | |||||||||
PGRP_LOCK(pgrp); | ||||||||||
LIST_FOREACH(q, &pgrp->pg_members, p_pglist) { | LIST_FOREACH(q, &pgrp->pg_members, p_pglist) { | |||||||||
if ((q->p_treeflag & P_TREE_GRPEXITED) != 0 || | if ((q->p_treeflag & P_TREE_GRPEXITED) != 0 || | |||||||||
q->p_pptr == NULL) | q->p_pptr == NULL) | |||||||||
continue; | continue; | |||||||||
if (isjobproc(jobc_parent(q), pgrp)) | if (isjobproc(jobc_parent(q, NULL), pgrp)) | |||||||||
cnt++; | cnt++; | |||||||||
} | } | |||||||||
KASSERT(pgrp->pg_jobc == cnt, ("pgrp %d %p pg_jobc %d cnt %d", | return (cnt); | |||||||||
pgrp->pg_id, pgrp, pgrp->pg_jobc, cnt)); | ||||||||||
PGRP_UNLOCK(pgrp); | ||||||||||
} | } | |||||||||
#endif | ||||||||||
/* | /* | |||||||||
* Move p to a process group | * Move p to a process group | |||||||||
*/ | */ | |||||||||
static void | static void | |||||||||
doenterpgrp(struct proc *p, struct pgrp *pgrp) | doenterpgrp(struct proc *p, struct pgrp *pgrp) | |||||||||
{ | { | |||||||||
struct pgrp *savepgrp; | struct pgrp *savepgrp; | |||||||||
struct proc *pp; | ||||||||||
sx_assert(&proctree_lock, SX_XLOCKED); | sx_assert(&proctree_lock, SX_XLOCKED); | |||||||||
PROC_LOCK_ASSERT(p, MA_NOTOWNED); | PROC_LOCK_ASSERT(p, MA_NOTOWNED); | |||||||||
PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | |||||||||
PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); | PGRP_LOCK_ASSERT(p->p_pgrp, MA_NOTOWNED); | |||||||||
SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); | SESS_LOCK_ASSERT(p->p_session, MA_NOTOWNED); | |||||||||
savepgrp = p->p_pgrp; | savepgrp = p->p_pgrp; | |||||||||
pp = jobc_parent(p, NULL); | ||||||||||
#ifdef INVARIANTS | ||||||||||
check_pgrp_jobc(pgrp); | ||||||||||
check_pgrp_jobc(savepgrp); | ||||||||||
#endif | ||||||||||
/* | ||||||||||
* Adjust eligibility of affected pgrps to participate in job control. | ||||||||||
*/ | ||||||||||
fixjobc_enterpgrp(p, pgrp); | ||||||||||
PGRP_LOCK(pgrp); | PGRP_LOCK(pgrp); | |||||||||
PGRP_LOCK(savepgrp); | PGRP_LOCK(savepgrp); | |||||||||
if (isjobproc(pp, savepgrp) && pgrp_calc_jobc(savepgrp) == 1) | ||||||||||
orphanpg(savepgrp); | ||||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
LIST_REMOVE(p, p_pglist); | LIST_REMOVE(p, p_pglist); | |||||||||
p->p_pgrp = pgrp; | p->p_pgrp = pgrp; | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist); | LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist); | |||||||||
if (isjobproc(pp, pgrp)) | ||||||||||
pgrp->pg_flags &= ~PGRP_ORPHANED; | ||||||||||
PGRP_UNLOCK(savepgrp); | PGRP_UNLOCK(savepgrp); | |||||||||
PGRP_UNLOCK(pgrp); | PGRP_UNLOCK(pgrp); | |||||||||
if (LIST_EMPTY(&savepgrp->pg_members)) | if (LIST_EMPTY(&savepgrp->pg_members)) | |||||||||
pgdelete(savepgrp); | pgdelete(savepgrp); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* remove process from process group | * remove process from process group | |||||||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | pgdelete(struct pgrp *pgrp) | |||||||||
/* Remove the reference to the pgrp before deallocating it. */ | /* Remove the reference to the pgrp before deallocating it. */ | |||||||||
if (tp != NULL) { | if (tp != NULL) { | |||||||||
tty_lock(tp); | tty_lock(tp); | |||||||||
tty_rel_pgrp(tp, pgrp); | tty_rel_pgrp(tp, pgrp); | |||||||||
} | } | |||||||||
proc_id_clear(PROC_ID_GROUP, pgrp->pg_id); | proc_id_clear(PROC_ID_GROUP, pgrp->pg_id); | |||||||||
mtx_destroy(&pgrp->pg_mtx); | uma_zfree(pgrp_zone, pgrp); | |||||||||
free(pgrp, M_PGRP); | ||||||||||
sess_release(savesess); | sess_release(savesess); | |||||||||
} | } | |||||||||
static void | ||||||||||
pgadjustjobc(struct pgrp *pgrp, bool entering) | ||||||||||
{ | ||||||||||
PGRP_LOCK(pgrp); | ||||||||||
if (entering) { | ||||||||||
MPASS(pgrp->pg_jobc >= 0); | ||||||||||
pgrp->pg_jobc++; | ||||||||||
} else { | ||||||||||
MPASS(pgrp->pg_jobc > 0); | ||||||||||
--pgrp->pg_jobc; | ||||||||||
if (pgrp->pg_jobc == 0) | ||||||||||
orphanpg(pgrp); | ||||||||||
} | ||||||||||
PGRP_UNLOCK(pgrp); | ||||||||||
} | ||||||||||
static void | static void | |||||||||
fixjobc_enterpgrp_q(struct pgrp *pgrp, struct proc *p, struct proc *q, bool adj) | ||||||||||
{ | ||||||||||
struct pgrp *childpgrp; | ||||||||||
bool future_jobc; | ||||||||||
sx_assert(&proctree_lock, SX_LOCKED); | ||||||||||
if ((q->p_treeflag & P_TREE_GRPEXITED) != 0) | ||||||||||
return; | ||||||||||
childpgrp = q->p_pgrp; | ||||||||||
future_jobc = childpgrp != pgrp && | ||||||||||
childpgrp->pg_session == pgrp->pg_session; | ||||||||||
if ((adj && !isjobproc(p, childpgrp) && future_jobc) || | ||||||||||
(!adj && isjobproc(p, childpgrp) && !future_jobc)) | ||||||||||
pgadjustjobc(childpgrp, adj); | ||||||||||
} | ||||||||||
/* | ||||||||||
* Adjust pgrp jobc counters when specified process changes process group. | ||||||||||
* We count the number of processes in each process group that "qualify" | ||||||||||
* the group for terminal job control (those with a parent in a different | ||||||||||
* process group of the same session). If that count reaches zero, the | ||||||||||
* process group becomes orphaned. Check both the specified process' | ||||||||||
* process group and that of its children. | ||||||||||
* We increment eligibility counts before decrementing, otherwise we | ||||||||||
* could reach 0 spuriously during the decrement. | ||||||||||
*/ | ||||||||||
static void | ||||||||||
fixjobc_enterpgrp(struct proc *p, struct pgrp *pgrp) | ||||||||||
{ | ||||||||||
struct proc *q; | ||||||||||
sx_assert(&proctree_lock, SX_LOCKED); | ||||||||||
PROC_LOCK_ASSERT(p, MA_NOTOWNED); | ||||||||||
PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | ||||||||||
SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED); | ||||||||||
if (p->p_pgrp == pgrp) | ||||||||||
return; | ||||||||||
if (isjobproc(jobc_parent(p), pgrp)) | ||||||||||
pgadjustjobc(pgrp, true); | ||||||||||
LIST_FOREACH(q, &p->p_children, p_sibling) { | ||||||||||
if ((q->p_treeflag & P_TREE_ORPHANED) != 0) | ||||||||||
continue; | ||||||||||
fixjobc_enterpgrp_q(pgrp, p, q, true); | ||||||||||
} | ||||||||||
LIST_FOREACH(q, &p->p_orphans, p_orphan) | ||||||||||
fixjobc_enterpgrp_q(pgrp, p, q, true); | ||||||||||
if (isjobproc(jobc_parent(p), p->p_pgrp)) | ||||||||||
pgadjustjobc(p->p_pgrp, false); | ||||||||||
LIST_FOREACH(q, &p->p_children, p_sibling) { | ||||||||||
if ((q->p_treeflag & P_TREE_ORPHANED) != 0) | ||||||||||
continue; | ||||||||||
fixjobc_enterpgrp_q(pgrp, p, q, false); | ||||||||||
} | ||||||||||
LIST_FOREACH(q, &p->p_orphans, p_orphan) | ||||||||||
fixjobc_enterpgrp_q(pgrp, p, q, false); | ||||||||||
} | ||||||||||
static void | ||||||||||
fixjobc_kill_q(struct proc *p, struct proc *q, bool adj) | ||||||||||
{ | ||||||||||
struct pgrp *childpgrp; | ||||||||||
sx_assert(&proctree_lock, SX_LOCKED); | ||||||||||
if ((q->p_treeflag & P_TREE_GRPEXITED) != 0) | ||||||||||
return; | ||||||||||
childpgrp = q->p_pgrp; | ||||||||||
if ((adj && isjobproc(jobc_reaper(q), childpgrp) && | ||||||||||
!isjobproc(p, childpgrp)) || (!adj && !isjobproc(jobc_reaper(q), | ||||||||||
childpgrp) && isjobproc(p, childpgrp))) | ||||||||||
pgadjustjobc(childpgrp, adj); | ||||||||||
} | ||||||||||
static void | ||||||||||
fixjobc_kill(struct proc *p) | fixjobc_kill(struct proc *p) | |||||||||
{ | { | |||||||||
struct proc *q; | struct proc *q; | |||||||||
struct pgrp *pgrp; | struct pgrp *pgrp; | |||||||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&proctree_lock, SX_LOCKED); | |||||||||
PROC_LOCK_ASSERT(p, MA_NOTOWNED); | PROC_LOCK_ASSERT(p, MA_NOTOWNED); | |||||||||
pgrp = p->p_pgrp; | pgrp = p->p_pgrp; | |||||||||
PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | PGRP_LOCK_ASSERT(pgrp, MA_NOTOWNED); | |||||||||
SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED); | SESS_LOCK_ASSERT(pgrp->pg_session, MA_NOTOWNED); | |||||||||
#ifdef INVARIANTS | ||||||||||
check_pgrp_jobc(pgrp); | ||||||||||
#endif | ||||||||||
/* | /* | |||||||||
* p no longer affects process group orphanage for children. | * p no longer affects process group orphanage for children. | |||||||||
* It is marked by the flag because p is only physically | * It is marked by the flag because p is only physically | |||||||||
* removed from its process group on wait(2). | * removed from its process group on wait(2). | |||||||||
*/ | */ | |||||||||
MPASS((p->p_treeflag & P_TREE_GRPEXITED) == 0); | MPASS((p->p_treeflag & P_TREE_GRPEXITED) == 0); | |||||||||
p->p_treeflag |= P_TREE_GRPEXITED; | p->p_treeflag |= P_TREE_GRPEXITED; | |||||||||
/* | /* | |||||||||
* Check p's parent to see whether p qualifies its own process | * Check if exiting p orphans its own group. | |||||||||
* group; if so, adjust count for p's process group. | ||||||||||
*/ | */ | |||||||||
if (isjobproc(jobc_parent(p), pgrp)) | pgrp = p->p_pgrp; | |||||||||
pgadjustjobc(pgrp, false); | if (isjobproc(jobc_parent(p, NULL), pgrp)) { | |||||||||
PGRP_LOCK(pgrp); | ||||||||||
if (pgrp_calc_jobc(pgrp) == 0) | ||||||||||
orphanpg(pgrp); | ||||||||||
PGRP_UNLOCK(pgrp); | ||||||||||
} | ||||||||||
/* | /* | |||||||||
* Check this process' children to see whether they qualify | * Check this process' children to see whether they qualify | |||||||||
* their process groups after reparenting to reaper. If so, | * their process groups after reparenting to reaper. | |||||||||
* adjust counts for children's process groups. | ||||||||||
*/ | */ | |||||||||
LIST_FOREACH(q, &p->p_children, p_sibling) { | LIST_FOREACH(q, &p->p_children, p_sibling) { | |||||||||
if ((q->p_treeflag & P_TREE_ORPHANED) != 0) | pgrp = q->p_pgrp; | |||||||||
continue; | PGRP_LOCK(pgrp); | |||||||||
fixjobc_kill_q(p, q, true); | if (pgrp_calc_jobc(pgrp) == 0) { | |||||||||
/* | ||||||||||
* We want to handle exactly the children that | ||||||||||
* has p as realparent. Then, when calculating | ||||||||||
* jobc_parent for children, we should ignore | ||||||||||
* P_TREE_GRPEXITED flag already set on p. | ||||||||||
*/ | ||||||||||
if (jobc_parent(q, p) == p && isjobproc(p, pgrp)) | ||||||||||
orphanpg(pgrp); | ||||||||||
} else | ||||||||||
pgrp->pg_flags &= ~PGRP_ORPHANED; | ||||||||||
PGRP_UNLOCK(pgrp); | ||||||||||
} | } | |||||||||
LIST_FOREACH(q, &p->p_orphans, p_orphan) | LIST_FOREACH(q, &p->p_orphans, p_orphan) { | |||||||||
fixjobc_kill_q(p, q, true); | pgrp = q->p_pgrp; | |||||||||
LIST_FOREACH(q, &p->p_children, p_sibling) { | PGRP_LOCK(pgrp); | |||||||||
if ((q->p_treeflag & P_TREE_ORPHANED) != 0) | if (pgrp_calc_jobc(pgrp) == 0) { | |||||||||
continue; | if (isjobproc(p, pgrp)) | |||||||||
fixjobc_kill_q(p, q, false); | orphanpg(pgrp); | |||||||||
} else | ||||||||||
pgrp->pg_flags &= ~PGRP_ORPHANED; | ||||||||||
PGRP_UNLOCK(pgrp); | ||||||||||
} | } | |||||||||
LIST_FOREACH(q, &p->p_orphans, p_orphan) | ||||||||||
fixjobc_kill_q(p, q, false); | ||||||||||
#ifdef INVARIANTS | ||||||||||
check_pgrp_jobc(pgrp); | ||||||||||
#endif | ||||||||||
} | } | |||||||||
void | void | |||||||||
killjobc(void) | killjobc(void) | |||||||||
{ | { | |||||||||
struct session *sp; | struct session *sp; | |||||||||
struct tty *tp; | struct tty *tp; | |||||||||
struct proc *p; | struct proc *p; | |||||||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (ttyvp != NULL) { | |||||||||
devfs_ctty_unref(ttyvp); | devfs_ctty_unref(ttyvp); | |||||||||
sx_xlock(&proctree_lock); | sx_xlock(&proctree_lock); | |||||||||
} | } | |||||||||
} | } | |||||||||
fixjobc_kill(p); | fixjobc_kill(p); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* A process group has become orphaned; | * A process group has become orphaned, mark it as such for signal | |||||||||
* if there are any stopped processes in the group, | * delivery code. If there are any stopped processes in the group, | |||||||||
* hang-up all process in that group. | * hang-up all process in that group. | |||||||||
*/ | */ | |||||||||
static void | static void | |||||||||
orphanpg(struct pgrp *pg) | orphanpg(struct pgrp *pg) | |||||||||
{ | { | |||||||||
struct proc *p; | struct proc *p; | |||||||||
PGRP_LOCK_ASSERT(pg, MA_OWNED); | PGRP_LOCK_ASSERT(pg, MA_OWNED); | |||||||||
pg->pg_flags |= PGRP_ORPHANED; | ||||||||||
LIST_FOREACH(p, &pg->pg_members, p_pglist) { | LIST_FOREACH(p, &pg->pg_members, p_pglist) { | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
if (P_SHOULDSTOP(p) == P_STOPPED_SIG) { | if (P_SHOULDSTOP(p) == P_STOPPED_SIG) { | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
LIST_FOREACH(p, &pg->pg_members, p_pglist) { | LIST_FOREACH(p, &pg->pg_members, p_pglist) { | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
kern_psignal(p, SIGHUP); | kern_psignal(p, SIGHUP); | |||||||||
kern_psignal(p, SIGCONT); | kern_psignal(p, SIGCONT); | |||||||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | FOREACH_THREAD_IN_PROC(p, td) { | |||||||||
thread_lock(td); | thread_lock(td); | |||||||||
kp->ki_pctcpu += sched_pctcpu(td); | kp->ki_pctcpu += sched_pctcpu(td); | |||||||||
kp->ki_estcpu += sched_estcpu(td); | kp->ki_estcpu += sched_estcpu(td); | |||||||||
thread_unlock(td); | thread_unlock(td); | |||||||||
} | } | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* Clear kinfo_proc and fill in any information that is common | * Fill in any information that is common to all threads in the process. | |||||||||
* to all threads in the process. | ||||||||||
* Must be called with the target process locked. | * Must be called with the target process locked. | |||||||||
*/ | */ | |||||||||
static void | static void | |||||||||
fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) | fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) | |||||||||
{ | { | |||||||||
struct thread *td0; | struct thread *td0; | |||||||||
struct tty *tp; | ||||||||||
struct session *sp; | ||||||||||
struct ucred *cred; | struct ucred *cred; | |||||||||
struct sigacts *ps; | struct sigacts *ps; | |||||||||
struct timeval boottime; | struct timeval boottime; | |||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | |||||||||
bzero(kp, sizeof(*kp)); | ||||||||||
kp->ki_structsize = sizeof(*kp); | kp->ki_structsize = sizeof(*kp); | |||||||||
kp->ki_paddr = p; | kp->ki_paddr = p; | |||||||||
kp->ki_addr =/* p->p_addr; */0; /* XXX */ | kp->ki_addr =/* p->p_addr; */0; /* XXX */ | |||||||||
kp->ki_args = p->p_args; | kp->ki_args = p->p_args; | |||||||||
kp->ki_textvp = p->p_textvp; | kp->ki_textvp = p->p_textvp; | |||||||||
#ifdef KTRACE | #ifdef KTRACE | |||||||||
kp->ki_tracep = p->p_tracevp; | kp->ki_tracep = p->p_tracevp; | |||||||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | #endif | |||||||||
calccru(p, &kp->ki_childutime, &kp->ki_childstime); | calccru(p, &kp->ki_childutime, &kp->ki_childstime); | |||||||||
/* Some callers want child times in a single value. */ | /* Some callers want child times in a single value. */ | |||||||||
kp->ki_childtime = kp->ki_childstime; | kp->ki_childtime = kp->ki_childstime; | |||||||||
timevaladd(&kp->ki_childtime, &kp->ki_childutime); | timevaladd(&kp->ki_childtime, &kp->ki_childutime); | |||||||||
FOREACH_THREAD_IN_PROC(p, td0) | FOREACH_THREAD_IN_PROC(p, td0) | |||||||||
kp->ki_cow += td0->td_cow; | kp->ki_cow += td0->td_cow; | |||||||||
if (p->p_comm[0] != '\0') | ||||||||||
strlcpy(kp->ki_comm, p->p_comm, sizeof(kp->ki_comm)); | ||||||||||
if (p->p_sysent && p->p_sysent->sv_name != NULL && | ||||||||||
p->p_sysent->sv_name[0] != '\0') | ||||||||||
strlcpy(kp->ki_emul, p->p_sysent->sv_name, sizeof(kp->ki_emul)); | ||||||||||
kp->ki_siglist = p->p_siglist; | ||||||||||
kp->ki_xstat = KW_EXITCODE(p->p_xexit, p->p_xsig); | ||||||||||
kp->ki_acflag = p->p_acflag; | ||||||||||
kp->ki_lock = p->p_lock; | ||||||||||
if (p->p_pptr) { | ||||||||||
kp->ki_ppid = p->p_oppid; | ||||||||||
if (p->p_flag & P_TRACED) | ||||||||||
kp->ki_tracer = p->p_pptr->p_pid; | ||||||||||
} | ||||||||||
} | ||||||||||
/* | ||||||||||
* Fill job-related process information. | ||||||||||
*/ | ||||||||||
static void | ||||||||||
fill_kinfo_proc_pgrp(struct proc *p, struct kinfo_proc *kp) | ||||||||||
{ | ||||||||||
struct tty *tp; | ||||||||||
struct session *sp; | ||||||||||
struct pgrp *pgrp; | ||||||||||
sx_assert(&proctree_lock, SA_LOCKED); | ||||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | ||||||||||
pgrp = p->p_pgrp; | ||||||||||
if (pgrp == NULL) | ||||||||||
return; | ||||||||||
kp->ki_pgid = pgrp->pg_id; | ||||||||||
kp->ki_jobc = pgrp_calc_jobc(pgrp); | ||||||||||
sp = pgrp->pg_session; | ||||||||||
tp = NULL; | tp = NULL; | |||||||||
if (p->p_pgrp) { | ||||||||||
kp->ki_pgid = p->p_pgrp->pg_id; | ||||||||||
kp->ki_jobc = p->p_pgrp->pg_jobc; | ||||||||||
sp = p->p_pgrp->pg_session; | ||||||||||
if (sp != NULL) { | if (sp != NULL) { | |||||||||
kp->ki_sid = sp->s_sid; | kp->ki_sid = sp->s_sid; | |||||||||
SESS_LOCK(sp); | SESS_LOCK(sp); | |||||||||
strlcpy(kp->ki_login, sp->s_login, | strlcpy(kp->ki_login, sp->s_login, sizeof(kp->ki_login)); | |||||||||
sizeof(kp->ki_login)); | ||||||||||
if (sp->s_ttyvp) | if (sp->s_ttyvp) | |||||||||
kp->ki_kiflag |= KI_CTTY; | kp->ki_kiflag |= KI_CTTY; | |||||||||
if (SESS_LEADER(p)) | if (SESS_LEADER(p)) | |||||||||
kp->ki_kiflag |= KI_SLEADER; | kp->ki_kiflag |= KI_SLEADER; | |||||||||
/* XXX proctree_lock */ | ||||||||||
tp = sp->s_ttyp; | tp = sp->s_ttyp; | |||||||||
SESS_UNLOCK(sp); | SESS_UNLOCK(sp); | |||||||||
} | } | |||||||||
} | ||||||||||
if ((p->p_flag & P_CONTROLT) && tp != NULL) { | if ((p->p_flag & P_CONTROLT) && tp != NULL) { | |||||||||
kp->ki_tdev = tty_udev(tp); | kp->ki_tdev = tty_udev(tp); | |||||||||
kp->ki_tdev_freebsd11 = kp->ki_tdev; /* truncate */ | kp->ki_tdev_freebsd11 = kp->ki_tdev; /* truncate */ | |||||||||
kp->ki_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; | kp->ki_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; | |||||||||
if (tp->t_session) | if (tp->t_session) | |||||||||
kp->ki_tsid = tp->t_session->s_sid; | kp->ki_tsid = tp->t_session->s_sid; | |||||||||
} else { | } else { | |||||||||
kp->ki_tdev = NODEV; | kp->ki_tdev = NODEV; | |||||||||
kp->ki_tdev_freebsd11 = kp->ki_tdev; /* truncate */ | kp->ki_tdev_freebsd11 = kp->ki_tdev; /* truncate */ | |||||||||
} | } | |||||||||
if (p->p_comm[0] != '\0') | ||||||||||
strlcpy(kp->ki_comm, p->p_comm, sizeof(kp->ki_comm)); | ||||||||||
if (p->p_sysent && p->p_sysent->sv_name != NULL && | ||||||||||
p->p_sysent->sv_name[0] != '\0') | ||||||||||
strlcpy(kp->ki_emul, p->p_sysent->sv_name, sizeof(kp->ki_emul)); | ||||||||||
kp->ki_siglist = p->p_siglist; | ||||||||||
kp->ki_xstat = KW_EXITCODE(p->p_xexit, p->p_xsig); | ||||||||||
kp->ki_acflag = p->p_acflag; | ||||||||||
kp->ki_lock = p->p_lock; | ||||||||||
if (p->p_pptr) { | ||||||||||
kp->ki_ppid = p->p_oppid; | ||||||||||
if (p->p_flag & P_TRACED) | ||||||||||
kp->ki_tracer = p->p_pptr->p_pid; | ||||||||||
} | } | |||||||||
} | ||||||||||
/* | /* | |||||||||
* Fill in information that is thread specific. Must be called with | * Fill in information that is thread specific. Must be called with | |||||||||
* target process locked. If 'preferthread' is set, overwrite certain | * target process locked. If 'preferthread' is set, overwrite certain | |||||||||
* process-related fields that are maintained for both threads and | * process-related fields that are maintained for both threads and | |||||||||
* processes. | * processes. | |||||||||
*/ | */ | |||||||||
static void | static void | |||||||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | ||||||||||
/* | /* | |||||||||
* Fill in a kinfo_proc structure for the specified process. | * Fill in a kinfo_proc structure for the specified process. | |||||||||
* Must be called with the target process locked. | * Must be called with the target process locked. | |||||||||
*/ | */ | |||||||||
void | void | |||||||||
fill_kinfo_proc(struct proc *p, struct kinfo_proc *kp) | fill_kinfo_proc(struct proc *p, struct kinfo_proc *kp) | |||||||||
{ | { | |||||||||
MPASS(FIRST_THREAD_IN_PROC(p) != NULL); | MPASS(FIRST_THREAD_IN_PROC(p) != NULL); | |||||||||
bzero(kp, sizeof(*kp)); | ||||||||||
fill_kinfo_proc_pgrp(p,kp); | ||||||||||
jhbUnsubmitted Not Done Inline Actions
jhb: | ||||||||||
fill_kinfo_proc_only(p, kp); | fill_kinfo_proc_only(p, kp); | |||||||||
fill_kinfo_thread(FIRST_THREAD_IN_PROC(p), kp, 0); | fill_kinfo_thread(FIRST_THREAD_IN_PROC(p), kp, 0); | |||||||||
fill_kinfo_aggregate(p, kp); | fill_kinfo_aggregate(p, kp); | |||||||||
} | } | |||||||||
struct pstats * | struct pstats * | |||||||||
pstats_alloc(void) | pstats_alloc(void) | |||||||||
{ | { | |||||||||
▲ Show 20 Lines • Show All 228 Lines • ▼ Show 20 Lines | ||||||||||
int | int | |||||||||
proc_iterate(int (*cb)(struct proc *, void *), void *cbarg) | proc_iterate(int (*cb)(struct proc *, void *), void *cbarg) | |||||||||
{ | { | |||||||||
struct proc *p; | struct proc *p; | |||||||||
int error, i, j; | int error, i, j; | |||||||||
for (i = 0; i < pidhashlock + 1; i++) { | for (i = 0; i < pidhashlock + 1; i++) { | |||||||||
sx_slock(&proctree_lock); | ||||||||||
sx_slock(&pidhashtbl_lock[i]); | sx_slock(&pidhashtbl_lock[i]); | |||||||||
for (j = i; j <= pidhash; j += pidhashlock + 1) { | for (j = i; j <= pidhash; j += pidhashlock + 1) { | |||||||||
LIST_FOREACH(p, &pidhashtbl[j], p_hash) { | LIST_FOREACH(p, &pidhashtbl[j], p_hash) { | |||||||||
if (p->p_state == PRS_NEW) | if (p->p_state == PRS_NEW) | |||||||||
continue; | continue; | |||||||||
error = cb(p, cbarg); | error = cb(p, cbarg); | |||||||||
PROC_LOCK_ASSERT(p, MA_NOTOWNED); | PROC_LOCK_ASSERT(p, MA_NOTOWNED); | |||||||||
if (error != 0) { | if (error != 0) { | |||||||||
sx_sunlock(&pidhashtbl_lock[i]); | sx_sunlock(&pidhashtbl_lock[i]); | |||||||||
sx_sunlock(&proctree_lock); | ||||||||||
return (error); | return (error); | |||||||||
} | } | |||||||||
} | } | |||||||||
} | } | |||||||||
sx_sunlock(&pidhashtbl_lock[i]); | sx_sunlock(&pidhashtbl_lock[i]); | |||||||||
sx_sunlock(&proctree_lock); | ||||||||||
} | } | |||||||||
return (0); | return (0); | |||||||||
} | } | |||||||||
struct kern_proc_out_args { | struct kern_proc_out_args { | |||||||||
struct sysctl_req *req; | struct sysctl_req *req; | |||||||||
int flags; | int flags; | |||||||||
int oid_number; | int oid_number; | |||||||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | if (req->flags & SCTL_MASK32) | |||||||||
flags |= KERN_PROC_MASK32; | flags |= KERN_PROC_MASK32; | |||||||||
#endif | #endif | |||||||||
if (oid_number == KERN_PROC_PID) { | if (oid_number == KERN_PROC_PID) { | |||||||||
if (namelen != 1) | if (namelen != 1) | |||||||||
return (EINVAL); | return (EINVAL); | |||||||||
error = sysctl_wire_old_buffer(req, 0); | error = sysctl_wire_old_buffer(req, 0); | |||||||||
if (error) | if (error) | |||||||||
return (error); | return (error); | |||||||||
sx_slock(&proctree_lock); | ||||||||||
error = pget((pid_t)name[0], PGET_CANSEE, &p); | error = pget((pid_t)name[0], PGET_CANSEE, &p); | |||||||||
if (error == 0) | if (error == 0) | |||||||||
error = sysctl_out_proc(p, req, flags); | error = sysctl_out_proc(p, req, flags); | |||||||||
sx_sunlock(&proctree_lock); | ||||||||||
return (error); | return (error); | |||||||||
} | } | |||||||||
switch (oid_number) { | switch (oid_number) { | |||||||||
case KERN_PROC_ALL: | case KERN_PROC_ALL: | |||||||||
if (namelen != 0) | if (namelen != 0) | |||||||||
return (EINVAL); | return (EINVAL); | |||||||||
break; | break; | |||||||||
▲ Show 20 Lines • Show All 1,633 Lines • Show Last 20 Lines |