371 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ProcFS - pid.c - generators for PID-specific files */
 | |
| 
 | |
| #include "inc.h"
 | |
| 
 | |
| #include <sys/mman.h>
 | |
| #include <minix/vm.h>
 | |
| 
 | |
| #define S_FRAME_SIZE	4096		/* use malloc if larger than this */
 | |
| static char s_frame[S_FRAME_SIZE];	/* static storage for process frame */
 | |
| static char *frame;			/* pointer to process frame buffer */
 | |
| 
 | |
| static void pid_psinfo(int slot);
 | |
| static void pid_cmdline(int slot);
 | |
| static void pid_environ(int slot);
 | |
| static 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.
 | |
|  */
 | |
| 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 the given slot a zombie process?
 | |
|  */
 | |
| static int
 | |
| is_zombie(int slot)
 | |
| {
 | |
| 
 | |
| 	return (slot >= NR_TASKS && 
 | |
| 		(mproc[slot - NR_TASKS].mp_flags & (TRACE_ZOMBIE | ZOMBIE)));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Print information used by ps(1) and top(1).
 | |
|  */
 | |
| static void
 | |
| pid_psinfo(int i)
 | |
| {
 | |
| 	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 & TRACE_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 | 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 (although 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)
 | |
| 	);
 | |
| 
 | |
| 	memset(&vui, 0, sizeof(vui));
 | |
| 
 | |
| 	if (!is_zombie(i)) {
 | |
| 		/* We don't care if this fails.  */
 | |
| 		(void)vm_info_usage(proc[i].p_endpoint, &vui);
 | |
| 	}
 | |
| 
 | |
| 	/* If the process is not a kernel task, we add some extra info. */
 | |
| 	if (!task) {
 | |
| 		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_OTHER:	f_state = FSTATE_TASK; break;
 | |
| 		default:			f_state = FSTATE_UNKNOWN;
 | |
| 		}
 | |
| 
 | |
| 		buf_printf(" %lu %lu %lu %c %d %u %u %u %d %c %d %llu",
 | |
| 		    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 */
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/* Always add kernel cycles. */
 | |
| 	buf_printf(" %lu %lu %lu %lu",
 | |
| 	    ex64hi(proc[i].p_kipc_cycles),
 | |
| 	    ex64lo(proc[i].p_kipc_cycles),
 | |
| 	    ex64hi(proc[i].p_kcall_cycles),
 | |
| 	    ex64lo(proc[i].p_kcall_cycles));
 | |
| 
 | |
| 	/* Add total memory for tasks at the end. */
 | |
| 	if (task)
 | |
| 		buf_printf(" %lu", vui.vui_total);
 | |
| 
 | |
| 	/* Newline at the end of the file. */
 | |
| 	buf_printf("\n");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * If we allocated memory dynamically during a call to get_frame(), free it up
 | |
|  * here.
 | |
|  */
 | |
| static void
 | |
| put_frame(void)
 | |
| {
 | |
| 
 | |
| 	if (frame != s_frame)
 | |
| 		free(frame);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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).
 | |
|  */
 | |
| static int
 | |
| get_frame(int slot, vir_bytes * basep, vir_bytes * sizep, size_t * nargsp)
 | |
| {
 | |
| 	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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Dump the process's command line as it is contained in the process itself.
 | |
|  * Each argument is terminated with a null character.
 | |
|  */
 | |
| static void
 | |
| pid_cmdline(int slot)
 | |
| {
 | |
| 	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();
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Dump the process's initial environment as it is contained in the process
 | |
|  * itself.  Each entry is terminated with a null character.
 | |
|  */
 | |
| static void
 | |
| pid_environ(int slot)
 | |
| {
 | |
| 	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();
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Print the virtual memory regions of a process.
 | |
|  */
 | |
| static void
 | |
| dump_regions(int slot)
 | |
| {
 | |
| 	struct vm_region_info vri[MAX_VRI_COUNT];
 | |
| 	vir_bytes next;
 | |
| 	int i, r, count;
 | |
| 
 | |
| 	count = 0;
 | |
| 	next = 0;
 | |
| 
 | |
| 	do {
 | |
| 		r = vm_info_region(proc[slot].p_endpoint, vri, MAX_VRI_COUNT,
 | |
| 		    &next);
 | |
| 
 | |
| 		if (r <= 0)
 | |
| 			break;
 | |
| 
 | |
| 		for (i = 0; i < r; i++) {
 | |
| 			buf_printf("%08lx-%08lx %c%c%c\n",
 | |
| 			    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' : '-');
 | |
| 
 | |
| 			count++;
 | |
| 		}
 | |
| 	} while (r == MAX_VRI_COUNT);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Print a memory map of the process.  Obtain the information from VM.
 | |
|  */
 | |
| static void
 | |
| pid_map(int slot)
 | |
| {
 | |
| 
 | |
| 	/* Zombies have no memory. */
 | |
| 	if (is_zombie(slot))
 | |
| 		return;
 | |
| 
 | |
| 	/* Kernel tasks also have no memory. */
 | |
| 	if (proc[slot].p_nr >= 0)
 | |
| 		dump_regions(slot);
 | |
| }
 | 
