* Copyright (c) 1987 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of California at Berkeley. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific written prior permission. This software
* is provided ``as is'' without express or implied warranty.
"@(#) Copyright (c) 1987 Regents of the University of California.\n\
static char sccsid
[] = "@(#)last.c 5.10 (Berkeley) %G%";
#define SECDAY (24*60*60) /* seconds in a day */
#define NO 0 /* false/no */
#define YES 1 /* true/yes */
static struct utmp buf
[1024]; /* utmp read buffer */
#define HMAX sizeof(buf[0].ut_host) /* size of utmp host field */
#define LMAX sizeof(buf[0].ut_line) /* size of utmp tty field */
#define NMAX sizeof(buf[0].ut_name) /* size of utmp name field */
char *name
; /* argument */
int type
; /* type of arg */
struct arg
*next
; /* linked list pointer */
ARG
*arglist
; /* head of linked list */
long logout
; /* log out time */
char tty
[LMAX
+ 1]; /* terminal name */
struct ttytab
*next
; /* linked list pointer */
TTY
*ttylist
; /* head of linked list */
static long currentout
, /* current logout value */
maxrec
; /* records to display */
static char *file
= "/usr/adm/wtmp"; /* wtmp file */
while ((ch
= getopt(argc
, argv
, "0123456789f:h:t:")) != EOF
)
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
* kludge: last was originally designed to take
maxrec
= atol(argv
[optind
- 1] + 1);
addarg(HOST_TYPE
, optarg
);
addarg(TTY_TYPE
, ttyconv(optarg
));
fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr
);
for (argv
+= optind
; *argv
; ++argv
) {
/* code to allow "last p5" to work */
addarg(TTY_TYPE
, ttyconv(*argv
));
addarg(USER_TYPE
, *argv
);
* read through the wtmp file
register struct utmp
*bp
; /* current structure */
register TTY
*T
; /* tty list entry */
struct stat stb
; /* stat of file for size */
long bl
, delta
, /* time difference */
*asctime(), *ctime(), *strcpy();
if ((wfd
= open(file
, O_RDONLY
, 0)) < 0 || fstat(wfd
, &stb
) == -1) {
bl
= (stb
.st_size
+ sizeof(buf
) - 1) / sizeof(buf
);
(void)time(&buf
[0].ut_time
);
(void)signal(SIGINT
, onintr
);
(void)signal(SIGQUIT
, onintr
);
if (lseek(wfd
, (long)(bl
* sizeof(buf
)), L_SET
) == -1 ||
(bytes
= read(wfd
, (char *)buf
, sizeof(buf
))) == -1) {
fprintf(stderr
, "last: %s: ", file
);
for (bp
= &buf
[bytes
/ sizeof(buf
[0]) - 1]; bp
>= buf
; --bp
) {
* if the terminal line is '~', the machine stopped.
* see utmp(5) for more info.
if (!strncmp(bp
->ut_line
, "~", LMAX
)) {
/* everybody just logged out */
for (T
= ttylist
; T
; T
= T
->next
)
T
->logout
= -bp
->ut_time
;
currentout
= -bp
->ut_time
;
crmsg
= strncmp(bp
->ut_name
, "shutdown", NMAX
) ? "crash" : "down ";
(void)strcpy(bp
->ut_name
, "reboot");
ct
= ctime(&bp
->ut_time
);
printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n", NMAX
, NMAX
, bp
->ut_name
, LMAX
, LMAX
, bp
->ut_line
, HMAX
, HMAX
, bp
->ut_host
, ct
, ct
+ 11);
/* find associated tty */
for (T
= ttylist
;; T
= T
->next
) {
if (!strncmp(T
->tty
, bp
->ut_line
, LMAX
))
if (bp
->ut_name
[0] && want(bp
, YES
)) {
ct
= ctime(&bp
->ut_time
);
printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ", NMAX
, NMAX
, bp
->ut_name
, LMAX
, LMAX
, bp
->ut_line
, HMAX
, HMAX
, bp
->ut_host
, ct
, ct
+ 11);
puts(" still logged in");
printf("- %5.5s", ctime(&T
->logout
)+11);
delta
= T
->logout
- bp
->ut_time
;
printf(" (%5.5s)\n", asctime(gmtime(&delta
))+11);
printf(" (%ld+%5.5s)\n", delta
/ SECDAY
, asctime(gmtime(&delta
))+11);
if (maxrec
!= -1 && !--maxrec
)
ct
= ctime(&buf
[0].ut_time
);
printf("\nwtmp begins %10.10s %5.5s \n", ct
, ct
+ 11);
register struct utmp
*bp
;
* when uucp and ftp log in over a network, the entry in
* the utmp file is the name plus their process id. See
* etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
if (!strncmp(bp
->ut_line
, "ftp", sizeof("ftp") - 1))
else if (!strncmp(bp
->ut_line
, "uucp", sizeof("uucp") - 1))
for (step
= arglist
; step
; step
= step
->next
)
if (!strncasecmp(step
->name
, bp
->ut_host
, HMAX
))
if (!strncmp(step
->name
, bp
->ut_line
, LMAX
))
if (!strncmp(step
->name
, bp
->ut_name
, NMAX
))
* add an entry to a linked list of arguments
if (!(cur
= (ARG
*)malloc((u_int
)sizeof(ARG
)))) {
fputs("last: malloc failure.\n", stderr
);
* add an entry to a linked list of ttys
if (!(cur
= (TTY
*)malloc((u_int
)sizeof(TTY
)))) {
fputs("last: malloc failure.\n", stderr
);
cur
->logout
= currentout
;
bcopy(ttyname
, cur
->tty
, LMAX
);
* convert the hostname to search pattern; if the supplied host name
* has a domain attached that is the same as the current domain, rip
* off the domain suffix since that's what login(1) does.
if (!(argdot
= index(arg
, '.')))
if (gethostname(name
, sizeof(name
))) {
perror("last: gethostname");
hostdot
= index(name
, '.');
if (hostdot
&& !strcasecmp(hostdot
, argdot
))
* convert tty to correct name.
* kludge -- we assume that all tty's end with
* a two character suffix.
/* either 6 for "ttyxx" or 8 for "console" */
if (!(mval
= malloc((u_int
)8))) {
fputs("last: malloc failure.\n", stderr
);
(void)strcpy(mval
, "console");
(void)strcpy(mval
, "tty");
(void)strcpy(mval
+ 3, arg
);
if (!strncmp(arg
, "/dev/", sizeof("/dev/") - 1))
* on interrupt, we inform the user how far we've gotten
ct
= ctime(&buf
[0].ut_time
);
printf("\ninterrupted %10.10s %5.5s \n", ct
, ct
+ 11);
(void)fflush(stdout
); /* fix required for rsh */