master xplshn/aruu / cmd / posix / ps.c
  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}