xref: /minix/minix/fs/procfs/pid.c (revision 9f988b79)
1 /* ProcFS - pid.c - generators for PID-specific files */
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 /*
18  * The files that are dynamically created in each PID directory.  The data
19  * field contains each file's read function.  Subdirectories are not yet
20  * supported.
21  */
22 struct file pid_files[] = {
23 	{ "psinfo",	REG_ALL_MODE,	(data_t) pid_psinfo	},
24 	{ "cmdline",	REG_ALL_MODE,	(data_t) pid_cmdline	},
25 	{ "environ",	REG_ALL_MODE,	(data_t) pid_environ	},
26 	{ "map",	REG_ALL_MODE,	(data_t) pid_map	},
27 	{ NULL,		0,		(data_t) NULL		}
28 };
29 
30 /*
31  * Is the given slot a zombie process?
32  */
33 static int
34 is_zombie(int slot)
35 {
36 
37 	return (slot >= NR_TASKS &&
38 		(mproc[slot - NR_TASKS].mp_flags & (TRACE_ZOMBIE | ZOMBIE)));
39 }
40 
41 /*
42  * Print information used by ps(1) and top(1).
43  */
44 static void
45 pid_psinfo(int i)
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 (although valid) information.
94 	 * Currently this is true, but in the future we may have to filter some
95 	 * fields.
96 	 */
97 	buf_printf("%d %c %d %s %c %d %d %lu %lu %lu %lu",
98 	    PSINFO_VERSION,			/* information version */
99 	    type,				/* process type */
100 	    (int)proc[i].p_endpoint,		/* process endpoint */
101 	    name,				/* process name */
102 	    state,				/* process state letter */
103 	    (int)P_BLOCKEDON(&proc[i]),		/* endpt blocked on, or NONE */
104 	    (int)proc[i].p_priority,		/* process priority */
105 	    (long)proc[i].p_user_time,		/* user time */
106 	    (long)proc[i].p_sys_time,		/* system time */
107 	    ex64hi(proc[i].p_cycles),		/* execution cycles */
108 	    ex64lo(proc[i].p_cycles)
109 	);
110 
111 	memset(&vui, 0, sizeof(vui));
112 
113 	if (!is_zombie(i)) {
114 		/* We don't care if this fails.  */
115 		(void)vm_info_usage(proc[i].p_endpoint, &vui);
116 	}
117 
118 	/* If the process is not a kernel task, we add some extra info. */
119 	if (!task) {
120 		if (mproc[pi].mp_flags & WAITING)
121 			p_state = PSTATE_WAITING;
122 		else if (mproc[pi].mp_flags & SIGSUSPENDED)
123 			p_state = PSTATE_SIGSUSP;
124 		else
125 			p_state = '-';
126 
127 		if (mproc[pi].mp_parent == pi)
128 			ppid = NO_PID;
129 		else
130 			ppid = mproc[mproc[pi].mp_parent].mp_pid;
131 
132 		switch (fproc[pi].fp_blocked_on) {
133 		case FP_BLOCKED_ON_NONE:	f_state = FSTATE_NONE; break;
134 		case FP_BLOCKED_ON_PIPE:	f_state = FSTATE_PIPE; break;
135 		case FP_BLOCKED_ON_LOCK:	f_state = FSTATE_LOCK; break;
136 		case FP_BLOCKED_ON_POPEN:	f_state = FSTATE_POPEN; break;
137 		case FP_BLOCKED_ON_SELECT:	f_state = FSTATE_SELECT; break;
138 		case FP_BLOCKED_ON_OTHER:	f_state = FSTATE_TASK; break;
139 		default:			f_state = FSTATE_UNKNOWN;
140 		}
141 
142 		buf_printf(" %lu %lu %lu %c %d %u %u %u %d %c %d %llu",
143 		    vui.vui_total,			/* total memory */
144 		    vui.vui_common,			/* common memory */
145 		    vui.vui_shared,			/* shared memory */
146 		    p_state,				/* sleep state */
147 		    ppid,				/* parent PID */
148 		    mproc[pi].mp_realuid,		/* real UID */
149 		    mproc[pi].mp_effuid,		/* effective UID */
150 		    mproc[pi].mp_procgrp,		/* process group */
151 		    mproc[pi].mp_nice,			/* nice value */
152 		    f_state,				/* VFS block state */
153 		    (int)(fproc[pi].fp_blocked_on == FP_BLOCKED_ON_OTHER) ?
154 			fproc[pi].fp_task : NONE,	/* block proc */
155 		    fproc[pi].fp_tty			/* controlling tty */
156 		);
157 	}
158 
159 	/* Always add kernel cycles. */
160 	buf_printf(" %lu %lu %lu %lu",
161 	    ex64hi(proc[i].p_kipc_cycles),
162 	    ex64lo(proc[i].p_kipc_cycles),
163 	    ex64hi(proc[i].p_kcall_cycles),
164 	    ex64lo(proc[i].p_kcall_cycles));
165 
166 	/* Add total memory for tasks at the end. */
167 	if (task)
168 		buf_printf(" %lu", vui.vui_total);
169 
170 	/* Newline at the end of the file. */
171 	buf_printf("\n");
172 }
173 
174 /*
175  * If we allocated memory dynamically during a call to get_frame(), free it up
176  * here.
177  */
178 static void
179 put_frame(void)
180 {
181 
182 	if (frame != s_frame)
183 		free(frame);
184 }
185 
186 /*
187  * Get the execution frame from the top of the given process's stack.  It may
188  * be very large, in which case we temporarily allocate memory for it (up to a
189  * certain size).
190  */
191 static int
192 get_frame(int slot, vir_bytes * basep, vir_bytes * sizep, size_t * nargsp)
193 {
194 	vir_bytes base, size;
195 	size_t nargs;
196 
197 	if (proc[slot].p_nr < 0 || is_zombie(slot))
198 		return FALSE;
199 
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 	} else
219 		frame = s_frame;
220 
221 	/* Copy in the complete process frame. */
222 	if (sys_datacopy(proc[slot].p_endpoint, base, SELF, (vir_bytes)frame,
223 	    (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 ||
233 	    sizeof(size_t) + sizeof(char *) * (nargs + 1) > size) {
234 		put_frame();
235 
236 		return FALSE;
237 	}
238 
239 	*basep = base;
240 	*sizep = size;
241 	*nargsp = nargs;
242 
243 	/* The caller now has to called put_frame() to clean up. */
244 	return TRUE;
245 }
246 
247 /*
248  * Dump the process's command line as it is contained in the process itself.
249  * Each argument is terminated with a null character.
250  */
251 static void
252 pid_cmdline(int slot)
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  * Dump the process's initial environment as it is contained in the process
280  * itself.  Each entry is terminated with a null character.
281  */
282 static void
283 pid_environ(int slot)
284 {
285 	vir_bytes base, size, ptr;
286 	size_t nargs, off, len;
287 	char **envp;
288 
289 	if (!get_frame(slot, &base, &size, &nargs))
290 		return;
291 
292 	off = sizeof(size_t) + sizeof(char *) * (nargs + 1);
293 	envp = (char **)&frame[off];
294 
295 	for (;;) {
296 		/* Make sure there is no buffer overrun. */
297 		if (off + sizeof(char *) > size)
298 			break;
299 
300 		ptr = (vir_bytes) *envp;
301 
302 		/* Stop at the terminating NULL pointer. */
303 		if (ptr == 0L)
304 			break;
305 
306 		ptr -= base;
307 
308 		/* Check for bad pointers. */
309 		if ((long)ptr < 0L || ptr >= size)
310 			break;
311 
312 		len = strlen(&frame[ptr]) + 1;
313 
314 		buf_append(&frame[ptr], len);
315 
316 		off += sizeof(char *);
317 		envp++;
318 	}
319 
320 	put_frame();
321 }
322 
323 /*
324  * Print the virtual memory regions of a process.
325  */
326 static void
327 dump_regions(int slot)
328 {
329 	struct vm_region_info vri[MAX_VRI_COUNT];
330 	vir_bytes next;
331 	int i, r, count;
332 
333 	count = 0;
334 	next = 0;
335 
336 	do {
337 		r = vm_info_region(proc[slot].p_endpoint, vri, MAX_VRI_COUNT,
338 		    &next);
339 
340 		if (r <= 0)
341 			break;
342 
343 		for (i = 0; i < r; i++) {
344 			buf_printf("%08lx-%08lx %c%c%c\n",
345 			    vri[i].vri_addr,
346 			    vri[i].vri_addr + vri[i].vri_length,
347 			    (vri[i].vri_prot & PROT_READ) ? 'r' : '-',
348 			    (vri[i].vri_prot & PROT_WRITE) ? 'w' : '-',
349 			    (vri[i].vri_prot & PROT_EXEC) ? 'x' : '-');
350 
351 			count++;
352 		}
353 	} while (r == MAX_VRI_COUNT);
354 }
355 
356 /*
357  * Print a memory map of the process.  Obtain the information from VM.
358  */
359 static void
360 pid_map(int slot)
361 {
362 
363 	/* Zombies have no memory. */
364 	if (is_zombie(slot))
365 		return;
366 
367 	/* Kernel tasks also have no memory. */
368 	if (proc[slot].p_nr >= 0)
369 		dump_regions(slot);
370 }
371