diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2016-03-12 09:50:33 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2016-03-12 09:50:33 +0200 |
commit | 3edbd2eb9288f1d2d675c422d5865b8be1f29c32 (patch) | |
tree | f022eda083b93ec5ca403a14ee081ba8f70a7d7c | |
download | swu-3edbd2eb9288f1d2d675c422d5865b8be1f29c32.tar.gz swu-3edbd2eb9288f1d2d675c422d5865b8be1f29c32.tar.bz2 |
Initial commit
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | swu.c | 337 |
2 files changed, 341 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0730f58 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +CFLAGS=-ggdb + +swu: swu.c + cc -oswu $(CFLAGS) $(CPPFLAGS) swu.c @@ -0,0 +1,337 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include <errno.h> +#include <glob.h> + +char *pattern = "/proc/*/status"; +int reverse; +int human; +int show_all; +int show_total; +int sizewidth = 8; +int verbose; +char *progname; + +void +err(char const *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +struct swu { + char *name; + pid_t pid; + off_t size; +}; + +struct swu *usage; +size_t nusage; + +#define ISWS(c) ((c)==' '||(c)=='\t') +#define SKIPWS(p) do { while (*p && ISWS(*p)) ++p; } while(0) + +#define PFX_NAME "Name" +#define PFX_PID "Pid" +#define PFX_SWAP "VmSwap" + +#define SET_NAME 0x1 +#define SET_PID 0x2 +#define SET_SWAP 0x4 +#define SET_ALL (SET_NAME|SET_PID|SET_SWAP) + +static char dig[] = "0123456789"; + +#define READNUM(fun, type) \ +type \ +fun(char const *s, char **end) \ +{ \ + type x = 0; \ + char *p; \ + for (; *s; s++) { \ + p = strchr(dig, *s); \ + if (!p) \ + break; \ + x *= 10; \ + x += p - dig; \ + } \ + *end = (char*) s; \ + return x; \ +} + +READNUM(readpid, pid_t); +READNUM(readsize, off_t); + +int +width(off_t l) +{ + int i; + + for (i = 0; l; i++) + l>>=1; + return i*100/332+1; +} + +static int +name_match(char const *name, char **argv) +{ + for (; *argv; argv++) { + if (strcmp(name, *argv) == 0) + return 1; + } + return 0; +} + +void +readstat(char const *name, char **argv) +{ + char buf[80]; + FILE *fp; + struct swu *swp = usage + nusage; + int f = 0; + + fp = fopen(name, "r"); + if (!fp) { + if (errno != ENOENT) + err("can't open %s: %s", name, strerror(errno)); + return; + } + + free(swp->name); + swp->name = NULL; + + while ((f & SET_ALL) != SET_ALL && fgets(buf, sizeof(buf), fp)) { + char *p = buf + strlen(buf); + if (p[-1] == '\n') + p[-1] = 0; + else { + if (verbose) + err("%s: line too long: %s", name, buf); + continue; + } + p = strchr(buf, ':'); + if (!p) { + if (verbose) + err("%s: unrecognized line: %s", name, buf); + continue; + } + *p++ = 0; + SKIPWS(p); + if (strcmp(buf, PFX_NAME) == 0) { + if (argv[0] && !name_match(p, argv)) + return; + swp->name = strdup(p); + if (!swp->name) { + err("out of memory"); + exit(2); + } + f |= SET_NAME; + } else if (strcmp(buf, PFX_PID) == 0) { + swp->pid = readpid(p, &p); + if (*p) + return; + f |= SET_PID; + } else if (strcmp(buf, PFX_SWAP) == 0) { + swp->size = readsize(p, &p); + SKIPWS(p); + if (strcmp(p, "kB") == 0) + swp->size *= 1024; + else if (*p) + return; + if (swp->size == 0 && !show_all) + return; + f |= SET_SWAP; + } + } + fclose(fp); + if ((f & SET_ALL) == SET_ALL) { + size_t w = width(swp->size); + if (w > sizewidth) + sizewidth = w; + ++nusage; + } +} + +static int +cmp_name(struct swu const *a, struct swu const *b) +{ + return strcmp(a->name, b->name); +} + +static int +cmp_pid(struct swu const *a, struct swu const *b) +{ + if (a->pid < b->pid) + return -1; + else + return 1; +} + +static int +cmp_size(struct swu const *a, struct swu const *b) +{ + if (a->size < b->size) + return -1; + else if (a->size > b->size) + return 1; + return 0; +} + +static int (*cmp)(struct swu const *, struct swu const *) = cmp_pid; + +static int +compare(const void *a, const void *b) +{ + struct swu const *au = a, *bu = b; + return reverse ? cmp(bu, au) : cmp(au, bu); +} + +static int +globerrfunc(const char *epath, int eerrno) +{ + err("%s %s", progname, epath, strerror(eerrno)); + return 1; +} + +static void +collect(char **argv) +{ + glob_t g; + size_t i; + + switch (glob (pattern, GLOB_NOSORT, globerrfunc, &g)) { + case 0: + break; + case GLOB_NOSPACE: + err("out of memory"); + exit(2); + case GLOB_NOMATCH: + err("no file matches %s", pattern); + exit(2); + default: + err("globbing error"); + exit(2); + } + + usage = calloc(g.gl_pathc, sizeof(*usage)); + if (!usage) { + err("out of memory"); + exit(2); + } + + for (i = 0; i < g.gl_pathc; i++) + if (strncmp(g.gl_pathv[i], "/proc/self/", 11)) + readstat(g.gl_pathv[i], argv); + globfree(&g); + qsort(usage, nusage, sizeof(usage[0]), compare); +} + +void +printsize(off_t size) +{ + static char *suf[] = { "", "K", "M", "G", NULL }; + if (human) { + int i; + unsigned fract = 0; + for (i = 0; size > 1024 && suf[i+1]; i++) { + fract = size % 1024; + size /= 1024; + } + if (fract) + printf("% *jd.%01u%s", + sizewidth, (intmax_t) size, fract/100, suf[i]); + else if (i) + printf("% *jd%s", + sizewidth + 2, (intmax_t) size, suf[i]); + else + printf("% *jd", sizewidth + 3, (intmax_t) size); + } else { + printf("% *jd", sizewidth + 3, (intmax_t) size); + } +} + + +void +output(void) +{ + size_t i; + off_t total = 0; + + for (i = 0; i < nusage; i++) { + printf("% 8ju", (uintmax_t) usage[i].pid); + printsize(usage[i].size); + printf(" %s\n", usage[i].name); + if (show_total) + total += usage[i].size; + } + if (show_total) { + printf("%8s", ""); + printsize(total); + printf(" %s\n", "total"); + } +} + +void +help(void) +{ + //FIXME +} + +int +main(int argc, char **argv) +{ + int c; + + progname = strrchr(argv[0], '/'); + if (progname) + ++progname; + else + progname = argv[0]; + while ((c = getopt(argc, argv, "aHhnprstv")) != EOF) { + switch (c) { + case 'a': + show_all = 1; + break; + case 'H': + human = 1; + break; + case 'h': + help(); + exit(0); + case 'n': + cmp = cmp_name; + break; + case 'p': + cmp = cmp_pid; + break; + case 'r': + reverse = 1; + break; + case 's': + cmp = cmp_size; + break; + case 't': + show_total = 1; + break; + case 'v': + verbose++; + break; + default: + exit(1); + } + } + if (argc > optind) + show_all = 1; + collect(argv + optind); + output(); + exit(0); +} |