/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #define PROC_ROOT "/proc" struct procinfo_struct { pid_t pid; pid_t ppid; pid_t pgid; unsigned long vsz; int state; unsigned foreground : 1; char *command; struct procinfo_struct *next; }; struct fieldwidth_struct { int pid; int ppid; int pgid; int vsz; int state; }; typedef int (*comparator_t)(const struct procinfo_struct *, const struct procinfo_struct *); struct proc_list_struct { comparator_t comparator; struct procinfo_struct *first; struct fieldwidth_struct fieldwidth; }; static const struct { const char *pid; const char *ppid; const char *pgid; const char *vsz; const char *state; const char *command; } header = { .pid = "PID", .ppid = "PPID", .pgid = "PGID", .vsz = "VSZ", .state = "STATE", .command = "COMMAND" }; static const char *proc_root = PROC_ROOT; static int screenwidth; static void get_stat_from_file(const char *filename, int *pid, int *ppid, int *pgid, unsigned long *vsz, int *state) { FILE *fp; unsigned long vsize; unsigned char c; if (!(fp = fopen(filename, "r"))) goto error; if (fscanf(fp, "%d %c %d %d %lu", pid, &c, ppid, pgid, &vsize) != 5) goto error_fclose; *vsz = vsize / 1024; *state = c; fclose(fp); return; error_fclose: fclose(fp); error: *pid = -1; *state = -1; *ppid = -1; *pgid = -1; *vsz = -1; } static void get_cmdline_from_file(const char *filename, char **s) { struct stat statbuf; size_t size; FILE *fp; char *s1; size_t len; if (stat(filename, &statbuf) == -1) goto error; if ((size = statbuf.st_size)) { if (!(*s = malloc(size))) error(EXIT_FAILURE, errno, "malloc"); if (!(fp = fopen(filename, "r"))) goto error_free; if (fread(*s, 1, size, fp) < size) goto error_fclose; fclose(fp); s1 = *s; while (size) { if ((len = strnlen(s1, size)) == size) goto error_free; s1 += len; *s1 = ' '; s1++; if (size < len + 1) goto error_free; size -= len + 1; } } else if (!(*s = strdup("(defunct)"))) error(EXIT_FAILURE, errno, "strdup"); return; error_fclose: fclose(fp); error_free: free(*s); error: *s = NULL; } static void build_procinfo(struct procinfo_struct *procinfo, pid_t pid, const char *path) { char *buf; pid_t fpgid; if (!(buf = malloc(strlen(path) + 1 + NAME_MAX + 1))) error(EXIT_FAILURE, errno, "malloc"); sprintf(buf, "%s/stat", path); get_stat_from_file(buf, &procinfo->pid, &procinfo->ppid, &procinfo->pgid, &procinfo->vsz, &procinfo->state); if (procinfo->state != -1) procinfo->foreground = procinfo->pgid != -1 && (fpgid = tcgetpgrp(fileno(stderr))) != -1 && procinfo->pgid == fpgid ? 1 : 0; sprintf(buf, "%s/cmdline", path); get_cmdline_from_file(buf, &procinfo->command); free(buf); } static void destroy_procinfo(struct procinfo_struct *procinfo) { if (procinfo->command) free(procinfo->command); } static void set_header_fieldwidth(struct fieldwidth_struct *fieldwidth) { fieldwidth->pid = strlen(header.pid); fieldwidth->ppid = strlen(header.ppid); fieldwidth->pgid = strlen(header.pgid); fieldwidth->vsz = strlen(header.vsz); fieldwidth->state = strlen(header.state); } static int num_fieldwidth(int n) { return snprintf(NULL, 0, "%d", n); } static void update_fieldwidth(struct fieldwidth_struct *fieldwidth, const struct procinfo_struct *procinfo) { int w; if ((w = num_fieldwidth(procinfo->pid)) > fieldwidth->pid) fieldwidth->pid = w; if ((w = procinfo->ppid != -1 ? num_fieldwidth(procinfo->ppid) : 1) > fieldwidth->ppid) fieldwidth->ppid = w; if ((w = procinfo->pgid != -1 ? num_fieldwidth(procinfo->pgid) : 1) > fieldwidth->pgid) fieldwidth->pgid = w; if ((w = procinfo->vsz != (unsigned long)-1 ? num_fieldwidth(procinfo->vsz) : 1) > fieldwidth->vsz) fieldwidth->vsz = w; if (fieldwidth->state < 2) fieldwidth->state = 2; } static void init_proc_list(struct proc_list_struct *proc_list, comparator_t comparator) { proc_list->comparator = comparator; proc_list->first = NULL; set_header_fieldwidth(&proc_list->fieldwidth); } static void destroy_proc_list(struct proc_list_struct *proc_list) { struct procinfo_struct *procinfo, *next; procinfo = proc_list->first; while (procinfo) { next = procinfo->next; destroy_procinfo(procinfo); free(procinfo); procinfo = next; } } static int pid_comparator(const struct procinfo_struct *procinfo1, const struct procinfo_struct *procinfo2) { return procinfo1->pid < procinfo2->pid; } static int compare(const struct procinfo_struct *procinfo1, const struct procinfo_struct *procinfo2, struct proc_list_struct *proc_list) { return proc_list->comparator(procinfo1, procinfo2); } static void insert_into_proc_list(struct proc_list_struct *proc_list, struct procinfo_struct *procinfo) { struct procinfo_struct *procinfo1, *procinfo2; procinfo1 = NULL; procinfo2 = proc_list->first; while (procinfo2 && !compare(procinfo, procinfo2, proc_list)) { procinfo1 = procinfo2; procinfo2 = procinfo2->next; } procinfo->next = procinfo2; if (!procinfo1) proc_list->first = procinfo; else procinfo1->next = procinfo; update_fieldwidth(&proc_list->fieldwidth, procinfo); } static int populate_proc_list(struct proc_list_struct *proc_list) { int err; DIR *dir; struct dirent *dent; int num; char *buf; struct procinfo_struct *procinfo; err = 0; if (!(dir = opendir(proc_root))) { error(0, errno, "cannot open %s", proc_root); err = 1; goto end; } while (1) { errno = 0; if (!(dent = readdir(dir))) { if (errno) { error(0, errno, "reading directory %s", proc_root); err = 1; goto closedir; } break; } if (sscanf(dent->d_name, "%d", &num) != 1) continue; if (!(buf = malloc(strlen(proc_root) + 1 + strlen(dent->d_name) + 1))) error(EXIT_FAILURE, errno, "malloc"); if (!(procinfo = malloc(sizeof (struct procinfo_struct)))) error(EXIT_FAILURE, errno, "malloc"); sprintf(buf, "%s/%s", proc_root, dent->d_name); build_procinfo(procinfo, num, buf); free(buf); insert_into_proc_list(proc_list, procinfo); } closedir: if (closedir(dir) == -1) { error(0, errno, "closing directory %s", proc_root); err = 1; } end: return !err; } static void print_header(const struct fieldwidth_struct *fieldwidth) { printf("%-*s", fieldwidth->pid, header.pid); putchar(' '); printf("%-*s", fieldwidth->ppid, header.ppid); putchar(' '); printf("%-*s", fieldwidth->pgid, header.pgid); putchar(' '); printf("%-*s", fieldwidth->vsz, header.vsz); putchar(' '); printf("%-*s", fieldwidth->state, header.state); putchar(' '); printf("%s", header.command); putchar('\n'); } static char *make_state_str(const struct procinfo_struct *procinfo, char *buf) { char *p; p = buf; *p++ = procinfo->state; if (procinfo->foreground) *p++ = '+'; *p = '\0'; return buf; } static void print_procinfo(const struct procinfo_struct *procinfo, const struct fieldwidth_struct *fieldwidth) { int rem_width; char state_buf[3]; rem_width = screenwidth; if (procinfo->pid != -1) printf("%*d", fieldwidth->pid, procinfo->pid); else printf("%*s", fieldwidth->pid, "?"); putchar(' '); rem_width -= fieldwidth->pid + 1; if (procinfo->ppid != -1) printf("%*d", fieldwidth->ppid, procinfo->ppid); else printf("%*s", fieldwidth->ppid, "?"); putchar(' '); rem_width -= fieldwidth->ppid + 1; if (procinfo->pgid != -1) printf("%*d", fieldwidth->pgid, procinfo->pgid); else printf("%*s", fieldwidth->pgid, "?"); putchar(' '); rem_width -= fieldwidth->pgid + 1; if (procinfo->vsz != (unsigned long)-1) printf("%*lu", fieldwidth->vsz, procinfo->vsz); else printf("%*s", fieldwidth->vsz, "?"); putchar(' '); rem_width -= fieldwidth->vsz + 1; printf("%-*s", fieldwidth->state, procinfo->state != -1 ? make_state_str(procinfo, state_buf) : "?"); putchar(' '); rem_width -= fieldwidth->state + 1; if (screenwidth >= 0) printf("%.*s", rem_width, procinfo->command ? : "?"); else printf("%s", procinfo->command ? : "?"); putchar('\n'); } static void print_proc_list(const struct proc_list_struct *proc_list) { const struct procinfo_struct *procinfo; print_header(&proc_list->fieldwidth); for (procinfo = proc_list->first; procinfo; procinfo = procinfo->next) print_procinfo(procinfo, &proc_list->fieldwidth); } static int list_processes(comparator_t comparator) { int err; struct proc_list_struct proc_list; err = 0; init_proc_list(&proc_list, comparator); if (!populate_proc_list(&proc_list)) err = 1; print_proc_list(&proc_list); destroy_proc_list(&proc_list); return !err; } static void __attribute__ ((noreturn)) die_bad_usage() { fprintf(stderr, "Try `%s --help' for more information.\n", program_invocation_name); exit(EXIT_FAILURE); } static void print_help() { printf("Usage: %s [OPTION]...\n", program_invocation_name); puts("Print a snapshot of the current processes."); putchar('\n'); puts(" -h, --help display this help and exit"); } int main(int argc, char **argv) { static const struct option longopts[] = { {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; int c; struct winsize winsize; while ((c = getopt_long(argc, argv, "h", longopts, NULL)) != -1) switch (c) { case 'h': print_help(); return EXIT_SUCCESS; case '?': die_bad_usage(); } screenwidth = ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1 ? winsize.ws_col : -1; return list_processes(pid_comparator) ? EXIT_SUCCESS : EXIT_FAILURE; }