replace /proc scanning code by more versatile one.
[platform/upstream/busybox.git] / libbb / procps.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright 1998 by Albert Cahalan; all rights reserved.
6  * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9  */
10
11 #include "libbb.h"
12
13
14 typedef struct {
15         uid_t uid;
16         char username[12];
17 } user_map_t;
18
19 static user_map_t *username_cache;
20 static int username_cache_size;
21
22 void clear_username_cache(void)
23 {
24         free(username_cache);
25         username_cache = NULL;
26         username_cache_size = 0;
27 }
28
29 const char* get_cached_username(uid_t uid)
30 {
31         int i;
32         for (i = 0; i < username_cache_size; i++)
33                 if (username_cache[i].uid == uid)
34                         return username_cache[i].username;
35         i = username_cache_size++;
36         username_cache = xrealloc(username_cache, username_cache_size * sizeof(*username_cache));
37         username_cache[i].uid = uid;
38         bb_getpwuid(username_cache[i].username, uid, sizeof(username_cache[i].username));
39         return username_cache[i].username;
40 }
41
42
43 #define PROCPS_BUFSIZE 1024
44
45 static int read_to_buf(const char *filename, void *buf)
46 {
47         ssize_t ret;
48         ret = open_read_close(filename, buf, PROCPS_BUFSIZE-1);
49         ((char *)buf)[ret > 0 ? ret : 0] = '\0';
50         return ret;
51 }
52
53 procps_status_t* alloc_procps_scan(int flags)
54 {
55         procps_status_t* sp = xzalloc(sizeof(procps_status_t));
56         sp->dir = xopendir("/proc");
57         return sp;
58 }
59
60 void free_procps_scan(procps_status_t* sp)
61 {
62         closedir(sp->dir);
63         free(sp->cmd);
64         free(sp);
65 }
66
67 void BUG_comm_size(void);
68 procps_status_t* procps_scan(procps_status_t* sp, int flags)
69 {
70         struct dirent *entry;
71         char buf[PROCPS_BUFSIZE];
72         char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
73         char *filename_tail;
74         long tasknice;
75         unsigned pid;
76         int n;
77         struct stat sb;
78
79         if (!sp)
80                 sp = alloc_procps_scan(flags);
81
82         for (;;) {
83                 entry = readdir(sp->dir);
84                 if (entry == NULL) {
85                         free_procps_scan(sp);
86                         return NULL;
87                 }
88                 if (safe_strtou(entry->d_name, &pid))
89                         continue;
90
91                 /* After this point we have to break, not continue
92                  * ("continue" would mean that current /proc/NNN
93                  * is not a valid process info) */
94
95                 memset(&sp->rss, 0, sizeof(*sp) - offsetof(procps_status_t, rss));
96
97                 sp->pid = pid;
98                 if (!(flags & ~PSSCAN_PID)) break;
99
100                 filename_tail = filename + sprintf(filename, "/proc/%d", pid);
101
102                 if (flags & PSSCAN_UIDGID) {
103                         if (stat(filename, &sb))
104                                 break;
105                         /* Need comment - is this effective or read UID/GID? */
106                         sp->uid = sb.st_uid;
107                         sp->gid = sb.st_gid;
108                 }
109         
110                 if (flags & PSSCAN_STAT) {
111                         char *cp;
112                         /* see proc(5) for some details on this */
113                         strcpy(filename_tail, "/stat");
114                         n = read_to_buf(filename, buf);
115                         if (n < 0)
116                                 break;
117                         cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
118                         if (!cp || cp[1] != ' ')
119                                 break;
120                         cp[0] = '\0';
121                         if (sizeof(sp->comm) < 16)
122                                 BUG_comm_size();
123                         sscanf(buf, "%*s (%15c", sp->comm);
124                         n = sscanf(cp+2,
125                                 "%c %u "               /* state, ppid */
126                                 "%u %u %*s %*s "       /* pgid, sid, tty, tpgid */
127                                 "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
128                                 "%lu %lu "             /* utime, stime */
129                                 "%*s %*s %*s "         /* cutime, cstime, priority */
130                                 "%ld "                 /* nice */
131                                 "%*s %*s %*s "         /* timeout, it_real_value, start_time */
132                                 "%*s "                 /* vsize */
133                                 "%lu",                 /* rss */
134                                 sp->state, &sp->ppid,
135                                 &sp->pgid, &sp->sid,
136                                 &sp->utime, &sp->stime,
137                                 &tasknice,
138                                 &sp->rss);
139                         if (n != 8)
140                                 break;
141
142                         if (sp->rss == 0 && sp->state[0] != 'Z')
143                                 sp->state[1] = 'W';
144                         else
145                                 sp->state[1] = ' ';
146                         if (tasknice < 0)
147                                 sp->state[2] = '<';
148                         else if (tasknice > 0)
149                                 sp->state[2] = 'N';
150                         else
151                                 sp->state[2] = ' ';
152
153 #ifdef PAGE_SHIFT
154                         sp->rss <<= (PAGE_SHIFT - 10);     /* 2**10 = 1kb */
155 #else
156                         sp->rss *= (getpagesize() >> 10);     /* 2**10 = 1kb */
157 #endif
158                 }
159
160                 if (flags & PSSCAN_CMD) {
161                         free(sp->cmd);
162                         sp->cmd = NULL;
163                         strcpy(filename_tail, "/cmdline");
164                         n = read_to_buf(filename, buf);
165                         if (n <= 0)
166                                 break;
167                         if (buf[n-1] == '\n') {
168                                 if (!--n)
169                                         break;
170                                 buf[n] = '\0';
171                         }
172                         do {
173                                 n--;
174                                 if ((unsigned char)(buf[n]) < ' ')
175                                         buf[n] = ' ';
176                         } while (n);
177                         sp->cmd = strdup(buf);
178                 }
179                 break;
180         }
181         return sp;
182 }
183 /* from kernel:
184         //             pid comm S ppid pgid sid tty_nr tty_pgrp flg
185         sprintf(buffer,"%d (%s) %c %d  %d   %d  %d     %d       %lu %lu \
186 %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
187 %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
188                 task->pid,
189                 tcomm,
190                 state,
191                 ppid,
192                 pgid,
193                 sid,
194                 tty_nr,
195                 tty_pgrp,
196                 task->flags,
197                 min_flt,
198
199                 cmin_flt,
200                 maj_flt,
201                 cmaj_flt,
202                 cputime_to_clock_t(utime),
203                 cputime_to_clock_t(stime),
204                 cputime_to_clock_t(cutime),
205                 cputime_to_clock_t(cstime),
206                 priority,
207                 nice,
208                 num_threads,
209         // 0,
210                 start_time,
211                 vsize,
212                 mm ? get_mm_rss(mm) : 0,
213                 rsslim,
214                 mm ? mm->start_code : 0,
215                 mm ? mm->end_code : 0,
216                 mm ? mm->start_stack : 0,
217                 esp,
218                 eip,
219 the rest is some obsolete cruft
220 */