418 lines
11 KiB
C
418 lines
11 KiB
C
|
/* ProcFS - pid.c - by Alen Stojanov and David van Moolenbroek */
|
||
|
|
||
|
#include "inc.h"
|
||
|
|
||
|
#include <sys/mman.h>
|
||
|
#include <minix/vm.h>
|
||
|
|
||
|
#define S_FRAME_SIZE 4096 /* use malloc if larger than this */
|
||
|
PRIVATE char s_frame[S_FRAME_SIZE]; /* static storage for process frame */
|
||
|
PRIVATE char *frame; /* pointer to process frame buffer */
|
||
|
|
||
|
FORWARD _PROTOTYPE( void pid_psinfo, (int slot) );
|
||
|
FORWARD _PROTOTYPE( void pid_cmdline, (int slot) );
|
||
|
FORWARD _PROTOTYPE( void pid_environ, (int slot) );
|
||
|
FORWARD _PROTOTYPE( void pid_map, (int slot) );
|
||
|
|
||
|
/* The files that are dynamically created in each PID directory. The data field
|
||
|
* contains each file's read function. Subdirectories are not yet supported.
|
||
|
*/
|
||
|
PUBLIC struct file pid_files[] = {
|
||
|
{ "psinfo", REG_ALL_MODE, (data_t) pid_psinfo },
|
||
|
{ "cmdline", REG_ALL_MODE, (data_t) pid_cmdline },
|
||
|
{ "environ", REG_ALL_MODE, (data_t) pid_environ },
|
||
|
{ "map", REG_ALL_MODE, (data_t) pid_map },
|
||
|
{ NULL, 0, (data_t) NULL }
|
||
|
};
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* is_zombie *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE int is_zombie(int slot)
|
||
|
{
|
||
|
/* Is the given slot a zombie process?
|
||
|
*/
|
||
|
|
||
|
return (slot >= NR_TASKS &&
|
||
|
(mproc[slot - NR_TASKS].mp_flags & (TRACE_ZOMBIE | ZOMBIE)));
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* pid_psinfo *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE void pid_psinfo(int i)
|
||
|
{
|
||
|
/* Print information used by ps(1) and top(1).
|
||
|
*/
|
||
|
int pi, task, state, type, p_state, f_state;
|
||
|
char name[PROC_NAME_LEN+1], *p;
|
||
|
struct vm_usage_info vui;
|
||
|
pid_t ppid;
|
||
|
|
||
|
pi = i - NR_TASKS;
|
||
|
task = proc[i].p_nr < 0;
|
||
|
|
||
|
/* Get the name of the process. Spaces would mess up the format.. */
|
||
|
if (task || mproc[i].mp_name[0] == 0)
|
||
|
strncpy(name, proc[i].p_name, sizeof(name) - 1);
|
||
|
else
|
||
|
strncpy(name, mproc[pi].mp_name, sizeof(name) - 1);
|
||
|
name[sizeof(name) - 1] = 0;
|
||
|
if ((p = strchr(name, ' ')) != NULL)
|
||
|
p[0] = 0;
|
||
|
|
||
|
/* Get the type of the process. */
|
||
|
if (task)
|
||
|
type = TYPE_TASK;
|
||
|
else if (mproc[i].mp_flags & PRIV_PROC)
|
||
|
type = TYPE_SYSTEM;
|
||
|
else
|
||
|
type = TYPE_USER;
|
||
|
|
||
|
/* Get the state of the process. */
|
||
|
if (!task) {
|
||
|
if (is_zombie(i))
|
||
|
state = STATE_ZOMBIE; /* zombie */
|
||
|
else if (mproc[pi].mp_flags & STOPPED)
|
||
|
state = STATE_STOP; /* stopped (traced) */
|
||
|
else if (proc[i].p_rts_flags == 0)
|
||
|
state = STATE_RUN; /* in run-queue */
|
||
|
else if (fp_is_blocked(&fproc[pi]) ||
|
||
|
(mproc[pi].mp_flags & (WAITING | PAUSED | SIGSUSPENDED)))
|
||
|
state = STATE_SLEEP; /* sleeping */
|
||
|
else
|
||
|
state = STATE_WAIT; /* waiting */
|
||
|
} else {
|
||
|
if (proc[i].p_rts_flags == 0)
|
||
|
state = STATE_RUN; /* in run-queue */
|
||
|
else
|
||
|
state = STATE_WAIT; /* other i.e. waiting */
|
||
|
}
|
||
|
|
||
|
/* We assume that even if a process has become a zombie, its kernel
|
||
|
* proc entry still contains the old (but valid) information. Currently
|
||
|
* this is true, but in the future we may have to filter some fields.
|
||
|
*/
|
||
|
buf_printf("%d %c %d %s %c %d %d %lu %lu %lu %lu",
|
||
|
PSINFO_VERSION, /* information version */
|
||
|
type, /* process type */
|
||
|
(int) proc[i].p_endpoint, /* process endpoint */
|
||
|
name, /* process name */
|
||
|
state, /* process state letter */
|
||
|
(int) P_BLOCKEDON(&proc[i]), /* endpt blocked on, or NONE */
|
||
|
(int) proc[i].p_priority, /* process priority */
|
||
|
(long) proc[i].p_user_time, /* user time */
|
||
|
(long) proc[i].p_sys_time, /* system time */
|
||
|
ex64hi(proc[i].p_cycles), /* execution cycles */
|
||
|
ex64lo(proc[i].p_cycles)
|
||
|
);
|
||
|
|
||
|
/* If the process is not a kernel task, we add some extra info. */
|
||
|
if (!task) {
|
||
|
memset(&vui, 0, sizeof(vui));
|
||
|
|
||
|
if (!is_zombie(i)) {
|
||
|
/* We don't care if this fails. It may still return
|
||
|
* zero memory usage for processes that don't have a
|
||
|
* pagetable, though. Look at vui_total instead.
|
||
|
*/
|
||
|
(void) vm_info_usage(proc[i].p_endpoint, &vui);
|
||
|
|
||
|
if (vui.vui_total == 0L) {
|
||
|
vui.vui_total =
|
||
|
(proc[i].p_memmap[T].mem_len +
|
||
|
proc[i].p_memmap[D].mem_len) <<
|
||
|
CLICK_SHIFT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mproc[pi].mp_flags & PAUSED)
|
||
|
p_state = PSTATE_PAUSED;
|
||
|
else if (mproc[pi].mp_flags & WAITING)
|
||
|
p_state = PSTATE_WAITING;
|
||
|
else if (mproc[pi].mp_flags & SIGSUSPENDED)
|
||
|
p_state = PSTATE_SIGSUSP;
|
||
|
else
|
||
|
p_state = '-';
|
||
|
|
||
|
if (mproc[pi].mp_parent == pi)
|
||
|
ppid = NO_PID;
|
||
|
else
|
||
|
ppid = mproc[mproc[pi].mp_parent].mp_pid;
|
||
|
|
||
|
switch (fproc[pi].fp_blocked_on) {
|
||
|
case FP_BLOCKED_ON_NONE: f_state = FSTATE_NONE; break;
|
||
|
case FP_BLOCKED_ON_PIPE: f_state = FSTATE_PIPE; break;
|
||
|
case FP_BLOCKED_ON_LOCK: f_state = FSTATE_LOCK; break;
|
||
|
case FP_BLOCKED_ON_POPEN: f_state = FSTATE_POPEN; break;
|
||
|
case FP_BLOCKED_ON_SELECT: f_state = FSTATE_SELECT; break;
|
||
|
case FP_BLOCKED_ON_DOPEN: f_state = FSTATE_DOPEN; break;
|
||
|
case FP_BLOCKED_ON_OTHER: f_state = FSTATE_TASK; break;
|
||
|
default: f_state = FSTATE_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
buf_printf(" %lu %lu %lu %c %d %u %u %u %d %c %d %u",
|
||
|
vui.vui_total, /* total memory */
|
||
|
vui.vui_common, /* common memory */
|
||
|
vui.vui_shared, /* shared memory */
|
||
|
p_state, /* sleep state */
|
||
|
ppid, /* parent PID */
|
||
|
mproc[pi].mp_realuid, /* real UID */
|
||
|
mproc[pi].mp_effuid, /* effective UID */
|
||
|
mproc[pi].mp_procgrp, /* process group */
|
||
|
mproc[pi].mp_nice, /* nice value */
|
||
|
f_state, /* VFS block state */
|
||
|
(int) (fproc[pi].fp_blocked_on == FP_BLOCKED_ON_OTHER)
|
||
|
? fproc[pi].fp_task : NONE, /* block proc */
|
||
|
fproc[pi].fp_tty /* controlling tty */
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/* Newline at the end of the file. */
|
||
|
buf_printf("\n");
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* put_frame *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE void put_frame(void)
|
||
|
{
|
||
|
/* If we allocated memory dynamically during a call to get_frame(),
|
||
|
* free it up here.
|
||
|
*/
|
||
|
|
||
|
if (frame != s_frame)
|
||
|
free(frame);
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* get_frame *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE int get_frame(int slot, vir_bytes *basep, vir_bytes *sizep,
|
||
|
size_t *nargsp)
|
||
|
{
|
||
|
/* Get the execution frame from the top of the given process's stack.
|
||
|
* It may be very large, in which case we temporarily allocate memory
|
||
|
* for it (up to a certain size).
|
||
|
*/
|
||
|
vir_bytes base, size;
|
||
|
size_t nargs;
|
||
|
|
||
|
if (proc[slot].p_nr < 0 || is_zombie(slot))
|
||
|
return FALSE;
|
||
|
|
||
|
/* Get the frame base address and size. Limit the size to whatever we
|
||
|
* can handle. If our static buffer is not sufficiently large to store
|
||
|
* the entire frame, allocate memory dynamically. It is then later
|
||
|
* freed by put_frame().
|
||
|
*/
|
||
|
base = mproc[slot - NR_TASKS].mp_frame_addr;
|
||
|
size = mproc[slot - NR_TASKS].mp_frame_len;
|
||
|
|
||
|
if (size < sizeof(size_t)) return FALSE;
|
||
|
|
||
|
if (size > ARG_MAX) size = ARG_MAX;
|
||
|
|
||
|
if (size > sizeof(s_frame)) {
|
||
|
frame = malloc(size);
|
||
|
|
||
|
if (frame == NULL)
|
||
|
return FALSE;
|
||
|
}
|
||
|
else frame = s_frame;
|
||
|
|
||
|
/* Copy in the complete process frame. */
|
||
|
if (sys_datacopy(proc[slot].p_endpoint, base,
|
||
|
SELF, (vir_bytes) frame, (phys_bytes) size) != OK) {
|
||
|
put_frame();
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
frame[size] = 0; /* terminate any last string */
|
||
|
|
||
|
nargs = * (size_t *) frame;
|
||
|
if (nargs < 1 || sizeof(size_t) + sizeof(char *) * (nargs + 1) > size) {
|
||
|
put_frame();
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
*basep = base;
|
||
|
*sizep = size;
|
||
|
*nargsp = nargs;
|
||
|
|
||
|
/* The caller now has to called put_frame() to clean up. */
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* pid_cmdline *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE void pid_cmdline(int slot)
|
||
|
{
|
||
|
/* Dump the process's command line as it is contained in the process
|
||
|
* itself. Each argument is terminated with a null character.
|
||
|
*/
|
||
|
vir_bytes base, size, ptr;
|
||
|
size_t i, len, nargs;
|
||
|
char **argv;
|
||
|
|
||
|
if (!get_frame(slot, &base, &size, &nargs))
|
||
|
return;
|
||
|
|
||
|
argv = (char **) &frame[sizeof(size_t)];
|
||
|
|
||
|
for (i = 0; i < nargs; i++) {
|
||
|
ptr = (vir_bytes) argv[i] - base;
|
||
|
|
||
|
/* Check for bad pointers. */
|
||
|
if ((long) ptr < 0L || ptr >= size)
|
||
|
break;
|
||
|
|
||
|
len = strlen(&frame[ptr]) + 1;
|
||
|
|
||
|
buf_append(&frame[ptr], len);
|
||
|
}
|
||
|
|
||
|
put_frame();
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* pid_environ *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE void pid_environ(int slot)
|
||
|
{
|
||
|
/* Dump the process's initial environment as it is contained in the
|
||
|
* process itself. Each entry is terminated with a null character.
|
||
|
*/
|
||
|
vir_bytes base, size, ptr;
|
||
|
size_t nargs, off, len;
|
||
|
char **envp;
|
||
|
|
||
|
if (!get_frame(slot, &base, &size, &nargs))
|
||
|
return;
|
||
|
|
||
|
off = sizeof(size_t) + sizeof(char *) * (nargs + 1);
|
||
|
envp = (char **) &frame[off];
|
||
|
|
||
|
for (;;) {
|
||
|
/* Make sure there is no buffer overrun. */
|
||
|
if (off + sizeof(char *) > size)
|
||
|
break;
|
||
|
|
||
|
ptr = (vir_bytes) *envp;
|
||
|
|
||
|
/* Stop at the terminating NULL pointer. */
|
||
|
if (ptr == 0L)
|
||
|
break;
|
||
|
|
||
|
ptr -= base;
|
||
|
|
||
|
/* Check for bad pointers. */
|
||
|
if ((long) ptr < 0L || ptr >= size)
|
||
|
break;
|
||
|
|
||
|
len = strlen(&frame[ptr]) + 1;
|
||
|
|
||
|
buf_append(&frame[ptr], len);
|
||
|
|
||
|
off += sizeof(char *);
|
||
|
envp++;
|
||
|
}
|
||
|
|
||
|
put_frame();
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* dump_regions *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE int dump_regions(int slot)
|
||
|
{
|
||
|
/* Print the virtual memory regions of a process.
|
||
|
*/
|
||
|
struct vm_region_info vri[MAX_VRI_COUNT];
|
||
|
vir_bytes next;
|
||
|
int i, r, seg, count;
|
||
|
|
||
|
count = 0;
|
||
|
next = 0;
|
||
|
|
||
|
do {
|
||
|
r = vm_info_region(proc[slot].p_endpoint, vri, MAX_VRI_COUNT,
|
||
|
&next);
|
||
|
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
if (r == 0)
|
||
|
break;
|
||
|
|
||
|
for (i = 0; i < r; i++) {
|
||
|
switch (vri[i].vri_seg) {
|
||
|
case T: seg = 'T'; break;
|
||
|
case D: seg = 'D'; break;
|
||
|
default: seg = '?'; break;
|
||
|
}
|
||
|
|
||
|
buf_printf("%c %08lx-%08lx %c%c%c %c\n",
|
||
|
seg, vri[i].vri_addr,
|
||
|
vri[i].vri_addr + vri[i].vri_length,
|
||
|
(vri[i].vri_prot & PROT_READ) ? 'r' : '-',
|
||
|
(vri[i].vri_prot & PROT_WRITE) ? 'w' : '-',
|
||
|
(vri[i].vri_prot & PROT_EXEC) ? 'x' : '-',
|
||
|
(vri[i].vri_flags & MAP_SHARED) ? 's' : 'p');
|
||
|
|
||
|
count++;
|
||
|
}
|
||
|
} while (r == MAX_VRI_COUNT);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* dump_segments *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE void dump_segments(int slot)
|
||
|
{
|
||
|
/* Print the memory segments of a process.
|
||
|
*/
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < NR_LOCAL_SEGS; i++) {
|
||
|
buf_printf("%c %08lx-%08lx %s -\n",
|
||
|
i == T ? 'T' : 'D',
|
||
|
proc[slot].p_memmap[i].mem_vir << CLICK_SHIFT,
|
||
|
(proc[slot].p_memmap[i].mem_vir +
|
||
|
proc[slot].p_memmap[i].mem_len) << CLICK_SHIFT,
|
||
|
(i == T) ? "r-x" :
|
||
|
(proc[slot].p_memmap[T].mem_len == 0) ? "rwx" : "rw-");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*===========================================================================*
|
||
|
* pid_map *
|
||
|
*===========================================================================*/
|
||
|
PRIVATE void pid_map(int slot)
|
||
|
{
|
||
|
/* Print a memory map of the process. Obtain the information from VM if
|
||
|
* possible; otherwise fall back on segments from the kernel.
|
||
|
*/
|
||
|
|
||
|
/* Zombies have no memory. */
|
||
|
if (is_zombie(slot))
|
||
|
return;
|
||
|
|
||
|
/* Kernel tasks also have no memory. */
|
||
|
if (proc[slot].p_nr >= 0) {
|
||
|
if (dump_regions(slot) != 0)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* For kernel tasks, or for processes that have no regions according to
|
||
|
* VM, we assume they are not using virtual memory, and we print their
|
||
|
* segments instead.
|
||
|
*/
|
||
|
dump_segments(slot);
|
||
|
}
|