#include "libbb.h"
-typedef struct unsigned_to_name_map_t {
- long id;
+typedef struct id_to_name_map_t {
+ uid_t id;
char name[USERNAME_MAX_SIZE];
-} unsigned_to_name_map_t;
+} id_to_name_map_t;
typedef struct cache_t {
- unsigned_to_name_map_t *cache;
+ id_to_name_map_t *cache;
int size;
} cache_t;
#if 0 /* more generic, but we don't need that yet */
/* Returns -N-1 if not found. */
/* cp->cache[N] is allocated and must be filled in this case */
-static int get_cached(cache_t *cp, unsigned id)
+static int get_cached(cache_t *cp, uid_t id)
{
int i;
for (i = 0; i < cp->size; i++)
}
#endif
-static char* get_cached(cache_t *cp, long id,
- char* FAST_FUNC x2x_utoa(long id))
+static char* get_cached(cache_t *cp, uid_t id,
+ char* FAST_FUNC x2x_utoa(uid_t id))
{
int i;
for (i = 0; i < cp->size; i++)
char *str = *endptr;
unsigned long n = 0;
- while ((c = *str++) != ' ') {
+ /* Need to stop on both ' ' and '\n' */
+ while ((c = *str++) > ' ') {
c = ((c|0x20) - '0');
if (c > 9)
- // c = c + '0' - 'a' + 10:
+ /* c = c + '0' - 'a' + 10: */
c = c - ('a' - '0' - 10);
n = n*16 + c;
}
/* We cut a lot of corners here for speed */
static unsigned long fast_strtoul_10(char **endptr)
{
- char c;
+ unsigned char c;
char *str = *endptr;
unsigned long n = *str - '0';
- while ((c = *++str) != ' ')
+ /* Need to stop on both ' ' and '\n' */
+ while ((c = *++str) > ' ')
n = n*10 + (c - '0');
*endptr = str + 1; /* We skip trailing space! */
return n;
}
+# if ENABLE_FEATURE_FAST_TOP
static long fast_strtol_10(char **endptr)
{
if (**endptr != '-')
(*endptr)++;
return - (long)fast_strtoul_10(endptr);
}
+# endif
static char *skip_fields(char *str, int count)
{
#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)
+ void (*cb)(struct smaprec *, void *), void *data)
{
FILE *file;
struct smaprec currec;
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
+ // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
// Size: nnn kB
// Rss: nnn kB
// .....
tp = strchr(buf, '-');
if (tp) {
// We reached next mapping - the line of this form:
- // f7d29000-f7d39000 rw-s ADR M:m OFS FILE
+ // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
if (cb) {
/* If we have a previous record, there's nothing more
strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
- // skipping "rw-s ADR M:m OFS "
+ // skipping "rw-s FILEOFS M:m INODE "
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) {
void BUG_comm_size(void);
procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
{
- struct dirent *entry;
- char buf[PROCPS_BUFSIZE];
- char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
- char *filename_tail;
- long tasknice;
- unsigned pid;
- int n;
- struct stat sb;
-
if (!sp)
sp = alloc_procps_scan();
for (;;) {
+ struct dirent *entry;
+ char buf[PROCPS_BUFSIZE];
+ long tasknice;
+ unsigned pid;
+ int n;
+ char filename[sizeof("/proc/%u/task/%u/cmdline") + sizeof(int)*3 * 2];
+ char *filename_tail;
+
#if ENABLE_FEATURE_SHOW_THREADS
- if ((flags & PSSCAN_TASKS) && sp->task_dir) {
+ if (sp->task_dir) {
entry = readdir(sp->task_dir);
if (entry)
goto got_entry;
closedir(sp->task_dir);
sp->task_dir = NULL;
- sp->main_thread_pid = 0;
}
#endif
entry = readdir(sp->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);
+ sprintf(filename, "/proc/%u/task", pid);
+ /* Note: if opendir fails, we just go to next /proc/XXX */
+ sp->task_dir = opendir(filename);
sp->main_thread_pid = pid;
continue;
}
}
#endif
- filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
+#if ENABLE_FEATURE_SHOW_THREADS
+ if (sp->task_dir)
+ filename_tail = filename + sprintf(filename, "/proc/%u/task/%u/", sp->main_thread_pid, pid);
+ else
+#endif
+ filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
if (flags & PSSCAN_UIDGID) {
+ struct stat sb;
if (stat(filename, &sb))
continue; /* process probably exited */
/* Effective UID/GID, not real */
sp->gid = sb.st_gid;
}
- if (flags & PSSCAN_STAT) {
+ /* These are all retrieved from proc/NN/stat in one go: */
+ if (flags & (PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
+ | PSSCAN_COMM | PSSCAN_STATE
+ | PSSCAN_VSZ | PSSCAN_RSS
+ | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
+ | PSSCAN_TTY | PSSCAN_NICE
+ | PSSCAN_CPU)
+ ) {
char *cp, *comm1;
int tty;
#if !ENABLE_FEATURE_FAST_TOP
if (n < 11)
continue; /* bogus data, get next /proc/XXX */
# if ENABLE_FEATURE_TOP_SMP_PROCESS
- if (n < 11+15)
+ if (n == 11)
sp->last_seen_on_cpu = 0;
# endif
//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 */
+#endif /* FEATURE_FAST_TOP */
#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
sp->niceness = tasknice;
void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
{
int sz;
- char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
+ char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
sprintf(filename, "/proc/%u/cmdline", pid);
sz = open_read_close(filename, buf, col - 1);
if (sz > 0) {
+ const char *base;
+ int comm_len;
+
buf[sz] = '\0';
while (--sz >= 0 && buf[sz] == '\0')
continue;
- do {
+ /* Prevent basename("process foo/bar") = "bar" */
+ strchrnul(buf, ' ')[0] = '\0';
+ base = bb_basename(buf); /* before we replace argv0's NUL with space */
+ while (sz >= 0) {
if ((unsigned char)(buf[sz]) < ' ')
buf[sz] = ' ';
- } while (--sz >= 0);
+ sz--;
+ }
+
+ /* If comm differs from argv0, prepend "{comm} ".
+ * It allows to see thread names set by prctl(PR_SET_NAME).
+ */
+ if (base[0] == '-') /* "-sh" (login shell)? */
+ base++;
+ comm_len = strlen(comm);
+ /* Why compare up to comm_len, not COMM_LEN-1?
+ * Well, some processes rewrite argv, and use _spaces_ there
+ * while rewriting. (KDE is observed to do it).
+ * I prefer to still treat argv0 "process foo bar"
+ * as 'equal' to comm "process".
+ */
+ if (strncmp(base, comm, comm_len) != 0) {
+ comm_len += 3;
+ if (col > comm_len)
+ memmove(buf + comm_len, buf, col - comm_len);
+ snprintf(buf, col, "{%s}", comm);
+ if (col <= comm_len)
+ return;
+ buf[comm_len - 1] = ' ';
+ buf[col - 1] = '\0';
+ }
+
} else {
snprintf(buf, col, "[%s]", comm);
}