13 |
|
|
14 |
|
|
15 |
typedef struct unsigned_to_name_map_t { |
typedef struct unsigned_to_name_map_t { |
16 |
unsigned id; |
long id; |
17 |
char name[USERNAME_MAX_SIZE]; |
char name[USERNAME_MAX_SIZE]; |
18 |
} unsigned_to_name_map_t; |
} unsigned_to_name_map_t; |
19 |
|
|
52 |
} |
} |
53 |
#endif |
#endif |
54 |
|
|
55 |
typedef char* FAST_FUNC ug_func(char *name, int bufsize, long uid); |
static char* get_cached(cache_t *cp, long id, |
56 |
static char* get_cached(cache_t *cp, unsigned id, ug_func* fp) |
char* FAST_FUNC x2x_utoa(long id)) |
57 |
{ |
{ |
58 |
int i; |
int i; |
59 |
for (i = 0; i < cp->size; i++) |
for (i = 0; i < cp->size; i++) |
63 |
cp->cache = xrealloc_vector(cp->cache, 2, i); |
cp->cache = xrealloc_vector(cp->cache, 2, i); |
64 |
cp->cache[i].id = id; |
cp->cache[i].id = id; |
65 |
/* Never fails. Generates numeric string if name isn't found */ |
/* Never fails. Generates numeric string if name isn't found */ |
66 |
fp(cp->cache[i].name, sizeof(cp->cache[i].name), id); |
safe_strncpy(cp->cache[i].name, x2x_utoa(id), sizeof(cp->cache[i].name)); |
67 |
return cp->cache[i].name; |
return cp->cache[i].name; |
68 |
} |
} |
69 |
const char* FAST_FUNC get_cached_username(uid_t uid) |
const char* FAST_FUNC get_cached_username(uid_t uid) |
70 |
{ |
{ |
71 |
return get_cached(&username, uid, bb_getpwuid); |
return get_cached(&username, uid, uid2uname_utoa); |
72 |
} |
} |
73 |
const char* FAST_FUNC get_cached_groupname(gid_t gid) |
const char* FAST_FUNC get_cached_groupname(gid_t gid) |
74 |
{ |
{ |
75 |
return get_cached(&groupname, gid, bb_getgrgid); |
return get_cached(&groupname, gid, gid2group_utoa); |
76 |
} |
} |
77 |
|
|
78 |
|
|
110 |
void FAST_FUNC free_procps_scan(procps_status_t* sp) |
void FAST_FUNC free_procps_scan(procps_status_t* sp) |
111 |
{ |
{ |
112 |
closedir(sp->dir); |
closedir(sp->dir); |
113 |
|
#if ENABLE_FEATURE_SHOW_THREADS |
114 |
|
if (sp->task_dir) |
115 |
|
closedir(sp->task_dir); |
116 |
|
#endif |
117 |
free(sp->argv0); |
free(sp->argv0); |
118 |
USE_SELINUX(free(sp->context);) |
free(sp->exe); |
119 |
|
IF_SELINUX(free(sp->context);) |
120 |
free(sp); |
free(sp); |
121 |
} |
} |
122 |
|
|
138 |
return n; |
return n; |
139 |
} |
} |
140 |
/* TOPMEM uses fast_strtoul_10, so... */ |
/* TOPMEM uses fast_strtoul_10, so... */ |
141 |
#undef ENABLE_FEATURE_FAST_TOP |
# undef ENABLE_FEATURE_FAST_TOP |
142 |
#define ENABLE_FEATURE_FAST_TOP 1 |
# define ENABLE_FEATURE_FAST_TOP 1 |
143 |
#endif |
#endif |
144 |
|
|
145 |
#if ENABLE_FEATURE_FAST_TOP |
#if ENABLE_FEATURE_FAST_TOP |
156 |
*endptr = str + 1; /* We skip trailing space! */ |
*endptr = str + 1; /* We skip trailing space! */ |
157 |
return n; |
return n; |
158 |
} |
} |
159 |
|
|
160 |
|
static long fast_strtol_10(char **endptr) |
161 |
|
{ |
162 |
|
if (**endptr != '-') |
163 |
|
return fast_strtoul_10(endptr); |
164 |
|
|
165 |
|
(*endptr)++; |
166 |
|
return - (long)fast_strtoul_10(endptr); |
167 |
|
} |
168 |
|
|
169 |
static char *skip_fields(char *str, int count) |
static char *skip_fields(char *str, int count) |
170 |
{ |
{ |
171 |
do { |
do { |
193 |
sp = alloc_procps_scan(); |
sp = alloc_procps_scan(); |
194 |
|
|
195 |
for (;;) { |
for (;;) { |
196 |
|
#if ENABLE_FEATURE_SHOW_THREADS |
197 |
|
if ((flags & PSSCAN_TASKS) && sp->task_dir) { |
198 |
|
entry = readdir(sp->task_dir); |
199 |
|
if (entry) |
200 |
|
goto got_entry; |
201 |
|
closedir(sp->task_dir); |
202 |
|
sp->task_dir = NULL; |
203 |
|
} |
204 |
|
#endif |
205 |
entry = readdir(sp->dir); |
entry = readdir(sp->dir); |
206 |
if (entry == NULL) { |
if (entry == NULL) { |
207 |
free_procps_scan(sp); |
free_procps_scan(sp); |
208 |
return NULL; |
return NULL; |
209 |
} |
} |
210 |
|
IF_FEATURE_SHOW_THREADS(got_entry:) |
211 |
pid = bb_strtou(entry->d_name, NULL, 10); |
pid = bb_strtou(entry->d_name, NULL, 10); |
212 |
if (errno) |
if (errno) |
213 |
continue; |
continue; |
214 |
|
#if ENABLE_FEATURE_SHOW_THREADS |
215 |
|
if ((flags & PSSCAN_TASKS) && !sp->task_dir) { |
216 |
|
/* We found another /proc/PID. Do not use it, |
217 |
|
* there will be /proc/PID/task/PID (same PID!), |
218 |
|
* so just go ahead and dive into /proc/PID/task. */ |
219 |
|
char task_dir[sizeof("/proc/%u/task") + sizeof(int)*3]; |
220 |
|
sprintf(task_dir, "/proc/%u/task", pid); |
221 |
|
sp->task_dir = xopendir(task_dir); |
222 |
|
continue; |
223 |
|
} |
224 |
|
#endif |
225 |
|
|
226 |
/* After this point we have to break, not continue |
/* After this point we can: |
227 |
* ("continue" would mean that current /proc/NNN |
* "break": stop parsing, return the data |
228 |
* is not a valid process info) */ |
* "continue": try next /proc/XXX |
229 |
|
*/ |
230 |
|
|
231 |
memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); |
memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); |
232 |
|
|
233 |
sp->pid = pid; |
sp->pid = pid; |
234 |
if (!(flags & ~PSSCAN_PID)) break; |
if (!(flags & ~PSSCAN_PID)) |
235 |
|
break; /* we needed only pid, we got it */ |
236 |
|
|
237 |
#if ENABLE_SELINUX |
#if ENABLE_SELINUX |
238 |
if (flags & PSSCAN_CONTEXT) { |
if (flags & PSSCAN_CONTEXT) { |
241 |
} |
} |
242 |
#endif |
#endif |
243 |
|
|
244 |
filename_tail = filename + sprintf(filename, "/proc/%d", pid); |
filename_tail = filename + sprintf(filename, "/proc/%u/", pid); |
245 |
|
|
246 |
if (flags & PSSCAN_UIDGID) { |
if (flags & PSSCAN_UIDGID) { |
247 |
if (stat(filename, &sb)) |
if (stat(filename, &sb)) |
248 |
break; |
continue; /* process probably exited */ |
249 |
/* Need comment - is this effective or real UID/GID? */ |
/* Effective UID/GID, not real */ |
250 |
sp->uid = sb.st_uid; |
sp->uid = sb.st_uid; |
251 |
sp->gid = sb.st_gid; |
sp->gid = sb.st_gid; |
252 |
} |
} |
258 |
unsigned long vsz, rss; |
unsigned long vsz, rss; |
259 |
#endif |
#endif |
260 |
/* see proc(5) for some details on this */ |
/* see proc(5) for some details on this */ |
261 |
strcpy(filename_tail, "/stat"); |
strcpy(filename_tail, "stat"); |
262 |
n = read_to_buf(filename, buf); |
n = read_to_buf(filename, buf); |
263 |
if (n < 0) |
if (n < 0) |
264 |
break; |
continue; /* process probably exited */ |
265 |
cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ |
cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ |
266 |
/*if (!cp || cp[1] != ' ') |
/*if (!cp || cp[1] != ' ') |
267 |
break;*/ |
continue;*/ |
268 |
cp[0] = '\0'; |
cp[0] = '\0'; |
269 |
if (sizeof(sp->comm) < 16) |
if (sizeof(sp->comm) < 16) |
270 |
BUG_comm_size(); |
BUG_comm_size(); |
284 |
"%lu " /* start_time */ |
"%lu " /* start_time */ |
285 |
"%lu " /* vsize */ |
"%lu " /* vsize */ |
286 |
"%lu " /* rss */ |
"%lu " /* rss */ |
287 |
#if ENABLE_FEATURE_TOP_SMP_PROCESS |
# if ENABLE_FEATURE_TOP_SMP_PROCESS |
288 |
"%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ |
"%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ |
289 |
"%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */ |
"%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */ |
290 |
"%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */ |
"%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */ |
291 |
"%d" /*cpu last seen on*/ |
"%d" /*cpu last seen on*/ |
292 |
#endif |
# endif |
293 |
, |
, |
294 |
sp->state, &sp->ppid, |
sp->state, &sp->ppid, |
295 |
&sp->pgid, &sp->sid, &tty, |
&sp->pgid, &sp->sid, &tty, |
298 |
&sp->start_time, |
&sp->start_time, |
299 |
&vsz, |
&vsz, |
300 |
&rss |
&rss |
301 |
#if ENABLE_FEATURE_TOP_SMP_PROCESS |
# if ENABLE_FEATURE_TOP_SMP_PROCESS |
302 |
, &sp->last_seen_on_cpu |
, &sp->last_seen_on_cpu |
303 |
#endif |
# endif |
304 |
); |
); |
305 |
|
|
306 |
if (n < 11) |
if (n < 11) |
307 |
break; |
continue; /* bogus data, get next /proc/XXX */ |
308 |
#if ENABLE_FEATURE_TOP_SMP_PROCESS |
# if ENABLE_FEATURE_TOP_SMP_PROCESS |
309 |
if (n < 11+15) |
if (n < 11+15) |
310 |
sp->last_seen_on_cpu = 0; |
sp->last_seen_on_cpu = 0; |
311 |
#endif |
# endif |
312 |
|
|
313 |
/* vsz is in bytes and we want kb */ |
/* vsz is in bytes and we want kb */ |
314 |
sp->vsz = vsz >> 10; |
sp->vsz = vsz >> 10; |
331 |
sp->utime = fast_strtoul_10(&cp); |
sp->utime = fast_strtoul_10(&cp); |
332 |
sp->stime = fast_strtoul_10(&cp); |
sp->stime = fast_strtoul_10(&cp); |
333 |
cp = skip_fields(cp, 3); /* cutime, cstime, priority */ |
cp = skip_fields(cp, 3); /* cutime, cstime, priority */ |
334 |
tasknice = fast_strtoul_10(&cp); |
tasknice = fast_strtol_10(&cp); |
335 |
cp = skip_fields(cp, 2); /* timeout, it_real_value */ |
cp = skip_fields(cp, 2); /* timeout, it_real_value */ |
336 |
sp->start_time = fast_strtoul_10(&cp); |
sp->start_time = fast_strtoul_10(&cp); |
337 |
/* vsz is in bytes and we want kb */ |
/* vsz is in bytes and we want kb */ |
338 |
sp->vsz = fast_strtoul_10(&cp) >> 10; |
sp->vsz = fast_strtoul_10(&cp) >> 10; |
339 |
/* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ |
/* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ |
340 |
sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; |
sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; |
341 |
#if ENABLE_FEATURE_TOP_SMP_PROCESS |
# if ENABLE_FEATURE_TOP_SMP_PROCESS |
342 |
/* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ |
/* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ |
343 |
/* (4): signal, blocked, sigignore, sigcatch */ |
/* (4): signal, blocked, sigignore, sigcatch */ |
344 |
/* (4): wchan, nswap, cnswap, exit_signal */ |
/* (4): wchan, nswap, cnswap, exit_signal */ |
345 |
cp = skip_fields(cp, 14); |
cp = skip_fields(cp, 14); |
346 |
//FIXME: is it safe to assume this field exists? |
//FIXME: is it safe to assume this field exists? |
347 |
sp->last_seen_on_cpu = fast_strtoul_10(&cp); |
sp->last_seen_on_cpu = fast_strtoul_10(&cp); |
348 |
#endif |
# endif |
349 |
#endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */ |
#endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */ |
350 |
|
|
351 |
|
#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS |
352 |
|
sp->niceness = tasknice; |
353 |
|
#endif |
354 |
|
|
355 |
if (sp->vsz == 0 && sp->state[0] != 'Z') |
if (sp->vsz == 0 && sp->state[0] != 'Z') |
356 |
sp->state[1] = 'W'; |
sp->state[1] = 'W'; |
357 |
else |
else |
368 |
if (flags & (PSSCAN_SMAPS)) { |
if (flags & (PSSCAN_SMAPS)) { |
369 |
FILE *file; |
FILE *file; |
370 |
|
|
371 |
strcpy(filename_tail, "/smaps"); |
strcpy(filename_tail, "smaps"); |
372 |
file = fopen_for_read(filename); |
file = fopen_for_read(filename); |
373 |
if (!file) |
if (file) { |
374 |
break; |
while (fgets(buf, sizeof(buf), file)) { |
375 |
while (fgets(buf, sizeof(buf), file)) { |
unsigned long sz; |
376 |
unsigned long sz; |
char *tp; |
377 |
char *tp; |
char w; |
|
char w; |
|
378 |
#define SCAN(str, name) \ |
#define SCAN(str, name) \ |
379 |
if (strncmp(buf, str, sizeof(str)-1) == 0) { \ |
if (strncmp(buf, str, sizeof(str)-1) == 0) { \ |
380 |
tp = skip_whitespace(buf + sizeof(str)-1); \ |
tp = skip_whitespace(buf + sizeof(str)-1); \ |
381 |
sp->name += fast_strtoul_10(&tp); \ |
sp->name += fast_strtoul_10(&tp); \ |
382 |
continue; \ |
continue; \ |
383 |
} |
} |
384 |
SCAN("Shared_Clean:" , shared_clean ); |
SCAN("Shared_Clean:" , shared_clean ); |
385 |
SCAN("Shared_Dirty:" , shared_dirty ); |
SCAN("Shared_Dirty:" , shared_dirty ); |
386 |
SCAN("Private_Clean:", private_clean); |
SCAN("Private_Clean:", private_clean); |
387 |
SCAN("Private_Dirty:", private_dirty); |
SCAN("Private_Dirty:", private_dirty); |
388 |
#undef SCAN |
#undef SCAN |
389 |
// f7d29000-f7d39000 rw-s ADR M:m OFS FILE |
// f7d29000-f7d39000 rw-s ADR M:m OFS FILE |
390 |
tp = strchr(buf, '-'); |
tp = strchr(buf, '-'); |
391 |
if (tp) { |
if (tp) { |
392 |
*tp = ' '; |
*tp = ' '; |
393 |
tp = buf; |
tp = buf; |
394 |
sz = fast_strtoul_16(&tp); /* start */ |
sz = fast_strtoul_16(&tp); /* start */ |
395 |
sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ |
sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ |
396 |
// tp -> "rw-s" string |
// tp -> "rw-s" string |
397 |
w = tp[1]; |
w = tp[1]; |
398 |
// skipping "rw-s ADR M:m OFS " |
// skipping "rw-s ADR M:m OFS " |
399 |
tp = skip_whitespace(skip_fields(tp, 4)); |
tp = skip_whitespace(skip_fields(tp, 4)); |
400 |
// filter out /dev/something (something != zero) |
// filter out /dev/something (something != zero) |
401 |
if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { |
if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { |
402 |
if (w == 'w') { |
if (w == 'w') { |
403 |
sp->mapped_rw += sz; |
sp->mapped_rw += sz; |
404 |
} else if (w == '-') { |
} else if (w == '-') { |
405 |
sp->mapped_ro += sz; |
sp->mapped_ro += sz; |
406 |
|
} |
407 |
} |
} |
|
} |
|
408 |
//else printf("DROPPING %s (%s)\n", buf, tp); |
//else printf("DROPPING %s (%s)\n", buf, tp); |
409 |
if (strcmp(tp, "[stack]\n") == 0) |
if (strcmp(tp, "[stack]\n") == 0) |
410 |
sp->stack += sz; |
sp->stack += sz; |
411 |
|
} |
412 |
} |
} |
413 |
|
fclose(file); |
414 |
} |
} |
|
fclose(file); |
|
415 |
} |
} |
416 |
#endif /* TOPMEM */ |
#endif /* TOPMEM */ |
417 |
|
#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS |
418 |
|
if (flags & PSSCAN_RUIDGID) { |
419 |
|
FILE *file; |
420 |
|
|
421 |
|
strcpy(filename_tail, "status"); |
422 |
|
file = fopen_for_read(filename); |
423 |
|
if (file) { |
424 |
|
while (fgets(buf, sizeof(buf), file)) { |
425 |
|
char *tp; |
426 |
|
#define SCAN_TWO(str, name, statement) \ |
427 |
|
if (strncmp(buf, str, sizeof(str)-1) == 0) { \ |
428 |
|
tp = skip_whitespace(buf + sizeof(str)-1); \ |
429 |
|
sscanf(tp, "%u", &sp->name); \ |
430 |
|
statement; \ |
431 |
|
} |
432 |
|
SCAN_TWO("Uid:", ruid, continue); |
433 |
|
SCAN_TWO("Gid:", rgid, break); |
434 |
|
#undef SCAN_TWO |
435 |
|
} |
436 |
|
fclose(file); |
437 |
|
} |
438 |
|
} |
439 |
|
#endif /* PS_ADDITIONAL_COLUMNS */ |
440 |
|
if (flags & PSSCAN_EXE) { |
441 |
|
strcpy(filename_tail, "exe"); |
442 |
|
free(sp->exe); |
443 |
|
sp->exe = xmalloc_readlink(filename); |
444 |
|
} |
445 |
|
/* Note: if /proc/PID/cmdline is empty, |
446 |
|
* code below "breaks". Therefore it must be |
447 |
|
* the last code to parse /proc/PID/xxx data |
448 |
|
* (we used to have /proc/PID/exe parsing after it |
449 |
|
* and were getting stale sp->exe). |
450 |
|
*/ |
451 |
#if 0 /* PSSCAN_CMD is not used */ |
#if 0 /* PSSCAN_CMD is not used */ |
452 |
if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { |
if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { |
453 |
free(sp->argv0); |
free(sp->argv0); |
454 |
sp->argv0 = NULL; |
sp->argv0 = NULL; |
455 |
free(sp->cmd); |
free(sp->cmd); |
456 |
sp->cmd = NULL; |
sp->cmd = NULL; |
457 |
strcpy(filename_tail, "/cmdline"); |
strcpy(filename_tail, "cmdline"); |
458 |
/* TODO: to get rid of size limits, read into malloc buf, |
/* TODO: to get rid of size limits, read into malloc buf, |
459 |
* then realloc it down to real size. */ |
* then realloc it down to real size. */ |
460 |
n = read_to_buf(filename, buf); |
n = read_to_buf(filename, buf); |
475 |
if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) { |
if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) { |
476 |
free(sp->argv0); |
free(sp->argv0); |
477 |
sp->argv0 = NULL; |
sp->argv0 = NULL; |
478 |
strcpy(filename_tail, "/cmdline"); |
strcpy(filename_tail, "cmdline"); |
479 |
n = read_to_buf(filename, buf); |
n = read_to_buf(filename, buf); |
480 |
if (n <= 0) |
if (n <= 0) |
481 |
break; |
break; |
491 |
} |
} |
492 |
#endif |
#endif |
493 |
break; |
break; |
494 |
} |
} /* for (;;) */ |
495 |
|
|
496 |
return sp; |
return sp; |
497 |
} |
} |
498 |
|
|
499 |
void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) |
void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) |
500 |
{ |
{ |
501 |
ssize_t sz; |
int sz; |
502 |
char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; |
char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; |
503 |
|
|
504 |
sprintf(filename, "/proc/%u/cmdline", pid); |
sprintf(filename, "/proc/%u/cmdline", pid); |
505 |
sz = open_read_close(filename, buf, col); |
sz = open_read_close(filename, buf, col - 1); |
506 |
if (sz > 0) { |
if (sz > 0) { |
507 |
buf[sz] = '\0'; |
buf[sz] = '\0'; |
508 |
while (--sz >= 0) |
while (--sz >= 0 && buf[sz] == '\0') |
509 |
|
continue; |
510 |
|
do { |
511 |
if ((unsigned char)(buf[sz]) < ' ') |
if ((unsigned char)(buf[sz]) < ' ') |
512 |
buf[sz] = ' '; |
buf[sz] = ' '; |
513 |
|
} while (--sz >= 0); |
514 |
} else { |
} else { |
515 |
snprintf(buf, col, "[%s]", comm); |
snprintf(buf, col, "[%s]", comm); |
516 |
} |
} |