#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/wait.h>
#include <sys/rwlock.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/signalvar.h>
#include <sys/pool.h>
#include <sys/vnode.h>
struct rwlock uidinfolk;
#define UIHASH(uid) (&uihashtbl[(uid) & uihash])
LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
u_long uihash;
struct tidhashhead *tidhashtbl;
u_long tidhash;
struct pidhashhead *pidhashtbl;
u_long pidhash;
struct pgrphashhead *pgrphashtbl;
u_long pgrphash;
struct processlist allprocess;
struct processlist zombprocess;
struct proclist allproc;
struct pool proc_pool;
struct pool process_pool;
struct pool rusage_pool;
struct pool ucred_pool;
struct pool pgrp_pool;
struct pool session_pool;
void pgdelete(struct pgrp *);
void fixjobc(struct process *, struct pgrp *, int);
static void orphanpg(struct pgrp *);
#ifdef DEBUG
void pgrpdump(void);
#endif
void
procinit(void)
{
LIST_INIT(&allprocess);
LIST_INIT(&zombprocess);
LIST_INIT(&allproc);
rw_init(&uidinfolk, "uidinfo");
tidhashtbl = hashinit(maxthread / 4, M_PROC, M_NOWAIT, &tidhash);
pidhashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pidhash);
pgrphashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pgrphash);
uihashtbl = hashinit(maxprocess / 16, M_PROC, M_NOWAIT, &uihash);
if (!tidhashtbl || !pidhashtbl || !pgrphashtbl || !uihashtbl)
panic("procinit: malloc");
pool_init(&proc_pool, sizeof(struct proc), 0, IPL_NONE,
PR_WAITOK, "procpl", NULL);
pool_init(&process_pool, sizeof(struct process), 0, IPL_NONE,
PR_WAITOK, "processpl", NULL);
pool_init(&rusage_pool, sizeof(struct rusage), 0, IPL_NONE,
PR_WAITOK, "zombiepl", NULL);
pool_init(&ucred_pool, sizeof(struct ucred), 0, IPL_MPFLOOR,
0, "ucredpl", NULL);
pool_init(&pgrp_pool, sizeof(struct pgrp), 0, IPL_NONE,
PR_WAITOK, "pgrppl", NULL);
pool_init(&session_pool, sizeof(struct session), 0, IPL_NONE,
PR_WAITOK, "sessionpl", NULL);
}
struct uidinfo *
uid_find(uid_t uid)
{
struct uidinfo *uip, *nuip;
struct uihashhead *uipp;
uipp = UIHASH(uid);
rw_enter_write(&uidinfolk);
LIST_FOREACH(uip, uipp, ui_hash)
if (uip->ui_uid == uid)
break;
if (uip)
return (uip);
rw_exit_write(&uidinfolk);
nuip = malloc(sizeof(*nuip), M_PROC, M_WAITOK|M_ZERO);
rw_enter_write(&uidinfolk);
LIST_FOREACH(uip, uipp, ui_hash)
if (uip->ui_uid == uid)
break;
if (uip) {
free(nuip, M_PROC, sizeof(*nuip));
return (uip);
}
nuip->ui_uid = uid;
LIST_INSERT_HEAD(uipp, nuip, ui_hash);
return (nuip);
}
void
uid_release(struct uidinfo *uip)
{
rw_exit_write(&uidinfolk);
}
int
chgproccnt(uid_t uid, int diff)
{
struct uidinfo *uip;
long count;
uip = uid_find(uid);
count = (uip->ui_proccnt += diff);
uid_release(uip);
if (count < 0)
panic("chgproccnt: procs < 0");
return count;
}
int
inferior(struct process *pr, struct process *parent)
{
for (; pr != parent; pr = pr->ps_pptr)
if (pr->ps_pid == 0 || pr->ps_pid == 1)
return (0);
return (1);
}
struct proc *
tfind(pid_t tid)
{
struct proc *p;
LIST_FOREACH(p, TIDHASH(tid), p_hash)
if (p->p_tid == tid)
return (p);
return (NULL);
}
struct proc *
tfind_user(pid_t tid, struct process *pr)
{
struct proc *p;
if (tid < THREAD_PID_OFFSET)
return NULL;
p = tfind(tid - THREAD_PID_OFFSET);
if (p != NULL && p->p_p != pr)
p = NULL;
return p;
}
struct process *
prfind(pid_t pid)
{
struct process *pr;
LIST_FOREACH(pr, PIDHASH(pid), ps_hash)
if (pr->ps_pid == pid)
return (pr);
return (NULL);
}
struct pgrp *
pgfind(pid_t pgid)
{
struct pgrp *pgrp;
LIST_FOREACH(pgrp, PGRPHASH(pgid), pg_hash)
if (pgrp->pg_id == pgid)
return (pgrp);
return (NULL);
}
struct process *
zombiefind(pid_t pid)
{
struct process *pr;
LIST_FOREACH(pr, &zombprocess, ps_list)
if (pr->ps_pid == pid)
return (pr);
return (NULL);
}
void
enternewpgrp(struct process *pr, struct pgrp *pgrp, struct session *newsess)
{
#ifdef DIAGNOSTIC
if (SESS_LEADER(pr))
panic("%s: session leader attempted setpgrp", __func__);
#endif
if (newsess != NULL) {
timeout_set(&newsess->s_verauthto, zapverauth, newsess);
newsess->s_leader = pr;
newsess->s_count = 1;
newsess->s_ttyvp = NULL;
newsess->s_ttyp = NULL;
memcpy(newsess->s_login, pr->ps_session->s_login,
sizeof(newsess->s_login));
atomic_clearbits_int(&pr->ps_flags, PS_CONTROLT);
pgrp->pg_session = newsess;
#ifdef DIAGNOSTIC
if (pr != curproc->p_p)
panic("%s: mksession but not curproc", __func__);
#endif
} else {
pgrp->pg_session = pr->ps_session;
pgrp->pg_session->s_count++;
}
pgrp->pg_id = pr->ps_pid;
LIST_INIT(&pgrp->pg_members);
LIST_INIT(&pgrp->pg_sigiolst);
LIST_INSERT_HEAD(PGRPHASH(pr->ps_pid), pgrp, pg_hash);
pgrp->pg_jobc = 0;
enterthispgrp(pr, pgrp);
}
void
enterthispgrp(struct process *pr, struct pgrp *pgrp)
{
struct pgrp *savepgrp = pr->ps_pgrp;
fixjobc(pr, pgrp, 1);
fixjobc(pr, savepgrp, 0);
LIST_REMOVE(pr, ps_pglist);
mtx_enter(&pr->ps_mtx);
pr->ps_pgrp = pgrp;
mtx_leave(&pr->ps_mtx);
LIST_INSERT_HEAD(&pgrp->pg_members, pr, ps_pglist);
if (LIST_EMPTY(&savepgrp->pg_members))
pgdelete(savepgrp);
}
void
leavepgrp(struct process *pr)
{
if (pr->ps_session->s_verauthppid == pr->ps_pid)
zapverauth(pr->ps_session);
LIST_REMOVE(pr, ps_pglist);
if (LIST_EMPTY(&pr->ps_pgrp->pg_members))
pgdelete(pr->ps_pgrp);
mtx_enter(&pr->ps_mtx);
pr->ps_pgrp = NULL;
mtx_leave(&pr->ps_mtx);
}
void
pgdelete(struct pgrp *pgrp)
{
sigio_freelist(&pgrp->pg_sigiolst);
if (pgrp->pg_session->s_ttyp != NULL &&
pgrp->pg_session->s_ttyp->t_pgrp == pgrp)
pgrp->pg_session->s_ttyp->t_pgrp = NULL;
LIST_REMOVE(pgrp, pg_hash);
SESSRELE(pgrp->pg_session);
pool_put(&pgrp_pool, pgrp);
}
void
zapverauth(void *v)
{
struct session *sess = v;
sess->s_verauthuid = 0;
sess->s_verauthppid = 0;
}
void
fixjobc(struct process *pr, struct pgrp *pgrp, int entering)
{
struct pgrp *hispgrp;
struct session *mysession = pgrp->pg_session;
if ((hispgrp = pr->ps_pptr->ps_pgrp) != pgrp &&
hispgrp->pg_session == mysession) {
if (entering)
pgrp->pg_jobc++;
else if (--pgrp->pg_jobc == 0)
orphanpg(pgrp);
}
LIST_FOREACH(pr, &pr->ps_children, ps_sibling)
if ((hispgrp = pr->ps_pgrp) != pgrp &&
hispgrp->pg_session == mysession &&
(pr->ps_flags & PS_ZOMBIE) == 0) {
if (entering)
hispgrp->pg_jobc++;
else if (--hispgrp->pg_jobc == 0)
orphanpg(hispgrp);
}
}
void
killjobc(struct process *pr)
{
if (SESS_LEADER(pr)) {
struct session *sp = pr->ps_session;
if (sp->s_ttyvp) {
struct vnode *ovp;
if (sp->s_ttyp->t_session == sp) {
if (sp->s_ttyp->t_pgrp)
pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
ttywait(sp->s_ttyp);
if (sp->s_ttyvp)
VOP_REVOKE(sp->s_ttyvp, REVOKEALL);
}
ovp = sp->s_ttyvp;
sp->s_ttyvp = NULL;
if (ovp)
vrele(ovp);
}
sp->s_leader = NULL;
}
fixjobc(pr, pr->ps_pgrp, 0);
}
static void
orphanpg(struct pgrp *pg)
{
struct process *pr;
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
if (pr->ps_flags & PS_STOPPED) {
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
prsignal(pr, SIGHUP);
prsignal(pr, SIGCONT);
}
return;
}
}
}
#ifdef DDB
void
proc_printit(struct proc *p, const char *modif,
int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
{
static const char *const pstat[] = {
"idle", "run", "sleep", "stop", "zombie", "dead", "onproc"
};
char pstbuf[5];
const char *pst = pstbuf;
if (p->p_stat < 1 || p->p_stat > sizeof(pstat) / sizeof(pstat[0]))
snprintf(pstbuf, sizeof(pstbuf), "%d", p->p_stat);
else
pst = pstat[(int)p->p_stat - 1];
(*pr)("PROC (%s) tid=%d pid=%d tcnt=%d stat=%s\n", p->p_p->ps_comm,
p->p_tid, p->p_p->ps_pid, p->p_p->ps_threadcnt, pst);
(*pr)(" flags process=%b proc=%b\n",
p->p_p->ps_flags, PS_BITS, p->p_flag, P_BITS);
(*pr)(" runpri=%u, usrpri=%u, slppri=%u, nice=%d\n",
p->p_runpri, p->p_usrpri, p->p_slppri, p->p_p->ps_nice);
(*pr)(" wchan=%p, wmesg=%s, ps_single=%p scnt=%d ecnt=%d\n",
p->p_wchan, (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "",
p->p_p->ps_single, p->p_p->ps_suspendcnt, p->p_p->ps_exitcnt);
(*pr)(" forw=%p, list=%p,%p\n",
TAILQ_NEXT(p, p_runq), p->p_list.le_next, p->p_list.le_prev);
(*pr)(" process=%p user=%p, vmspace=%p\n",
p->p_p, p->p_addr, p->p_vmspace);
(*pr)(" estcpu=%u, cpticks=%d, pctcpu=%u.%u, "
"user=%llu, sys=%llu, intr=%llu\n",
p->p_estcpu, p->p_cpticks, p->p_pctcpu / 100, p->p_pctcpu % 100,
p->p_tu.tu_uticks, p->p_tu.tu_sticks, p->p_tu.tu_iticks);
}
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
void
db_kill_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
struct process *pr;
struct proc *p;
pr = prfind(addr);
if (pr == NULL) {
db_printf("%ld: No such process", addr);
return;
}
p = TAILQ_FIRST(&pr->ps_threads);
sigabort(p);
}
void
db_stop_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
struct process *pr;
pr = prfind(addr);
if (pr == NULL) {
db_printf("%ld: No such process", addr);
return;
}
prsignal(pr, SIGSTOP);
}
void
db_show_all_procs(db_expr_t addr, int haddr, db_expr_t count, char *modif)
{
char *mode;
int skipzomb = 0;
int has_kernel_lock = 0;
struct proc *p;
struct process *pr, *ppr;
if (modif[0] == 0)
modif[0] = 'n';
mode = "mawno";
while (*mode && *mode != modif[0])
mode++;
if (*mode == 0 || *mode == 'm') {
db_printf("usage: show all procs [/a] [/n] [/w]\n");
db_printf("\t/a == show process address info\n");
db_printf("\t/n == show normal process info [default]\n");
db_printf("\t/w == show process pgrp/wait info\n");
db_printf("\t/o == show normal info for non-idle SONPROC\n");
return;
}
pr = LIST_FIRST(&allprocess);
switch (*mode) {
case 'a':
db_printf(" TID %-9s %18s %18s %18s\n",
"COMMAND", "STRUCT PROC *", "UAREA *", "VMSPACE/VM_MAP");
break;
case 'n':
db_printf(" PID %6s %5s %5s S %10s %-12s %-15s\n",
"TID", "PPID", "UID", "FLAGS", "WAIT", "COMMAND");
break;
case 'w':
db_printf(" TID %-15s %-5s %18s %s\n",
"COMMAND", "PGRP", "WAIT-CHANNEL", "WAIT-MSG");
break;
case 'o':
skipzomb = 1;
db_printf(" TID %5s %5s %10s %10s %3s %-30s\n",
"PID", "UID", "PRFLAGS", "PFLAGS", "CPU", "COMMAND");
break;
}
while (pr != NULL) {
ppr = pr->ps_pptr;
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
#ifdef MULTIPROCESSOR
if (p->p_cpu != NULL &&
__mp_lock_held(&kernel_lock, p->p_cpu))
has_kernel_lock = 1;
else
has_kernel_lock = 0;
#endif
if (p->p_stat) {
if (*mode == 'o') {
if (p->p_stat != SONPROC)
continue;
if (p->p_cpu != NULL && p->p_cpu->
ci_schedstate.spc_idleproc == p)
continue;
}
if (*mode == 'n') {
db_printf("%c%5d ", (p == curproc ?
'*' : ' '), pr->ps_pid);
} else {
db_printf("%c%6d ", (p == curproc ?
'*' : ' '), p->p_tid);
}
switch (*mode) {
case 'a':
db_printf("%-9.9s %18p %18p %18p\n",
pr->ps_comm, p, p->p_addr, p->p_vmspace);
break;
case 'n':
db_printf("%6d %5d %5d %d %#10x "
"%-12.12s %-15s\n",
p->p_tid, ppr ? ppr->ps_pid : -1,
pr->ps_ucred->cr_ruid, p->p_stat,
p->p_flag | pr->ps_flags,
(p->p_wchan && p->p_wmesg) ?
p->p_wmesg : "", pr->ps_comm);
break;
case 'w':
db_printf("%-15s %-5d %18p %s\n",
pr->ps_comm, (pr->ps_pgrp ?
pr->ps_pgrp->pg_id : -1),
p->p_wchan,
(p->p_wchan && p->p_wmesg) ?
p->p_wmesg : "");
break;
case 'o':
db_printf("%5d %5d %#10x %#10x %3d"
"%c %-31s\n",
pr->ps_pid, pr->ps_ucred->cr_ruid,
pr->ps_flags, p->p_flag,
CPU_INFO_UNIT(p->p_cpu),
has_kernel_lock ? 'K' : ' ',
pr->ps_comm);
break;
}
}
}
pr = LIST_NEXT(pr, ps_list);
if (pr == NULL && skipzomb == 0) {
skipzomb = 1;
pr = LIST_FIRST(&zombprocess);
}
}
}
#endif
#ifdef DEBUG
void
pgrpdump(void)
{
struct pgrp *pgrp;
struct process *pr;
int i;
for (i = 0; i <= pgrphash; i++) {
if (!LIST_EMPTY(&pgrphashtbl[i])) {
printf("\tindx %d\n", i);
LIST_FOREACH(pgrp, &pgrphashtbl[i], pg_hash) {
printf("\tpgrp %p, pgid %d, sess %p, sesscnt %d, mem %p\n",
pgrp, pgrp->pg_id, pgrp->pg_session,
pgrp->pg_session->s_count,
LIST_FIRST(&pgrp->pg_members));
LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
printf("\t\tpid %d addr %p pgrp %p\n",
pr->ps_pid, pr, pr->ps_pgrp);
}
}
}
}
}
#endif