0612b6d6b5d5da8a7bed983682d954e33244bdda
[platform/upstream/coreutils.git] / src / who.c
1 /* GNU's who.
2    Copyright (C) 1992 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* Written by jla; revised by djm */
19
20 /* Output format:
21    name [state] line time [idle] host
22    state: -T
23    name, line, time: not -q
24    idle: -u
25
26    Options:
27    -m           Same as 'who am i', for POSIX.
28    -q           Only user names and # logged on; overrides all other options.
29    -s           Name, line, time (default).
30    -i, -u       Idle hours and minutes; '.' means active in last minute;
31                 'old' means idle for >24 hours.
32    -H           Print column headings at top.
33    -w, -T       -s plus mesg (+ or -, or ? if bad line). */
34
35 #include <stdio.h>
36 #include <sys/types.h>
37 #include <utmp.h>
38 #include <time.h>
39 #include <getopt.h>
40 #ifndef _POSIX_SOURCE
41 #include <sys/param.h>
42 #endif
43 #include "system.h"
44
45 #ifndef UTMP_FILE
46 #ifdef _PATH_UTMP               /* 4.4BSD.  */
47 #define UTMP_FILE _PATH_UTMP
48 #else                           /* !_PATH_UTMP */
49 #define UTMP_FILE "/etc/utmp"
50 #endif                          /* !_PATH_UTMP */
51 #endif                          /* !UTMP_FILE */
52
53 #ifndef MAXHOSTNAMELEN
54 #define MAXHOSTNAMELEN 64
55 #endif
56
57 #define MESG_BIT 020            /* Group write bit. */
58
59 char *ttyname ();
60
61 char *idle_string ();
62 char *xmalloc ();
63 struct utmp *search_entries ();
64 void error ();
65 void list_entries ();
66 void print_entry ();
67 void print_heading ();
68 void scan_entries ();
69 void usage ();
70 void who ();
71 void who_am_i ();
72
73 /* The name this program was run with. */
74 char *program_name;
75
76 /* If nonzero, display only a list of usernames and count of
77    the users logged on.
78    Ignored for `who am i'. */
79 static int short_list;
80
81 /* If nonzero, display the hours:minutes since each user has touched
82    the keyboard, or "." if within the last minute, or "old" if
83    not within the last day. */
84 static int include_idle;
85
86 /* If nonzero, display a line at the top describing each field. */
87 static int include_heading;
88
89 /* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
90    or a `?' if their tty cannot be statted. */
91 static int include_mesg;
92
93 static struct option longopts[] =
94 {
95   {"count", 0, NULL, 'q'},
96   {"idle", 0, NULL, 'u'},
97   {"heading", 0, NULL, 'H'},
98   {"message", 0, NULL, 'T'},
99   {"mesg", 0, NULL, 'T'},
100   {"writable", 0, NULL, 'T'},
101   {NULL, 0, NULL, 0}
102 };
103
104 void
105 main (argc, argv)
106      int argc;
107      char **argv;
108 {
109   int optc, longind;
110   int my_line_only = 0;
111
112   program_name = argv[0];
113
114   while ((optc = getopt_long (argc, argv, "imqsuwHT", longopts, &longind))
115          != EOF)
116     {
117       switch (optc)
118         {
119         case 'm':
120           my_line_only = 1;
121           break;
122
123         case 'q':
124           short_list = 1;
125           break;
126
127         case 's':
128           break;
129
130         case 'i':
131         case 'u':
132           include_idle = 1;
133           break;
134
135         case 'H':
136           include_heading = 1;
137           break;
138
139         case 'w':
140         case 'T':
141           include_mesg = 1;
142           break;
143
144         default:
145           usage ();
146         }
147     }
148
149   if (chdir ("/dev"))
150     error (1, errno, "cannot change directory to /dev");
151
152   switch (argc - optind)
153     {
154     case 0:                     /* who */
155       if (my_line_only)
156         who_am_i (UTMP_FILE);
157       else
158         who (UTMP_FILE);
159       break;
160
161     case 1:                     /* who <utmp file> */
162       if (my_line_only)
163         who_am_i (argv[optind]);
164       else
165         who (argv[optind]);
166       break;
167
168     case 2:                     /* who <blurf> <glop> */
169       who_am_i (UTMP_FILE);
170       break;
171
172     default:                    /* lose */
173       usage ();
174     }
175
176   exit (0);
177 }
178
179 static struct utmp *utmp_contents;
180
181 /* Display a list of who is on the system, according to utmp file FILENAME. */
182
183 void
184 who (filename)
185      char *filename;
186 {
187   int users;
188
189   users = read_utmp (filename);
190   if (short_list)
191     list_entries (users);
192   else
193     scan_entries (users);
194 }
195
196 /* Read the utmp file FILENAME into UTMP_CONTENTS and return the
197    number of entries it contains. */
198
199 int
200 read_utmp (filename)
201      char *filename;
202 {
203   register int desc;
204   struct stat file_stats;
205
206   desc = open (filename, O_RDONLY, 0);
207   if (desc < 0)
208     error (1, errno, "%s", filename);
209
210   fstat (desc, &file_stats);
211   if (file_stats.st_size > 0)
212     utmp_contents = (struct utmp *) xmalloc ((unsigned) file_stats.st_size);
213   else
214     {
215       close (desc);
216       return 0;
217     }
218
219   /* Use < instead of != in case the utmp just grew.  */
220   if (read (desc, utmp_contents, file_stats.st_size) < file_stats.st_size)
221     error (1, errno, "%s", filename);
222
223   if (close (desc))
224     error (1, errno, "%s", filename);
225
226   return file_stats.st_size / sizeof (struct utmp);
227 }
228
229 /* Display a line of information about entry THIS. */
230
231 void
232 print_entry (this)
233      struct utmp *this;
234 {
235   struct stat stats;
236   time_t last_change;
237   char mesg;
238   char line[sizeof (this->ut_line) + 1];
239
240   strncpy (line, this->ut_line, sizeof (this->ut_line));
241   line[sizeof (this->ut_line)] = 0;
242   if (stat (line, &stats) == 0)
243     {
244       mesg = (stats.st_mode & MESG_BIT) ? '+' : '-';
245       last_change = stats.st_atime;
246     }
247   else
248     {
249       mesg = '?';
250       last_change = 0;
251     }
252   
253   printf ("%-*.*s",
254           sizeof (this->ut_name), sizeof (this->ut_name),
255           this->ut_name);
256   if (include_mesg)
257     printf ("  %c  ", mesg);
258   printf (" %-*.*s",
259           sizeof (this->ut_line), sizeof (this->ut_line),
260           this->ut_line);
261   printf (" %-12.12s", ctime (&this->ut_time) + 4);
262   if (include_idle)
263     {
264       if (last_change)
265         printf (" %s", idle_string (last_change));
266       else
267         printf ("   .  ");
268     }
269 #ifdef HAVE_UT_HOST
270   if (this->ut_host[0])
271     printf (" (%-.*s)", sizeof (this->ut_host), this->ut_host);
272 #endif
273
274   putchar ('\n');
275 }
276
277 /* Print the username of each valid entry and the number of valid entries
278    in `utmp_contents', which should have N elements. */
279
280 void
281 list_entries (n)
282      int n;
283 {
284   register struct utmp *this = utmp_contents;
285   register int entries = 0;
286
287   while (n--)
288     {
289       if (this->ut_name[0]
290 #ifdef USER_PROCESS
291           && this->ut_type == USER_PROCESS
292 #endif
293          )
294         {
295           printf ("%s ", this->ut_name);
296           entries++;
297         }
298       this++;
299     }
300   printf ("\n# users=%u\n", entries);
301 }
302
303 void
304 print_heading ()
305 {
306   struct utmp *ut;
307
308   printf ("%-*s ", sizeof (ut->ut_name), "USER");
309   if (include_mesg)
310     printf ("MESG ");
311   printf ("%-*s ", sizeof (ut->ut_line), "LINE");
312   printf ("LOGIN-TIME   ");
313   if (include_idle)
314     printf ("IDLE  ");
315   printf ("FROM\n");
316 }
317
318 /* Display `utmp_contents', which should have N entries. */
319
320 void
321 scan_entries (n)
322      int n;
323 {
324   register struct utmp *this = utmp_contents;
325
326   if (include_heading)
327     print_heading ();
328
329   while (n--)
330     {
331       if (this->ut_name[0]
332 #ifdef USER_PROCESS
333           && this->ut_type == USER_PROCESS
334 #endif
335          )
336         print_entry (this);
337       this++;
338     }
339 }
340
341 /* Search `utmp_contents', which should have N entries, for
342    an entry with a `ut_line' field identical to LINE.
343    Return the first matching entry found, or NULL if there
344    is no matching entry. */
345
346 struct utmp *
347 search_entries (n, line)
348      int n;
349      char *line;
350 {
351   register struct utmp *this = utmp_contents;
352
353   while (n--)
354     {
355       if (this->ut_name[0]
356 #ifdef USER_PROCESS
357           && this->ut_type == USER_PROCESS
358 #endif
359           && !strncmp (line, this->ut_line, sizeof (this->ut_line)))
360         return this;
361       this++;
362     }
363   return NULL;
364 }
365
366 /* Display the entry in utmp file FILENAME for this tty on standard input,
367    or nothing if there is no entry for it. */
368
369 void
370 who_am_i (filename)
371      char *filename;
372 {
373   register struct utmp *utmp_entry;
374   char hostname[MAXHOSTNAMELEN + 1];
375   char *tty;
376
377   if (gethostname (hostname, MAXHOSTNAMELEN + 1))
378     *hostname = 0;
379
380   if (include_heading)
381     {
382       printf ("%*s ", strlen (hostname), " ");
383       print_heading ();
384     }
385
386   tty = ttyname (0);
387   if (tty == NULL)
388     return;
389   tty += 5;                     /* Remove "/dev/".  */
390   
391   utmp_entry = search_entries (read_utmp (filename), tty);
392   if (utmp_entry == NULL)
393     return;
394
395   printf ("%s!", hostname);
396   print_entry (utmp_entry);
397 }
398
399 /* Return a string representing the time between WHEN and the time
400    that this function is first run. */
401
402 char *
403 idle_string (when)
404      time_t when;
405 {
406   static time_t now = 0;
407   static char idle[10];
408   time_t seconds_idle;
409
410   if (now == 0)
411     time (&now);
412
413   seconds_idle = now - when;
414   if (seconds_idle < 60)        /* One minute. */
415     return "  .  ";
416   if (seconds_idle < (24 * 60 * 60)) /* One day. */
417     {
418       sprintf (idle, "%02d:%02d",
419                seconds_idle / (60 * 60),
420                (seconds_idle % (60 * 60)) / 60);
421       return idle;
422     }
423   return " old ";
424 }
425
426 void
427 usage ()
428 {
429   fprintf (stderr, "\
430 Usage: %s [-imqsuwHT] [--count] [--idle] [--heading] [--message] [--mesg]\n\
431        [--writable] [file] [am i]\n",
432            program_name);
433   exit (1);
434 }