source: trunk/minix/commands/simple/last.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 11.6 KB
RevLine 
[9]1/* last - display login history Author: Terrence W. Holm */
2
3/* last- Display the user log-in history.
4 * Last(1) searches backwards through the file of log-in
5 * records (/usr/adm/wtmp), displaying the length of
6 * log-in sessions as requested by the options:
7 *
8 * Usage: last [-r] [-count] [-f file] [name] [tty] ...
9 *
10 * -r Search backwards only until the last reboot
11 * record.
12 *
13 * -count Only print out <count> records. Last(1) stops
14 * when either -r or -count is satisfied, or at
15 * the end of the file if neither is given.
16 *
17 * -f file Use "file" instead of "/usr/adm/wtmp".
18 *
19 * name Print records for the user "name".
20 *
21 * tty Print records for the terminal "tty". Actually,
22 * a list of names may be given and all records
23 * that match either the user or tty name are
24 * printed. If no names are given then all records
25 * are displayed.
26 *
27 * A sigquit (^\) causes last(1) to display how far it
28 * has gone back in the log-in record file, it then
29 * continues. This is used to check on the progress of
30 * long running searches. A sigint will stop last(1).
31 *
32 * Author: Terrence W. Holm May 1988
33 *
34 * Revision:
35 * Fred van Kempen, October 1989
36 * -Adapted to MSS.
37 * -Adapted to new utmp database.
38 *
39 * Fred van Kempen, December 1989
40 * -Adapted to POSIX (MINIX 1.5)
41 *
42 * Fred van Kempen, January 1990
43 * -Final edit for 1.5
44 *
45 * Philip Homburg, March 1992
46 * -Include host in output
47 *
48 * Kees J. Bot, July 1997
49 * -Approximate system uptime from last reboot record
50 */
51#include <sys/types.h>
52#include <signal.h>
53#include <string.h>
54#include <utmp.h>
55#include <time.h>
56#include <stdlib.h>
57#include <stdio.h>
58#include <errno.h>
59
60#include <minix/paths.h>
61
62#define FALSE 0
63#define TRUE 1
64#define RLOGIN 1
65
66#define BUFFER_SIZE 4096 /* Room for wtmp records */
67#define MAX_WTMP_COUNT ( BUFFER_SIZE / sizeof(struct utmp) )
68
69#define min( a, b ) ( (a < b) ? a : b )
70#define max( a, b ) ( (a > b) ? a : b )
71
72
73typedef struct logout { /* A logout time record */
74 char line[12]; /* The terminal name */
75 long time; /* The logout time */
76 struct logout *next; /* Next in linked list */
77} logout;
78
79
80static char *Version = "@(#) LAST 1.7 (10/24/92)";
81
82
83/* command-line option flags */
84char boot_limit = FALSE; /* stop on latest reboot */
85char count_limit = FALSE; /* stop after print_count */
86char tell_uptime = FALSE; /* tell uptime since last reboot */
87int print_count;
88char *prog; /* name of this program */
89int arg_count; /* used to select specific */
90char **args; /* users and ttys */
91
92/* global variables */
93long boot_time = 0; /* Zero means no reboot yet */
94char *boot_down; /* "crash" or "down " flag */
95logout *first_link = NULL; /* List of logout times */
96int interrupt = FALSE; /* If sigint or sigquit occurs */
97
98_PROTOTYPE(int main, (int argc, char **argv));
99_PROTOTYPE(void Sigint, (int sig));
100_PROTOTYPE(void Sigquit, (int sig));
101_PROTOTYPE(void usage, (void));
102_PROTOTYPE(void Process, (struct utmp *wtmp));
103_PROTOTYPE(int Print_Record, (struct utmp *wtmp));
104_PROTOTYPE(void Print_Duration, (long from, long to));
105_PROTOTYPE(void Print_Uptime, (void));
106_PROTOTYPE(void Record_Logout_Time, (struct utmp *wtmp));
107
108/* Sigint() and Sigquit() Flag occurrence of an interrupt. */
109void Sigint(sig)
110int sig;
111{
112 interrupt = SIGINT;
113}
114
115
116void Sigquit(sig)
117int sig;
118{
119 interrupt = SIGQUIT;
120}
121
122
123void usage()
124{
125 fprintf(stderr,
126 "Usage: last [-r] [-u] [-count] [-f file] [name] [tty] ...\n");
127 exit(-1);
128}
129
130
131/* A log-in record format file contains four types of records.
132 *
133 * [1] generated on a system reboot:
134 *
135 * line="~", name="reboot", host="", time=date()
136 *
137 *
138 * [2] generated after a shutdown:
139 *
140 * line="~", name="shutdown", host="", time=date()
141 *
142 *
143 * [3] generated on a successful login(1)
144 *
145 * line=ttyname(), name=cuserid(), host=, time=date()
146 *
147 *
148 * [4] generated by init(8) on a logout
149 *
150 * line=ttyname(), name="", host="", time=date()
151 *
152 *
153 * Note: This version of last(1) does not recognize the '|' and '}' time
154 * change records. Last(1) pairs up line login's and logout's to
155 * generate four types of output lines:
156 *
157 * [1] a system reboot or shutdown
158 *
159 * reboot ~ Mon May 16 14:16
160 * shutdown ~ Mon May 16 14:15
161 *
162 * [2] a login with a matching logout
163 *
164 * edwin tty1 Thu May 26 20:05 - 20:32 (00:27)
165 *
166 * [3] a login followed by a reboot or shutdown
167 *
168 * root tty0 Mon May 16 13:57 - crash (00:19)
169 * root tty1 Mon May 16 13:45 - down (00:30)
170 *
171 * [4] a login not followed by a logout or reboot
172 *
173 * terry tty0 Thu May 26 21:19 still logged in
174 */
175void Process(wtmp)
176struct utmp *wtmp;
177{
178 logout *link;
179 logout *next_link;
180 char is_reboot;
181
182 /* suppress the job number on an "ftp" line */
183 if (!strncmp(wtmp->ut_line, "ftp", (size_t)3)) strncpy(wtmp->ut_line, "ftp", (size_t)8);
184
185 if (!strcmp(wtmp->ut_line, "~")) {
186 /* A reboot or shutdown record */
187 if (boot_limit) exit(0);
188
189 if (Print_Record(wtmp)) putchar('\n');
190 boot_time = wtmp->ut_time;
191
192 is_reboot = !strcmp(wtmp->ut_name, "reboot");
193 if (is_reboot)
194 boot_down = "crash";
195 else
196 boot_down = "down ";
197
198 if (tell_uptime) {
199 if (!is_reboot) {
200 fprintf(stderr,
201 "%s: no reboot record added to wtmp file on system boot!\n",
202 prog);
203 exit(1);
204 }
205 Print_Uptime();
206 exit(0);
207 }
208
209 /* remove any logout records */
210 for (link = first_link; link != NULL; link = next_link) {
211 next_link = link->next;
212 free(link);
213 }
214 first_link = NULL;
215 } else if (wtmp->ut_name[0] == '\0') {
216 /* A logout record */
217 Record_Logout_Time(wtmp);
218 } else {
219 /* A login record */
220 for (link = first_link; link != NULL; link = link->next)
221 if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
222 /* found corresponding logout record */
223 if (Print_Record(wtmp)) {
224 printf("- %.5s ", ctime(&link->time) + 11);
225 Print_Duration(wtmp->ut_time, link->time);
226 }
227 /* record login time */
228 link->time = wtmp->ut_time;
229 return;
230 }
231 /* could not find a logout record for this login tty */
232 if (Print_Record(wtmp))
233 if (boot_time == 0) /* still on */
234 printf(" still logged in\n");
235 else { /* system crashed while on */
236 printf("- %s ", boot_down);
237 Print_Duration(wtmp->ut_time, boot_time);
238 }
239 Record_Logout_Time(wtmp); /* Needed in case of 2
240 * consecutive logins */
241 }
242}
243
244
245/* Print_Record(wtmp) If the record was requested, then print out
246 * the user name, terminal, host and time.
247 */
248int Print_Record(wtmp)
249struct utmp *wtmp;
250{
251 int i;
252 char print_flag = FALSE;
253
254 /* just interested in the uptime? */
255 if (tell_uptime) return(FALSE);
256
257 /* check if we have already printed the requested number of records */
258 if (count_limit && print_count == 0) exit(0);
259
260 for (i = 0; i < arg_count; ++i)
261 if (!strncmp(args[i], wtmp->ut_name, sizeof(wtmp->ut_name)) ||
262 !strncmp(args[i], wtmp->ut_line, sizeof(wtmp->ut_line)))
263 print_flag = TRUE;
264
265 if (arg_count == 0 || print_flag) {
266#ifdef RLOGIN
267 printf("%-8.8s %-8.8s %-16.16s %.16s ",
268 wtmp->ut_name, wtmp->ut_line, wtmp->ut_host,
269 ctime(&wtmp->ut_time));
270#else
271 printf("%-8.8s %-8.8s %.16s ",
272 wtmp->ut_name, wtmp->ut_line, ctime(&wtmp->ut_time));
273#endif
274 --print_count;
275 return(TRUE);
276 }
277 return(FALSE);
278}
279
280
281/* Print_Duration(from, to) Calculate and print the days and hh:mm between
282 * the log-in and the log-out.
283 */
284void Print_Duration(from, to)
285long from;
286long to;
287{
288 long delta, days, hours, minutes;
289
290 delta = max(to - from, 0);
291 days = delta / (24L * 60L * 60L);
292 delta = delta % (24L * 60L * 60L);
293 hours = delta / (60L * 60L);
294 delta = delta % (60L * 60L);
295 minutes = delta / 60L;
296
297 if (days > 0)
298 printf("(%ld+", days);
299 else
300 printf(" (");
301
302 printf("%02ld:%02ld)\n", hours, minutes);
303}
304
305
306/* Print_Uptime() Calculate and print the "uptime" between the last recorded
307 * boot and the current time.
308 */
309void Print_Uptime()
310{
311#define NLOADS 3
312 int nloads;
313 double loads[NLOADS];
314 char *utmp_file = _PATH_UTMP;
315 unsigned nusers;
316 struct utmp ut;
317 FILE *uf;
318 time_t now;
319 struct tm *tm;
320 unsigned long up;
321
322 /* Count the number of active users in the utmp file. */
323 if ((uf = fopen(utmp_file, "r")) == NULL) {
324 fprintf(stderr, "%s: %s: %s\n", prog, utmp_file, strerror(errno));
325 exit(1);
326 }
327
328 nusers = 0;
329 while (fread(&ut, sizeof(ut), 1, uf) == 1) {
330#ifdef USER_PROCESS
331 if (ut.ut_type == USER_PROCESS) nusers++;
332#else
333 if (ut.ut_name[0] != 0 && ut.ut_line[0] != 0) nusers++;
334#endif
335 }
336 fclose(uf);
337
338 /* Current time. */
339 now = time((time_t *) NULL);
340 tm = localtime(&now);
341
342 /* Uptime. */
343 up = now - boot_time;
344
345 printf(" %d:%02d up", tm->tm_hour, tm->tm_min);
346 if (up >= 24 * 3600L) {
347 unsigned long days = up / (24 * 3600L);
348 printf(" %lu day%s,", days, days == 1 ? "" : "s");
349 }
350 printf(" %lu:%02lu,", (up % (24 * 3600L)) / 3600, (up % 3600) / 60);
351 printf(" %u user%s", nusers, nusers == 1 ? "" : "s");
352 if((nloads = getloadavg(loads, NLOADS)) > 0) {
353 int i;
354 printf(", load averages:");
355 for(i = 0; i < nloads; i++)
356 printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
357 }
358 printf("\n");
359}
360
361
362/* Record_Logout_Time(wtmp) A linked list of "last logout time" is kept.
363 * Each element of the list is for one terminal.
364 */
365void Record_Logout_Time(wtmp)
366struct utmp *wtmp;
367{
368 logout *link;
369
370 /* see if the terminal is already in the list */
371 for (link = first_link; link != NULL; link = link->next)
372 if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) {
373 link->time = wtmp->ut_time;
374 return;
375 }
376 /* allocate a new logout record, for a tty not previously encountered */
377 link = (logout *) malloc(sizeof(logout));
378 if (link == NULL) {
379 fprintf(stderr, "%s: malloc failure\n", prog);
380 exit(1);
381 }
382 strncpy(link->line, wtmp->ut_line, (size_t)8);
383 link->time = wtmp->ut_time;
384 link->next = first_link;
385
386 first_link = link;
387}
388
389
390int main(argc, argv)
391int argc;
392char *argv[];
393{
394 char *wtmp_file = _PATH_WTMP;
395 FILE *f;
396 long size; /* Number of wtmp records in the file */
397 int wtmp_count; /* How many to read into wtmp_buffer */
398 struct utmp wtmp_buffer[MAX_WTMP_COUNT];
399
400 if ((prog = strrchr(argv[0], '/')) == NULL) prog = argv[0]; else prog++;
401
402 --argc;
403 ++argv;
404
405 while (argc > 0 && *argv[0] == '-') {
406 if (!strcmp(argv[0], "-r"))
407 boot_limit = TRUE;
408 else
409 if (!strcmp(argv[0], "-u"))
410 tell_uptime = TRUE;
411 else if (argc > 1 && !strcmp(argv[0], "-f")) {
412 wtmp_file = argv[1];
413 --argc;
414 ++argv;
415 } else if ((print_count = atoi(argv[0] + 1)) > 0)
416 count_limit = TRUE;
417 else
418 usage();
419
420 --argc;
421 ++argv;
422 }
423
424 arg_count = argc;
425 args = argv;
426
427 if (!strcmp(prog, "uptime")) tell_uptime = TRUE;
428
429 if ((f = fopen(wtmp_file, "r")) == NULL) {
430 perror(wtmp_file);
431 exit(1);
432 }
433 if (fseek(f, 0L, 2) != 0 || (size = ftell(f)) % sizeof(struct utmp) != 0) {
434 fprintf(stderr, "%s: invalid wtmp file\n", prog);
435 exit(1);
436 }
437 if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
438 signal(SIGINT, Sigint);
439 signal(SIGQUIT, Sigquit);
440 }
441 size /= sizeof(struct utmp); /* Number of records in wtmp */
442
443 if (size == 0) wtmp_buffer[0].ut_time = time((time_t *)0);
444
445 while (size > 0) {
446 wtmp_count = (int) min(size, MAX_WTMP_COUNT);
447 size -= (long) wtmp_count;
448
449 fseek(f, size * sizeof(struct utmp), 0);
450
451
452 if (fread(&wtmp_buffer[0], sizeof(struct utmp), (size_t)wtmp_count, f)
453 != wtmp_count) {
454 fprintf(stderr, "%s: read error on wtmp file\n", prog);
455 exit(1);
456 }
457 while (--wtmp_count >= 0) {
458 Process(&wtmp_buffer[wtmp_count]);
459 if (interrupt) {
460 printf("\ninterrupted %.16s \n",
461 ctime(&wtmp_buffer[wtmp_count].ut_time));
462
463 if (interrupt == SIGINT) exit(2);
464
465 interrupt = FALSE;
466 signal(SIGQUIT, Sigquit);
467 }
468 }
469
470 } /* end while(size > 0) */
471
472 if (tell_uptime) {
473 fprintf(stderr,
474 "%s: no reboot record in wtmp file to compute uptime from\n",
475 prog);
476 return(1);
477 }
478
479 printf("\nwtmp begins %.16s \n", ctime(&wtmp_buffer[0].ut_time));
480 return(0);
481}
Note: See TracBrowser for help on using the repository browser.