Updated with Tizen:Base source codes
[external/procps.git] / w.c
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.
5  *
6  * Changes by Albert Cahalan, 2002.
7  */
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"
15 #include <ctype.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <pwd.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <utmp.h>
29 #include <locale.h>
30 #include <termios.h>
31
32 static int ignoreuser = 0;      /* for '-u' */
33 static proc_t **procs;          /* our snapshot of the process table */
34
35 typedef struct utmp utmp_t;
36
37 #ifdef W_SHOWFROM
38 #   define FROM_STRING "on"
39 #else
40 #   define FROM_STRING "off"
41 #endif
42
43 /* Uh... same thing as UT_NAMESIZE */
44 #define USERSZ (sizeof u->ut_user)
45
46
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.
50  */
51 static void print_host(const char *restrict host, int len) {
52     const char *last;
53     int width = 0;
54
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
57      * of utmp uses.
58      */
59     if (len > 16) len = 16;
60     last = host + len;
61     for ( ; host < last ; host++){
62         if (isprint(*host) && *host != ' ') {
63             fputc(*host, stdout);
64             ++width;
65         } else {
66             break;
67         }
68     }
69     // space-fill, and a '-' too if needed to ensure the column exists
70     if(width < 16) fputs("-               "+width, stdout);
71 }
72
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? */
76       printf("   ?   ");
77       return;
78     }
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);
85     else
86         fprintf(fout, " %2lu.%02us", t, centi_sec);
87 }
88
89 /**** stat the device file to get an idle time */
90 static time_t idletime(const char *restrict const tty) {
91     struct stat sbuf;
92     if (stat(tty, &sbuf) != 0)
93         return 0;
94     return time(NULL) - sbuf.st_atime;
95 }
96
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" };
102     time_t curt;
103     struct tm *logtm, *curtm;
104     int today;
105
106     curt = time(NULL);
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);
115         else
116             fprintf(fout, " %3s%02d  ", weekday[logtm->tm_wday], logtm->tm_hour);
117     } else {
118         fprintf(fout, " %02d:%02d  ", logtm->tm_hour, logtm->tm_min);
119     }
120 }
121
122
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'.
127  */
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) {
129     int line;
130     proc_t **pptr = procs;
131     const proc_t *best = NULL;
132     const proc_t *secondbest = NULL;
133     unsigned uid = ~0U;
134
135     *found_utpid = 0;
136     if(!ignoreuser){
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 */
145     }
146     line = tty_to_dev(tty);
147     *jcpu = 0;
148     for(; *pptr; pptr++) {
149         const proc_t *restrict const tmp = *pptr;
150         if(unlikely(tmp->tgid == u->ut_pid)) {
151             *found_utpid = 1;
152             best = tmp;
153         }
154         if(tmp->tty != line) continue;
155         (*jcpu) += tmp->utime + tmp->stime;
156         secondbest = tmp;
157         /* same time-logic here as for "best" below */
158         if(!  (secondbest && tmp->start_time <= secondbest->start_time)  ){
159             secondbest = tmp;
160         }
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;
164         best = tmp;
165     }
166     return best ? best : secondbest;
167 }
168
169
170 /***** showinfo */
171 static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
172     unsigned long long jcpu;
173     int ut_pid_found;
174     unsigned i;
175     char uname[USERSZ + 1] = "",
176         tty[5 + sizeof u->ut_line + 1] = "/dev/";
177     const proc_t *best;
178
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];
182         else
183             tty[i+5] = '\0';
184
185     best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
186
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.
190      */
191     if (!ut_pid_found)
192         return;
193
194     strncpy(uname, u->ut_user, USERSZ);         /* force NUL term for printf */
195     if (formtype) {
196         printf("%-9.8s%-9.8s", uname, u->ut_line);
197         if (from)
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 */
201             printf(" ?xdm? ");
202         else
203             print_time_ival7(idletime(tty), 0, stdout);
204         print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
205         if (best) {
206             unsigned long long pcpu = best->utime + best->stime;
207             print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
208         } else
209             printf("   ?   ");
210     } else {
211         printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
212         if (from)
213             print_host(u->ut_host, sizeof u->ut_host);
214         if (*u->ut_line == ':')                 /* idle unknown for xdm logins */
215             printf(" ?xdm? ");
216         else
217             print_time_ival7(idletime(tty), 0, stdout);
218     }
219     fputs(" ", stdout);
220     if (likely(best)) {
221         char cmdbuf[512];
222         escape_command(cmdbuf, best, sizeof cmdbuf, &maxcmd, ESC_ARGS);
223         fputs(cmdbuf,stdout);
224     } else {
225         printf("-");
226     }
227     fputc('\n', stdout);
228 }
229
230 /***** main */
231 int main(int argc, char **argv) {
232     char *user = NULL;
233     utmp_t *u;
234     struct winsize win;
235     int header=1, longform=1, from=1, args, maxcmd=80, ch;
236
237 #ifndef W_SHOWFROM
238     from = 0;
239 #endif
240
241     setlocale(LC_ALL, "");
242     for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
243         switch (ch) {
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;
250           default:
251             printf("usage: w -hlsufV [user]\n"
252                    "    -h    skip header\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);
258             exit(1);
259         }
260
261     if ((argv[optind]))
262         user = (argv[optind]);
263
264     if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
265         maxcmd = win.ws_col;
266     if (maxcmd < 71) {
267         fprintf(stderr, "%d column window is too narrow\n", maxcmd);
268         exit(1);
269     }
270     maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
271     if (maxcmd < 3)
272         fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
273
274     procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT);
275
276     if (header) {                               /* print uptime and headers */
277         print_uptime();
278         printf("USER     TTY      ");
279         if (from)
280             printf("FROM            ");
281         if (longform)
282             printf("  LOGIN@   IDLE   JCPU   PCPU WHAT\n");
283         else
284             printf("   IDLE WHAT\n");
285     }
286
287     utmpname(UTMP_FILE);
288     setutent();
289     if (user) {
290         for (;;) {
291             u = getutent();
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);
295         }
296     } else {
297         for (;;) {
298             u = getutent();
299             if (unlikely(!u)) break;
300             if (u->ut_type != USER_PROCESS) continue;
301             if (*u->ut_user) showinfo(u, longform, maxcmd, from);
302         }
303     }
304     endutent();
305
306     return 0;
307 }