* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)last.c 5.7 (Berkeley) %G%";
#define MAXTTYS 500 /* max ttys last can handle */
#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
*head
; /* head of linked list */
long logout
; /* log out time */
char tty
[LMAX
+ 1]; /* terminal name */
static TTYS tab
[MAXTTYS
+ 1]; /* tty table */
static long 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
);
* kludge -- we assume that all tty's end with
* a two character suffix.
if (strlen(optarg
) == 2) {
/* either 6 for "ttyxx" or 8 for "console" */
if (!(mval
= malloc((u_int
)8))) {
fputs("last: malloc failure.\n", stderr
);
if (!strcmp(optarg
, "co"))
(void)strcpy(mval
, "console");
(void)strcpy(mval
, "tty");
(void)strcpy(mval
+ 3, optarg
);
addarg(TTY_TYPE
, optarg
);
fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr
);
for (argv
+= optind
; *argv
; ++argv
)
addarg(USER_TYPE
, *argv
);
* read through the wtmp file
register struct utmp
*bp
; /* current structure */
register TTYS
*T
; /* table 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
);
tab
[MAXTTYS
].logout
= -1; /* end flag value */
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
)) {
for (T
= tab
; T
->logout
!= -1; ++T
)
T
->logout
= -bp
->ut_time
;
crmsg
= strncmp(bp
->ut_name
, "shutdown", NMAX
)
(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);
for (T
= tab
;;) { /* find assoc. tty */
if (T
->logout
<= 0) { /* unused entry */
bcopy(bp
->ut_line
, T
->tty
, LMAX
);
if (!strncmp(T
->tty
, bp
->ut_line
, LMAX
))
if ((++T
)->logout
== -1) {
fputs("last: too many terminals.\n", stderr
);
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", 3))
else if (!strncmp(bp
->ut_line
, "uucp", 4))
for (step
= head
; 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
);
* 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
))
* 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 */