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