1 /* w - show what logged in users are doing. Almost entirely rewritten from
2 * scratch by Charles Blake circa June 1996. Some vestigal traces of the
3 * original may exist. That was done in 1993 by Larry Greenfield with some
4 * fixes by Michael K. Johnson.
6 * Changes by Albert Cahalan, 2002.
8 #include "proc/version.h"
9 #include "proc/whattime.h"
10 #include "proc/readproc.h"
11 #include "proc/devname.h"
12 #include "proc/procps.h"
13 #include "proc/sysinfo.h"
14 #include "proc/escape.h"
21 #include <sys/ioctl.h>
24 #include <sys/types.h>
32 static int ignoreuser = 0; /* for '-u' */
33 static proc_t **procs; /* our snapshot of the process table */
35 typedef struct utmp utmp_t;
38 # define FROM_STRING "on"
40 # define FROM_STRING "off"
43 /* Uh... same thing as UT_NAMESIZE */
44 #define USERSZ (sizeof u->ut_user)
47 /* This routine is careful since some programs leave utmp strings
48 * unprintable. Always outputs at least 16 chars padded with spaces
49 * on the right if necessary.
51 static void print_host(const char *restrict host, int len) {
55 /* FIXME: there should really be a way to configure this... */
56 /* for now, we'll just limit it to the 16 that the libc5 version
59 if (len > 16) len = 16;
61 for ( ; host < last ; host++){
62 if (isprint(*host) && *host != ' ') {
69 // space-fill, and a '-' too if needed to ensure the column exists
70 if(width < 16) fputs("- "+width, stdout);
73 /***** compact 7 char format for time intervals (belongs in libproc?) */
74 static void print_time_ival7(time_t t, int centi_sec, FILE* fout) {
75 if((long)t < (long)0){ /* system clock changed? */
79 if (t >= 48*60*60) /* > 2 days */
80 fprintf(fout, " %2ludays", t/(24*60*60));
81 else if (t >= 60*60) /* > 1 hour */
82 fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60));
83 else if (t > 60) /* > 1 minute */
84 fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
86 fprintf(fout, " %2lu.%02us", t, centi_sec);
89 /**** stat the device file to get an idle time */
90 static time_t idletime(const char *restrict const tty) {
92 if (stat(tty, &sbuf) != 0)
94 return time(NULL) - sbuf.st_atime;
97 /***** 7 character formatted login time */
98 static void print_logintime(time_t logt, FILE* fout) {
99 char weekday[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
100 month [][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
101 "Aug", "Sep", "Oct", "Nov", "Dec" };
103 struct tm *logtm, *curtm;
107 curtm = localtime(&curt);
108 /* localtime returns a pointer to static memory */
109 today = curtm->tm_yday;
110 logtm = localtime(&logt);
111 if (curt - logt > 12*60*60 && logtm->tm_yday != today) {
112 if (curt - logt > 6*24*60*60)
113 fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon],
114 logtm->tm_year % 100);
116 fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday], logtm->tm_hour);
118 fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min);
123 /* This function scans the process table accumulating total cpu times for
124 * any processes "associated" with this login session. It also searches
125 * for the "best" process to report as "(w)hat" the user for that login
126 * session is doing currently. This the essential core of 'w'.
128 static const proc_t *getproc(const utmp_t *restrict const u, const char *restrict const tty, unsigned long long *restrict const jcpu, int *restrict const found_utpid) {
130 proc_t **pptr = procs;
131 const proc_t *best = NULL;
132 const proc_t *secondbest = NULL;
137 char buf[UT_NAMESIZE+1];
138 struct passwd *passwd_data; /* pointer to static data */
139 strncpy(buf,u->ut_user,UT_NAMESIZE);
140 buf[UT_NAMESIZE] = '\0';
141 passwd_data = getpwnam(buf);
142 if(!passwd_data) return NULL;
143 uid = passwd_data->pw_uid;
144 /* OK to have passwd_data go out of scope here */
146 line = tty_to_dev(tty);
148 for(; *pptr; pptr++) {
149 const proc_t *restrict const tmp = *pptr;
150 if(unlikely(tmp->tgid == u->ut_pid)) {
154 if(tmp->tty != line) continue;
155 (*jcpu) += tmp->utime + tmp->stime;
157 /* same time-logic here as for "best" below */
158 if(! (secondbest && tmp->start_time <= secondbest->start_time) ){
161 if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue;
162 if(tmp->tgid != tmp->tpgid) continue;
163 if(best && tmp->start_time <= best->start_time) continue;
166 return best ? best : secondbest;
171 static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
172 unsigned long long jcpu;
175 char uname[USERSZ + 1] = "",
176 tty[5 + sizeof u->ut_line + 1] = "/dev/";
179 for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */
180 if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/'))
181 tty[i+5] = u->ut_line[i];
185 best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
187 /* just skip if stale utmp entry (i.e. login proc doesn't exist). If there
188 * is a desire a cmdline flag could be added to optionally show it with a
189 * prefix of (stale) in front of cmd or something like that.
194 strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */
196 printf("%-9.8s%-9.8s", uname, u->ut_line);
198 print_host(u->ut_host, sizeof u->ut_host);
199 print_logintime(u->ut_time, stdout);
200 if (*u->ut_line == ':') /* idle unknown for xdm logins */
203 print_time_ival7(idletime(tty), 0, stdout);
204 print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
206 unsigned long long pcpu = best->utime + best->stime;
207 print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
211 printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
213 print_host(u->ut_host, sizeof u->ut_host);
214 if (*u->ut_line == ':') /* idle unknown for xdm logins */
217 print_time_ival7(idletime(tty), 0, stdout);
222 escape_command(cmdbuf, best, sizeof cmdbuf, &maxcmd, ESC_ARGS);
223 fputs(cmdbuf,stdout);
231 int main(int argc, char **argv) {
235 int header=1, longform=1, from=1, args, maxcmd=80, ch;
241 setlocale(LC_ALL, "");
242 for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
244 case 'h': header = 0; break;
245 case 'l': longform = 1; break;
246 case 's': longform = 0; break;
247 case 'f': from = !from; break;
248 case 'V': display_version(); exit(0);
249 case 'u': ignoreuser = 1; break;
251 printf("usage: w -hlsufV [user]\n"
253 " -l long listing (default)\n"
254 " -s short listing\n"
255 " -u ignore uid of processes\n"
256 " -f toggle FROM field (default %s)\n"
257 " -V display version\n", FROM_STRING);
262 user = (argv[optind]);
264 if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
267 fprintf(stderr, "%d column window is too narrow\n", maxcmd);
270 maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
272 fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
274 procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT);
276 if (header) { /* print uptime and headers */
282 printf(" LOGIN@ IDLE JCPU PCPU WHAT\n");
284 printf(" IDLE WHAT\n");
292 if (unlikely(!u)) break;
293 if (u->ut_type != USER_PROCESS) continue;
294 if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from);
299 if (unlikely(!u)) break;
300 if (u->ut_type != USER_PROCESS) continue;
301 if (*u->ut_user) showinfo(u, longform, maxcmd, from);