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