aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/ps.c
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2013-08-20 15:37:42 -0500
committerRob Landley <rob@landley.net>2013-08-20 15:37:42 -0500
commit7459c34c83156f37eb31dbba83807d497a64277f (patch)
tree87a53c60ed9899151ade042839e717298dd8fa76 /toys/pending/ps.c
parent276a99f9fe7c2819ea61accfcb35088707882b58 (diff)
downloadtoybox-7459c34c83156f37eb31dbba83807d497a64277f.tar.gz
Add watch and ps, sent by Ashwini Sharma.
Diffstat (limited to 'toys/pending/ps.c')
-rw-r--r--toys/pending/ps.c419
1 files changed, 419 insertions, 0 deletions
diff --git a/toys/pending/ps.c b/toys/pending/ps.c
new file mode 100644
index 00000000..19521b6f
--- /dev/null
+++ b/toys/pending/ps.c
@@ -0,0 +1,419 @@
+/* ps.c - Show running process statistics.
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html
+
+USE_PS(NEWTOY(ps, ">0o*T", TOYFLAG_BIN))
+
+config PS
+ bool "ps"
+ default n
+ help
+ usage: ps [-o COL1,COL2=HEADER] [-T]
+
+ Show list of processes
+ -o COL1,COL2=HEADER Select columns for display
+ -T Show threads
+*/
+#define FOR_ps
+#include "toys.h"
+
+GLOBALS(
+ struct arg_list *llist_o;
+ unsigned screen_width;
+)
+
+#define BUFF_SIZE 1024
+struct header_list {
+ char *name;
+ char *header;
+ char *format;
+ int width;
+ int position;
+ struct header_list *next;
+};
+
+struct header_list def_header[] = {
+ {"user", "USER", "%-*s ", 8, 0, NULL},
+ {"group", "GROUP", "%-*s ", 8, 1, NULL},
+ {"comm", "COMMAND", "%-*s ",16, 2, NULL},
+ {"args", "COMMAND", "%-*s ",30, 3, NULL},
+ {"pid", "PID", "%*s ", 5, 4, NULL},
+ {"ppid","PPID", "%*s ", 5, 5, NULL},
+ {"pgid", "PGID", "%*s ", 5, 6, NULL},
+ {"etime","ELAPSED", "%*s ", 7, 7, NULL},
+ {"nice", "NI", "%*s ", 5, 8, NULL},
+ {"rgroup","RGROUP", "%-*s ", 8, 9, NULL},
+ {"ruser","RUSER", "%-*s ", 8, 10, NULL},
+ {"time", "TIME", "%*s ", 6, 11, NULL},
+ {"tty", "TT", "%-*s ", 6, 12, NULL},
+ {"vsz","VSZ", "%*s ", 7, 13, NULL},
+ {"stat", "STAT", "%-*s ", 4, 14, NULL},
+ {"rss", "RSS", "%*s ", 4, 15, NULL},
+ {NULL, NULL, NULL, 0, 0, NULL},
+};
+
+struct header_list *o_list = NULL; //List of Header attributes.
+
+/*
+ * create list of header attributes taking care of -o (-o ooid=MOM..)
+ * and width of attributes.
+ */
+static void list_add(struct header_list **list, struct header_list *data, char *c_data)
+{
+ struct header_list *temp = *list, *new = xzalloc(sizeof(struct header_list));
+
+ new->name = data->name;
+ if (c_data) new->header = c_data;
+ else new->header = xstrdup(data->header);
+ if (c_data && (strlen(c_data) > data->width)) new->width = strlen(c_data);
+ else new->width = data->width;
+ new->format = data->format;
+ new->position = data->position;
+
+ if (temp) {
+ while (temp->next != NULL) temp = temp->next;
+ temp->next = new;
+ } else (*list) = new;
+}
+
+//print the default header OR header with -o args
+static void print_header(void)
+{
+ int i = 0;
+ char *ptr = NULL, *str, *temp;
+ struct arg_list *node = TT.llist_o;
+
+ if (!(toys.optflags & FLAG_o)) {
+ xprintf(" PID"" ""USER"" "" TIME"" ""COMMAND");
+ list_add(&o_list, &def_header[4], NULL); //pid
+ list_add(&o_list, &def_header[0], NULL); //user
+ list_add(&o_list, &def_header[11], NULL); //time
+ list_add(&o_list, &def_header[3], NULL); //comm
+ xputc('\n');
+ return ;
+ }
+ while (node) {
+ i = 0;
+ str = xstrdup(node->arg);
+ while (str) {
+ if ((ptr = strsep(&str, ","))) { //seprate list
+ if ((temp = strchr(ptr, '='))) { // Handle ppid = MOM
+ *temp = 0;
+ temp++;
+ while (def_header[i].name) {
+ if (!(strcmp(def_header[i].name, ptr))) { // search from default header
+ if (str) ptr = xmsprintf("%s,%s", temp, str); //handle condition like ppid = M,OM
+ else ptr = xmsprintf("%s", temp);
+ list_add(&o_list, &def_header[i], ptr);
+ break;
+ }
+ i++;
+ }
+ if (!def_header[i].name) perror_exit("Invalid arg for -o option");
+ break;
+ } else {
+ while (def_header[i].name) {
+ if (!(strcmp(def_header[i].name, ptr))) {
+ list_add(&o_list, &def_header[i], NULL);
+ break;
+ }
+ i++;
+ }
+ if (!def_header[i].name) perror_exit("Invalid arg for -o option");
+ i = 0;
+ }
+ }
+ }
+ node = node->next;
+ }
+ struct header_list *p = o_list;
+ while (p) { //print Header
+ printf(p->format , p->width, p->header);
+ p = p->next;
+ }
+ xputc('\n');
+}
+
+//get uid/gid for processes.
+static void get_uid_gid(char *p, char *id_str, unsigned *id)
+{
+ FILE *f;
+ if(!p) return;
+ f = xfopen(p, "r");
+ while (fgets(toybuf, BUFF_SIZE, f)) {
+ if (!strncmp(toybuf, id_str, strlen(id_str))) {
+ sscanf(toybuf, "%*s %u", id);
+ break;
+ }
+ }
+ fclose(f);
+}
+
+//get etime for processes.
+void get_etime(unsigned long s_time)
+{
+ unsigned long min;
+ unsigned sec;
+ struct sysinfo info;
+ char *temp;
+ sysinfo(&info);
+ min = s_time/sysconf(_SC_CLK_TCK);
+ min = info.uptime - min;
+ sec = min % 60;
+ min = min / 60;
+ temp = xmsprintf("%3lu:%02u", min,sec);
+ xprintf("%*.*s",7,7,temp);
+ free(temp);
+}
+
+//get time attributes for processes.
+void get_time(unsigned long s_time, unsigned long u_time)
+{
+ unsigned long min;
+ unsigned sec;
+ char *temp;
+ min = (s_time + u_time)/sysconf(_SC_CLK_TCK);
+ sec = min % 60;
+ min = min / 60;
+ temp = xmsprintf("%3lu:%02u", min,sec);
+ xprintf("%*.*s",6,6,temp);
+ free(temp);
+}
+
+/*
+ * read command line taking care of in between NUL's
+ * in command line
+ */
+static void read_cmdline(int fd, char *cmd_ptr)
+{
+ int size = read(fd, cmd_ptr, 1024); //sizeof(cmd_buf)
+ cmd_ptr[size] = '\0';
+ while (--size > 0 && cmd_ptr[size] == '\0'); //reach to last char
+
+ while (size >= 0) {
+ if ((unsigned char)cmd_ptr[size] < ' ') cmd_ptr[size] = ' ';
+ size--;
+ }
+}
+
+/*
+ * get the processes stats and print the stats
+ * corresponding to header attributes.
+ */
+static void do_ps_line(int pid, int tid)
+{
+ char *stat_buff = toybuf + 1024, *cmd_buff = toybuf + 2048;
+ char state[4] = {0,};
+ int tty, tty_major, tty_minor, fd, n, nice, width_counter = 0;
+ struct stat stats;
+ struct passwd *pw;
+ struct group *gr;
+ char *name, *user, *group, *ruser, *rgroup, *ptr;
+ long rss;
+ unsigned long stime, utime, start_time, vsz;
+ unsigned ppid, ruid, rgid, pgid;
+ struct header_list *p = o_list;
+
+ sprintf(stat_buff, "/proc/%d", pid);
+ if(stat(stat_buff, &stats)) return;
+
+ if (tid) {
+ if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/task/%d/stat", pid, tid) >= BUFF_SIZE) return;
+ if (snprintf(cmd_buff, BUFF_SIZE, "/proc/%d/task/%d/cmdline", pid, tid) >= BUFF_SIZE) return;
+ } else {
+ if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/stat", pid) >= BUFF_SIZE) return;
+ if (snprintf(cmd_buff, BUFF_SIZE, "/proc/%d/cmdline", pid) >= BUFF_SIZE) return;
+ }
+
+ fd = xopen(stat_buff, O_RDONLY);
+ n = readall(fd, stat_buff, BUFF_SIZE);
+ xclose(fd);
+ if (n < 0) return;
+ stat_buff[n] = 0; //Null terminate the buffer.
+ ptr = strchr(stat_buff, '(');
+ ptr++;
+ name = ptr;
+ ptr = strrchr(stat_buff, ')');
+ *ptr = '\0'; //unecessary if?
+ name = xmsprintf("[%s]", name);
+ ptr += 2; // goto STATE
+ n = sscanf(ptr, "%c %u %u %*u %d %*s %*s %*s %*s %*s %*s "
+ "%lu %lu %*s %*s %*s %d %*s %*s %lu %lu %ld",
+ &state[0],&ppid, &pgid, &tty, &utime, &stime,
+ &nice,&start_time, &vsz,&rss);
+
+ if (tid) pid = tid;
+ vsz >>= 10; //Convert into KB
+ rss = rss * 4; //Express in pages
+ tty_major = (tty >> 8) & 0xfff;
+ tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
+
+ if (vsz == 0 && state[0] != 'Z') state[1] = 'W';
+ else state[1] = ' ';
+ if (nice < 0 ) state[2] = '<';
+ else if (nice) state[2] = 'N';
+ else state[2] = ' ';
+
+ if (tid) {
+ if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/task/%d/status", pid, tid) >= BUFF_SIZE)
+ goto clean;
+ } else {
+ if (snprintf(stat_buff, BUFF_SIZE, "/proc/%d/status", pid) >= BUFF_SIZE)
+ goto clean;
+ }
+
+ fd = -1;
+ while (p) {
+ int width;
+ width = p->width;
+ width_counter += (width + 1); //how much screen we hv filled, +1, extra space b/w headers
+ switch (p->position) {
+ case 0:
+ pw = getpwuid(stats.st_uid);
+ if (!pw) user = xmsprintf("%d",(int)stats.st_uid);
+ else user = xmsprintf("%s", pw->pw_name);
+ printf("%-*.*s", width, width, user);
+ free(user);
+ break;
+ case 1:
+ gr = getgrgid(stats.st_gid);
+ if (!gr) group = xmsprintf("%d",(int)stats.st_gid);
+ else group = xmsprintf("%s", gr->gr_name);
+ printf("%-*.*s", width, width, group);
+ free(group);
+ break;
+ case 2:
+ name[strlen(name) - 1] = '\0';
+ printf("%-*.*s", width,width, name + 1);
+ name[strlen(name)] = ']'; //Refill it for further process.
+ break;
+ case 3:
+ {
+ int j = 0;
+ width_counter -= width;
+ if(p->next) j = width; //is args is in middle. ( -o pid,args,ppid)
+ else j = (TT.screen_width - width_counter % TT.screen_width); //how much screen left.
+ if (fd == -1) fd = open(cmd_buff, O_RDONLY); //don't want to die
+ else xlseek(fd, 0, SEEK_SET);
+ if (fd < 0) cmd_buff[0] = 0;
+ else read_cmdline(fd, cmd_buff);
+ if (cmd_buff[0]) printf("%-*.*s", j, j, cmd_buff);
+ else printf("%-*.*s", j, j, name);
+ width_counter += width;
+ break;
+ }
+ case 4:
+ printf("%*d", width, pid);
+ break;
+ case 5:
+ printf("%*d", width, ppid);
+ break;
+ case 6:
+ printf("%*d", width, pgid);
+ break;
+ case 7:
+ get_etime(start_time);
+ break;
+ case 8:
+ printf("%*d", width, nice);
+ break;
+ case 9:
+ get_uid_gid(stat_buff, "Gid:", &rgid);
+ gr = getgrgid(rgid);
+ if (!gr) rgroup = xmsprintf("%d",(int)stats.st_gid);
+ else rgroup = xmsprintf("%s", gr->gr_name);
+ printf("%-*.*s", width,width, rgroup);
+ free(rgroup);
+ break;
+ case 10:
+ get_uid_gid(stat_buff, "Uid:", &ruid);
+ pw = getpwuid(ruid);
+ if (!pw) ruser = xmsprintf("%d",(int)stats.st_uid);
+ else ruser = xmsprintf("%s", pw->pw_name);
+ printf("%-*.*s", width, width, ruser);
+ free(ruser);
+ break;
+ case 11:
+ get_time(utime, stime);
+ break;
+ case 12:
+ if (tty_major) {
+ char *temp = xmsprintf("%d,%d", tty_major,tty_minor);
+ printf("%-*s", width, temp);
+ free(temp);
+ } else printf("%-*s", width, "?");
+ break;
+ case 13:
+ printf("%*lu", width, vsz);
+ break;
+ case 14:
+ printf("%-*s", width, state);
+ break;
+ case 15:
+ printf("%*lu", width, rss);
+ break;
+ }
+ p = p->next;
+ xputc(' '); //space char
+ }
+ if (fd >= 0) xclose(fd);
+ xputc('\n');
+clean:
+ free(name);
+}
+
+//Do stats for threads (for -T option)
+void do_ps_threads(int pid)
+{
+ DIR *d;
+ int tid;
+ struct dirent *de;
+ char *tmp = xmsprintf("/proc/%d/task",pid);
+
+ if (!(d = opendir(tmp))) {
+ free(tmp);
+ return;
+ }
+ while ((de = readdir(d))) {
+ if (isdigit(de->d_name[0])) {
+ tid = atoi(de->d_name);
+ if (tid == pid) continue;
+ do_ps_line(pid, tid);
+ }
+ }
+ closedir(d);
+ free(tmp);
+}
+
+void ps_main(void)
+{
+ DIR *dp;
+ struct dirent *entry;
+ int pid;
+
+ TT.screen_width = 80; //default width
+ terminal_size(&TT.screen_width, NULL);
+ print_header();
+
+ if (!(dp = opendir("/proc"))) perror_exit("opendir");
+ while ((entry = readdir(dp))) {
+ if (isdigit(*entry->d_name)) {
+ pid = atoi(entry->d_name);
+ do_ps_line(pid, 0);
+ if (toys.optflags & FLAG_T)
+ do_ps_threads(pid);
+ }
+ }
+ closedir(dp);
+ if (CFG_TOYBOX_FREE) {
+ struct header_list *temp = o_list;
+ while(temp) {
+ o_list = o_list->next;
+ free(temp->header);
+ free(temp);
+ temp = o_list;
+ }
+ }
+}