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 * SELinux support: (c) 2007 by Yuichi Nakamura <ynakam@hitachisoft.jp> 8 * 9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 10 */ 11 12#include "libbb.h" 13 14 15typedef struct unsigned_to_name_map_t { 16 unsigned 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 clear_username_cache(void) 34{ 35 clear_cache(&username); 36 clear_cache(&groupname); 37} 38 39 40typedef char* ug_func(char *name, int bufsize, long uid); 41static char* get_cached(cache_t *cp, unsigned id, ug_func* fp) 42{ 43 int i; 44 for (i = 0; i < cp->size; i++) 45 if (cp->cache[i].id == id) 46 return cp->cache[i].name; 47 i = cp->size++; 48 cp->cache = xrealloc(cp->cache, cp->size * sizeof(*cp->cache)); 49 cp->cache[i].id = id; 50 /* Never fails. Generates numeric string if name isn't found */ 51 fp(cp->cache[i].name, sizeof(cp->cache[i].name), id); 52 return cp->cache[i].name; 53} 54const char* get_cached_username(uid_t uid) 55{ 56 return get_cached(&username, uid, bb_getpwuid); 57} 58const char* get_cached_groupname(gid_t gid) 59{ 60 return get_cached(&groupname, gid, bb_getgrgid); 61} 62 63 64#define PROCPS_BUFSIZE 1024 65 66static int read_to_buf(const char *filename, void *buf) 67{ 68 int fd; 69 /* open_read_close() would do two reads, checking for EOF. 70 * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */ 71 ssize_t ret = -1; 72 fd = open(filename, O_RDONLY); 73 if (fd >= 0) { 74 ret = read(fd, buf, PROCPS_BUFSIZE-1); 75 close(fd); 76 } 77 ((char *)buf)[ret > 0 ? ret : 0] = '\0'; 78 return ret; 79} 80 81procps_status_t *alloc_procps_scan(int flags) 82{ 83 procps_status_t* sp = xzalloc(sizeof(procps_status_t)); 84 sp->dir = xopendir("/proc"); 85 return sp; 86} 87 88void free_procps_scan(procps_status_t* sp) 89{ 90 closedir(sp->dir); 91 free(sp->argv0); 92 USE_SELINUX(free(sp->context);) 93 free(sp); 94} 95 96#if ENABLE_FEATURE_FAST_TOP 97/* We cut a lot of corners here for speed */ 98static unsigned long fast_strtoul_10(char **endptr) 99{ 100 char c; 101 char *str = *endptr; 102 unsigned long n = *str - '0'; 103 104 while ((c = *++str) != ' ') 105 n = n*10 + (c - '0'); 106 107 *endptr = str + 1; /* We skip trailing space! */ 108 return n; 109} 110static char *skip_fields(char *str, int count) 111{ 112 do { 113 while (*str++ != ' ') 114 continue; 115 /* we found a space char, str points after it */ 116 } while (--count); 117 return str; 118} 119#endif 120 121void BUG_comm_size(void); 122procps_status_t *procps_scan(procps_status_t* sp, int flags) 123{ 124 struct dirent *entry; 125 char buf[PROCPS_BUFSIZE]; 126 char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; 127 char *filename_tail; 128 long tasknice; 129 unsigned pid; 130 int n; 131 struct stat sb; 132 133 if (!sp) 134 sp = alloc_procps_scan(flags); 135 136 for (;;) { 137 entry = readdir(sp->dir); 138 if (entry == NULL) { 139 free_procps_scan(sp); 140 return NULL; 141 } 142 pid = bb_strtou(entry->d_name, NULL, 10); 143 if (errno) 144 continue; 145 146 /* After this point we have to break, not continue 147 * ("continue" would mean that current /proc/NNN 148 * is not a valid process info) */ 149 150 memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); 151 152 sp->pid = pid; 153 if (!(flags & ~PSSCAN_PID)) break; 154 155#if ENABLE_SELINUX 156 if (flags & PSSCAN_CONTEXT) { 157 if (getpidcon(sp->pid, &sp->context) < 0) 158 sp->context = NULL; 159 } 160#endif 161 162 filename_tail = filename + sprintf(filename, "/proc/%d", pid); 163 164 if (flags & PSSCAN_UIDGID) { 165 if (stat(filename, &sb)) 166 break; 167 /* Need comment - is this effective or real UID/GID? */ 168 sp->uid = sb.st_uid; 169 sp->gid = sb.st_gid; 170 } 171 172 if (flags & PSSCAN_STAT) { 173 char *cp, *comm1; 174 int tty; 175#if !ENABLE_FEATURE_FAST_TOP 176 unsigned long vsz, rss; 177#endif 178 179 /* see proc(5) for some details on this */ 180 strcpy(filename_tail, "/stat"); 181 n = read_to_buf(filename, buf); 182 if (n < 0) 183 break; 184 cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ 185 /*if (!cp || cp[1] != ' ') 186 break;*/ 187 cp[0] = '\0'; 188 if (sizeof(sp->comm) < 16) 189 BUG_comm_size(); 190 comm1 = strchr(buf, '('); 191 /*if (comm1)*/ 192 safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm)); 193 194#if !ENABLE_FEATURE_FAST_TOP 195 n = sscanf(cp+2, 196 "%c %u " /* state, ppid */ 197 "%u %u %d %*s " /* pgid, sid, tty, tpgid */ 198 "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ 199 "%lu %lu " /* utime, stime */ 200 "%*s %*s %*s " /* cutime, cstime, priority */ 201 "%ld " /* nice */ 202 "%*s %*s %*s " /* timeout, it_real_value, start_time */ 203 "%lu " /* vsize */ 204 "%lu " /* rss */ 205 /* "%lu %lu %lu %lu %lu %lu " rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 206 /* "%u %u %u %u " signal, blocked, sigignore, sigcatch */ 207 /* "%lu %lu %lu" wchan, nswap, cnswap */ 208 , 209 sp->state, &sp->ppid, 210 &sp->pgid, &sp->sid, &tty, 211 &sp->utime, &sp->stime, 212 &tasknice, 213 &vsz, 214 &rss); 215 if (n != 10) 216 break; 217 sp->vsz = vsz >> 10; /* vsize is in bytes and we want kb */ 218 sp->rss = rss >> 10; 219 sp->tty_major = (tty >> 8) & 0xfff; 220 sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00); 221#else 222/* This costs ~100 bytes more but makes top faster by 20% 223 * If you run 10000 processes, this may be important for you */ 224 sp->state[0] = cp[2]; 225 cp += 4; 226 sp->ppid = fast_strtoul_10(&cp); 227 sp->pgid = fast_strtoul_10(&cp); 228 sp->sid = fast_strtoul_10(&cp); 229 tty = fast_strtoul_10(&cp); 230 sp->tty_major = (tty >> 8) & 0xfff; 231 sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00); 232 cp = skip_fields(cp, 6); /* tpgid, flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ 233 sp->utime = fast_strtoul_10(&cp); 234 sp->stime = fast_strtoul_10(&cp); 235 cp = skip_fields(cp, 3); /* cutime, cstime, priority */ 236 tasknice = fast_strtoul_10(&cp); 237 cp = skip_fields(cp, 3); /* timeout, it_real_value, start_time */ 238 sp->vsz = fast_strtoul_10(&cp) >> 10; /* vsize is in bytes and we want kb */ 239 sp->rss = fast_strtoul_10(&cp) >> 10; 240#endif 241 242 if (sp->vsz == 0 && sp->state[0] != 'Z') 243 sp->state[1] = 'W'; 244 else 245 sp->state[1] = ' '; 246 if (tasknice < 0) 247 sp->state[2] = '<'; 248 else if (tasknice) /* > 0 */ 249 sp->state[2] = 'N'; 250 else 251 sp->state[2] = ' '; 252 253 } 254 255 if (flags & PSSCAN_ARGV0) { 256 if (sp->argv0) { 257 free(sp->argv0); 258 sp->argv0 = NULL; 259 } 260 strcpy(filename_tail, "/cmdline"); 261 n = read_to_buf(filename, buf); 262 if (n <= 0) 263 break; 264 if (flags & PSSCAN_ARGV0) 265 sp->argv0 = xstrdup(buf); 266 } 267 break; 268 } 269 return sp; 270} 271 272void read_cmdline(char *buf, int col, unsigned pid, const char *comm) 273{ 274 ssize_t sz; 275 char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; 276 277 sprintf(filename, "/proc/%u/cmdline", pid); 278 sz = open_read_close(filename, buf, col); 279 if (sz > 0) { 280 buf[sz] = '\0'; 281 while (--sz >= 0) 282 if ((unsigned char)(buf[sz]) < ' ') 283 buf[sz] = ' '; 284 } else { 285 snprintf(buf, col, "[%s]", comm); 286 } 287} 288 289/* from kernel: 290 // pid comm S ppid pgid sid tty_nr tty_pgrp flg 291 sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ 292%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ 293%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n", 294 task->pid, 295 tcomm, 296 state, 297 ppid, 298 pgid, 299 sid, 300 tty_nr, 301 tty_pgrp, 302 task->flags, 303 min_flt, 304 cmin_flt, 305 maj_flt, 306 cmaj_flt, 307 cputime_to_clock_t(utime), 308 cputime_to_clock_t(stime), 309 cputime_to_clock_t(cutime), 310 cputime_to_clock_t(cstime), 311 priority, 312 nice, 313 num_threads, 314 // 0, 315 start_time, 316 vsize, 317 mm ? get_mm_rss(mm) : 0, 318 rsslim, 319 mm ? mm->start_code : 0, 320 mm ? mm->end_code : 0, 321 mm ? mm->start_stack : 0, 322 esp, 323 eip, 324the rest is some obsolete cruft 325*/ 326