1
2
3
4
5
6
7
8
9
10
11#include "libbb.h"
12
13
14typedef struct id_to_name_map_t {
15 uid_t id;
16 char name[USERNAME_MAX_SIZE];
17} id_to_name_map_t;
18
19typedef struct cache_t {
20 id_to_name_map_t *cache;
21 int size;
22} cache_t;
23
24static cache_t *cache_user_group;
25
26void FAST_FUNC clear_username_cache(void)
27{
28 if (cache_user_group) {
29 free(cache_user_group[0].cache);
30 free(cache_user_group[1].cache);
31 free(cache_user_group);
32 cache_user_group = NULL;
33 }
34}
35
36static char* get_cached(int user_group, uid_t id,
37 char* FAST_FUNC x2x_utoa(uid_t id))
38{
39 cache_t *cp;
40 int i;
41
42 if (!cache_user_group)
43 cache_user_group = xzalloc(sizeof(cache_user_group[0]) * 2);
44
45 cp = &cache_user_group[user_group];
46
47 for (i = 0; i < cp->size; i++)
48 if (cp->cache[i].id == id)
49 return cp->cache[i].name;
50 i = cp->size++;
51 cp->cache = xrealloc_vector(cp->cache, 2, i);
52 cp->cache[i].id = id;
53
54 safe_strncpy(cp->cache[i].name, x2x_utoa(id), sizeof(cp->cache[i].name));
55 return cp->cache[i].name;
56}
57const char* FAST_FUNC get_cached_username(uid_t uid)
58{
59 return get_cached(0, uid, uid2uname_utoa);
60}
61const char* FAST_FUNC get_cached_groupname(gid_t gid)
62{
63 return get_cached(1, gid, gid2group_utoa);
64}
65
66
67#define PROCPS_BUFSIZE 1024
68
69static int read_to_buf(const char *filename, void *buf)
70{
71 int fd;
72
73
74 ssize_t ret = -1;
75 fd = open(filename, O_RDONLY);
76 if (fd >= 0) {
77 ret = read(fd, buf, PROCPS_BUFSIZE-1);
78 close(fd);
79 }
80 ((char *)buf)[ret > 0 ? ret : 0] = '\0';
81 return ret;
82}
83
84static procps_status_t* FAST_FUNC alloc_procps_scan(void)
85{
86 procps_status_t* sp = xzalloc(sizeof(procps_status_t));
87 unsigned n = bb_getpagesize();
88 while (1) {
89 n >>= 1;
90 if (!n) break;
91 sp->shift_pages_to_bytes++;
92 }
93 sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10;
94 sp->dir = xopendir("/proc");
95 return sp;
96}
97
98void FAST_FUNC free_procps_scan(procps_status_t* sp)
99{
100 closedir(sp->dir);
101#if ENABLE_FEATURE_SHOW_THREADS
102 if (sp->task_dir)
103 closedir(sp->task_dir);
104#endif
105 free(sp->argv0);
106 free(sp->exe);
107 IF_SELINUX(free(sp->context);)
108 free(sp);
109}
110
111#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
112static unsigned long long fast_strtoull_16(char **endptr)
113{
114 unsigned char c;
115 char *str = *endptr;
116 unsigned long long n = 0;
117
118
119 while ((c = *str++) > ' ') {
120 c = ((c|0x20) - '0');
121 if (c > 9)
122
123 c = c - ('a' - '0' - 10);
124 n = n*16 + c;
125 }
126 *endptr = str;
127 return n;
128}
129#endif
130
131#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
132
133static unsigned long fast_strtoul_10(char **endptr)
134{
135 unsigned char c;
136 char *str = *endptr;
137 unsigned long n = *str - '0';
138
139
140 while ((c = *++str) > ' ')
141 n = n*10 + (c - '0');
142
143 *endptr = str + 1;
144 return n;
145}
146
147# if ENABLE_FEATURE_FAST_TOP
148static long fast_strtol_10(char **endptr)
149{
150 if (**endptr != '-')
151 return fast_strtoul_10(endptr);
152
153 (*endptr)++;
154 return - (long)fast_strtoul_10(endptr);
155}
156# endif
157
158static char *skip_fields(char *str, int count)
159{
160 do {
161 while (*str++ != ' ')
162 continue;
163
164 } while (--count);
165 return str;
166}
167#endif
168
169#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
170static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix)
171{
172 char *tp = is_prefixed_with(buf, prefix);
173 if (tp) {
174 tp = skip_whitespace(tp);
175 }
176 return tp;
177}
178
179int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
180 void (*cb)(struct smaprec *, void *), void *data)
181{
182 FILE *file;
183 struct smaprec currec;
184 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
185 char buf[PROCPS_BUFSIZE];
186#if !ENABLE_PMAP
187 void (*cb)(struct smaprec *, void *) = NULL;
188 void *data = NULL;
189#endif
190
191 sprintf(filename, "/proc/%u/smaps", (int)pid);
192
193 file = fopen_for_read(filename);
194 if (!file)
195 return 1;
196
197 memset(&currec, 0, sizeof(currec));
198 while (fgets(buf, PROCPS_BUFSIZE, file)) {
199
200
201
202
203
204
205 char *tp, *p;
206
207#define SCAN(S, X) \
208 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \
209 total->X += currec.X = fast_strtoul_10(&tp); \
210 continue; \
211 }
212 if (cb) {
213 SCAN("Pss:" , smap_pss );
214 SCAN("Swap:" , smap_swap );
215 }
216 SCAN("Private_Dirty:", private_dirty);
217 SCAN("Private_Clean:", private_clean);
218 SCAN("Shared_Dirty:" , shared_dirty );
219 SCAN("Shared_Clean:" , shared_clean );
220#undef SCAN
221 tp = strchr(buf, '-');
222 if (tp) {
223
224
225
226 if (cb) {
227
228
229
230 if (currec.smap_size)
231 cb(&currec, data);
232 free(currec.smap_name);
233 }
234 memset(&currec, 0, sizeof(currec));
235
236 *tp = ' ';
237 tp = buf;
238 currec.smap_start = fast_strtoull_16(&tp);
239 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
240
241 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
242
243
244 tp = skip_whitespace(skip_fields(tp, 4));
245
246 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) {
247 if (currec.smap_mode[1] == 'w') {
248 currec.mapped_rw = currec.smap_size;
249 total->mapped_rw += currec.smap_size;
250 } else if (currec.smap_mode[1] == '-') {
251 currec.mapped_ro = currec.smap_size;
252 total->mapped_ro += currec.smap_size;
253 }
254 }
255
256 if (strcmp(tp, "[stack]\n") == 0)
257 total->stack += currec.smap_size;
258 if (cb) {
259 p = skip_non_whitespace(tp);
260 if (p == tp) {
261 currec.smap_name = xstrdup(" [ anon ]");
262 } else {
263 *p = '\0';
264 currec.smap_name = xstrdup(tp);
265 }
266 }
267 total->smap_size += currec.smap_size;
268 }
269 }
270 fclose(file);
271
272 if (cb) {
273 if (currec.smap_size)
274 cb(&currec, data);
275 free(currec.smap_name);
276 }
277
278 return 0;
279}
280#endif
281
282procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
283{
284 if (!sp)
285 sp = alloc_procps_scan();
286
287 for (;;) {
288 struct dirent *entry;
289 char buf[PROCPS_BUFSIZE];
290 long tasknice;
291 unsigned pid;
292 int n;
293 char filename[sizeof("/proc/%u/task/%u/cmdline") + sizeof(int)*3 * 2];
294 char *filename_tail;
295
296#if ENABLE_FEATURE_SHOW_THREADS
297 if (sp->task_dir) {
298 entry = readdir(sp->task_dir);
299 if (entry)
300 goto got_entry;
301 closedir(sp->task_dir);
302 sp->task_dir = NULL;
303 }
304#endif
305 entry = readdir(sp->dir);
306 if (entry == NULL) {
307 free_procps_scan(sp);
308 return NULL;
309 }
310 IF_FEATURE_SHOW_THREADS(got_entry:)
311 pid = bb_strtou(entry->d_name, NULL, 10);
312 if (errno)
313 continue;
314#if ENABLE_FEATURE_SHOW_THREADS
315 if ((flags & PSSCAN_TASKS) && !sp->task_dir) {
316
317
318
319 sprintf(filename, "/proc/%u/task", pid);
320
321 sp->task_dir = opendir(filename);
322 sp->main_thread_pid = pid;
323 continue;
324 }
325#endif
326
327
328
329
330
331
332 memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
333
334 sp->pid = pid;
335 if (!(flags & ~PSSCAN_PID))
336 break;
337
338#if ENABLE_SELINUX
339 if (flags & PSSCAN_CONTEXT) {
340 if (getpidcon(sp->pid, &sp->context) < 0)
341 sp->context = NULL;
342 }
343#endif
344
345#if ENABLE_FEATURE_SHOW_THREADS
346 if (sp->task_dir)
347 filename_tail = filename + sprintf(filename, "/proc/%u/task/%u/", sp->main_thread_pid, pid);
348 else
349#endif
350 filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
351
352 if (flags & PSSCAN_UIDGID) {
353 struct stat sb;
354 if (stat(filename, &sb))
355 continue;
356
357 sp->uid = sb.st_uid;
358 sp->gid = sb.st_gid;
359 }
360
361
362 if (flags & (PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
363 | PSSCAN_COMM | PSSCAN_STATE
364 | PSSCAN_VSZ | PSSCAN_RSS
365 | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
366 | PSSCAN_TTY | PSSCAN_NICE
367 | PSSCAN_CPU)
368 ) {
369 int s_idx;
370 char *cp, *comm1;
371 int tty;
372#if !ENABLE_FEATURE_FAST_TOP
373 unsigned long vsz, rss;
374#endif
375
376 strcpy(filename_tail, "stat");
377 n = read_to_buf(filename, buf);
378 if (n < 0)
379 continue;
380 cp = strrchr(buf, ')');
381
382
383 cp[0] = '\0';
384 BUILD_BUG_ON(sizeof(sp->comm) < 16);
385 comm1 = strchr(buf, '(');
386
387 safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm));
388
389#if !ENABLE_FEATURE_FAST_TOP
390 n = sscanf(cp+2,
391 "%c %u "
392 "%u %u %d %*s "
393 "%*s %*s %*s %*s %*s "
394 "%lu %lu "
395 "%*s %*s %*s "
396 "%ld "
397 "%*s %*s "
398 "%lu "
399 "%lu "
400 "%lu "
401# if ENABLE_FEATURE_TOP_SMP_PROCESS
402 "%*s %*s %*s %*s %*s %*s "
403 "%*s %*s %*s %*s "
404 "%*s %*s %*s %*s "
405 "%d"
406# endif
407 ,
408 sp->state, &sp->ppid,
409 &sp->pgid, &sp->sid, &tty,
410 &sp->utime, &sp->stime,
411 &tasknice,
412 &sp->start_time,
413 &vsz,
414 &rss
415# if ENABLE_FEATURE_TOP_SMP_PROCESS
416 , &sp->last_seen_on_cpu
417# endif
418 );
419
420 if (n < 11)
421 continue;
422# if ENABLE_FEATURE_TOP_SMP_PROCESS
423 if (n == 11)
424 sp->last_seen_on_cpu = 0;
425# endif
426
427
428 sp->vsz = vsz >> 10;
429
430 sp->rss = rss << sp->shift_pages_to_kb;
431 sp->tty_major = (tty >> 8) & 0xfff;
432 sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
433#else
434
435
436 sp->state[0] = cp[2];
437 cp += 4;
438 sp->ppid = fast_strtoul_10(&cp);
439 sp->pgid = fast_strtoul_10(&cp);
440 sp->sid = fast_strtoul_10(&cp);
441 tty = fast_strtoul_10(&cp);
442 sp->tty_major = (tty >> 8) & 0xfff;
443 sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
444 cp = skip_fields(cp, 6);
445 sp->utime = fast_strtoul_10(&cp);
446 sp->stime = fast_strtoul_10(&cp);
447 cp = skip_fields(cp, 3);
448 tasknice = fast_strtol_10(&cp);
449 cp = skip_fields(cp, 2);
450 sp->start_time = fast_strtoul_10(&cp);
451
452 sp->vsz = fast_strtoul_10(&cp) >> 10;
453
454 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
455# if ENABLE_FEATURE_TOP_SMP_PROCESS
456
457
458
459 cp = skip_fields(cp, 14);
460
461 sp->last_seen_on_cpu = fast_strtoul_10(&cp);
462# endif
463#endif
464
465#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
466 sp->niceness = tasknice;
467#endif
468 sp->state[1] = ' ';
469 sp->state[2] = ' ';
470 s_idx = 1;
471 if (sp->vsz == 0 && sp->state[0] != 'Z') {
472
473 sp->state[1] = 'W';
474 s_idx = 2;
475 }
476 if (tasknice != 0) {
477 if (tasknice < 0)
478 sp->state[s_idx] = '<';
479 else
480 sp->state[s_idx] = 'N';
481 }
482 }
483
484#if ENABLE_FEATURE_TOPMEM
485 if (flags & PSSCAN_SMAPS)
486 procps_read_smaps(pid, &sp->smaps, NULL, NULL);
487#endif
488#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
489 if (flags & PSSCAN_RUIDGID) {
490 FILE *file;
491
492 strcpy(filename_tail, "status");
493 file = fopen_for_read(filename);
494 if (file) {
495 while (fgets(buf, sizeof(buf), file)) {
496 char *tp;
497#define SCAN_TWO(str, name, statement) \
498 if ((tp = is_prefixed_with(buf, str)) != NULL) { \
499 tp = skip_whitespace(tp); \
500 sscanf(tp, "%u", &sp->name); \
501 statement; \
502 }
503 SCAN_TWO("Uid:", ruid, continue);
504 SCAN_TWO("Gid:", rgid, break);
505#undef SCAN_TWO
506 }
507 fclose(file);
508 }
509 }
510#endif
511 if (flags & PSSCAN_EXE) {
512 strcpy(filename_tail, "exe");
513 free(sp->exe);
514 sp->exe = xmalloc_readlink(filename);
515 }
516
517
518
519
520
521
522#if 0
523 if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) {
524 free(sp->argv0);
525 sp->argv0 = NULL;
526 free(sp->cmd);
527 sp->cmd = NULL;
528 strcpy(filename_tail, "cmdline");
529
530
531 n = read_to_buf(filename, buf);
532 if (n <= 0)
533 break;
534 if (flags & PSSCAN_ARGV0)
535 sp->argv0 = xstrdup(buf);
536 if (flags & PSSCAN_CMD) {
537 do {
538 n--;
539 if ((unsigned char)(buf[n]) < ' ')
540 buf[n] = ' ';
541 } while (n);
542 sp->cmd = xstrdup(buf);
543 }
544 }
545#else
546 if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) {
547 free(sp->argv0);
548 sp->argv0 = NULL;
549 strcpy(filename_tail, "cmdline");
550 n = read_to_buf(filename, buf);
551 if (n <= 0)
552 break;
553 if (flags & PSSCAN_ARGVN) {
554 sp->argv_len = n;
555 sp->argv0 = xmemdup(buf, n + 1);
556
557 } else {
558 sp->argv_len = 0;
559 sp->argv0 = xstrdup(buf);
560 }
561 }
562#endif
563 break;
564 }
565
566 return sp;
567}
568
569void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
570{
571 int sz;
572 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
573
574 sprintf(filename, "/proc/%u/cmdline", pid);
575 sz = open_read_close(filename, buf, col - 1);
576 if (sz > 0) {
577 const char *base;
578 int comm_len;
579
580 buf[sz] = '\0';
581 while (--sz >= 0 && buf[sz] == '\0')
582 continue;
583
584 strchrnul(buf, ' ')[0] = '\0';
585 base = bb_basename(buf);
586 while (sz >= 0) {
587 if ((unsigned char)(buf[sz]) < ' ')
588 buf[sz] = ' ';
589 sz--;
590 }
591 if (base[0] == '-')
592 base++;
593
594
595
596
597 if (!comm)
598 return;
599 comm_len = strlen(comm);
600
601
602
603
604
605
606 if (strncmp(base, comm, comm_len) != 0) {
607 comm_len += 3;
608 if (col > comm_len)
609 memmove(buf + comm_len, buf, col - comm_len);
610 snprintf(buf, col, "{%s}", comm);
611 if (col <= comm_len)
612 return;
613 buf[comm_len - 1] = ' ';
614 buf[col - 1] = '\0';
615 }
616 } else {
617 snprintf(buf, col, "[%s]", comm ? comm : "?");
618 }
619}
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658