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