1
2
3
4
5
6
7
8
9
10
11
12#include "libbb.h"
13
14
15typedef struct unsigned_to_name_map_t {
16 long id;
17 char name[USERNAME_MAX_SIZE];
18} unsigned_to_name_map_t;
19
20typedef struct cache_t {
21 unsigned_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, unsigned 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, long id,
56 char* FAST_FUNC x2x_utoa(long 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 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 char c;
147 char *str = *endptr;
148 unsigned long n = *str - '0';
149
150 while ((c = *++str) != ' ')
151 n = n*10 + (c - '0');
152
153 *endptr = str + 1;
154 return n;
155}
156
157# if ENABLE_FEATURE_FAST_TOP
158static long fast_strtol_10(char **endptr)
159{
160 if (**endptr != '-')
161 return fast_strtoul_10(endptr);
162
163 (*endptr)++;
164 return - (long)fast_strtoul_10(endptr);
165}
166# endif
167
168static char *skip_fields(char *str, int count)
169{
170 do {
171 while (*str++ != ' ')
172 continue;
173
174 } while (--count);
175 return str;
176}
177#endif
178
179#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
180int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
181 void (*cb)(struct smaprec *, void *), void *data)
182{
183 FILE *file;
184 struct smaprec currec;
185 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
186 char buf[PROCPS_BUFSIZE];
187#if !ENABLE_PMAP
188 void (*cb)(struct smaprec *, void *) = NULL;
189 void *data = NULL;
190#endif
191
192 sprintf(filename, "/proc/%u/smaps", (int)pid);
193
194 file = fopen_for_read(filename);
195 if (!file)
196 return 1;
197
198 memset(&currec, 0, sizeof(currec));
199 while (fgets(buf, PROCPS_BUFSIZE, file)) {
200
201
202
203
204
205
206 char *tp = buf, *p;
207
208#define SCAN(S, X) \
209 if (strncmp(tp, S, sizeof(S)-1) == 0) { \
210 tp = skip_whitespace(tp + sizeof(S)-1); \
211 total->X += currec.X = fast_strtoul_10(&tp); \
212 continue; \
213 }
214 if (cb) {
215 SCAN("Pss:" , smap_pss );
216 SCAN("Swap:" , smap_swap );
217 }
218 SCAN("Private_Dirty:", private_dirty);
219 SCAN("Private_Clean:", private_clean);
220 SCAN("Shared_Dirty:" , shared_dirty );
221 SCAN("Shared_Clean:" , shared_clean );
222#undef SCAN
223 tp = strchr(buf, '-');
224 if (tp) {
225
226
227
228 if (cb) {
229
230
231
232 if (currec.smap_size)
233 cb(&currec, data);
234 free(currec.smap_name);
235 }
236 memset(&currec, 0, sizeof(currec));
237
238 *tp = ' ';
239 tp = buf;
240 currec.smap_start = fast_strtoul_16(&tp);
241 currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10;
242
243 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
244
245
246 tp = skip_whitespace(skip_fields(tp, 4));
247
248 if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) {
249 if (currec.smap_mode[1] == 'w') {
250 currec.mapped_rw = currec.smap_size;
251 total->mapped_rw += currec.smap_size;
252 } else if (currec.smap_mode[1] == '-') {
253 currec.mapped_ro = currec.smap_size;
254 total->mapped_ro += currec.smap_size;
255 }
256 }
257
258 if (strcmp(tp, "[stack]\n") == 0)
259 total->stack += currec.smap_size;
260 if (cb) {
261 p = skip_non_whitespace(tp);
262 if (p == tp) {
263 currec.smap_name = xstrdup(" [ anon ]");
264 } else {
265 *p = '\0';
266 currec.smap_name = xstrdup(tp);
267 }
268 }
269 total->smap_size += currec.smap_size;
270 }
271 }
272 fclose(file);
273
274 if (cb) {
275 if (currec.smap_size)
276 cb(&currec, data);
277 free(currec.smap_name);
278 }
279
280 return 0;
281}
282#endif
283
284void BUG_comm_size(void);
285procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
286{
287 struct dirent *entry;
288 char buf[PROCPS_BUFSIZE];
289 char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
290 char *filename_tail;
291 long tasknice;
292 unsigned pid;
293 int n;
294 struct stat sb;
295
296 if (!sp)
297 sp = alloc_procps_scan();
298
299 for (;;) {
300#if ENABLE_FEATURE_SHOW_THREADS
301 if ((flags & PSSCAN_TASKS) && sp->task_dir) {
302 entry = readdir(sp->task_dir);
303 if (entry)
304 goto got_entry;
305 closedir(sp->task_dir);
306 sp->task_dir = NULL;
307 }
308#endif
309 entry = readdir(sp->dir);
310 if (entry == NULL) {
311 free_procps_scan(sp);
312 return NULL;
313 }
314 IF_FEATURE_SHOW_THREADS(got_entry:)
315 pid = bb_strtou(entry->d_name, NULL, 10);
316 if (errno)
317 continue;
318#if ENABLE_FEATURE_SHOW_THREADS
319 if ((flags & PSSCAN_TASKS) && !sp->task_dir) {
320
321
322
323 char task_dir[sizeof("/proc/%u/task") + sizeof(int)*3];
324 sprintf(task_dir, "/proc/%u/task", pid);
325 sp->task_dir = xopendir(task_dir);
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 filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
349
350 if (flags & PSSCAN_UIDGID) {
351 if (stat(filename, &sb))
352 continue;
353
354 sp->uid = sb.st_uid;
355 sp->gid = sb.st_gid;
356 }
357
358 if (flags & PSSCAN_STAT) {
359 char *cp, *comm1;
360 int tty;
361#if !ENABLE_FEATURE_FAST_TOP
362 unsigned long vsz, rss;
363#endif
364
365 strcpy(filename_tail, "stat");
366 n = read_to_buf(filename, buf);
367 if (n < 0)
368 continue;
369 cp = strrchr(buf, ')');
370
371
372 cp[0] = '\0';
373 if (sizeof(sp->comm) < 16)
374 BUG_comm_size();
375 comm1 = strchr(buf, '(');
376
377 safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm));
378
379#if !ENABLE_FEATURE_FAST_TOP
380 n = sscanf(cp+2,
381 "%c %u "
382 "%u %u %d %*s "
383 "%*s %*s %*s %*s %*s "
384 "%lu %lu "
385 "%*s %*s %*s "
386 "%ld "
387 "%*s %*s "
388 "%lu "
389 "%lu "
390 "%lu "
391# if ENABLE_FEATURE_TOP_SMP_PROCESS
392 "%*s %*s %*s %*s %*s %*s "
393 "%*s %*s %*s %*s "
394 "%*s %*s %*s %*s "
395 "%d"
396# endif
397 ,
398 sp->state, &sp->ppid,
399 &sp->pgid, &sp->sid, &tty,
400 &sp->utime, &sp->stime,
401 &tasknice,
402 &sp->start_time,
403 &vsz,
404 &rss
405# if ENABLE_FEATURE_TOP_SMP_PROCESS
406 , &sp->last_seen_on_cpu
407# endif
408 );
409
410 if (n < 11)
411 continue;
412# if ENABLE_FEATURE_TOP_SMP_PROCESS
413 if (n < 11+15)
414 sp->last_seen_on_cpu = 0;
415# endif
416
417
418 sp->vsz = vsz >> 10;
419
420 sp->rss = rss << sp->shift_pages_to_kb;
421 sp->tty_major = (tty >> 8) & 0xfff;
422 sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
423#else
424
425
426 sp->state[0] = cp[2];
427 cp += 4;
428 sp->ppid = fast_strtoul_10(&cp);
429 sp->pgid = fast_strtoul_10(&cp);
430 sp->sid = fast_strtoul_10(&cp);
431 tty = fast_strtoul_10(&cp);
432 sp->tty_major = (tty >> 8) & 0xfff;
433 sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
434 cp = skip_fields(cp, 6);
435 sp->utime = fast_strtoul_10(&cp);
436 sp->stime = fast_strtoul_10(&cp);
437 cp = skip_fields(cp, 3);
438 tasknice = fast_strtol_10(&cp);
439 cp = skip_fields(cp, 2);
440 sp->start_time = fast_strtoul_10(&cp);
441
442 sp->vsz = fast_strtoul_10(&cp) >> 10;
443
444 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
445# if ENABLE_FEATURE_TOP_SMP_PROCESS
446
447
448
449 cp = skip_fields(cp, 14);
450
451 sp->last_seen_on_cpu = fast_strtoul_10(&cp);
452# endif
453#endif
454
455#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
456 sp->niceness = tasknice;
457#endif
458
459 if (sp->vsz == 0 && sp->state[0] != 'Z')
460 sp->state[1] = 'W';
461 else
462 sp->state[1] = ' ';
463 if (tasknice < 0)
464 sp->state[2] = '<';
465 else if (tasknice)
466 sp->state[2] = 'N';
467 else
468 sp->state[2] = ' ';
469 }
470
471#if ENABLE_FEATURE_TOPMEM
472 if (flags & PSSCAN_SMAPS)
473 procps_read_smaps(pid, &sp->smaps, NULL, NULL);
474#endif
475#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
476 if (flags & PSSCAN_RUIDGID) {
477 FILE *file;
478
479 strcpy(filename_tail, "status");
480 file = fopen_for_read(filename);
481 if (file) {
482 while (fgets(buf, sizeof(buf), file)) {
483 char *tp;
484#define SCAN_TWO(str, name, statement) \
485 if (strncmp(buf, str, sizeof(str)-1) == 0) { \
486 tp = skip_whitespace(buf + sizeof(str)-1); \
487 sscanf(tp, "%u", &sp->name); \
488 statement; \
489 }
490 SCAN_TWO("Uid:", ruid, continue);
491 SCAN_TWO("Gid:", rgid, break);
492#undef SCAN_TWO
493 }
494 fclose(file);
495 }
496 }
497#endif
498 if (flags & PSSCAN_EXE) {
499 strcpy(filename_tail, "exe");
500 free(sp->exe);
501 sp->exe = xmalloc_readlink(filename);
502 }
503
504
505
506
507
508
509#if 0
510 if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) {
511 free(sp->argv0);
512 sp->argv0 = NULL;
513 free(sp->cmd);
514 sp->cmd = NULL;
515 strcpy(filename_tail, "cmdline");
516
517
518 n = read_to_buf(filename, buf);
519 if (n <= 0)
520 break;
521 if (flags & PSSCAN_ARGV0)
522 sp->argv0 = xstrdup(buf);
523 if (flags & PSSCAN_CMD) {
524 do {
525 n--;
526 if ((unsigned char)(buf[n]) < ' ')
527 buf[n] = ' ';
528 } while (n);
529 sp->cmd = xstrdup(buf);
530 }
531 }
532#else
533 if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) {
534 free(sp->argv0);
535 sp->argv0 = NULL;
536 strcpy(filename_tail, "cmdline");
537 n = read_to_buf(filename, buf);
538 if (n <= 0)
539 break;
540 if (flags & PSSCAN_ARGVN) {
541 sp->argv_len = n;
542 sp->argv0 = xmalloc(n + 1);
543 memcpy(sp->argv0, buf, n + 1);
544
545 } else {
546 sp->argv_len = 0;
547 sp->argv0 = xstrdup(buf);
548 }
549 }
550#endif
551 break;
552 }
553
554 return sp;
555}
556
557void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
558{
559 int sz;
560 char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
561
562 sprintf(filename, "/proc/%u/cmdline", pid);
563 sz = open_read_close(filename, buf, col - 1);
564 if (sz > 0) {
565 buf[sz] = '\0';
566 while (--sz >= 0 && buf[sz] == '\0')
567 continue;
568 do {
569 if ((unsigned char)(buf[sz]) < ' ')
570 buf[sz] = ' ';
571 } while (--sz >= 0);
572 } else {
573 snprintf(buf, col, "[%s]", comm);
574 }
575}
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614