diff --git a/Makefile b/Makefile index 9bba950..eaec230 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,16 @@ include config.mk .POSIX: .SUFFIXES: .c .o -HDR = arg.h ubase.h util.h +HDR = arg.h grabmntinfo.h proc.h util.h LIB = \ + util/agetcwd.o \ + util/apathmax.o \ util/eprintf.o \ util/estrtol.o \ - util/grabmntinfo.o + util/grabmntinfo.o \ + util/proc.o \ + util/recurse.o \ + util/tty.o SRC = \ df.c \ @@ -18,6 +23,7 @@ SRC = \ mkswap.c \ mount.c \ pivot_root.c \ + ps.c \ reboot.c \ rmmod.c \ stat.c \ diff --git a/proc.h b/proc.h new file mode 100644 index 0000000..6785ade --- /dev/null +++ b/proc.h @@ -0,0 +1,32 @@ +/* See LICENSE file for copyright and license details. */ +struct procstat { + int pid; + char comm[PATH_MAX + 2]; /* + 2 for '(' and ')' */ + unsigned char state; + int ppid; + int pgrp; + int sid; + int tty_nr; + int tpgid; + unsigned flags; + unsigned long minflt; + unsigned long cminflt; + unsigned long majflt; + unsigned long cmajflt; + unsigned long utime; + unsigned long stime; + long cutime; + long cstime; + long priority; + long nice; + long num_threads; + long itrealvalue; + unsigned long long starttime; + unsigned long vsize; + long rss; + long rsslim; +}; + +int parsestat(pid_t pid, struct procstat *ps); +int proceuid(pid_t pid, uid_t *euid); +int validps(const char *path); diff --git a/ps.c b/ps.c new file mode 100644 index 0000000..652fded --- /dev/null +++ b/ps.c @@ -0,0 +1,116 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include "proc.h" +#include "util.h" + +static void usage(void); +static void psout(struct procstat *ps); +static void psr(const char *path); + +enum { + PS_aflag = 1 << 0, + PS_Aflag = 1 << 1, + PS_dflag = 1 << 2 +}; + +static int flags; + +int +main(int argc, char *argv[]) +{ + ARGBEGIN { + case 'a': + flags |= PS_aflag; + break; + case 'A': + flags |= PS_Aflag; + break; + case 'd': + flags |= PS_dflag; + break; + case 'e': + flags |= PS_Aflag; + break; + case 'f': + eprintf("not implemented\n"); + default: + usage(); + } ARGEND; + + printf(" PID TTY TIME CMD\n"); + recurse("/proc", psr); + + return 0; +} + +static void +usage(void) +{ + eprintf("usage: [-aAdef] %s\n", argv0); +} + +static void +psout(struct procstat *ps) +{ + char *ttystr, *myttystr; + int tty_maj, tty_min; + uid_t myeuid, peuid; + unsigned sut; + + /* Ignore session leaders */ + if (flags & PS_dflag) + if (ps->pid == ps->sid) + return; + + sut = (ps->stime + ps->utime) / 100; + + devtotty(ps->tty_nr, &tty_maj, &tty_min); + ttystr = ttytostr(tty_maj, tty_min); + /* Only print processes that are associated with + * a terminal */ + if (ttystr[0] == '?' && (flags & PS_aflag)) { + free(ttystr); + return; + } + + if (!flags) { + myttystr = ttyname(STDIN_FILENO); + if (myttystr) { + if (strcmp(myttystr + strlen("/dev/"), ttystr)) { + free(ttystr); + return; + } + } else { + ttystr[0] = '?'; + ttystr[1] = '\0'; + } + proceuid(ps->pid, &peuid); + myeuid = geteuid(); + if (myeuid != peuid) { + free(ttystr); + return; + } + } + + printf("%5d %-6s %02u:%02u:%02u %s\n", ps->pid, ttystr, + sut / 3600, (sut % 3600) / 60, sut % 60, ps->comm); + free(ttystr); +} + +static void +psr(const char *path) +{ + struct procstat ps; + pid_t pid; + + if (!validps(path)) + return; + pid = estrtol(path, 10); + parsestat(pid, &ps); + psout(&ps); +} diff --git a/util.h b/util.h index d2f9399..e686733 100644 --- a/util.h +++ b/util.h @@ -6,6 +6,11 @@ extern char *argv0; -void eprintf(const char *, ...); +char *agetcwd(void); +void apathmax(char **, long *); +void devtotty(int dev, int *tty_maj, int *tty_min); void enprintf(int, const char *, ...); +void eprintf(const char *, ...); long estrtol(const char *, int); +void recurse(const char *, void (*)(const char *)); +char *ttytostr(int tty_maj, int tty_min); diff --git a/util/agetcwd.c b/util/agetcwd.c new file mode 100644 index 0000000..a672bcf --- /dev/null +++ b/util/agetcwd.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include "../util.h" + +char * +agetcwd(void) +{ + char *buf; + long size; + + apathmax(&buf, &size); + if(!getcwd(buf, size)) + eprintf("getcwd:"); + + return buf; +} + diff --git a/util/apathmax.c b/util/apathmax.c new file mode 100644 index 0000000..5dc831f --- /dev/null +++ b/util/apathmax.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "../util.h" + +void +apathmax(char **p, long *size) +{ + errno = 0; + + if((*size = pathconf("/", _PC_PATH_MAX)) == -1) { + if(errno == 0) { + *size = BUFSIZ; + } else { + eprintf("pathconf:"); + } + } + + if(!(*p = malloc(*size))) + eprintf("malloc:"); +} + diff --git a/util/proc.c b/util/proc.c new file mode 100644 index 0000000..ac6b991 --- /dev/null +++ b/util/proc.c @@ -0,0 +1,73 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include "../proc.h" +#include "../util.h" + +int +proceuid(pid_t pid, uid_t *euid) +{ + FILE *fp; + char buf[BUFSIZ], *p; + char path[PATH_MAX]; + uid_t procuid, proceuid; + + snprintf(path, sizeof(path), "/proc/%d/status", pid); + if (!(fp = fopen(path, "r"))) + eprintf("%s fopen:", path); + while (fgets(buf, sizeof(buf), fp)) { + if (!strncmp(buf, "Uid:", 4)) { + p = buf + strlen("Uid:"); + sscanf(p, "%u %u", &procuid, &proceuid); + *euid = proceuid; + fclose(fp); + return 0; + } + } + fclose(fp); + return -1; +} + +int +parsestat(pid_t pid, struct procstat *ps) +{ + char path[PATH_MAX]; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + if (!(fp = fopen(path, "r"))) + eprintf("fopen %s:", path); + fscanf(fp, "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu", + &ps->pid, ps->comm, + &ps->state, &ps->ppid, &ps->pgrp, + &ps->sid, &ps->tty_nr, &ps->tpgid, &ps->flags, + &ps->minflt, &ps->cminflt, &ps->majflt, &ps->cmajflt, + &ps->utime, &ps->stime); + fscanf(fp, "%ld %ld %ld %ld %ld %ld %lld %lu %ld %ld", + &ps->cutime, &ps->cstime, &ps->priority, &ps->nice, + &ps->num_threads, &ps->itrealvalue, &ps->starttime, + &ps->vsize, &ps->rss, &ps->rsslim); + /* Filter out '(' and ')' from comm */ + ps->comm[strlen(ps->comm) - 1] = '\0'; + memmove(ps->comm, ps->comm + 1, strlen(ps->comm)); + fclose(fp); + return 0; +} + +int +validps(const char *path) +{ + char *end; + + errno = 0; + strtol(path, &end, 10); + if (*end != '\0') + return 0; + if (errno != 0) + return 0; + return 1; +} diff --git a/util/recurse.c b/util/recurse.c new file mode 100644 index 0000000..b3d1f8c --- /dev/null +++ b/util/recurse.c @@ -0,0 +1,40 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include + +#include "../util.h" + +void +recurse(const char *path, void (*fn)(const char *)) +{ + char *cwd; + struct dirent *d; + struct stat st; + DIR *dp; + + if(lstat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { + return; + } else if(!(dp = opendir(path))) { + eprintf("opendir %s:", path); + } + + cwd = agetcwd(); + if(chdir(path) == -1) + eprintf("chdir %s:", path); + + while((d = readdir(dp))) { + if(strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) + fn(d->d_name); + } + + closedir(dp); + if(chdir(cwd) == -1) + eprintf("chdir %s:", cwd); + + free(cwd); +} + diff --git a/util/tty.c b/util/tty.c new file mode 100644 index 0000000..4b5e518 --- /dev/null +++ b/util/tty.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include "../util.h" + +void +devtotty(int dev, int *tty_maj, int *tty_min) +{ + *tty_maj = (dev >> 8) & 0xfff; + *tty_min = (dev & 0xff) | ((dev >> 12) & 0xfff00); +} + +char * +ttytostr(int tty_maj, int tty_min) +{ + const char *pts = "pts/"; + const char *tty = "tty/"; + char *ttystr; + size_t len; + + /* Up to 10k ttys */ + len = strlen(pts) + 4 + 1; + ttystr = malloc(len); + if (!ttystr) + eprintf("malloc:"); + switch (tty_maj) { + case 136: + snprintf(ttystr, len, "%s%d", pts, tty_min); + break; + case 4: + snprintf(ttystr, len, "%s%d", tty, tty_min); + default: + ttystr[0] = '?'; + ttystr[1] = '\0'; + break; + } + return ttystr; +}