X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fprocps.c;h=14d4481bd1d2dab3e4ffdecf3304a10b23d38780;hb=0834a6d3b9daec1f460c3cc836136ace12c53df0;hp=f799099fd33cb8887fd6980ade421ee32a7a1481;hpb=3b3ca113ed00a6781a28172bb3a6860a5f79ea02;p=oweals%2Fbusybox.git diff --git a/libbb/procps.c b/libbb/procps.c index f799099fd..14d4481bd 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -6,14 +6,14 @@ * Copyright (C) 2002 by Vladimir Oleynik * SELinux support: (c) 2007 by Yuichi Nakamura * - * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ #include "libbb.h" typedef struct unsigned_to_name_map_t { - unsigned id; + long id; char name[USERNAME_MAX_SIZE]; } unsigned_to_name_map_t; @@ -45,34 +45,34 @@ static int get_cached(cache_t *cp, unsigned id) for (i = 0; i < cp->size; i++) if (cp->cache[i].id == id) return i; - i = cp->size; - cp->cache = xrealloc_vector(cp->cache, 2, cp->size++); + i = cp->size++; + cp->cache = xrealloc_vector(cp->cache, 2, i); cp->cache[i++].id = id; return -i; } #endif -typedef char* FAST_FUNC ug_func(char *name, int bufsize, long uid); -static char* get_cached(cache_t *cp, unsigned id, ug_func* fp) +static char* get_cached(cache_t *cp, long id, + char* FAST_FUNC x2x_utoa(long id)) { int i; for (i = 0; i < cp->size; i++) if (cp->cache[i].id == id) return cp->cache[i].name; - i = cp->size; - cp->cache = xrealloc_vector(cp->cache, 2, cp->size++); + i = cp->size++; + cp->cache = xrealloc_vector(cp->cache, 2, i); cp->cache[i].id = id; /* Never fails. Generates numeric string if name isn't found */ - fp(cp->cache[i].name, sizeof(cp->cache[i].name), id); + safe_strncpy(cp->cache[i].name, x2x_utoa(id), sizeof(cp->cache[i].name)); return cp->cache[i].name; } const char* FAST_FUNC get_cached_username(uid_t uid) { - return get_cached(&username, uid, bb_getpwuid); + return get_cached(&username, uid, uid2uname_utoa); } const char* FAST_FUNC get_cached_groupname(gid_t gid) { - return get_cached(&groupname, gid, bb_getgrgid); + return get_cached(&groupname, gid, gid2group_utoa); } @@ -110,8 +110,13 @@ static procps_status_t* FAST_FUNC alloc_procps_scan(void) void FAST_FUNC free_procps_scan(procps_status_t* sp) { closedir(sp->dir); +#if ENABLE_FEATURE_SHOW_THREADS + if (sp->task_dir) + closedir(sp->task_dir); +#endif free(sp->argv0); - USE_SELINUX(free(sp->context);) + free(sp->exe); + IF_SELINUX(free(sp->context);) free(sp); } @@ -132,12 +137,9 @@ static unsigned long fast_strtoul_16(char **endptr) *endptr = str; /* We skip trailing space! */ return n; } -/* TOPMEM uses fast_strtoul_10, so... */ -#undef ENABLE_FEATURE_FAST_TOP -#define ENABLE_FEATURE_FAST_TOP 1 #endif -#if ENABLE_FEATURE_FAST_TOP +#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP /* We cut a lot of corners here for speed */ static unsigned long fast_strtoul_10(char **endptr) { @@ -151,6 +153,16 @@ static unsigned long fast_strtoul_10(char **endptr) *endptr = str + 1; /* We skip trailing space! */ return n; } + +static long fast_strtol_10(char **endptr) +{ + if (**endptr != '-') + return fast_strtoul_10(endptr); + + (*endptr)++; + return - (long)fast_strtoul_10(endptr); +} + static char *skip_fields(char *str, int count) { do { @@ -162,6 +174,111 @@ static char *skip_fields(char *str, int count) } #endif +#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP +int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total, + void (*cb)(struct smaprec *, void *), void *data) +{ + FILE *file; + struct smaprec currec; + char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; + char buf[PROCPS_BUFSIZE]; +#if !ENABLE_PMAP + void (*cb)(struct smaprec *, void *) = NULL; + void *data = NULL; +#endif + + sprintf(filename, "/proc/%u/smaps", (int)pid); + + file = fopen_for_read(filename); + if (!file) + return 1; + + memset(&currec, 0, sizeof(currec)); + while (fgets(buf, PROCPS_BUFSIZE, file)) { + // Each mapping datum has this form: + // f7d29000-f7d39000 rw-s ADR M:m OFS FILE + // Size: nnn kB + // Rss: nnn kB + // ..... + + char *tp = buf, *p; + +#define SCAN(S, X) \ + if (strncmp(tp, S, sizeof(S)-1) == 0) { \ + tp = skip_whitespace(tp + sizeof(S)-1); \ + total->X += currec.X = fast_strtoul_10(&tp); \ + continue; \ + } + if (cb) { + SCAN("Pss:" , smap_pss ); + SCAN("Swap:" , smap_swap ); + } + SCAN("Private_Dirty:", private_dirty); + SCAN("Private_Clean:", private_clean); + SCAN("Shared_Dirty:" , shared_dirty ); + SCAN("Shared_Clean:" , shared_clean ); +#undef SCAN + tp = strchr(buf, '-'); + if (tp) { + // We reached next mapping - the line of this form: + // f7d29000-f7d39000 rw-s ADR M:m OFS FILE + + if (cb) { + /* If we have a previous record, there's nothing more + * for it, call the callback and clear currec + */ + if (currec.smap_size) + cb(&currec, data); + free(currec.smap_name); + } + memset(&currec, 0, sizeof(currec)); + + *tp = ' '; + tp = buf; + currec.smap_start = fast_strtoul_16(&tp); + currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10; + + strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); + + // skipping "rw-s ADR M:m OFS " + tp = skip_whitespace(skip_fields(tp, 4)); + // filter out /dev/something (something != zero) + if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { + if (currec.smap_mode[1] == 'w') { + currec.mapped_rw = currec.smap_size; + total->mapped_rw += currec.smap_size; + } else if (currec.smap_mode[1] == '-') { + currec.mapped_ro = currec.smap_size; + total->mapped_ro += currec.smap_size; + } + } + + if (strcmp(tp, "[stack]\n") == 0) + total->stack += currec.smap_size; + if (cb) { + p = skip_non_whitespace(tp); + if (p == tp) { + currec.smap_name = xstrdup(" [ anon ]"); + } else { + *p = '\0'; + currec.smap_name = xstrdup(tp); + } + } + total->smap_size += currec.smap_size; + } + } + fclose(file); + + if (cb) { + if (currec.smap_size) + cb(&currec, data); + free(currec.smap_name); + } + + return 0; +} +#endif + void BUG_comm_size(void); procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) { @@ -178,23 +295,46 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) sp = alloc_procps_scan(); for (;;) { +#if ENABLE_FEATURE_SHOW_THREADS + if ((flags & PSSCAN_TASKS) && sp->task_dir) { + entry = readdir(sp->task_dir); + if (entry) + goto got_entry; + closedir(sp->task_dir); + sp->task_dir = NULL; + } +#endif entry = readdir(sp->dir); if (entry == NULL) { free_procps_scan(sp); return NULL; } + IF_FEATURE_SHOW_THREADS(got_entry:) pid = bb_strtou(entry->d_name, NULL, 10); if (errno) continue; +#if ENABLE_FEATURE_SHOW_THREADS + if ((flags & PSSCAN_TASKS) && !sp->task_dir) { + /* We found another /proc/PID. Do not use it, + * there will be /proc/PID/task/PID (same PID!), + * so just go ahead and dive into /proc/PID/task. */ + char task_dir[sizeof("/proc/%u/task") + sizeof(int)*3]; + sprintf(task_dir, "/proc/%u/task", pid); + sp->task_dir = xopendir(task_dir); + continue; + } +#endif - /* After this point we have to break, not continue - * ("continue" would mean that current /proc/NNN - * is not a valid process info) */ + /* After this point we can: + * "break": stop parsing, return the data + * "continue": try next /proc/XXX + */ memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); sp->pid = pid; - if (!(flags & ~PSSCAN_PID)) break; + if (!(flags & ~PSSCAN_PID)) + break; /* we needed only pid, we got it */ #if ENABLE_SELINUX if (flags & PSSCAN_CONTEXT) { @@ -203,12 +343,12 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) } #endif - filename_tail = filename + sprintf(filename, "/proc/%d", pid); + filename_tail = filename + sprintf(filename, "/proc/%u/", pid); if (flags & PSSCAN_UIDGID) { if (stat(filename, &sb)) - break; - /* Need comment - is this effective or real UID/GID? */ + continue; /* process probably exited */ + /* Effective UID/GID, not real */ sp->uid = sb.st_uid; sp->gid = sb.st_gid; } @@ -219,15 +359,14 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) #if !ENABLE_FEATURE_FAST_TOP unsigned long vsz, rss; #endif - /* see proc(5) for some details on this */ - strcpy(filename_tail, "/stat"); + strcpy(filename_tail, "stat"); n = read_to_buf(filename, buf); if (n < 0) - break; + continue; /* process probably exited */ cp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ /*if (!cp || cp[1] != ' ') - break;*/ + continue;*/ cp[0] = '\0'; if (sizeof(sp->comm) < 16) BUG_comm_size(); @@ -247,9 +386,12 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) "%lu " /* start_time */ "%lu " /* vsize */ "%lu " /* rss */ - /* "%lu %lu %lu %lu %lu %lu " rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ - /* "%u %u %u %u " signal, blocked, sigignore, sigcatch */ - /* "%lu %lu %lu" wchan, nswap, cnswap */ +# if ENABLE_FEATURE_TOP_SMP_PROCESS + "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ + "%*s %*s %*s %*s " /*signal, blocked, sigignore, sigcatch */ + "%*s %*s %*s %*s " /*wchan, nswap, cnswap, exit_signal */ + "%d" /*cpu last seen on*/ +# endif , sp->state, &sp->ppid, &sp->pgid, &sp->sid, &tty, @@ -257,9 +399,19 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) &tasknice, &sp->start_time, &vsz, - &rss); - if (n != 11) - break; + &rss +# if ENABLE_FEATURE_TOP_SMP_PROCESS + , &sp->last_seen_on_cpu +# endif + ); + + if (n < 11) + continue; /* bogus data, get next /proc/XXX */ +# if ENABLE_FEATURE_TOP_SMP_PROCESS + if (n < 11+15) + sp->last_seen_on_cpu = 0; +# endif + /* vsz is in bytes and we want kb */ sp->vsz = vsz >> 10; /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ @@ -281,13 +433,25 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) sp->utime = fast_strtoul_10(&cp); sp->stime = fast_strtoul_10(&cp); cp = skip_fields(cp, 3); /* cutime, cstime, priority */ - tasknice = fast_strtoul_10(&cp); + tasknice = fast_strtol_10(&cp); cp = skip_fields(cp, 2); /* timeout, it_real_value */ sp->start_time = fast_strtoul_10(&cp); /* vsz is in bytes and we want kb */ sp->vsz = fast_strtoul_10(&cp) >> 10; /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; +# if ENABLE_FEATURE_TOP_SMP_PROCESS + /* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ + /* (4): signal, blocked, sigignore, sigcatch */ + /* (4): wchan, nswap, cnswap, exit_signal */ + cp = skip_fields(cp, 14); +//FIXME: is it safe to assume this field exists? + sp->last_seen_on_cpu = fast_strtoul_10(&cp); +# endif +#endif /* end of !ENABLE_FEATURE_TOP_SMP_PROCESS */ + +#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS + sp->niceness = tasknice; #endif if (sp->vsz == 0 && sp->state[0] != 'Z') @@ -300,67 +464,53 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) sp->state[2] = 'N'; else sp->state[2] = ' '; - } #if ENABLE_FEATURE_TOPMEM - if (flags & (PSSCAN_SMAPS)) { + if (flags & PSSCAN_SMAPS) + procps_read_smaps(pid, &sp->smaps, NULL, NULL); +#endif /* TOPMEM */ +#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS + if (flags & PSSCAN_RUIDGID) { FILE *file; - strcpy(filename_tail, "/smaps"); - file = fopen(filename, "r"); - if (!file) - break; - while (fgets(buf, sizeof(buf), file)) { - unsigned long sz; - char *tp; - char w; -#define SCAN(str, name) \ + strcpy(filename_tail, "status"); + file = fopen_for_read(filename); + if (file) { + while (fgets(buf, sizeof(buf), file)) { + char *tp; +#define SCAN_TWO(str, name, statement) \ if (strncmp(buf, str, sizeof(str)-1) == 0) { \ tp = skip_whitespace(buf + sizeof(str)-1); \ - sp->name += fast_strtoul_10(&tp); \ - continue; \ + sscanf(tp, "%u", &sp->name); \ + statement; \ } - SCAN("Shared_Clean:" , shared_clean ); - SCAN("Shared_Dirty:" , shared_dirty ); - SCAN("Private_Clean:", private_clean); - SCAN("Private_Dirty:", private_dirty); -#undef SCAN - // f7d29000-f7d39000 rw-s ADR M:m OFS FILE - tp = strchr(buf, '-'); - if (tp) { - *tp = ' '; - tp = buf; - sz = fast_strtoul_16(&tp); /* start */ - sz = (fast_strtoul_16(&tp) - sz) >> 10; /* end - start */ - // tp -> "rw-s" string - w = tp[1]; - // skipping "rw-s ADR M:m OFS " - tp = skip_whitespace(skip_fields(tp, 4)); - // filter out /dev/something (something != zero) - if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) { - if (w == 'w') { - sp->mapped_rw += sz; - } else if (w == '-') { - sp->mapped_ro += sz; - } - } -//else printf("DROPPING %s (%s)\n", buf, tp); - if (strcmp(tp, "[stack]\n") == 0) - sp->stack += sz; + SCAN_TWO("Uid:", ruid, continue); + SCAN_TWO("Gid:", rgid, break); +#undef SCAN_TWO } + fclose(file); } - fclose(file); } -#endif /* TOPMEM */ - +#endif /* PS_ADDITIONAL_COLUMNS */ + if (flags & PSSCAN_EXE) { + strcpy(filename_tail, "exe"); + free(sp->exe); + sp->exe = xmalloc_readlink(filename); + } + /* Note: if /proc/PID/cmdline is empty, + * code below "breaks". Therefore it must be + * the last code to parse /proc/PID/xxx data + * (we used to have /proc/PID/exe parsing after it + * and were getting stale sp->exe). + */ #if 0 /* PSSCAN_CMD is not used */ if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) { free(sp->argv0); sp->argv0 = NULL; free(sp->cmd); sp->cmd = NULL; - strcpy(filename_tail, "/cmdline"); + strcpy(filename_tail, "cmdline"); /* TODO: to get rid of size limits, read into malloc buf, * then realloc it down to real size. */ n = read_to_buf(filename, buf); @@ -381,7 +531,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) { free(sp->argv0); sp->argv0 = NULL; - strcpy(filename_tail, "/cmdline"); + strcpy(filename_tail, "cmdline"); n = read_to_buf(filename, buf); if (n <= 0) break; @@ -397,22 +547,26 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags) } #endif break; - } + } /* for (;;) */ + return sp; } void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) { - ssize_t sz; + int sz; char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; sprintf(filename, "/proc/%u/cmdline", pid); - sz = open_read_close(filename, buf, col); + sz = open_read_close(filename, buf, col - 1); if (sz > 0) { buf[sz] = '\0'; - while (--sz >= 0) + while (--sz >= 0 && buf[sz] == '\0') + continue; + do { if ((unsigned char)(buf[sz]) < ' ') buf[sz] = ' '; + } while (--sz >= 0); } else { snprintf(buf, col, "[%s]", comm); }