ubase

suckless linux base utils
git clone git://git.suckless.org/ubase
Log | Files | Refs | README | LICENSE

ps.c (3852B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/ioctl.h>
      3 #include <sys/sysinfo.h>
      4 
      5 #include <errno.h>
      6 #include <libgen.h>
      7 #include <limits.h>
      8 #include <pwd.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <time.h>
     13 #include <unistd.h>
     14 
     15 #include "proc.h"
     16 #include "util.h"
     17 
     18 static void psout(struct procstat *ps);
     19 static void psr(const char *file);
     20 
     21 enum {
     22 	PS_aflag = 1 << 0,
     23 	PS_Aflag = 1 << 1,
     24 	PS_dflag = 1 << 2,
     25 	PS_fflag = 1 << 3
     26 };
     27 
     28 static int flags;
     29 
     30 static void
     31 psout(struct procstat *ps)
     32 {
     33 	struct procstatus pstatus;
     34 	char cmdline[BUFSIZ], *cmd;
     35 	char buf[BUFSIZ];
     36 	char ttystr[TTY_NAME_MAX], *myttystr;
     37 	int tty_maj, tty_min;
     38 	uid_t myeuid;
     39 	unsigned sutime;
     40 	time_t start;
     41 	char stimestr[sizeof("%H:%M")];
     42 	struct sysinfo info;
     43 	struct passwd *pw;
     44 	struct winsize w;
     45 
     46 	/* Ignore session leaders */
     47 	if (flags & PS_dflag)
     48 		if (ps->pid == ps->sid)
     49 			return;
     50 
     51 	devtotty(ps->tty_nr, &tty_maj, &tty_min);
     52 	ttytostr(tty_maj, tty_min, ttystr, sizeof(ttystr));
     53 
     54 	/* Only print processes that are associated with
     55 	 * a terminal and they are not session leaders */
     56 	if (flags & PS_aflag)
     57 		if (ps->pid == ps->sid || ttystr[0] == '?')
     58 			return;
     59 
     60 	if (parsestatus(ps->pid, &pstatus) < 0)
     61 		return;
     62 
     63 	/* This is the default case, only print processes that have
     64 	 * the same controlling terminal as the invoker and the same
     65 	 * euid as the current user */
     66 	if (!(flags & (PS_aflag | PS_Aflag | PS_dflag))) {
     67 		myttystr = ttyname(0);
     68 		if (myttystr) {
     69 			if (strcmp(myttystr + strlen("/dev/"), ttystr))
     70 				return;
     71 		} else {
     72 			/* The invoker has no controlling terminal - just
     73 			 * go ahead and print the processes anyway */
     74 			ttystr[0] = '?';
     75 			ttystr[1] = '\0';
     76 		}
     77 		myeuid = geteuid();
     78 		if (myeuid != pstatus.euid)
     79 			return;
     80 	}
     81 
     82 	sutime = (ps->stime + ps->utime) / sysconf(_SC_CLK_TCK);
     83 
     84 	ioctl(1, TIOCGWINSZ, &w);
     85 	if (!(flags & PS_fflag)) {
     86 		snprintf(buf, sizeof(buf), "%5d %-6s   %02u:%02u:%02u %s", ps->pid, ttystr,
     87 			 sutime / 3600, (sutime % 3600) / 60, sutime % 60,
     88 			 ps->comm);
     89 		if (w.ws_col)
     90 			printf("%.*s\n", w.ws_col, buf);
     91 		else
     92 			printf("%s\n", buf);
     93 	} else {
     94 		errno = 0;
     95 		pw = getpwuid(pstatus.uid);
     96 		if (!pw)
     97 			eprintf("getpwuid %d:", pstatus.uid);
     98 
     99 		if (sysinfo(&info) < 0)
    100 			eprintf("sysinfo:");
    101 
    102 		start = time(NULL) - info.uptime;
    103 		start += (ps->starttime / sysconf(_SC_CLK_TCK));
    104 		strftime(stimestr, sizeof(stimestr),
    105 			 "%H:%M", localtime(&start));
    106 
    107 		/* For kthreads/zombies /proc/<pid>/cmdline will be
    108 		 * empty so use ps->comm in that case */
    109 		if (parsecmdline(ps->pid, cmdline, sizeof(cmdline)) < 0)
    110 			cmd = ps->comm;
    111 		else
    112 			cmd = cmdline;
    113 
    114 		snprintf(buf, sizeof(buf), "%-8s %5d %5d  ? %5s %-5s    %02u:%02u:%02u %s%s%s",
    115 			 pw->pw_name, ps->pid,
    116 			 ps->ppid, stimestr, ttystr,
    117 			 sutime / 3600, (sutime % 3600) / 60, sutime % 60,
    118 			 (cmd == ps->comm) ? "[" : "", cmd,
    119 			 (cmd == ps->comm) ? "]" : "");
    120 		if (w.ws_col)
    121 			printf("%.*s\n", w.ws_col, buf);
    122 		else
    123 			printf("%s\n", buf);
    124 	}
    125 }
    126 
    127 static void
    128 psr(const char *file)
    129 {
    130 	char path[PATH_MAX], *p;
    131 	struct procstat ps;
    132 	pid_t pid;
    133 
    134 	if (strlcpy(path, file, sizeof(path)) >= sizeof(path))
    135 		eprintf("path too long\n");
    136 	p = basename(path);
    137 	if (pidfile(p) == 0)
    138 		return;
    139 	pid = estrtol(p, 10);
    140 	if (parsestat(pid, &ps) < 0)
    141 		return;
    142 	psout(&ps);
    143 }
    144 
    145 static void
    146 usage(void)
    147 {
    148 	eprintf("usage: %s [-aAdef]\n", argv0);
    149 }
    150 
    151 int
    152 main(int argc, char *argv[])
    153 {
    154 	ARGBEGIN {
    155 	case 'a':
    156 		flags |= PS_aflag;
    157 		break;
    158 	case 'A':
    159 		flags |= PS_Aflag;
    160 		break;
    161 	case 'd':
    162 		flags |= PS_dflag;
    163 		break;
    164 	case 'e':
    165 		flags |= PS_Aflag;
    166 		break;
    167 	case 'f':
    168 		flags |= PS_fflag;
    169 		break;
    170 	default:
    171 		usage();
    172 	} ARGEND;
    173 
    174 	if (!(flags & PS_fflag))
    175 		printf("  PID TTY          TIME CMD\n");
    176 	else
    177 		printf("UID        PID  PPID  C STIME TTY          TIME CMD\n");
    178 	recurse("/proc", psr);
    179 	return 0;
    180 }