2 Copyright (C) 1992 Free Software Foundation, Inc.
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)
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.
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. */
18 /* Written by jla; revised by djm */
21 name [state] line time [idle] host
23 name, line, time: not -q
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). */
36 #if defined (CONFIG_BROKETS)
37 /* We use <config.h> instead of "config.h" so that a compilation
38 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
39 (which it would do because it found this file in $srcdir). */
47 #include <sys/types.h>
52 #include <sys/param.h>
59 #ifdef _PATH_UTMP /* 4.4BSD. */
60 #define UTMP_FILE _PATH_UTMP
61 #else /* !_PATH_UTMP */
62 #define UTMP_FILE "/etc/utmp"
63 #endif /* !_PATH_UTMP */
64 #endif /* !UTMP_FILE */
66 #ifndef MAXHOSTNAMELEN
67 #define MAXHOSTNAMELEN 64
70 #define MESG_BIT 020 /* Group write bit. */
77 static int read_utmp ();
78 static char *idle_string ();
79 static struct utmp
*search_entries ();
80 static void list_entries ();
81 static void print_entry ();
82 static void print_heading ();
83 static void scan_entries ();
86 static void who_am_i ();
88 /* The name this program was run with. */
91 /* If non-zero, display usage information and exit. */
94 /* If non-zero, print the version on standard output and exit. */
95 static int show_version
;
97 /* If nonzero, display only a list of usernames and count of
99 Ignored for `who am i'. */
100 static int short_list
;
102 /* If nonzero, display the hours:minutes since each user has touched
103 the keyboard, or "." if within the last minute, or "old" if
104 not within the last day. */
105 static int include_idle
;
107 /* If nonzero, display a line at the top describing each field. */
108 static int include_heading
;
110 /* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
111 or a `?' if their tty cannot be statted. */
112 static int include_mesg
;
114 static struct option
const longopts
[] =
116 {"count", no_argument
, NULL
, 'q'},
117 {"help", no_argument
, &show_help
, 1},
118 {"idle", no_argument
, NULL
, 'u'},
119 {"heading", no_argument
, NULL
, 'H'},
120 {"message", no_argument
, NULL
, 'T'},
121 {"mesg", no_argument
, NULL
, 'T'},
122 {"version", no_argument
, &show_version
, 1},
123 {"writable", no_argument
, NULL
, 'T'},
133 int my_line_only
= 0;
135 program_name
= argv
[0];
137 while ((optc
= getopt_long (argc
, argv
, "imqsuwHT", longopts
, &longind
))
177 printf ("%s\n", version_string
);
185 error (1, errno
, "cannot change directory to /dev");
187 switch (argc
- optind
)
191 who_am_i (UTMP_FILE
);
196 case 1: /* who <utmp file> */
198 who_am_i (argv
[optind
]);
203 case 2: /* who <blurf> <glop> */
204 who_am_i (UTMP_FILE
);
214 static struct utmp
*utmp_contents
;
216 /* Display a list of who is on the system, according to utmp file FILENAME. */
224 users
= read_utmp (filename
);
226 list_entries (users
);
228 scan_entries (users
);
231 /* Read the utmp file FILENAME into UTMP_CONTENTS and return the
232 number of entries it contains. */
239 struct stat file_stats
;
241 desc
= open (filename
, O_RDONLY
, 0);
243 error (1, errno
, "%s", filename
);
245 fstat (desc
, &file_stats
);
246 if (file_stats
.st_size
> 0)
247 utmp_contents
= (struct utmp
*) xmalloc ((unsigned) file_stats
.st_size
);
254 /* Use < instead of != in case the utmp just grew. */
255 if (read (desc
, utmp_contents
, file_stats
.st_size
) < file_stats
.st_size
)
256 error (1, errno
, "%s", filename
);
258 if (close (desc
) != 0)
259 error (1, errno
, "%s", filename
);
261 return file_stats
.st_size
/ sizeof (struct utmp
);
264 /* Display a line of information about entry THIS. */
273 char line
[sizeof (this->ut_line
) + 1];
275 strncpy (line
, this->ut_line
, sizeof (this->ut_line
));
276 line
[sizeof (this->ut_line
)] = 0;
277 if (stat (line
, &stats
) == 0)
279 mesg
= (stats
.st_mode
& MESG_BIT
) ? '+' : '-';
280 last_change
= stats
.st_atime
;
289 (int) sizeof (this->ut_name
), (int) sizeof (this->ut_name
),
292 printf (" %c ", mesg
);
294 (int) sizeof (this->ut_line
), (int) sizeof (this->ut_line
),
296 printf (" %-12.12s", ctime (&this->ut_time
) + 4);
300 printf (" %s", idle_string (last_change
));
305 if (this->ut_host
[0])
306 printf (" (%-.*s)", (int) sizeof (this->ut_host
), this->ut_host
);
312 /* Print the username of each valid entry and the number of valid entries
313 in `utmp_contents', which should have N elements. */
319 register struct utmp
*this = utmp_contents
;
320 register int entries
= 0;
326 && this->ut_type
== USER_PROCESS
330 printf ("%s ", this->ut_name
);
335 printf ("\n# users=%u\n", entries
);
343 printf ("%-*s ", (int) sizeof (ut
->ut_name
), "USER");
346 printf ("%-*s ", (int) sizeof (ut
->ut_line
), "LINE");
347 printf ("LOGIN-TIME ");
353 /* Display `utmp_contents', which should have N entries. */
359 register struct utmp
*this = utmp_contents
;
368 && this->ut_type
== USER_PROCESS
376 /* Search `utmp_contents', which should have N entries, for
377 an entry with a `ut_line' field identical to LINE.
378 Return the first matching entry found, or NULL if there
379 is no matching entry. */
382 search_entries (n
, line
)
386 register struct utmp
*this = utmp_contents
;
392 && this->ut_type
== USER_PROCESS
394 && !strncmp (line
, this->ut_line
, sizeof (this->ut_line
)))
401 /* Display the entry in utmp file FILENAME for this tty on standard input,
402 or nothing if there is no entry for it. */
408 register struct utmp
*utmp_entry
;
409 char hostname
[MAXHOSTNAMELEN
+ 1];
412 if (gethostname (hostname
, MAXHOSTNAMELEN
+ 1))
417 printf ("%*s ", (int) strlen (hostname
), " ");
424 tty
+= 5; /* Remove "/dev/". */
426 utmp_entry
= search_entries (read_utmp (filename
), tty
);
427 if (utmp_entry
== NULL
)
430 printf ("%s!", hostname
);
431 print_entry (utmp_entry
);
434 /* Return a string representing the time between WHEN and the time
435 that this function is first run. */
441 static time_t now
= 0;
442 static char idle
[10];
448 seconds_idle
= now
- when
;
449 if (seconds_idle
< 60) /* One minute. */
451 if (seconds_idle
< (24 * 60 * 60)) /* One day. */
453 sprintf (idle
, "%02d:%02d",
454 (int) (seconds_idle
/ (60 * 60)),
455 (int) ((seconds_idle
% (60 * 60)) / 60));
465 Usage: %s [-imqsuwHT] [--count] [--idle] [--heading] [--message] [--mesg]\n\
466 [--writable] [file] [am i]\n",