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