xref: /minix/minix/fs/procfs/pid.c (revision 7f5f010b)
1 /* ProcFS - pid.c - by Alen Stojanov and David van Moolenbroek */
2 
3 #include "inc.h"
4 
5 #include <sys/mman.h>
6 #include <minix/vm.h>
7 
8 #define S_FRAME_SIZE	4096		/* use malloc if larger than this */
9 static char s_frame[S_FRAME_SIZE];	/* static storage for process frame */
10 static char *frame;			/* pointer to process frame buffer */
11 
12 static void pid_psinfo(int slot);
13 static void pid_cmdline(int slot);
14 static void pid_environ(int slot);
15 static void pid_map(int slot);
16 
17 /* The files that are dynamically created in each PID directory. The data field
18  * contains each file's read function. Subdirectories are not yet supported.
19  */
20 struct file pid_files[] = {
21 	{ "psinfo",	REG_ALL_MODE,	(data_t) pid_psinfo	},
22 	{ "cmdline",	REG_ALL_MODE,	(data_t) pid_cmdline	},
23 	{ "environ",	REG_ALL_MODE,	(data_t) pid_environ	},
24 	{ "map",	REG_ALL_MODE,	(data_t) pid_map	},
25 	{ NULL,		0,		(data_t) NULL		}
26 };
27 
28 /*===========================================================================*
29  *				is_zombie				     *
30  *===========================================================================*/
31 static int is_zombie(int slot)
32 {
33 	/* Is the given slot a zombie process?
34 	 */
35 
36 	return (slot >= NR_TASKS &&
37 		(mproc[slot - NR_TASKS].mp_flags & (TRACE_ZOMBIE | ZOMBIE)));
38 }
39 
40 /*===========================================================================*
41  *				pid_psinfo				     *
42  *===========================================================================*/
43 static void pid_psinfo(int i)
44 {
45 	/* Print information used by ps(1) and top(1).
46 	 */
47 	int pi, task, state, type, p_state, f_state;
48 	char name[PROC_NAME_LEN+1], *p;
49 	struct vm_usage_info vui;
50 	pid_t ppid;
51 
52 	pi = i - NR_TASKS;
53 	task = proc[i].p_nr < 0;
54 
55 	/* Get the name of the process. Spaces would mess up the format.. */
56 	if (task || mproc[i].mp_name[0] == 0)
57 		strncpy(name, proc[i].p_name, sizeof(name) - 1);
58 	else
59 		strncpy(name, mproc[pi].mp_name, sizeof(name) - 1);
60 	name[sizeof(name) - 1] = 0;
61 	if ((p = strchr(name, ' ')) != NULL)
62 		p[0] = 0;
63 
64 	/* Get the type of the process. */
65 	if (task)
66 		type = TYPE_TASK;
67 	else if (mproc[i].mp_flags & PRIV_PROC)
68 		type = TYPE_SYSTEM;
69 	else
70 		type = TYPE_USER;
71 
72 	/* Get the state of the process. */
73 	if (!task) {
74 		if (is_zombie(i))
75 			state = STATE_ZOMBIE;	/* zombie */
76 		else if (mproc[pi].mp_flags & TRACE_STOPPED)
77 			state = STATE_STOP;	/* stopped (traced) */
78 		else if (proc[i].p_rts_flags == 0)
79 			state = STATE_RUN;	/* in run-queue */
80 		else if (fp_is_blocked(&fproc[pi]) ||
81 		(mproc[pi].mp_flags & (WAITING | SIGSUSPENDED)))
82 			state = STATE_SLEEP;	/* sleeping */
83 		else
84 			state = STATE_WAIT;	/* waiting */
85 	} else {
86 		if (proc[i].p_rts_flags == 0)
87 			state = STATE_RUN;	/* in run-queue */
88 		else
89 			state = STATE_WAIT;	/* other i.e. waiting */
90 	}
91 
92 	/* We assume that even if a process has become a zombie, its kernel
93 	 * proc entry still contains the old (but valid) information. Currently
94 	 * this is true, but in the future we may have to filter some fields.
95 	 */
96 	buf_printf("%d %c %d %s %c %d %d %lu %lu %lu %lu",
97 		PSINFO_VERSION,			/* information version */
98 		type,				/* process type */
99 		(int) proc[i].p_endpoint,	/* process endpoint */
100 		name,				/* process name */
101 		state,				/* process state letter */
102 		(int) P_BLOCKEDON(&proc[i]),	/* endpt blocked on, or NONE */
103 		(int) proc[i].p_priority,	/* process priority */
104 		(long) proc[i].p_user_time,	/* user time */
105 		(long) proc[i].p_sys_time,	/* system time */
106 		ex64hi(proc[i].p_cycles),	/* execution cycles */
107 		ex64lo(proc[i].p_cycles)
108 	);
109 
110 	memset(&vui, 0, sizeof(vui));
111 
112 	if (!is_zombie(i)) {
113 		/* We don't care if this fails.  */
114 		(void) vm_info_usage(proc[i].p_endpoint, &vui);
115 	}
116 
117 	/* If the process is not a kernel task, we add some extra info. */
118 	if (!task) {
119 		if (mproc[pi].mp_flags & WAITING)
120 			p_state = PSTATE_WAITING;
121 		else if (mproc[pi].mp_flags & SIGSUSPENDED)
122 			p_state = PSTATE_SIGSUSP;
123 		else
124 			p_state = '-';
125 
126 		if (mproc[pi].mp_parent == pi)
127 			ppid = NO_PID;
128 		else
129 			ppid = mproc[mproc[pi].mp_parent].mp_pid;
130 
131 		switch (fproc[pi].fp_blocked_on) {
132 		case FP_BLOCKED_ON_NONE:	f_state = FSTATE_NONE; break;
133 		case FP_BLOCKED_ON_PIPE:	f_state = FSTATE_PIPE; break;
134 		case FP_BLOCKED_ON_LOCK:	f_state = FSTATE_LOCK; break;
135 		case FP_BLOCKED_ON_POPEN:	f_state = FSTATE_POPEN; break;
136 		case FP_BLOCKED_ON_SELECT:	f_state = FSTATE_SELECT; break;
137 		case FP_BLOCKED_ON_OTHER:	f_state = FSTATE_TASK; break;
138 		default:			f_state = FSTATE_UNKNOWN;
139 		}
140 
141 		buf_printf(" %lu %lu %lu %c %d %u %u %u %d %c %d %u",
142 			vui.vui_total,			/* total memory */
143 			vui.vui_common,			/* common memory */
144 			vui.vui_shared,			/* shared memory */
145 			p_state,			/* sleep state */
146 			ppid,				/* parent PID */
147 			mproc[pi].mp_realuid,		/* real UID */
148 			mproc[pi].mp_effuid,		/* effective UID */
149 			mproc[pi].mp_procgrp,		/* process group */
150 			mproc[pi].mp_nice,		/* nice value */
151 			f_state,			/* VFS block state */
152 			(int) (fproc[pi].fp_blocked_on == FP_BLOCKED_ON_OTHER)
153 				? fproc[pi].fp_task : NONE, /* block proc */
154 			fproc[pi].fp_tty		/* controlling tty */
155 		);
156 	}
157 
158 	/* always add kernel cycles */
159 	buf_printf(" %lu %lu %lu %lu",
160 		ex64hi(proc[i].p_kipc_cycles),
161 		ex64lo(proc[i].p_kipc_cycles),
162 		ex64hi(proc[i].p_kcall_cycles),
163 		ex64lo(proc[i].p_kcall_cycles));
164 
165 	/* add total memory for tasks at the end */
166 	if(task) buf_printf(" %lu", vui.vui_total);
167 
168 	/* Newline at the end of the file. */
169 	buf_printf("\n");
170 }
171 
172 /*===========================================================================*
173  *				put_frame				     *
174  *===========================================================================*/
175 static void put_frame(void)
176 {
177 	/* If we allocated memory dynamically during a call to get_frame(),
178 	 * free it up here.
179 	 */
180 
181 	if (frame != s_frame)
182 		free(frame);
183 }
184 
185 /*===========================================================================*
186  *				get_frame				     *
187  *===========================================================================*/
188 static int get_frame(int slot, vir_bytes *basep, vir_bytes *sizep,
189 	size_t *nargsp)
190 {
191 	/* Get the execution frame from the top of the given process's stack.
192 	 * It may be very large, in which case we temporarily allocate memory
193 	 * for it (up to a certain size).
194 	 */
195 	vir_bytes base, size;
196 	size_t nargs;
197 
198 	if (proc[slot].p_nr < 0 || is_zombie(slot))
199 		return FALSE;
200 
201 	/* Get the frame base address and size. Limit the size to whatever we
202 	 * can handle. If our static buffer is not sufficiently large to store
203 	 * the entire frame, allocate memory dynamically. It is then later
204 	 * freed by put_frame().
205 	 */
206 	base = mproc[slot - NR_TASKS].mp_frame_addr;
207 	size = mproc[slot - NR_TASKS].mp_frame_len;
208 
209 	if (size < sizeof(size_t)) return FALSE;
210 
211 	if (size > ARG_MAX) size = ARG_MAX;
212 
213 	if (size > sizeof(s_frame)) {
214 		frame = malloc(size);
215 
216 		if (frame == NULL)
217 			return FALSE;
218 	}
219 	else frame = s_frame;
220 
221 	/* Copy in the complete process frame. */
222 	if (sys_datacopy(proc[slot].p_endpoint, base,
223 			SELF, (vir_bytes) frame, (phys_bytes) size) != OK) {
224 		put_frame();
225 
226 		return FALSE;
227 	}
228 
229 	frame[size] = 0; /* terminate any last string */
230 
231 	nargs = * (size_t *) frame;
232 	if (nargs < 1 || sizeof(size_t) + sizeof(char *) * (nargs + 1) > size) {
233 		put_frame();
234 
235 		return FALSE;
236 	}
237 
238 	*basep = base;
239 	*sizep = size;
240 	*nargsp = nargs;
241 
242 	/* The caller now has to called put_frame() to clean up. */
243 	return TRUE;
244 }
245 
246 /*===========================================================================*
247  *				pid_cmdline				     *
248  *===========================================================================*/
249 static void pid_cmdline(int slot)
250 {
251 	/* Dump the process's command line as it is contained in the process
252 	 * itself. Each argument is terminated with a null character.
253 	 */
254 	vir_bytes base, size, ptr;
255 	size_t i, len, nargs;
256 	char **argv;
257 
258 	if (!get_frame(slot, &base, &size, &nargs))
259 		return;
260 
261 	argv = (char **) &frame[sizeof(size_t)];
262 
263 	for (i = 0; i < nargs; i++) {
264 		ptr = (vir_bytes) argv[i] - base;
265 
266 		/* Check for bad pointers. */
267 		if ((long) ptr < 0L || ptr >= size)
268 			break;
269 
270 		len = strlen(&frame[ptr]) + 1;
271 
272 		buf_append(&frame[ptr], len);
273 	}
274 
275 	put_frame();
276 }
277 
278 /*===========================================================================*
279  *				pid_environ				     *
280  *===========================================================================*/
281 static void pid_environ(int slot)
282 {
283 	/* Dump the process's initial environment as it is contained in the
284 	 * process itself. Each entry is terminated with a null character.
285 	 */
286 	vir_bytes base, size, ptr;
287 	size_t nargs, off, len;
288 	char **envp;
289 
290 	if (!get_frame(slot, &base, &size, &nargs))
291 		return;
292 
293 	off = sizeof(size_t) + sizeof(char *) * (nargs + 1);
294 	envp = (char **) &frame[off];
295 
296 	for (;;) {
297 		/* Make sure there is no buffer overrun. */
298 		if (off + sizeof(char *) > size)
299 			break;
300 
301 		ptr = (vir_bytes) *envp;
302 
303 		/* Stop at the terminating NULL pointer. */
304 		if (ptr == 0L)
305 			break;
306 
307 		ptr -= base;
308 
309 		/* Check for bad pointers. */
310 		if ((long) ptr < 0L || ptr >= size)
311 			break;
312 
313 		len = strlen(&frame[ptr]) + 1;
314 
315 		buf_append(&frame[ptr], len);
316 
317 		off += sizeof(char *);
318 		envp++;
319 	}
320 
321 	put_frame();
322 }
323 
324 /*===========================================================================*
325  *				dump_regions				     *
326  *===========================================================================*/
327 static int dump_regions(int slot)
328 {
329 	/* Print the virtual memory regions of a process.
330 	 */
331 	struct vm_region_info vri[MAX_VRI_COUNT];
332 	vir_bytes next;
333 	int i, r, count;
334 
335 	count = 0;
336 	next = 0;
337 
338 	do {
339 		r = vm_info_region(proc[slot].p_endpoint, vri, MAX_VRI_COUNT,
340 			&next);
341 
342 		if (r < 0)
343 			return r;
344 
345 		if (r == 0)
346 			break;
347 
348 		for (i = 0; i < r; i++) {
349 			buf_printf("%08lx-%08lx %c%c%c\n",
350 				vri[i].vri_addr, vri[i].vri_addr + vri[i].vri_length,
351 				(vri[i].vri_prot & PROT_READ) ? 'r' : '-',
352 				(vri[i].vri_prot & PROT_WRITE) ? 'w' : '-',
353 				(vri[i].vri_prot & PROT_EXEC) ? 'x' : '-');
354 
355 			count++;
356 		}
357 	} while (r == MAX_VRI_COUNT);
358 
359 	return count;
360 }
361 
362 /*===========================================================================*
363  *				pid_map					     *
364  *===========================================================================*/
365 static void pid_map(int slot)
366 {
367 	/* Print a memory map of the process. Obtain the information from VM if
368 	 * possible; otherwise fall back on segments from the kernel.
369 	 */
370 
371 	/* Zombies have no memory. */
372 	if (is_zombie(slot))
373 		return;
374 
375 	/* Kernel tasks also have no memory. */
376 	if (proc[slot].p_nr >= 0) {
377 		if (dump_regions(slot) != 0)
378 			return;
379 	}
380 }
381