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