changeset 1020:fe7725c1819a

Add watch and ps, sent by Ashwini Sharma.
author Rob Landley <rob@landley.net>
date Tue, 20 Aug 2013 15:37:42 -0500
parents f9271a80fedc
children 773e4862e790
files toys/pending/ps.c toys/pending/watch.c
diffstat 2 files changed, 474 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/ps.c	Tue Aug 20 15:37:42 2013 -0500
@@ -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;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/watch.c	Tue Aug 20 15:37:42 2013 -0500
@@ -0,0 +1,55 @@
+/* watch.c - Execute a program periodically
+ *
+ * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
+ * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+ *
+USE_WATCH(NEWTOY(watch, "^<1n#<0=2t", TOYFLAG_USR|TOYFLAG_BIN))
+
+config WATCH
+  bool "watch"
+  default n
+  help
+    Usage: watch [-n SEC] [-t] PROG ARGS
+
+    Run PROG periodically
+
+    -n  Loop period in seconds (default 2)
+    -t  Don't print header
+*/
+#define FOR_watch
+#include "toys.h"
+
+GLOBALS(
+  int interval; 
+)
+
+void watch_main(void)
+{
+  int i = 0, hlen;
+  time_t t;
+  unsigned width = 80, len = sizeof("1234-67-90 23:56:89");//time format
+  char *header, *cmd = *toys.optargs;
+
+  while(toys.optargs[++i]) cmd = xmsprintf("%s %s", cmd, toys.optargs[i]);
+  header = xmsprintf("Every %us: %s", TT.interval, cmd);
+
+  while(1) {
+    xprintf("\033[H\033[J");
+    if(!(toys.optflags & FLAG_t)) {
+      xprintf("%s", header);
+      hlen = strlen(header);
+      terminal_size(&width, NULL);
+      if (!width) width = 80; //on serial it may return 0.
+      if (width > (hlen + len)) {                         
+        time(&t);                                         
+        strftime(toybuf, len, "%Y-%m-%d %H:%M:%S", localtime(&t));
+        xprintf("%*s", width - hlen, toybuf);             
+      }
+      xprintf("\n\n"); // 1'\n' for space between header and result
+    }
+    fflush(NULL); //making sure the screen is clear
+    system(cmd);
+    sleep(TT.interval);
+  }
+  if (CFG_TOYBOX_FREE) free(header);
+}