425 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
		
			12 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 */
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/* 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));
 | |
| 
 | |
| 	/* 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);
 | |
| }
 | 
