/* * Wait for a stopped process to be set running again by some other debugger. * This is typically not required by /proc-based debuggers, since the usual * model is that one debugger controls one victim. But DTrace, as usual, has * its own needs: the stop() action assumes that prun(1) or some other tool * will be applied to resume the victim process. This could be solved by * adding a PCWRUN directive to /proc, but that seems like overkill unless * other debuggers end up needing this functionality, so we implement a cheap * equivalent to PCWRUN using the set of existing kernel mechanisms. * * Our intent is really not just to wait for the victim to run, but rather to * wait for it to run and then stop again for a reason other than the current * PR_REQUESTED stop. Since PCWSTOP/Pstopstatus() can be applied repeatedly * to a stopped process and will return the same result without affecting the * victim, we can just perform these operations repeatedly until Pstate() * changes, the representative LWP ID changes, or the stop timestamp advances. * dt_gproc_control() will then rediscover the new state and continue as usual. * When the process is still stopped in the same exact state, we sleep for a * brief interval before waiting again so as not to spin consuming CPU cycles. */ static void dt_proc_waitrun(dt_proc_t *dpr) { struct ps_prochandle *P = dpr->dpr_proc; const lwpstatus_t *psp = &Pstatus(P)->pr_lwp; int krflag = psp->pr_flags & (PR_KLC | PR_RLC); timestruc_t tstamp = psp->pr_tstamp; lwpid_t lwpid = psp->pr_lwpid; const long wstop = PCWSTOP; int pfd = Pctlfd(P); assert(DT_MUTEX_HELD(&dpr->dpr_lock)); assert(psp->pr_flags & PR_STOPPED); assert(Pstate(P) == PS_STOP); /* * While we are waiting for the victim to run, clear PR_KLC and PR_RLC * so that if the libdtrace client is killed, the victim stays stopped. * dt_proc_destroy() will also observe this and perform PRELEASE_HANG. */ (void) Punsetflags(P, krflag); Psync(P); (void) pthread_mutex_unlock(&dpr->dpr_lock); while (!dpr->dpr_quit) { if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR) continue; /* check dpr_quit and continue waiting */ (void) pthread_mutex_lock(&dpr->dpr_lock); (void) Pstopstatus(P, PCNULL, 0); psp = &Pstatus(P)->pr_lwp; /* * If we've reached a new state, found a new representative, or * the stop timestamp has changed, restore PR_KLC/PR_RLC to its * original setting and then return with dpr_lock held. */ if (Pstate(P) != PS_STOP || psp->pr_lwpid != lwpid || bcmp(&psp->pr_tstamp, &tstamp, sizeof (tstamp)) != 0) { (void) Psetflags(P, krflag); Psync(P); return; } (void) pthread_mutex_unlock(&dpr->dpr_lock); (void) poll(NULL, 0, MILLISEC / 2); } (void) pthread_mutex_lock(&dpr->dpr_lock); }
static void dt_proc_bpdestroy(dt_proc_t *dpr, int delbkpts) { #if defined(sun) int state = Pstate(dpr->dpr_proc); #else int state = proc_state(dpr->dpr_proc); #endif dt_bkpt_t *dbp, *nbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = nbp) { printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); #ifdef DOODAD if (delbkpts && dbp->dbp_active && state != PS_LOST && state != PS_UNDEAD) { (void) Pdelbkpt(dpr->dpr_proc, dbp->dbp_addr, dbp->dbp_instr); } #endif nbp = dt_list_next(dbp); dt_list_delete(&dpr->dpr_bps, dbp); dt_free(dpr->dpr_hdl, dbp); } }
/* * Create labels for non-anon, non-heap mappings */ char * make_name(struct ps_prochandle *Pr, int lflag, uintptr_t addr, const char *mapname, char *buf, size_t bufsz) { const pstatus_t *Psp = Pstatus(Pr); struct stat statb; char path[PATH_MAX]; int len; if (lflag || Pstate(Pr) == PS_DEAD) { if (Pobjname(Pr, addr, buf, bufsz) != NULL) return (buf); } else { if (Pobjname_resolved(Pr, addr, buf, bufsz) != NULL) { /* Verify that the path exists */ if ((len = resolvepath(buf, buf, bufsz)) > 0) { buf[len] = '\0'; return (buf); } } } if (Pstate(Pr) == PS_DEAD || *mapname == '\0') return (NULL); /* first see if we can find a path via /proc */ (void) proc_snprintf(path, sizeof (path), "/proc/%d/path/%s", (int)Psp->pr_pid, mapname); len = readlink(path, buf, bufsz - 1); if (len >= 0) { buf[len] = '\0'; return (buf); } /* fall back to object information reported by /proc */ (void) proc_snprintf(path, sizeof (path), "/proc/%d/object/%s", (int)Psp->pr_pid, mapname); if (stat(path, &statb) == 0) { dev_t dev = statb.st_dev; ino_t ino = statb.st_ino; (void) snprintf(buf, bufsz, "dev:%lu,%lu ino:%lu", (ulong_t)major(dev), (ulong_t)minor(dev), ino); return (buf); } return (NULL); }
/*ARGSUSED*/ static void prochandler(struct ps_prochandle *P, const char *msg, void *arg) { #if !defined(__APPLE__) const psinfo_t *prp = Ppsinfo(P); int pid = Pstatus(P)->pr_pid; char name[SIG2STR_MAX]; #else #define SIG2STR_MAX 32 /* Not referenced so long as prp just below is NULL. */ #define proc_signame(x,y,z) "Unknown" /* Not referenced so long as prp just below is NULL. */ typedef struct psinfo { int pr_wstat; } psinfo_t; const psinfo_t *prp = NULL; int pid = Pstatus(P)->pr_pid; #endif /* __APPLE__ */ if (msg != NULL) { notice("pid %d: %s\n", pid, msg); return; } switch (Pstate(P)) { case PS_UNDEAD: /* * Ideally we would like to always report pr_wstat here, but it * isn't possible given current /proc semantics. If we grabbed * the process, Ppsinfo() will either fail or return a zeroed * psinfo_t depending on how far the parent is in reaping it. * When /proc provides a stable pr_wstat in the status file, * this code can be improved by examining this new pr_wstat. */ if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { notice("pid %d terminated by %s\n", pid, proc_signame(WTERMSIG(prp->pr_wstat), name, sizeof (name))); } else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(prp->pr_wstat)); } else { notice("pid %d has exited\n", pid); } g_exited = 1; break; case PS_LOST: #if !defined(__APPLE__) notice("pid %d exec'd a set-id or unobservable program\n", pid); #else notice("pid %d has exited\n", pid); #endif g_exited = 1; break; } }
/*ARGSUSED*/ static int look_xmap(void *data, const prxmap_t *pmp, const char *object_name, int last, int doswap) { struct totals *t = data; const pstatus_t *Psp = Pstatus(Pr); char mname[PATH_MAX]; char *lname = NULL; char *ln; /* * If the mapping is not anon or not part of the heap, make a name * for it. We don't want to report the heap as a.out's data. */ if (!(pmp->pr_mflags & MA_ANON) || pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase || pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) { lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname, mname, sizeof (mname)); } if (lname != NULL) { if ((ln = strrchr(lname, '/')) != NULL) lname = ln + 1; } else if ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD) { lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr, pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL); } (void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr); printK(ROUNDUP_KB(pmp->pr_size), size_width); printK(pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE), size_width); printK(ANON(pmp) * (pmp->pr_pagesize / KILOBYTE), size_width); printK(pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE), size_width); (void) printf(lname ? " %4s %-6s %s\n" : " %4s %s\n", pagesize(pmp), mflags(pmp->pr_mflags), lname); t->total_size += ROUNDUP_KB(pmp->pr_size); t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE); t->total_anon += ANON(pmp) * (pmp->pr_pagesize / KILOBYTE); t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE)); return (0); }
static void dt_proc_bpdestroy(dt_proc_t *dpr, int delbkpts) { int state = Pstate(dpr->dpr_proc); dt_bkpt_t *dbp, *nbp; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = nbp) { if (delbkpts && dbp->dbp_active && state != PS_LOST && state != PS_UNDEAD) { (void) Pdelbkpt(dpr->dpr_proc, dbp->dbp_addr, dbp->dbp_instr); } nbp = dt_list_next(dbp); dt_list_delete(&dpr->dpr_bps, dbp); dt_free(dpr->dpr_hdl, dbp); } }
static int dt_proc_create_thread(dtrace_hdl_t *dtp, dt_proc_t *dpr, uint_t stop) { dt_proc_control_data_t data; sigset_t nset, oset; pthread_attr_t a; int err; #if defined(linux) /***********************************************/ /* Kernel bug -- a parent which attaches to */ /* a proc -- cannot share with a child */ /* thread. So we have to detach and */ /* reattach in the child thread if we are */ /* to work properly. */ /***********************************************/ if (stop == DT_PROC_STOP_GRAB) { do_ptrace(__func__, PTRACE_DETACH, dpr->dpr_pid, 0, 0); } #endif (void) pthread_mutex_lock(&dpr->dpr_lock); dpr->dpr_stop |= stop; /* set bit for initial rendezvous */ (void) pthread_attr_init(&a); (void) pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); (void) sigfillset(&nset); (void) sigdelset(&nset, SIGABRT); /* unblocked for assert() */ #if defined(sun) (void) sigdelset(&nset, SIGCANCEL); /* see dt_proc_destroy() */ #else (void) sigdelset(&nset, SIGUSR1); /* see dt_proc_destroy() */ #endif data.dpcd_hdl = dtp; data.dpcd_proc = dpr; (void) pthread_sigmask(SIG_SETMASK, &nset, &oset); err = pthread_create(&dpr->dpr_tid, &a, dt_proc_control, &data); (void) pthread_sigmask(SIG_SETMASK, &oset, NULL); do_ptrace(__func__, PTRACE_DETACH, dpr->dpr_pid, 0, 0); /* * If the control thread was created, then wait on dpr_cv for either * dpr_done to be set (the victim died or the control thread failed) * or DT_PROC_STOP_IDLE to be set, indicating that the victim is now * stopped by /proc and the control thread is at the rendezvous event. * On success, we return with the process and control thread stopped: * the caller can then apply dt_proc_continue() to resume both. */ if (err == 0) { //printf("0..waiting for dt_proc_control....dpr_done=%d stop=%d !stop=%d\n", dpr->dpr_done, dpr->dpr_stop, !(dpr->dpr_stop & DT_PROC_STOP_IDLE)); while (!dpr->dpr_done && !(dpr->dpr_stop & DT_PROC_STOP_IDLE)) { //printf("1..waiting for dt_proc_control....dpr_done=%d stop=%d\n", dpr->dpr_done, dpr->dpr_stop); (void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock); } //printf("2..waiting for dt_proc_control....dpr_done=%d stop=%d\n", dpr->dpr_done, dpr->dpr_stop); /* * If dpr_done is set, the control thread aborted before it * reached the rendezvous event. This is either due to PS_LOST * or PS_UNDEAD (i.e. the process died). We try to provide a * small amount of useful information to help figure it out. */ if (dpr->dpr_done) { const psinfo_t *prp = Ppsinfo(dpr->dpr_proc); int stat = prp ? prp->pr_wstat : 0; int pid = dpr->dpr_pid; if (Pstate(dpr->dpr_proc) == PS_LOST) { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process exec'd " "set-id or unobservable program\n", pid); } else if (WIFSIGNALED(stat)) { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process died " "from signal %d\n", pid, WTERMSIG(stat)); } else { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process exited " "with status %d\n", pid, WEXITSTATUS(stat)); } err = ESRCH; /* cause grab() or create() to fail */ } } else { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to create control thread for process-id %d: %s\n", (int)dpr->dpr_pid, strerror(err)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); (void) pthread_attr_destroy(&a); return (err); }
/* * Main loop for all victim process control threads. We initialize all the * appropriate /proc control mechanisms, and then enter a loop waiting for * the process to stop on an event or die. We process any events by calling * appropriate subroutines, and exit when the victim dies or we lose control. * * The control thread synchronizes the use of dpr_proc with other libdtrace * threads using dpr_lock. We hold the lock for all of our operations except * waiting while the process is running: this is accomplished by writing a * PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. If the * libdtrace client wishes to exit or abort our wait, SIGCANCEL can be used. */ static void * dt_proc_control(void *arg) { dt_proc_control_data_t *datap = arg; dtrace_hdl_t *dtp = datap->dpcd_hdl; dt_proc_t *dpr = datap->dpcd_proc; dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs; struct ps_prochandle *P = dpr->dpr_proc; #if defined(sun) int pfd = Pctlfd(P); const long wstop = PCWSTOP; #endif int notify = B_FALSE; /* * We disable the POSIX thread cancellation mechanism so that the * client program using libdtrace can't accidentally cancel our thread. * dt_proc_destroy() uses SIGCANCEL explicitly to simply poke us out * of PCWSTOP with EINTR, at which point we will see dpr_quit and exit. */ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); dpr->dpr_pid = proc_getpid(P); int pid = dpr->dpr_pid; /* * Set up the corresponding process for tracing by libdtrace. We want * to be able to catch breakpoints and efficiently single-step over * them, and we need to enable librtld_db to watch libdl activity. */ do_ptrace(__func__, PTRACE_ATTACH, dpr->dpr_pid, 0, 0); (void) pthread_mutex_lock(&dpr->dpr_lock); (void) Punsetflags(P, PR_ASYNC); /* require synchronous mode */ (void) Psetflags(P, PR_BPTADJ); /* always adjust eip on x86 */ (void) Punsetflags(P, PR_FORK); /* do not inherit on fork */ (void) Pfault(P, FLTBPT, B_TRUE); /* always trace breakpoints */ (void) Pfault(P, FLTTRACE, B_TRUE); /* always trace single-step */ /* * We must trace exit from exec() system calls so that if the exec is * successful, we can reset our breakpoints and re-initialize libproc. */ (void) Psysexit(P, SYS_exec, B_TRUE); (void) Psysexit(P, SYS_execve, B_TRUE); /* * We must trace entry and exit for fork() system calls in order to * disable our breakpoints temporarily during the fork. We do not set * the PR_FORK flag, so if fork succeeds the child begins executing and * does not inherit any other tracing behaviors or a control thread. */ (void) Psysentry(P, SYS_vfork, B_TRUE); (void) Psysexit(P, SYS_vfork, B_TRUE); (void) Psysentry(P, SYS_fork1, B_TRUE); (void) Psysexit(P, SYS_fork1, B_TRUE); (void) Psysentry(P, SYS_forkall, B_TRUE); (void) Psysexit(P, SYS_forkall, B_TRUE); (void) Psysentry(P, SYS_forksys, B_TRUE); (void) Psysexit(P, SYS_forksys, B_TRUE); Psync(P); /* enable all /proc changes */ dt_proc_attach(dpr, B_FALSE); /* enable rtld breakpoints */ /* * If PR_KLC is set, we created the process; otherwise we grabbed it. * Check for an appropriate stop request and wait for dt_proc_continue. */ dpr->dpr_stop |= DT_PROC_STOP_CREATE; if (Pstatus(P)->pr_flags & PR_KLC) dt_proc_stop(dpr, DT_PROC_STOP_CREATE); else dt_proc_stop(dpr, DT_PROC_STOP_GRAB); if (Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); /* * Wait for the process corresponding to this control thread to stop, * process the event, and then set it running again. We want to sleep * with dpr_lock *unheld* so that other parts of libdtrace can use the * ps_prochandle in the meantime (e.g. ustack()). To do this, we write * a PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. * Once the process stops, we wake up, grab dpr_lock, and then call * Pwait() (which will return immediately) and do our processing. */ //printf("%s: waiting to quit\n", __func__); while (!dpr->dpr_quit) { const lwpstatus_t *psp; #if defined(sun) if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR) continue; /* check dpr_quit and continue waiting */ #else /* Wait for the process to report status. */ proc_wait(P); #endif (void) pthread_mutex_lock(&dpr->dpr_lock); pwait_locked: if (Pstopstatus(P, PCNULL, 0) == -1 && errno == EINTR) { //printf("%s stopstatus (loop) pr_pid pid=%d\n", __func__, Pstatus(dpr->dpr_proc)->pr_pid); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; /* check dpr_quit and continue waiting */ } switch (Pstate(P)) { case PS_STOP: psp = &Pstatus(P)->pr_lwp; dt_dprintf("pid %d: proc stopped showing %d/%d\n", pid, psp->pr_why, psp->pr_what); #if defined(sun) /* * If the process stops showing PR_REQUESTED, then the * DTrace stop() action was applied to it or another * debugging utility (e.g. pstop(1)) asked it to stop. * In either case, the user's intention is for the * process to remain stopped until another external * mechanism (e.g. prun(1)) is applied. So instead of * setting the process running ourself, we wait for * someone else to do so. Once that happens, we return * to our normal loop waiting for an event of interest. */ if (psp->pr_why == PR_REQUESTED) { dt_proc_waitrun(dpr); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; } /* * If the process stops showing one of the events that * we are tracing, perform the appropriate response. * Note that we ignore PR_SUSPENDED, PR_CHECKPOINT, and * PR_JOBCONTROL by design: if one of these conditions * occurs, we will fall through to Psetrun() but the * process will remain stopped in the kernel by the * corresponding mechanism (e.g. job control stop). */ if (psp->pr_why == PR_FAULTED && psp->pr_what == FLTBPT) dt_proc_bpmatch(dtp, dpr); else if (psp->pr_why == PR_SYSENTRY && IS_SYS_FORK(psp->pr_what)) dt_proc_bpdisable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_FORK(psp->pr_what)) dt_proc_bpenable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_EXEC(psp->pr_what)) dt_proc_attach(dpr, B_TRUE); #endif //printf("In PS_STOP dpr_stop=%x\n", dpr->dpr_stop); break; case PS_LOST: //printf("in PS_LOST\n"); if (Preopen(P) == 0) goto pwait_locked; dt_dprintf("pid %d: proc lost: %s\n", pid, strerror(errno)); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; case PS_UNDEAD: case PS_DEAD: dt_dprintf("pid %d: proc died\n", pid); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; } if (Pstate(P) != PS_UNDEAD && Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); } /* * If the control thread detected PS_UNDEAD or PS_LOST, then enqueue * the dt_proc_t structure on the dt_proc_hash_t notification list. */ if (notify) dt_proc_notify(dtp, dph, dpr, NULL); /* * Destroy and remove any remaining breakpoints, set dpr_done and clear * dpr_tid to indicate the control thread has exited, and notify any * waiting thread in dt_proc_destroy() that we have succesfully exited. */ (void) pthread_mutex_lock(&dpr->dpr_lock); dt_proc_bpdestroy(dpr, B_TRUE); dpr->dpr_done = B_TRUE; dpr->dpr_tid = 0; (void) pthread_cond_broadcast(&dpr->dpr_cv); (void) pthread_mutex_unlock(&dpr->dpr_lock); return (NULL); }
static int look(char *arg) { struct ps_prochandle *Pr; int gcode; size_t sz; void *pdata; char *x; int i; boolean_t nodata; prpriv_t *ppriv; procname = arg; /* for perr() */ if ((Pr = proc_arg_grab(arg, set ? PR_ARG_PIDS : PR_ARG_ANY, PGRAB_RETAIN | PGRAB_FORCE | (set ? 0 : PGRAB_RDONLY) | PGRAB_NOSTOP, &gcode)) == NULL) { (void) fprintf(stderr, "%s: cannot examine %s: %s\n", command, arg, Pgrab_error(gcode)); return (1); } if (Ppriv(Pr, &ppriv) == -1) { perr(command); Prelease(Pr, 0); return (1); } sz = PRIV_PRPRIV_SIZE(ppriv); /* * The ppriv fields are unsigned and may overflow, so check them * separately. Size must be word aligned, so check that too. * Make sure size is "smallish" too. */ if ((sz & 3) || ppriv->pr_nsets == 0 || sz / ppriv->pr_nsets < ppriv->pr_setsize || ppriv->pr_infosize > sz || sz > 1024 * 1024) { (void) fprintf(stderr, "%s: %s: bad PRNOTES section, size = %lx\n", command, arg, (long)sz); Prelease(Pr, 0); Ppriv_free(Pr, ppriv); return (1); } if (set) { privupdate(ppriv, arg); if (Psetpriv(Pr, ppriv) != 0) { perr(command); Prelease(Pr, 0); Ppriv_free(Pr, ppriv); return (1); } Prelease(Pr, 0); Ppriv_free(Pr, ppriv); return (0); } if (Pstate(Pr) == PS_DEAD) { (void) printf("core '%s' of %d:\t%.70s\n", arg, (int)Ppsinfo(Pr)->pr_pid, Ppsinfo(Pr)->pr_psargs); pdata = Pprivinfo(Pr); nodata = Pstate(Pr) == PS_DEAD && pdata == NULL; } else { (void) printf("%d:\t%.70s\n", (int)Ppsinfo(Pr)->pr_pid, Ppsinfo(Pr)->pr_psargs); pdata = NULL; nodata = B_FALSE; } x = (char *)ppriv + sz - ppriv->pr_infosize; while (x < (char *)ppriv + sz) { /* LINTED: alignment */ priv_info_t *pi = (priv_info_t *)x; priv_info_uint_t *pii; switch (pi->priv_info_type) { case PRIV_INFO_FLAGS: /* LINTED: alignment */ pii = (priv_info_uint_t *)x; (void) printf("flags ="); flags2str(pii->val); (void) putchar('\n'); break; default: (void) fprintf(stderr, "%s: unknown priv_info: %d\n", arg, pi->priv_info_type); break; } if (pi->priv_info_size > ppriv->pr_infosize || pi->priv_info_size <= sizeof (priv_info_t) || (pi->priv_info_size & 3) != 0) { (void) fprintf(stderr, "%s: bad priv_info_size: %u\n", arg, pi->priv_info_size); break; } x += pi->priv_info_size; } for (i = 0; i < ppriv->pr_nsets; i++) { extern const char *__priv_getsetbynum(const void *, int); const char *setnm = pdata ? __priv_getsetbynum(pdata, i) : priv_getsetbynum(i); priv_chunk_t *pc = (priv_chunk_t *)&ppriv->pr_sets[ppriv->pr_setsize * i]; (void) printf("\t%c: ", setnm && !nodata ? *setnm : '?'); if (!nodata) { extern char *__priv_set_to_str(void *, const priv_set_t *, char, int); priv_set_t *pset = (priv_set_t *)pc; char *s; if (pdata) s = __priv_set_to_str(pdata, pset, ',', mode); else s = priv_set_to_str(pset, ',', mode); (void) puts(s); free(s); } else { int j; for (j = 0; j < ppriv->pr_setsize; j++) (void) printf("%08x", pc[j]); (void) putchar('\n'); } } Prelease(Pr, 0); Ppriv_free(Pr, ppriv); return (0); }
int main(int argc, char **argv) { int rflag = 0, sflag = 0, xflag = 0, Fflag = 0; int errflg = 0, Sflag = 0; int rc = 0; int opt; const char *bar8 = "-------"; const char *bar16 = "----------"; const char *bar; struct rlimit rlim; struct stat64 statbuf; char buf[128]; int mapfd; int prg_gflags = PGRAB_RDONLY; int prr_flags = 0; boolean_t use_agent_lwp = B_FALSE; if ((command = strrchr(argv[0], '/')) != NULL) command++; else command = argv[0]; while ((opt = getopt(argc, argv, "arsxSlLFA:")) != EOF) { switch (opt) { case 'a': /* include shared mappings in -[xS] */ aflag = 1; break; case 'r': /* show reserved mappings */ rflag = 1; break; case 's': /* show hardware page sizes */ sflag = 1; break; case 'S': /* show swap reservations */ Sflag = 1; break; case 'x': /* show extended mappings */ xflag = 1; break; case 'l': /* show unresolved link map names */ lflag = 1; break; case 'L': /* show lgroup information */ Lflag = 1; use_agent_lwp = B_TRUE; break; case 'F': /* force grabbing (no O_EXCL) */ Fflag = PGRAB_FORCE; break; case 'A': if (parse_addr_range(optarg, &start_addr, &end_addr) != 0) errflg++; break; default: errflg = 1; break; } } argc -= optind; argv += optind; if ((Sflag && (xflag || rflag || sflag)) || (xflag && rflag) || (aflag && (!xflag && !Sflag)) || (Lflag && (xflag || Sflag))) { errflg = 1; } if (errflg || argc <= 0) { (void) fprintf(stderr, "usage:\t%s [-rslF] [-A start[,end]] { pid | core } ...\n", command); (void) fprintf(stderr, "\t\t(report process address maps)\n"); (void) fprintf(stderr, "\t%s -L [-rslF] [-A start[,end]] pid ...\n", command); (void) fprintf(stderr, "\t\t(report process address maps lgroups mappings)\n"); (void) fprintf(stderr, "\t%s -x [-aslF] [-A start[,end]] pid ...\n", command); (void) fprintf(stderr, "\t\t(show resident/anon/locked mapping details)\n"); (void) fprintf(stderr, "\t%s -S [-alF] [-A start[,end]] { pid | core } ...\n", command); (void) fprintf(stderr, "\t\t(show swap reservations)\n\n"); (void) fprintf(stderr, "\t-a: include shared mappings in -[xS] summary\n"); (void) fprintf(stderr, "\t-r: show reserved address maps\n"); (void) fprintf(stderr, "\t-s: show hardware page sizes\n"); (void) fprintf(stderr, "\t-l: show unresolved dynamic linker map names\n"); (void) fprintf(stderr, "\t-F: force grabbing of the target process\n"); (void) fprintf(stderr, "\t-L: show lgroup mappings\n"); (void) fprintf(stderr, "\t-A start,end: limit output to the specified range\n"); return (2); } /* * Make sure we'll have enough file descriptors to handle a target * that has many many mappings. */ if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; (void) setrlimit(RLIMIT_NOFILE, &rlim); (void) enable_extended_FILE_stdio(-1, -1); } /* * The implementation of -L option creates an agent LWP in the target * process address space. The agent LWP issues meminfo(2) system calls * on behalf of the target process. If we are interrupted prematurely, * the target process remains in the stopped state with the agent still * attached to it. To prevent such situation we catch signals from * terminal and terminate gracefully. */ if (use_agent_lwp) { /* * Buffer output to stdout, stderr while process is grabbed. * Prevents infamous deadlocks due to pmap `pgrep xterm` and * other variants. */ (void) proc_initstdio(); prg_gflags = PGRAB_RETAIN | Fflag; prr_flags = PRELEASE_RETAIN; if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) (void) sigset(SIGHUP, intr); if (sigset(SIGINT, SIG_IGN) == SIG_DFL) (void) sigset(SIGINT, intr); if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) (void) sigset(SIGQUIT, intr); (void) sigset(SIGPIPE, intr); (void) sigset(SIGTERM, intr); } while (argc-- > 0) { char *arg; int gcode; psinfo_t psinfo; int tries = 0; if (use_agent_lwp) (void) proc_flushstdio(); if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY, prg_gflags, &gcode)) == NULL) { (void) fprintf(stderr, "%s: cannot examine %s: %s\n", command, arg, Pgrab_error(gcode)); rc++; continue; } procname = arg; /* for perr() */ addr_width = (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8; size_width = (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8; bar = addr_width == 8 ? bar8 : bar16; (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t)); proc_unctrl_psinfo(&psinfo); if (Pstate(Pr) != PS_DEAD) { (void) snprintf(buf, sizeof (buf), "/proc/%d/map", (int)psinfo.pr_pid); if ((mapfd = open(buf, O_RDONLY)) < 0) { (void) fprintf(stderr, "%s: cannot " "examine %s: lost control of " "process\n", command, arg); rc++; Prelease(Pr, prr_flags); continue; } } else { mapfd = -1; } again: map_count = 0; if (Pstate(Pr) == PS_DEAD) { (void) printf("core '%s' of %d:\t%.70s\n", arg, (int)psinfo.pr_pid, psinfo.pr_psargs); if (rflag || sflag || xflag || Sflag || Lflag) { (void) printf(" -%c option is not compatible " "with core files\n", xflag ? 'x' : sflag ? 's' : rflag ? 'r' : Lflag ? 'L' : 'S'); Prelease(Pr, prr_flags); rc++; continue; } } else { (void) printf("%d:\t%.70s\n", (int)psinfo.pr_pid, psinfo.pr_psargs); } if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) { struct totals t; /* * Since we're grabbing the process readonly, we need * to make sure the address space doesn't change during * execution. */ if (Pstate(Pr) != PS_DEAD) { if (tries++ == MAX_TRIES) { Prelease(Pr, prr_flags); (void) close(mapfd); (void) fprintf(stderr, "%s: cannot " "examine %s: address space is " "changing\n", command, arg); continue; } if (fstat64(mapfd, &statbuf) != 0) { Prelease(Pr, prr_flags); (void) close(mapfd); (void) fprintf(stderr, "%s: cannot " "examine %s: lost control of " "process\n", command, arg); continue; } } nstacks = psinfo.pr_nlwp * 2; stacks = calloc(nstacks, sizeof (stacks[0])); if (stacks != NULL) { int n = 0; (void) Plwp_iter(Pr, getstack, &n); qsort(stacks, nstacks, sizeof (stacks[0]), cmpstacks); } (void) memset(&t, 0, sizeof (t)); if (Pgetauxval(Pr, AT_BASE) != -1L && Prd_agent(Pr) == NULL) { (void) fprintf(stderr, "%s: warning: " "librtld_db failed to initialize; " "shared library information will not be " "available\n", command); } /* * Gather data */ if (xflag) rc += xmapping_iter(Pr, gather_xmap, NULL, 0); else if (Sflag) rc += xmapping_iter(Pr, gather_xmap, NULL, 1); else { if (rflag) rc += rmapping_iter(Pr, gather_map, NULL); else if (sflag) rc += xmapping_iter(Pr, gather_xmap, NULL, 0); else if (lflag) rc += Pmapping_iter(Pr, gather_map, NULL); else rc += Pmapping_iter_resolved(Pr, gather_map, NULL); } /* * Ensure mappings are consistent. */ if (Pstate(Pr) != PS_DEAD) { struct stat64 newbuf; if (fstat64(mapfd, &newbuf) != 0 || memcmp(&newbuf.st_mtim, &statbuf.st_mtim, sizeof (newbuf.st_mtim)) != 0) { if (stacks != NULL) { free(stacks); stacks = NULL; } goto again; } } /* * Display data. */ if (xflag) { (void) printf("%*s%*s%*s%*s%*s " "%sMode Mapped File\n", addr_width, "Address", size_width, "Kbytes", size_width, "RSS", size_width, "Anon", size_width, "Locked", sflag ? "Pgsz " : ""); rc += iter_xmap(sflag ? look_xmap : look_xmap_nopgsz, &t); (void) printf("%s%s %s %s %s %s\n", addr_width == 8 ? "-" : "------", bar, bar, bar, bar, bar); (void) printf("%stotal Kb", addr_width == 16 ? " " : ""); printK(t.total_size, size_width); printK(t.total_rss, size_width); printK(t.total_anon, size_width); printK(t.total_locked, size_width); (void) printf("\n"); } else if (Sflag) { (void) printf("%*s%*s%*s Mode" " Mapped File\n", addr_width, "Address", size_width, "Kbytes", size_width, "Swap"); rc += iter_xmap(look_xmap_nopgsz, &t); (void) printf("%s%s %s %s\n", addr_width == 8 ? "-" : "------", bar, bar, bar); (void) printf("%stotal Kb", addr_width == 16 ? " " : ""); printK(t.total_size, size_width); printK(t.total_swap, size_width); (void) printf("\n"); } else { if (rflag) { rc += iter_map(look_map, &t); } else if (sflag) { if (Lflag) { (void) printf("%*s %*s %4s" " %-6s %s %s\n", addr_width, "Address", size_width, "Bytes", "Pgsz", "Mode ", "Lgrp", "Mapped File"); rc += iter_xmap(look_smap, &t); } else { (void) printf("%*s %*s %4s" " %-6s %s\n", addr_width, "Address", size_width, "Bytes", "Pgsz", "Mode ", "Mapped File"); rc += iter_xmap(look_smap, &t); } } else { rc += iter_map(look_map, &t); } (void) printf(" %stotal %*luK\n", addr_width == 16 ? " " : "", size_width, t.total_size); } if (stacks != NULL) { free(stacks); stacks = NULL; } } Prelease(Pr, prr_flags); if (mapfd != -1) (void) close(mapfd); } if (use_agent_lwp) (void) proc_finistdio(); return (rc); }
/* * Force the parent process (ppid) to wait for its child process (pid). */ static int reap(char *arg, pid_t *reap_pid, int *exit_status) { struct ps_prochandle *Pr; siginfo_t siginfo; psinfo_t psinfo; prusage_t usage; pid_t pid, ppid; time_t elapsed; int gret; /* * get the specified pid and the psinfo struct */ if ((pid = proc_arg_psinfo(arg, PR_ARG_PIDS, &psinfo, &gret)) == -1) { (void) fprintf(stderr, "%s: cannot examine %s: %s\n", command, arg, Pgrab_error(gret)); return (1); } if (psinfo.pr_nlwp != 0) { (void) fprintf(stderr, "%s: process not defunct: %d\n", command, (int)pid); return (1); } *exit_status = psinfo.pr_wstat; *reap_pid = psinfo.pr_pid; ppid = psinfo.pr_ppid; if (ppid == 1) { (void) fprintf(stderr, "%s: Failed to reap %d: the only " "non-defunct ancestor is 'init'\n", command, (int)pid); return (1); } if (proc_usage(pid, &usage, &gret) == 0) { elapsed = usage.pr_tstamp.tv_sec - usage.pr_term.tv_sec; } else { (void) fprintf(stderr, "%s: cannot examine %d: %s\n", command, (int)pid, Pgrab_error(gret)); return (1); } if ((Fflag == 0) && (elapsed < NOREAP_TIME)) { (void) fprintf(stderr, "%s: unsafe to reap %d; it has been " "defunct less than %d seconds\n", command, (int)pid, NOREAP_TIME); return (1); } if ((Pr = Pgrab(ppid, Fflag | PGRAB_NOSTOP, &gret)) == NULL) { (void) fprintf(stderr, "%s: cannot examine %d: %s\n", command, (int)ppid, Pgrab_error(gret)); return (1); } if ((Fflag == 0) && (Pstate(Pr) == PS_STOP)) { Prelease(Pr, 0); (void) fprintf(stderr, "%s: unsafe to reap %d; parent is " "stopped and may reap status upon restart\n", command, (int)pid); return (1); } /* * Pstop() will fail if the process to be stopped has become a zombie. * This means that we can say with certainty that the child of this * process has not changed parents (i.e. been reparented to init) once * the Pstop() succeeds. */ if (Pstop(Pr, 1000) != 0) { Prelease(Pr, 0); (void) fprintf(stderr, "%s: failed to stop %d: %s", command, (int)ppid, strerror(errno)); return (1); } if (pr_waitid(Pr, P_PID, pid, &siginfo, WEXITED|WNOHANG) != 0) { Prelease(Pr, 0); (void) fprintf(stderr, "%s: waitid() in process %d failed: %s", command, (int)ppid, strerror(errno)); return (1); } Prelease(Pr, 0); return (0); }
static int lwplook(look_arg_t *arg, const lwpstatus_t *psp, const lwpsinfo_t *pip) { int flags; uint32_t sighold, sighold1, sighold2; uint32_t sigpend, sigpend1, sigpend2; int cursig; char buf[32]; if (!proc_lwp_in_set(arg->lwps, pip->pr_lwpid)) return (0); arg->count++; if (psp == NULL) return (lwplook_zombie(pip)); /* * PR_PCINVAL is just noise if the lwp is not stopped. * Don't bother reporting it unless the lwp is stopped. */ flags = psp->pr_flags & LWPFLAGS; if (!(flags & PR_STOPPED)) flags &= ~PR_PCINVAL; (void) printf(" /%d:\tflags = %s", (int)psp->pr_lwpid, prflags(flags)); if ((flags & PR_ASLEEP) || (psp->pr_syscall && !(arg->pflags & PR_ISSYS))) { if (flags & PR_ASLEEP) { if ((flags & ~PR_ASLEEP) != 0) (void) printf("|"); (void) printf("ASLEEP"); } if (psp->pr_syscall && !(arg->pflags & PR_ISSYS)) { uint_t i; (void) printf(" %s(", proc_sysname(psp->pr_syscall, buf, sizeof (buf))); for (i = 0; i < psp->pr_nsysarg; i++) { if (i != 0) (void) printf(","); (void) printf("0x%lx", psp->pr_sysarg[i]); } (void) printf(")"); } } (void) printf("\n"); if (flags & PR_STOPPED) { (void) printf("\twhy = %s", prwhy(psp->pr_why)); if (psp->pr_why != PR_REQUESTED && psp->pr_why != PR_SUSPENDED) (void) printf(" what = %s", prwhat(psp->pr_why, psp->pr_what)); (void) printf("\n"); } #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32) /* assumption */ sighold = *((uint32_t *)&psp->pr_lwphold); sighold1 = *((uint32_t *)&psp->pr_lwphold + 1); sighold2 = *((uint32_t *)&psp->pr_lwphold + 2); sigpend = *((uint32_t *)&psp->pr_lwppend); sigpend1 = *((uint32_t *)&psp->pr_lwppend + 1); sigpend2 = *((uint32_t *)&psp->pr_lwppend + 2); #else #error "fix me: MAXSIG out of bounds" #endif cursig = psp->pr_cursig; if (sighold | sighold1 | sighold2) (void) printf("\tsigmask = 0x%.8x,0x%.8x,0x%.8x\n", sighold, sighold1, sighold2); if (sigpend | sigpend1 | sigpend2) (void) printf("\tlwppend = 0x%.8x,0x%.8x,0x%.8x\n", sigpend, sigpend1, sigpend2); if (cursig) (void) printf("\tcursig = %s\n", proc_signame(cursig, buf, sizeof (buf))); if (rflag) { if (Pstate(Pr) == PS_DEAD || (arg->pflags & PR_STOPPED)) { #if defined(__sparc) && defined(_ILP32) /* * If we're SPARC/32-bit, see if we can get extra * register state for this lwp. If it's a v8plus * program, print the 64-bit register values. */ prxregset_t prx; if (Plwp_getxregs(Pr, psp->pr_lwpid, &prx) == 0 && prx.pr_type == XR_TYPE_V8P) dumpregs_v8p(psp->pr_reg, &prx, is64); else #endif /* __sparc && _ILP32 */ dumpregs(psp->pr_reg, is64); } else (void) printf("\tNot stopped, can't show registers\n"); } return (0); }
static int look(char *arg) { int gcode; int gcode2; pstatus_t pstatus; psinfo_t psinfo; int flags; sigset_t sigmask; fltset_t fltmask; sysset_t entryset; sysset_t exitset; uint32_t sigtrace, sigtrace1, sigtrace2, fltbits; uint32_t sigpend, sigpend1, sigpend2; uint32_t *bits; char buf[PRSIGBUFSZ]; look_arg_t lookarg; if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY, PGRAB_RETAIN | PGRAB_FORCE | PGRAB_RDONLY | PGRAB_NOSTOP, &gcode, &lookarg.lwps)) == NULL) { if (gcode == G_NOPROC && proc_arg_psinfo(arg, PR_ARG_PIDS, &psinfo, &gcode2) > 0 && psinfo.pr_nlwp == 0) { (void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid); return (0); } (void) fprintf(stderr, "%s: cannot examine %s: %s\n", command, arg, Pgrab_error(gcode)); return (1); } (void) memcpy(&pstatus, Pstatus(Pr), sizeof (pstatus_t)); (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t)); proc_unctrl_psinfo(&psinfo); if (psinfo.pr_nlwp == 0) { (void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid); Prelease(Pr, PRELEASE_RETAIN); return (0); } is64 = (pstatus.pr_dmodel == PR_MODEL_LP64); sigmask = pstatus.pr_sigtrace; fltmask = pstatus.pr_flttrace; entryset = pstatus.pr_sysentry; exitset = pstatus.pr_sysexit; if (Pstate(Pr) == PS_DEAD) { (void) printf("core '%s' of %d:\t%.70s\n", arg, (int)psinfo.pr_pid, psinfo.pr_psargs); } else { (void) printf("%d:\t%.70s\n", (int)psinfo.pr_pid, psinfo.pr_psargs); } (void) printf("\tdata model = %s", is64? "_LP64" : "_ILP32"); if ((flags = (pstatus.pr_flags & PROCFLAGS)) != 0) (void) printf(" flags = %s", prflags(flags)); (void) printf("\n"); fltbits = *((uint32_t *)&fltmask); if (fltbits) (void) printf("\tflttrace = 0x%.8x\n", fltbits); #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32) /* assumption */ sigtrace = *((uint32_t *)&sigmask); sigtrace1 = *((uint32_t *)&sigmask + 1); sigtrace2 = *((uint32_t *)&sigmask + 2); #else #error "fix me: MAXSIG out of bounds" #endif if (sigtrace | sigtrace1 | sigtrace2) (void) printf("\tsigtrace = 0x%.8x 0x%.8x 0x%.8x\n\t %s\n", sigtrace, sigtrace1, sigtrace2, proc_sigset2str(&sigmask, "|", 1, buf, sizeof (buf))); bits = ((uint32_t *)&entryset); if (bits[0] | bits[1] | bits[2] | bits[3] | bits[4] | bits[5] | bits[6] | bits[7]) (void) printf( "\tentryset = " "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n" "\t " "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7]); bits = ((uint32_t *)&exitset); if (bits[0] | bits[1] | bits[2] | bits[3] | bits[4] | bits[5] | bits[6] | bits[7]) (void) printf( "\texitset = " "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n" "\t " "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7]); #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32) /* assumption */ sigpend = *((uint32_t *)&pstatus.pr_sigpend); sigpend1 = *((uint32_t *)&pstatus.pr_sigpend + 1); sigpend2 = *((uint32_t *)&pstatus.pr_sigpend + 2); #else #error "fix me: MAXSIG out of bounds" #endif if (sigpend | sigpend1 | sigpend2) (void) printf("\tsigpend = 0x%.8x,0x%.8x,0x%.8x\n", sigpend, sigpend1, sigpend2); lookarg.pflags = pstatus.pr_flags; lookarg.count = 0; (void) Plwp_iter_all(Pr, (proc_lwp_all_f *)lwplook, &lookarg); if (lookarg.count == 0) (void) printf("No matching lwps found"); (void) printf("\n"); Prelease(Pr, PRELEASE_RETAIN); return (0); }
static int dt_proc_create_thread(dtrace_hdl_t *dtp, dt_proc_t *dpr, uint_t stop) { dt_proc_control_data_t data; sigset_t nset, oset; pthread_attr_t a; int err; (void) pthread_mutex_lock(&dpr->dpr_lock); dpr->dpr_stop |= stop; /* set bit for initial rendezvous */ (void) pthread_attr_init(&a); (void) pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); (void) sigfillset(&nset); (void) sigdelset(&nset, SIGABRT); /* unblocked for assert() */ data.dpcd_hdl = dtp; data.dpcd_proc = dpr; (void) pthread_sigmask(SIG_SETMASK, &nset, &oset); err = pthread_create(&dpr->dpr_tid, &a, dt_proc_control, &data); (void) pthread_sigmask(SIG_SETMASK, &oset, NULL); /* * If the control thread was created, then wait on dpr_cv for either * dpr_done to be set (the victim died or the control thread failed) * or DT_PROC_STOP_IDLE to be set, indicating that the victim is now * stopped by /proc and the control thread is at the rendezvous event. * On success, we return with the process and control thread stopped: * the caller can then apply dt_proc_continue() to resume both. */ if (err == 0) { while (!dpr->dpr_done && !(dpr->dpr_stop & DT_PROC_STOP_IDLE)) (void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock); /* * If dpr_done is set, the control thread aborted before it * reached the rendezvous event. This is either due to PS_LOST * or PS_UNDEAD (i.e. the process died). We try to provide a * small amount of useful information to help figure it out. */ if (dpr->dpr_done) { int stat = 0; int pid = dpr->dpr_pid; if (Pstate(dpr->dpr_proc) == PS_LOST) { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process exec'd " "set-id or unobservable program\n", pid); } else if (WIFSIGNALED(stat)) { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process died " "from signal %d\n", pid, WTERMSIG(stat)); } else { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process exited " "with status %d\n", pid, WEXITSTATUS(stat)); } err = ESRCH; /* cause grab() or create() to fail */ } } else { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to create control thread for process-id %d: %s\n", (int)dpr->dpr_pid, strerror(err)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); (void) pthread_attr_destroy(&a); return (err); }
static int look(char *arg) { struct ps_prochandle *Pr; static prcred_t *prcred = NULL; int gcode; procname = arg; /* for perr() */ if (prcred == NULL) { prcred = malloc(sizeof (prcred_t) + (ngroups_max - 1) * sizeof (gid_t)); if (prcred == NULL) { (void) perr("malloc"); exit(1); } } if ((Pr = proc_arg_grab(arg, doset ? PR_ARG_PIDS : PR_ARG_ANY, PGRAB_RETAIN | PGRAB_FORCE | (doset ? 0 : PGRAB_RDONLY) | PGRAB_NOSTOP, &gcode)) == NULL) { (void) fprintf(stderr, "%s: cannot examine %s: %s\n", command, arg, Pgrab_error(gcode)); return (1); } if (Pcred(Pr, prcred, ngroups_max) == -1) { (void) perr("getcred"); Prelease(Pr, 0); return (1); } if (doset) { credupdate(prcred); if (Psetcred(Pr, prcred) != 0) { (void) perr("setcred"); Prelease(Pr, 0); return (1); } Prelease(Pr, 0); return (0); } if (Pstate(Pr) == PS_DEAD) (void) printf("core of %d:\t", (int)Pstatus(Pr)->pr_pid); else (void) printf("%d:\t", (int)Pstatus(Pr)->pr_pid); if (!all && prcred->pr_euid == prcred->pr_ruid && prcred->pr_ruid == prcred->pr_suid) (void) printf("e/r/suid=%u ", prcred->pr_euid); else (void) printf("euid=%u ruid=%u suid=%u ", prcred->pr_euid, prcred->pr_ruid, prcred->pr_suid); if (!all && prcred->pr_egid == prcred->pr_rgid && prcred->pr_rgid == prcred->pr_sgid) (void) printf("e/r/sgid=%u\n", prcred->pr_egid); else (void) printf("egid=%u rgid=%u sgid=%u\n", prcred->pr_egid, prcred->pr_rgid, prcred->pr_sgid); if (prcred->pr_ngroups != 0 && (all || prcred->pr_ngroups != 1 || prcred->pr_groups[0] != prcred->pr_rgid)) { int i; (void) printf("\tgroups:"); for (i = 0; i < prcred->pr_ngroups; i++) (void) printf(" %u", prcred->pr_groups[i]); (void) printf("\n"); } Prelease(Pr, 0); return (0); }
/*ARGSUSED*/ static void prochandler(struct ps_prochandle *P, const char *msg, void *arg) { #if defined(sun) const psinfo_t *prp = Ppsinfo(P); int pid = Pstatus(P)->pr_pid; char name[SIG2STR_MAX]; #else int wstatus = proc_getwstat(P); int pid = proc_getpid(P); #endif if (msg != NULL) { notice("pid %d: %s\n", pid, msg); return; } #if defined(sun) switch (Pstate(P)) { #else switch (proc_state(P)) { #endif case PS_UNDEAD: #if defined(sun) /* * Ideally we would like to always report pr_wstat here, but it * isn't possible given current /proc semantics. If we grabbed * the process, Ppsinfo() will either fail or return a zeroed * psinfo_t depending on how far the parent is in reaping it. * When /proc provides a stable pr_wstat in the status file, * this code can be improved by examining this new pr_wstat. */ if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { notice("pid %d terminated by %s\n", pid, proc_signame(WTERMSIG(prp->pr_wstat), name, sizeof (name))); #else if (WIFSIGNALED(wstatus)) { notice("pid %d terminated by %d\n", pid, WTERMSIG(wstatus)); #endif #if defined(sun) } else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(prp->pr_wstat)); #else } else if (WEXITSTATUS(wstatus) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(wstatus)); #endif } else { notice("pid %d has exited\n", pid); } g_pslive--; break; case PS_LOST: notice("pid %d exec'd a set-id or unobservable program\n", pid); g_pslive--; break; } } /*ARGSUSED*/ static int errhandler(const dtrace_errdata_t *data, void *arg) { error(data->dteda_msg); return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int drophandler(const dtrace_dropdata_t *data, void *arg) { error(data->dtdda_msg); return (DTRACE_HANDLE_OK); }
/*ARGSUSED*/ int pt_regs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { mdb_tgt_t *t = mdb.m_target; mdb_tgt_tid_t tid; prgregset_t grs; uint64_t xgregs[8]; uint64_t xoregs[8]; int rwidth, i; #if defined(__sparc) && defined(_ILP32) static const uint32_t zero[8] = { 0 }; prxregset_t xrs; #endif #define GETREG2(x) ((uintptr_t)grs[(x)]), ((uintptr_t)grs[(x)]) if (argc != 0) return (DCMD_USAGE); if (t->t_pshandle == NULL || Pstate(t->t_pshandle) == PS_UNDEAD) { mdb_warn("no process active\n"); return (DCMD_ERR); } if (Pstate(t->t_pshandle) == PS_LOST) { mdb_warn("debugger has lost control of process\n"); return (DCMD_ERR); } if (flags & DCMD_ADDRSPEC) tid = (mdb_tgt_tid_t)addr; else tid = PTL_TID(t); if (PTL_GETREGS(t, tid, grs) != 0) { mdb_warn("failed to get current register set"); return (DCMD_ERR); } for (i = 0; i < 8; i++) { xgregs[i] = (ulong_t)grs[R_G0 + i]; xoregs[i] = (ulong_t)grs[R_O0 + i]; } if (Pstatus(t->t_pshandle)->pr_dmodel == PR_MODEL_LP64) rwidth = 16; else rwidth = 8; #if defined(__sparc) && defined(_ILP32) /* * If we are debugging a 32-bit SPARC process on an UltraSPARC CPU, * the globals and outs can have 32 upper bits hiding in the xregs. */ if (PTL_GETXREGS(t, tid, &xrs) == 0 && xrs.pr_type == XR_TYPE_V8P) { for (i = 0; i < 8; i++) { xgregs[i] |= (uint64_t) xrs.pr_un.pr_v8p.pr_xg[XR_G0 + i] << 32; xoregs[i] |= (uint64_t) xrs.pr_un.pr_v8p.pr_xo[XR_O0 + i] << 32; } if (bcmp(xrs.pr_un.pr_v8p.pr_xg, zero, sizeof (zero)) || bcmp(xrs.pr_un.pr_v8p.pr_xo, zero, sizeof (zero))) rwidth = 16; /* one or more have upper bits set */ } #endif /* __sparc && _ILP32 */ for (i = 0; i < 8; i++) { mdb_printf("%%g%d = 0x%0*llx %15llA %%l%d = 0x%0?p %A\n", i, rwidth, xgregs[i], xgregs[i], i, GETREG2(R_L0 + i)); } for (i = 0; i < 8; i++) { mdb_printf("%%o%d = 0x%0*llx %15llA %%i%d = 0x%0?p %A\n", i, rwidth, xoregs[i], xoregs[i], i, GETREG2(R_I0 + i)); } mdb_printf("\n"); #ifdef __sparcv9 mdb_printf(" %%ccr = 0x%02x xcc=%c%c%c%c icc=%c%c%c%c\n", grs[R_CCR], (grs[R_CCR] & KREG_CCR_XCC_N_MASK) ? 'N' : 'n', (grs[R_CCR] & KREG_CCR_XCC_Z_MASK) ? 'Z' : 'z', (grs[R_CCR] & KREG_CCR_XCC_V_MASK) ? 'V' : 'v', (grs[R_CCR] & KREG_CCR_XCC_C_MASK) ? 'C' : 'c', (grs[R_CCR] & KREG_CCR_ICC_N_MASK) ? 'N' : 'n', (grs[R_CCR] & KREG_CCR_ICC_Z_MASK) ? 'Z' : 'z', (grs[R_CCR] & KREG_CCR_ICC_V_MASK) ? 'V' : 'v', (grs[R_CCR] & KREG_CCR_ICC_C_MASK) ? 'C' : 'c'); #else /* __sparcv9 */ mdb_printf(" %%psr = 0x%08x impl=0x%x ver=0x%x icc=%c%c%c%c\n" " ec=%u ef=%u pil=%u s=%u ps=%u et=%u cwp=0x%x\n", grs[R_PSR], (grs[R_PSR] & KREG_PSR_IMPL_MASK) >> KREG_PSR_IMPL_SHIFT, (grs[R_PSR] & KREG_PSR_VER_MASK) >> KREG_PSR_VER_SHIFT, (grs[R_PSR] & KREG_PSR_ICC_N_MASK) ? 'N' : 'n', (grs[R_PSR] & KREG_PSR_ICC_Z_MASK) ? 'Z' : 'z', (grs[R_PSR] & KREG_PSR_ICC_V_MASK) ? 'V' : 'v', (grs[R_PSR] & KREG_PSR_ICC_C_MASK) ? 'C' : 'c', grs[R_PSR] & KREG_PSR_EC_MASK, grs[R_PSR] & KREG_PSR_EF_MASK, (grs[R_PSR] & KREG_PSR_PIL_MASK) >> KREG_PSR_PIL_SHIFT, grs[R_PSR] & KREG_PSR_S_MASK, grs[R_PSR] & KREG_PSR_PS_MASK, grs[R_PSR] & KREG_PSR_ET_MASK, (grs[R_PSR] & KREG_PSR_CWP_MASK) >> KREG_PSR_CWP_SHIFT); #endif /* __sparcv9 */ mdb_printf(" %%y = 0x%0?p\n", grs[R_Y]); mdb_printf(" %%pc = 0x%0?p %A\n", GETREG2(R_PC)); mdb_printf(" %%npc = 0x%0?p %A\n", GETREG2(R_nPC)); mdb_printf(" %%sp = 0x%0?p\n", grs[R_SP]); mdb_printf(" %%fp = 0x%0?p\n\n", grs[R_FP]); #ifdef __sparcv9 mdb_printf(" %%asi = 0x%02lx\n", grs[R_ASI]); mdb_printf("%%fprs = 0x%02lx\n", grs[R_FPRS]); #else /* __sparcv9 */ mdb_printf(" %%wim = 0x%08x\n", grs[R_WIM]); mdb_printf(" %%tbr = 0x%08x\n", grs[R_TBR]); #endif /* __sparcv9 */ return (DCMD_OK); }
/*ARGSUSED*/ static int look_smap(void *data, const prxmap_t *pmp, const char *object_name, int last, int doswap) { struct totals *t = data; const pstatus_t *Psp = Pstatus(Pr); size_t size; char mname[PATH_MAX]; char *lname = NULL; const char *format; size_t psz = pmp->pr_pagesize; uintptr_t vaddr = pmp->pr_vaddr; uintptr_t segment_end = vaddr + pmp->pr_size; lgrp_id_t lgrp; memory_chunk_t mchunk; /* * If the mapping is not anon or not part of the heap, make a name * for it. We don't want to report the heap as a.out's data. */ if (!(pmp->pr_mflags & MA_ANON) || pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase || pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) { lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname, mname, sizeof (mname)); } if (lname == NULL && ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD)) { lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr, pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL); } /* * Adjust the address range if -A is specified. */ size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz, &vaddr, &segment_end); if (size == 0) return (0); if (!Lflag) { /* * Display the whole mapping */ if (lname != NULL) format = "%.*lX %*luK %4s %-6s %s\n"; else format = "%.*lX %*luK %4s %s\n"; size = ROUNDUP_KB(size); (void) printf(format, addr_width, vaddr, size_width - 1, size, pagesize(pmp), mflags(pmp->pr_mflags), lname); t->total_size += size; return (0); } if (lname != NULL) format = "%.*lX %*luK %4s %-6s%s %s\n"; else format = "%.*lX %*luK %4s%s %s\n"; /* * We need to display lgroups backing physical memory, so we break the * segment into individual pages and coalesce pages with the same lgroup * into one "segment". */ /* * Initialize address descriptions for the mapping. */ mem_chunk_init(&mchunk, segment_end, psz); size = 0; /* * Walk mapping (page by page) and display contiguous ranges of memory * allocated to same lgroup. */ do { size_t size_contig; /* * Get contiguous region of memory starting from vaddr allocated * from the same lgroup. */ size_contig = get_contiguous_region(&mchunk, vaddr, segment_end, pmp->pr_pagesize, &lgrp); (void) printf(format, addr_width, vaddr, size_width - 1, size_contig / KILOBYTE, pagesize(pmp), mflags(pmp->pr_mflags), lgrp2str(lgrp), lname); vaddr += size_contig; size += size_contig; } while (vaddr < segment_end && !interrupt); t->total_size += ROUNDUP_KB(size); return (0); }
/*ARGSUSED*/ int pt_fpregs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { mdb_tgt_t *t = mdb.m_target; mdb_tgt_tid_t tid; int is_v8plus, is_v9, i; #ifdef __sparcv9 prgregset_t grs; #endif prfpregset_t fprs; prxregset_t xrs; uint32_t *regs; int ns, nd, nq; enum { FPR_MIXED = 0x0, /* show single, double, and status */ FPR_SINGLE = 0x1, /* show single-precision only */ FPR_DOUBLE = 0x2, /* show double-precision only */ FPR_QUAD = 0x4 /* show quad-precision only */ }; uint_t opts = FPR_MIXED; /* * The prfpregset structure only provides us with the FPU in the form * of 32-bit integers, doubles, or quads. We use this union of the * various types to display floats, doubles, and long doubles. */ union { struct { uint32_t i1; uint32_t i2; uint32_t i3; uint32_t i4; } ip; float f; double d; long double ld; } fpu; if (mdb_getopts(argc, argv, 's', MDB_OPT_SETBITS, FPR_SINGLE, &opts, 'd', MDB_OPT_SETBITS, FPR_DOUBLE, &opts, 'q', MDB_OPT_SETBITS, FPR_QUAD, &opts, NULL) != argc) return (DCMD_USAGE); if (t->t_pshandle == NULL || Pstate(t->t_pshandle) == PS_UNDEAD) { mdb_warn("no process active\n"); return (DCMD_ERR); } if (Pstate(t->t_pshandle) == PS_LOST) { mdb_warn("debugger has lost control of process\n"); return (DCMD_ERR); } if (flags & DCMD_ADDRSPEC) tid = (mdb_tgt_tid_t)addr; else tid = PTL_TID(t); is_v9 = Pstatus(t->t_pshandle)->pr_dmodel == PR_MODEL_LP64; is_v8plus = is_v9 == 0 && PTL_GETXREGS(t, tid, &xrs) == 0 && xrs.pr_type == XR_TYPE_V8P; #ifdef __sparcv9 if (is_v9 && opts == FPR_MIXED) { if (PTL_GETREGS(t, tid, grs) == 0) mdb_printf("fprs %lx\n", grs[R_FPRS]); else mdb_warn("failed to read fprs register"); } #endif if (is_v8plus && opts == FPR_MIXED) mdb_printf("fprs %x\n", xrs.pr_un.pr_v8p.pr_fprs); if (PTL_GETFPREGS(t, tid, &fprs) != 0) { mdb_warn("failed to get floating point registers"); return (DCMD_ERR); } if (opts == FPR_MIXED) { uint64_t fsr = fprs.pr_fsr; if (is_v8plus) fsr |= (uint64_t)xrs.pr_un.pr_v8p.pr_xfsr << 32; mdb_printf("fsr %llx\n", fsr); } /* * Set up the regs pointer to be a pointer to a contiguous chunk of * memory containing all the floating pointer register data. Set * ns, nd, and nq to indicate the number of registers of each type. */ if (is_v9) { regs = fprs.pr_fr.pr_regs; ns = 64; nd = 32; nq = 16; } else if (is_v8plus) { regs = mdb_alloc(sizeof (uint32_t) * 64, UM_SLEEP | UM_GC); bcopy(fprs.pr_fr.pr_regs, regs, sizeof (uint32_t) * 32); bcopy(xrs.pr_un.pr_v8p.pr_xfr.pr_regs, regs + 32, sizeof (uint32_t) * 32); ns = 64; nd = 32; nq = 16; } else { regs = fprs.pr_fr.pr_regs; ns = 32; nd = 16; nq = 0; } if (opts == FPR_MIXED) { for (i = 0; i < ns; i++) { fpu.ip.i1 = regs[i]; mdb_printf("f%-3d %08x %e", i, fpu.ip.i1, fpu.f); if (i & 1) { fpu.ip.i1 = regs[i - 1]; fpu.ip.i2 = regs[i]; mdb_printf(" %g", fpu.d); } mdb_printf("\n"); } } if (opts & FPR_SINGLE) { for (i = 0; i < ns; i++) { fpu.ip.i1 = regs[i]; mdb_printf("f%-3d %08x %e\n", i, fpu.ip.i1, fpu.f); } } if (opts & FPR_DOUBLE) { for (i = 0; i < nd; i++) { fpu.ip.i1 = regs[i * 2 + 0]; fpu.ip.i2 = regs[i * 2 + 1]; mdb_printf("f%-3d %08x.%08x %g\n", i * 2, fpu.ip.i1, fpu.ip.i2, fpu.d); } } if (opts & FPR_QUAD) { for (i = 0; i < nq; i++) { fpu.ip.i1 = regs[i * 4 + 0]; fpu.ip.i2 = regs[i * 4 + 1]; fpu.ip.i3 = regs[i * 4 + 2]; fpu.ip.i4 = regs[i * 4 + 3]; mdb_printf("f%-3d %08x.%08x.%08x.%08x %s\n", i * 4, fpu.ip.i1, fpu.ip.i2, fpu.ip.i3, fpu.ip.i4, longdoubletos(&fpu.ld, 16, 'e')); } } return (DCMD_OK); }
/*ARGSUSED*/ static int look_xmap_nopgsz(void *data, const prxmap_t *pmp, const char *object_name, int last, int doswap) { struct totals *t = data; const pstatus_t *Psp = Pstatus(Pr); char mname[PATH_MAX]; char *lname = NULL; char *ln; static uintptr_t prev_vaddr; static size_t prev_size; static offset_t prev_offset; static int prev_mflags; static char *prev_lname; static char prev_mname[PATH_MAX]; static ulong_t prev_rss; static ulong_t prev_anon; static ulong_t prev_locked; static ulong_t prev_swap; int merged = 0; static int first = 1; ulong_t swap = 0; int kperpage; /* * Calculate swap reservations */ if (pmp->pr_mflags & MA_SHARED) { if (aflag && (pmp->pr_mflags & MA_NORESERVE) == 0) { /* Swap reserved for entire non-ism SHM */ swap = pmp->pr_size / pmp->pr_pagesize; } } else if (pmp->pr_mflags & MA_NORESERVE) { /* Swap reserved on fault for each anon page */ swap = pmp->pr_anon; } else if (pmp->pr_mflags & MA_WRITE) { /* Swap reserve for entire writable segment */ swap = pmp->pr_size / pmp->pr_pagesize; } /* * If the mapping is not anon or not part of the heap, make a name * for it. We don't want to report the heap as a.out's data. */ if (!(pmp->pr_mflags & MA_ANON) || pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase || pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) { lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname, mname, sizeof (mname)); } if (lname != NULL) { if ((ln = strrchr(lname, '/')) != NULL) lname = ln + 1; } else if ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD) { lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr, pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL); } kperpage = pmp->pr_pagesize / KILOBYTE; t->total_size += ROUNDUP_KB(pmp->pr_size); t->total_rss += pmp->pr_rss * kperpage; t->total_anon += ANON(pmp) * kperpage; t->total_locked += pmp->pr_locked * kperpage; t->total_swap += swap * kperpage; if (first == 1) { first = 0; prev_vaddr = pmp->pr_vaddr; prev_size = pmp->pr_size; prev_offset = pmp->pr_offset; prev_mflags = pmp->pr_mflags; if (lname == NULL) { prev_lname = NULL; } else { (void) strcpy(prev_mname, lname); prev_lname = prev_mname; } prev_rss = pmp->pr_rss * kperpage; prev_anon = ANON(pmp) * kperpage; prev_locked = pmp->pr_locked * kperpage; prev_swap = swap * kperpage; if (last == 0) { return (0); } merged = 1; } else if (prev_vaddr + prev_size == pmp->pr_vaddr && prev_mflags == pmp->pr_mflags && ((prev_mflags & MA_ISM) || prev_offset + prev_size == pmp->pr_offset) && ((lname == NULL && prev_lname == NULL) || (lname != NULL && prev_lname != NULL && strcmp(lname, prev_lname) == 0))) { prev_size += pmp->pr_size; prev_rss += pmp->pr_rss * kperpage; prev_anon += ANON(pmp) * kperpage; prev_locked += pmp->pr_locked * kperpage; prev_swap += swap * kperpage; if (last == 0) { return (0); } merged = 1; } (void) printf("%.*lX", addr_width, (ulong_t)prev_vaddr); printK(ROUNDUP_KB(prev_size), size_width); if (doswap) printK(prev_swap, size_width); else { printK(prev_rss, size_width); printK(prev_anon, size_width); printK(prev_locked, size_width); } (void) printf(prev_lname ? " %-6s %s\n" : "%s\n", mflags(prev_mflags), prev_lname); if (last == 0) { prev_vaddr = pmp->pr_vaddr; prev_size = pmp->pr_size; prev_offset = pmp->pr_offset; prev_mflags = pmp->pr_mflags; if (lname == NULL) { prev_lname = NULL; } else { (void) strcpy(prev_mname, lname); prev_lname = prev_mname; } prev_rss = pmp->pr_rss * kperpage; prev_anon = ANON(pmp) * kperpage; prev_locked = pmp->pr_locked * kperpage; prev_swap = swap * kperpage; } else if (merged == 0) { (void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr); printK(ROUNDUP_KB(pmp->pr_size), size_width); if (doswap) printK(swap * kperpage, size_width); else { printK(pmp->pr_rss * kperpage, size_width); printK(ANON(pmp) * kperpage, size_width); printK(pmp->pr_locked * kperpage, size_width); } (void) printf(lname ? " %-6s %s\n" : " %s\n", mflags(pmp->pr_mflags), lname); } if (last != 0) first = 1; return (0); }
int main(int argc, char **argv) { secflagdelta_t act; psecflagwhich_t which = PSF_INHERIT; int ret = 0; int pgrab_flags = PGRAB_RDONLY; int opt; char *idtypename = NULL; idtype_t idtype = P_PID; boolean_t usage = B_FALSE; boolean_t e_flag = B_FALSE; boolean_t l_flag = B_FALSE; boolean_t s_flag = B_FALSE; int errc = 0; while ((opt = getopt(argc, argv, "eFi:ls:")) != -1) { switch (opt) { case 'e': e_flag = B_TRUE; break; case 'F': pgrab_flags |= PGRAB_FORCE; break; case 'i': idtypename = optarg; break; case 's': s_flag = B_TRUE; if ((strlen(optarg) >= 2) && ((optarg[1] == '='))) { switch (optarg[0]) { case 'L': which = PSF_LOWER; break; case 'U': which = PSF_UPPER; break; case 'I': which = PSF_INHERIT; break; case 'E': errx(1, "the effective flags cannot " "be changed", optarg[0]); default: errx(1, "unknown security flag " "set: '%c'", optarg[0]); } optarg += 2; } if (secflags_parse(NULL, optarg, &act) == -1) errx(1, "couldn't parse security flags: %s", optarg); break; case 'l': l_flag = B_TRUE; break; default: usage = B_TRUE; break; } } argc -= optind; argv += optind; if (l_flag && ((idtypename != NULL) || s_flag || (argc != 0))) usage = B_TRUE; if ((idtypename != NULL) && !s_flag) usage = B_TRUE; if (e_flag && !s_flag) usage = B_TRUE; if (!l_flag && argc <= 0) usage = B_TRUE; if (usage) { (void) fprintf(stderr, gettext("usage:\t%s [-F] { pid | core } ...\n"), __progname); (void) fprintf(stderr, gettext("\t%s -s spec [-i idtype] id ...\n"), __progname); (void) fprintf(stderr, gettext("\t%s -s spec -e command [arg]...\n"), __progname); (void) fprintf(stderr, gettext("\t%s -l\n"), __progname); return (2); } if (l_flag) { secflag_t i; const char *name; for (i = 0; (name = secflag_to_str(i)) != NULL; i++) (void) printf("%s\n", name); return (0); } else if (s_flag && e_flag) { /* * Don't use the strerror() message for EPERM, "Not Owner" * which is misleading. */ errc = psecflags(P_PID, P_MYID, which, &act); switch (errc) { case 0: break; case EPERM: errx(1, gettext("failed setting " "security-flags: Permission denied")); break; default: err(1, gettext("failed setting security-flags")); } (void) execvp(argv[0], &argv[0]); err(1, "%s", argv[0]); } else if (s_flag) { int i; id_t id; if (idtypename != NULL) if (str2idtype(idtypename, &idtype) == -1) errx(1, gettext("No such id type: '%s'"), idtypename); for (i = 0; i < argc; i++) { if ((id = getid(idtype, argv[i])) == (id_t)-1) { errx(1, gettext("invalid or non-existent " "identifier: '%s'"), argv[i]); } /* * Don't use the strerror() message for EPERM, "Not * Owner" which is misleading. */ if (psecflags(idtype, id, which, &act) != 0) { switch (errno) { case EPERM: errx(1, gettext("failed setting " "security-flags: " "Permission denied")); break; default: err(1, gettext("failed setting " "security-flags")); } } } return (0); } /* Display the flags for the given pids */ while (argc-- > 0) { struct ps_prochandle *Pr; const char *arg; psinfo_t psinfo; prsecflags_t *psf; int gcode; if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY, pgrab_flags, &gcode)) == NULL) { warnx(gettext("cannot examine %s: %s"), arg, Pgrab_error(gcode)); ret = 1; continue; } (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t)); proc_unctrl_psinfo(&psinfo); if (Pstate(Pr) == PS_DEAD) { (void) printf(gettext("core '%s' of %d:\t%.70s\n"), arg, (int)psinfo.pr_pid, psinfo.pr_psargs); } else { (void) printf("%d:\t%.70s\n", (int)psinfo.pr_pid, psinfo.pr_psargs); } if (Psecflags(Pr, &psf) != 0) err(1, gettext("cannot read secflags of %s"), arg); print_flags("E", psf->pr_effective); print_flags("I", psf->pr_inherit); print_flags("L", psf->pr_lower); print_flags("U", psf->pr_upper); Psecflags_free(psf); Prelease(Pr, 0); } return (ret); }