busybox/libbb/procps.c
<<
>>
Prefs
   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 source tree.
  10 */
  11#include "libbb.h"
  12
  13
  14typedef struct id_to_name_map_t {
  15        uid_t id;
  16        char name[USERNAME_MAX_SIZE];
  17} id_to_name_map_t;
  18
  19typedef struct cache_t {
  20        id_to_name_map_t *cache;
  21        int size;
  22} cache_t;
  23
  24static cache_t username, groupname;
  25
  26static void clear_cache(cache_t *cp)
  27{
  28        free(cp->cache);
  29        cp->cache = NULL;
  30        cp->size = 0;
  31}
  32void FAST_FUNC clear_username_cache(void)
  33{
  34        clear_cache(&username);
  35        clear_cache(&groupname);
  36}
  37
  38#if 0 /* more generic, but we don't need that yet */
  39/* Returns -N-1 if not found. */
  40/* cp->cache[N] is allocated and must be filled in this case */
  41static int get_cached(cache_t *cp, uid_t id)
  42{
  43        int i;
  44        for (i = 0; i < cp->size; i++)
  45                if (cp->cache[i].id == id)
  46                        return i;
  47        i = cp->size++;
  48        cp->cache = xrealloc_vector(cp->cache, 2, i);
  49        cp->cache[i++].id = id;
  50        return -i;
  51}
  52#endif
  53
  54static char* get_cached(cache_t *cp, uid_t id,
  55                        char* FAST_FUNC x2x_utoa(uid_t id))
  56{
  57        int i;
  58        for (i = 0; i < cp->size; i++)
  59                if (cp->cache[i].id == id)
  60                        return cp->cache[i].name;
  61        i = cp->size++;
  62        cp->cache = xrealloc_vector(cp->cache, 2, i);
  63        cp->cache[i].id = id;
  64        /* Never fails. Generates numeric string if name isn't found */
  65        safe_strncpy(cp->cache[i].name, x2x_utoa(id), sizeof(cp->cache[i].name));
  66        return cp->cache[i].name;
  67}
  68const char* FAST_FUNC get_cached_username(uid_t uid)
  69{
  70        return get_cached(&username, uid, uid2uname_utoa);
  71}
  72const char* FAST_FUNC get_cached_groupname(gid_t gid)
  73{
  74        return get_cached(&groupname, gid, gid2group_utoa);
  75}
  76
  77
  78#define PROCPS_BUFSIZE 1024
  79
  80static int read_to_buf(const char *filename, void *buf)
  81{
  82        int fd;
  83        /* open_read_close() would do two reads, checking for EOF.
  84         * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */
  85        ssize_t ret = -1;
  86        fd = open(filename, O_RDONLY);
  87        if (fd >= 0) {
  88                ret = read(fd, buf, PROCPS_BUFSIZE-1);
  89                close(fd);
  90        }
  91        ((char *)buf)[ret > 0 ? ret : 0] = '\0';
  92        return ret;
  93}
  94
  95static procps_status_t* FAST_FUNC alloc_procps_scan(void)
  96{
  97        unsigned n = getpagesize();
  98        procps_status_t* sp = xzalloc(sizeof(procps_status_t));
  99        sp->dir = xopendir("/proc");
 100        while (1) {
 101                n >>= 1;
 102                if (!n) break;
 103                sp->shift_pages_to_bytes++;
 104        }
 105        sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10;
 106        return sp;
 107}
 108
 109void FAST_FUNC free_procps_scan(procps_status_t* sp)
 110{
 111        closedir(sp->dir);
 112#if ENABLE_FEATURE_SHOW_THREADS
 113        if (sp->task_dir)
 114                closedir(sp->task_dir);
 115#endif
 116        free(sp->argv0);
 117        free(sp->exe);
 118        IF_SELINUX(free(sp->context);)
 119        free(sp);
 120}
 121
 122#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
 123static unsigned long fast_strtoul_16(char **endptr)
 124{
 125        unsigned char c;
 126        char *str = *endptr;
 127        unsigned long n = 0;
 128
 129        /* Need to stop on both ' ' and '\n' */
 130        while ((c = *str++) > ' ') {
 131                c = ((c|0x20) - '0');
 132                if (c > 9)
 133                        /* c = c + '0' - 'a' + 10: */
 134                        c = c - ('a' - '0' - 10);
 135                n = n*16 + c;
 136        }
 137        *endptr = str; /* We skip trailing space! */
 138        return n;
 139}
 140#endif
 141
 142#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
 143/* We cut a lot of corners here for speed */
 144static unsigned long fast_strtoul_10(char **endptr)
 145{
 146        unsigned char c;
 147        char *str = *endptr;
 148        unsigned long n = *str - '0';
 149
 150        /* Need to stop on both ' ' and '\n' */
 151        while ((c = *++str) > ' ')
 152                n = n*10 + (c - '0');
 153
 154        *endptr = str + 1; /* We skip trailing space! */
 155        return n;
 156}
 157
 158# if ENABLE_FEATURE_FAST_TOP
 159static long fast_strtol_10(char **endptr)
 160{
 161        if (**endptr != '-')
 162                return fast_strtoul_10(endptr);
 163
 164        (*endptr)++;
 165        return - (long)fast_strtoul_10(endptr);
 166}
 167# endif
 168
 169static char *skip_fields(char *str, int count)
 170{
 171        do {
 172                while (*str++ != ' ')
 173                        continue;
 174                /* we found a space char, str points after it */
 175        } while (--count);
 176        return str;
 177}
 178#endif
 179
 180#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
 181int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
 182                void (*cb)(struct smaprec *, void *), void *data)
 183{
 184        FILE *file;
 185        struct smaprec currec;
 186        char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
 187        char buf[PROCPS_BUFSIZE];
 188#if !ENABLE_PMAP
 189        void (*cb)(struct smaprec *, void *) = NULL;
 190        void *data = NULL;
 191#endif
 192
 193        sprintf(filename, "/proc/%u/smaps", (int)pid);
 194
 195        file = fopen_for_read(filename);
 196        if (!file)
 197                return 1;
 198
 199        memset(&currec, 0, sizeof(currec));
 200        while (fgets(buf, PROCPS_BUFSIZE, file)) {
 201                // Each mapping datum has this form:
 202                // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
 203                // Size:                nnn kB
 204                // Rss:                 nnn kB
 205                // .....
 206
 207                char *tp, *p;
 208
 209#define SCAN(S, X) \
 210                if ((tp = is_prefixed_with(buf, S)) != NULL) {       \
 211                        tp = skip_whitespace(tp);                    \
 212                        total->X += currec.X = fast_strtoul_10(&tp); \
 213                        continue;                                    \
 214                }
 215                if (cb) {
 216                        SCAN("Pss:"  , smap_pss     );
 217                        SCAN("Swap:" , smap_swap    );
 218                }
 219                SCAN("Private_Dirty:", private_dirty);
 220                SCAN("Private_Clean:", private_clean);
 221                SCAN("Shared_Dirty:" , shared_dirty );
 222                SCAN("Shared_Clean:" , shared_clean );
 223#undef SCAN
 224                tp = strchr(buf, '-');
 225                if (tp) {
 226                        // We reached next mapping - the line of this form:
 227                        // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
 228
 229                        if (cb) {
 230                                /* If we have a previous record, there's nothing more
 231                                 * for it, call the callback and clear currec
 232                                 */
 233                                if (currec.smap_size)
 234                                        cb(&currec, data);
 235                                free(currec.smap_name);
 236                        }
 237                        memset(&currec, 0, sizeof(currec));
 238
 239                        *tp = ' ';
 240                        tp = buf;
 241                        currec.smap_start = fast_strtoul_16(&tp);
 242                        currec.smap_size = (fast_strtoul_16(&tp) - currec.smap_start) >> 10;
 243
 244                        strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
 245
 246                        // skipping "rw-s FILEOFS M:m INODE "
 247                        tp = skip_whitespace(skip_fields(tp, 4));
 248                        // filter out /dev/something (something != zero)
 249                        if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) {
 250                                if (currec.smap_mode[1] == 'w') {
 251                                        currec.mapped_rw = currec.smap_size;
 252                                        total->mapped_rw += currec.smap_size;
 253                                } else if (currec.smap_mode[1] == '-') {
 254                                        currec.mapped_ro = currec.smap_size;
 255                                        total->mapped_ro += currec.smap_size;
 256                                }
 257                        }
 258
 259                        if (strcmp(tp, "[stack]\n") == 0)
 260                                total->stack += currec.smap_size;
 261                        if (cb) {
 262                                p = skip_non_whitespace(tp);
 263                                if (p == tp) {
 264                                        currec.smap_name = xstrdup("  [ anon ]");
 265                                } else {
 266                                        *p = '\0';
 267                                        currec.smap_name = xstrdup(tp);
 268                                }
 269                        }
 270                        total->smap_size += currec.smap_size;
 271                }
 272        }
 273        fclose(file);
 274
 275        if (cb) {
 276                if (currec.smap_size)
 277                        cb(&currec, data);
 278                free(currec.smap_name);
 279        }
 280
 281        return 0;
 282}
 283#endif
 284
 285procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
 286{
 287        if (!sp)
 288                sp = alloc_procps_scan();
 289
 290        for (;;) {
 291                struct dirent *entry;
 292                char buf[PROCPS_BUFSIZE];
 293                long tasknice;
 294                unsigned pid;
 295                int n;
 296                char filename[sizeof("/proc/%u/task/%u/cmdline") + sizeof(int)*3 * 2];
 297                char *filename_tail;
 298
 299#if ENABLE_FEATURE_SHOW_THREADS
 300                if (sp->task_dir) {
 301                        entry = readdir(sp->task_dir);
 302                        if (entry)
 303                                goto got_entry;
 304                        closedir(sp->task_dir);
 305                        sp->task_dir = NULL;
 306                }
 307#endif
 308                entry = readdir(sp->dir);
 309                if (entry == NULL) {
 310                        free_procps_scan(sp);
 311                        return NULL;
 312                }
 313 IF_FEATURE_SHOW_THREADS(got_entry:)
 314                pid = bb_strtou(entry->d_name, NULL, 10);
 315                if (errno)
 316                        continue;
 317#if ENABLE_FEATURE_SHOW_THREADS
 318                if ((flags & PSSCAN_TASKS) && !sp->task_dir) {
 319                        /* We found another /proc/PID. Do not use it,
 320                         * there will be /proc/PID/task/PID (same PID!),
 321                         * so just go ahead and dive into /proc/PID/task. */
 322                        sprintf(filename, "/proc/%u/task", pid);
 323                        /* Note: if opendir fails, we just go to next /proc/XXX */
 324                        sp->task_dir = opendir(filename);
 325                        sp->main_thread_pid = pid;
 326                        continue;
 327                }
 328#endif
 329
 330                /* After this point we can:
 331                 * "break": stop parsing, return the data
 332                 * "continue": try next /proc/XXX
 333                 */
 334
 335                memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
 336
 337                sp->pid = pid;
 338                if (!(flags & ~PSSCAN_PID))
 339                        break; /* we needed only pid, we got it */
 340
 341#if ENABLE_SELINUX
 342                if (flags & PSSCAN_CONTEXT) {
 343                        if (getpidcon(sp->pid, &sp->context) < 0)
 344                                sp->context = NULL;
 345                }
 346#endif
 347
 348#if ENABLE_FEATURE_SHOW_THREADS
 349                if (sp->task_dir)
 350                        filename_tail = filename + sprintf(filename, "/proc/%u/task/%u/", sp->main_thread_pid, pid);
 351                else
 352#endif
 353                        filename_tail = filename + sprintf(filename, "/proc/%u/", pid);
 354
 355                if (flags & PSSCAN_UIDGID) {
 356                        struct stat sb;
 357                        if (stat(filename, &sb))
 358                                continue; /* process probably exited */
 359                        /* Effective UID/GID, not real */
 360                        sp->uid = sb.st_uid;
 361                        sp->gid = sb.st_gid;
 362                }
 363
 364                /* These are all retrieved from proc/NN/stat in one go: */
 365                if (flags & (PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
 366                        | PSSCAN_COMM | PSSCAN_STATE
 367                        | PSSCAN_VSZ | PSSCAN_RSS
 368                        | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
 369                        | PSSCAN_TTY | PSSCAN_NICE
 370                        | PSSCAN_CPU)
 371                ) {
 372                        int s_idx;
 373                        char *cp, *comm1;
 374                        int tty;
 375#if !ENABLE_FEATURE_FAST_TOP
 376                        unsigned long vsz, rss;
 377#endif
 378                        /* see proc(5) for some details on this */
 379                        strcpy(filename_tail, "stat");
 380                        n = read_to_buf(filename, buf);
 381                        if (n < 0)
 382                                continue; /* process probably exited */
 383                        cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
 384                        /*if (!cp || cp[1] != ' ')
 385                                continue;*/
 386                        cp[0] = '\0';
 387                        BUILD_BUG_ON(sizeof(sp->comm) < 16);
 388                        comm1 = strchr(buf, '(');
 389                        /*if (comm1)*/
 390                                safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm));
 391
 392#if !ENABLE_FEATURE_FAST_TOP
 393                        n = sscanf(cp+2,
 394                                "%c %u "               /* state, ppid */
 395                                "%u %u %d %*s "        /* pgid, sid, tty, tpgid */
 396                                "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
 397                                "%lu %lu "             /* utime, stime */
 398                                "%*s %*s %*s "         /* cutime, cstime, priority */
 399                                "%ld "                 /* nice */
 400                                "%*s %*s "             /* timeout, it_real_value */
 401                                "%lu "                 /* start_time */
 402                                "%lu "                 /* vsize */
 403                                "%lu "                 /* rss */
 404# if ENABLE_FEATURE_TOP_SMP_PROCESS
 405                                "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
 406                                "%*s %*s %*s %*s "         /*signal, blocked, sigignore, sigcatch */
 407                                "%*s %*s %*s %*s "         /*wchan, nswap, cnswap, exit_signal */
 408                                "%d"                       /*cpu last seen on*/
 409# endif
 410                                ,
 411                                sp->state, &sp->ppid,
 412                                &sp->pgid, &sp->sid, &tty,
 413                                &sp->utime, &sp->stime,
 414                                &tasknice,
 415                                &sp->start_time,
 416                                &vsz,
 417                                &rss
 418# if ENABLE_FEATURE_TOP_SMP_PROCESS
 419                                , &sp->last_seen_on_cpu
 420# endif
 421                                );
 422
 423                        if (n < 11)
 424                                continue; /* bogus data, get next /proc/XXX */
 425# if ENABLE_FEATURE_TOP_SMP_PROCESS
 426                        if (n == 11)
 427                                sp->last_seen_on_cpu = 0;
 428# endif
 429
 430                        /* vsz is in bytes and we want kb */
 431                        sp->vsz = vsz >> 10;
 432                        /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
 433                        sp->rss = rss << sp->shift_pages_to_kb;
 434                        sp->tty_major = (tty >> 8) & 0xfff;
 435                        sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
 436#else
 437/* This costs ~100 bytes more but makes top faster by 20%
 438 * If you run 10000 processes, this may be important for you */
 439                        sp->state[0] = cp[2];
 440                        cp += 4;
 441                        sp->ppid = fast_strtoul_10(&cp);
 442                        sp->pgid = fast_strtoul_10(&cp);
 443                        sp->sid = fast_strtoul_10(&cp);
 444                        tty = fast_strtoul_10(&cp);
 445                        sp->tty_major = (tty >> 8) & 0xfff;
 446                        sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
 447                        cp = skip_fields(cp, 6); /* tpgid, flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
 448                        sp->utime = fast_strtoul_10(&cp);
 449                        sp->stime = fast_strtoul_10(&cp);
 450                        cp = skip_fields(cp, 3); /* cutime, cstime, priority */
 451                        tasknice = fast_strtol_10(&cp);
 452                        cp = skip_fields(cp, 2); /* timeout, it_real_value */
 453                        sp->start_time = fast_strtoul_10(&cp);
 454                        /* vsz is in bytes and we want kb */
 455                        sp->vsz = fast_strtoul_10(&cp) >> 10;
 456                        /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
 457                        sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
 458# if ENABLE_FEATURE_TOP_SMP_PROCESS
 459                        /* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
 460                        /* (4): signal, blocked, sigignore, sigcatch */
 461                        /* (4): wchan, nswap, cnswap, exit_signal */
 462                        cp = skip_fields(cp, 14);
 463//FIXME: is it safe to assume this field exists?
 464                        sp->last_seen_on_cpu = fast_strtoul_10(&cp);
 465# endif
 466#endif /* FEATURE_FAST_TOP */
 467
 468#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
 469                        sp->niceness = tasknice;
 470#endif
 471                        sp->state[1] = ' ';
 472                        sp->state[2] = ' ';
 473                        s_idx = 1;
 474                        if (sp->vsz == 0 && sp->state[0] != 'Z') {
 475                                /* not sure what the purpose of this flag */
 476                                sp->state[1] = 'W';
 477                                s_idx = 2;
 478                        }
 479                        if (tasknice != 0) {
 480                                if (tasknice < 0)
 481                                        sp->state[s_idx] = '<';
 482                                else /* > 0 */
 483                                        sp->state[s_idx] = 'N';
 484                        }
 485                }
 486
 487#if ENABLE_FEATURE_TOPMEM
 488                if (flags & PSSCAN_SMAPS)
 489                        procps_read_smaps(pid, &sp->smaps, NULL, NULL);
 490#endif /* TOPMEM */
 491#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
 492                if (flags & PSSCAN_RUIDGID) {
 493                        FILE *file;
 494
 495                        strcpy(filename_tail, "status");
 496                        file = fopen_for_read(filename);
 497                        if (file) {
 498                                while (fgets(buf, sizeof(buf), file)) {
 499                                        char *tp;
 500#define SCAN_TWO(str, name, statement) \
 501        if ((tp = is_prefixed_with(buf, str)) != NULL) { \
 502                tp = skip_whitespace(tp); \
 503                sscanf(tp, "%u", &sp->name); \
 504                statement; \
 505        }
 506                                        SCAN_TWO("Uid:", ruid, continue);
 507                                        SCAN_TWO("Gid:", rgid, break);
 508#undef SCAN_TWO
 509                                }
 510                                fclose(file);
 511                        }
 512                }
 513#endif /* PS_ADDITIONAL_COLUMNS */
 514                if (flags & PSSCAN_EXE) {
 515                        strcpy(filename_tail, "exe");
 516                        free(sp->exe);
 517                        sp->exe = xmalloc_readlink(filename);
 518                }
 519                /* Note: if /proc/PID/cmdline is empty,
 520                 * code below "breaks". Therefore it must be
 521                 * the last code to parse /proc/PID/xxx data
 522                 * (we used to have /proc/PID/exe parsing after it
 523                 * and were getting stale sp->exe).
 524                 */
 525#if 0 /* PSSCAN_CMD is not used */
 526                if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) {
 527                        free(sp->argv0);
 528                        sp->argv0 = NULL;
 529                        free(sp->cmd);
 530                        sp->cmd = NULL;
 531                        strcpy(filename_tail, "cmdline");
 532                        /* TODO: to get rid of size limits, read into malloc buf,
 533                         * then realloc it down to real size. */
 534                        n = read_to_buf(filename, buf);
 535                        if (n <= 0)
 536                                break;
 537                        if (flags & PSSCAN_ARGV0)
 538                                sp->argv0 = xstrdup(buf);
 539                        if (flags & PSSCAN_CMD) {
 540                                do {
 541                                        n--;
 542                                        if ((unsigned char)(buf[n]) < ' ')
 543                                                buf[n] = ' ';
 544                                } while (n);
 545                                sp->cmd = xstrdup(buf);
 546                        }
 547                }
 548#else
 549                if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) {
 550                        free(sp->argv0);
 551                        sp->argv0 = NULL;
 552                        strcpy(filename_tail, "cmdline");
 553                        n = read_to_buf(filename, buf);
 554                        if (n <= 0)
 555                                break;
 556                        if (flags & PSSCAN_ARGVN) {
 557                                sp->argv_len = n;
 558                                sp->argv0 = xmemdup(buf, n + 1);
 559                                /* sp->argv0[n] = '\0'; - buf has it */
 560                        } else {
 561                                sp->argv_len = 0;
 562                                sp->argv0 = xstrdup(buf);
 563                        }
 564                }
 565#endif
 566                break;
 567        } /* for (;;) */
 568
 569        return sp;
 570}
 571
 572void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
 573{
 574        int sz;
 575        char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
 576
 577        sprintf(filename, "/proc/%u/cmdline", pid);
 578        sz = open_read_close(filename, buf, col - 1);
 579        if (sz > 0) {
 580                const char *base;
 581                int comm_len;
 582
 583                buf[sz] = '\0';
 584                while (--sz >= 0 && buf[sz] == '\0')
 585                        continue;
 586                /* Prevent basename("process foo/bar") = "bar" */
 587                strchrnul(buf, ' ')[0] = '\0';
 588                base = bb_basename(buf); /* before we replace argv0's NUL with space */
 589                while (sz >= 0) {
 590                        if ((unsigned char)(buf[sz]) < ' ')
 591                                buf[sz] = ' ';
 592                        sz--;
 593                }
 594                if (base[0] == '-') /* "-sh" (login shell)? */
 595                        base++;
 596
 597                /* If comm differs from argv0, prepend "{comm} ".
 598                 * It allows to see thread names set by prctl(PR_SET_NAME).
 599                 */
 600                if (!comm)
 601                        return;
 602                comm_len = strlen(comm);
 603                /* Why compare up to comm_len, not COMM_LEN-1?
 604                 * Well, some processes rewrite argv, and use _spaces_ there
 605                 * while rewriting. (KDE is observed to do it).
 606                 * I prefer to still treat argv0 "process foo bar"
 607                 * as 'equal' to comm "process".
 608                 */
 609                if (strncmp(base, comm, comm_len) != 0) {
 610                        comm_len += 3;
 611                        if (col > comm_len)
 612                                memmove(buf + comm_len, buf, col - comm_len);
 613                        snprintf(buf, col, "{%s}", comm);
 614                        if (col <= comm_len)
 615                                return;
 616                        buf[comm_len - 1] = ' ';
 617                        buf[col - 1] = '\0';
 618                }
 619        } else {
 620                snprintf(buf, col, "[%s]", comm ? comm : "?");
 621        }
 622}
 623
 624/* from kernel:
 625        //             pid comm S ppid pgid sid tty_nr tty_pgrp flg
 626        sprintf(buffer,"%d (%s) %c %d  %d   %d  %d     %d       %lu %lu \
 627%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
 628%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
 629                task->pid,
 630                tcomm,
 631                state,
 632                ppid,
 633                pgid,
 634                sid,
 635                tty_nr,
 636                tty_pgrp,
 637                task->flags,
 638                min_flt,
 639                cmin_flt,
 640                maj_flt,
 641                cmaj_flt,
 642                cputime_to_clock_t(utime),
 643                cputime_to_clock_t(stime),
 644                cputime_to_clock_t(cutime),
 645                cputime_to_clock_t(cstime),
 646                priority,
 647                nice,
 648                num_threads,
 649                // 0,
 650                start_time,
 651                vsize,
 652                mm ? get_mm_rss(mm) : 0,
 653                rsslim,
 654                mm ? mm->start_code : 0,
 655                mm ? mm->end_code : 0,
 656                mm ? mm->start_stack : 0,
 657                esp,
 658                eip,
 659the rest is some obsolete cruft
 660*/
 661