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