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