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}