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