From 7459c34c83156f37eb31dbba83807d497a64277f Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 20 Aug 2013 15:37:42 -0500 Subject: Add watch and ps, sent by Ashwini Sharma. --- toys/pending/ps.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++ toys/pending/watch.c | 55 +++++++ 2 files changed, 474 insertions(+) create mode 100644 toys/pending/ps.c create mode 100644 toys/pending/watch.c 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 + * Copyright 2013 Kyungwan Han + * 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; + } + } +} diff --git a/toys/pending/watch.c b/toys/pending/watch.c new file mode 100644 index 00000000..9ed3e9b7 --- /dev/null +++ b/toys/pending/watch.c @@ -0,0 +1,55 @@ +/* watch.c - Execute a program periodically + * + * Copyright 2013 Sandeep Sharma + * Copyright 2013 Kyungwan Han + * +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); +} -- cgit v1.2.3