+
+typedef struct unsigned_to_name_map_t {
+ long id;
+ char name[USERNAME_MAX_SIZE];
+} unsigned_to_name_map_t;
+
+typedef struct cache_t {
+ unsigned_to_name_map_t *cache;
+ int size;
+} cache_t;
+
+static cache_t username, groupname;
+
+static void clear_cache(cache_t *cp)
+{
+ free(cp->cache);
+ cp->cache = NULL;
+ cp->size = 0;
+}
+void FAST_FUNC clear_username_cache(void)
+{
+ clear_cache(&username);
+ clear_cache(&groupname);
+}
+
+#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)
+{
+ int i;
+ 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, i);
+ cp->cache[i++].id = id;
+ return -i;
+}
+#endif
+
+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, i);
+ cp->cache[i].id = id;
+ /* Never fails. Generates numeric string if name isn't found */
+ 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, uid2uname_utoa);
+}
+const char* FAST_FUNC get_cached_groupname(gid_t gid)
+{
+ return get_cached(&groupname, gid, gid2group_utoa);
+}
+
+
+#define PROCPS_BUFSIZE 1024
+
+static int read_to_buf(const char *filename, void *buf)
+{
+ int fd;
+ /* open_read_close() would do two reads, checking for EOF.
+ * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */
+ ssize_t ret = -1;
+ fd = open(filename, O_RDONLY);
+ if (fd >= 0) {
+ ret = read(fd, buf, PROCPS_BUFSIZE-1);
+ close(fd);
+ }
+ ((char *)buf)[ret > 0 ? ret : 0] = '\0';
+ return ret;
+}
+
+static procps_status_t* FAST_FUNC alloc_procps_scan(void)
+{
+ unsigned n = getpagesize();
+ procps_status_t* sp = xzalloc(sizeof(procps_status_t));
+ sp->dir = xopendir("/proc");
+ while (1) {
+ n >>= 1;
+ if (!n) break;
+ sp->shift_pages_to_bytes++;
+ }
+ sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10;
+ return sp;
+}
+
+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);
+ free(sp->exe);
+ IF_SELINUX(free(sp->context);)
+ free(sp);
+}
+
+#if ENABLE_FEATURE_TOPMEM
+static unsigned long fast_strtoul_16(char **endptr)
+{
+ unsigned char c;
+ char *str = *endptr;
+ unsigned long n = 0;
+
+ while ((c = *str++) != ' ') {
+ c = ((c|0x20) - '0');
+ if (c > 9)
+ // c = c + '0' - 'a' + 10:
+ c = c - ('a' - '0' - 10);
+ n = n*16 + c;
+ }
+ *endptr = str; /* We skip trailing space! */
+ return n;
+}
+#endif
+
+#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)
+{
+ char c;
+ char *str = *endptr;
+ unsigned long n = *str - '0';
+
+ while ((c = *++str) != ' ')
+ n = n*10 + (c - '0');
+
+ *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 {
+ while (*str++ != ' ')
+ continue;
+ /* we found a space char, str points after it */
+ } while (--count);
+ return str;
+}
+#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)