* 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
[] = "@(#)finger.c 5.9 (Berkeley) %G%";
* This is a finger program. It prints out useful information about users
* by digging it up from various system files. It is not very portable
* because the most useful parts of the information (the full user name,
* office, and phone numbers) are all stored in the VAX-unused gecos field
* of /etc/passwd, which, unfortunately, other UNIXes use for other things.
* There are three output formats, all of which give login name, teletype
* line number, and login time. The short output format is reminiscent
* of finger on ITS, and gives one line of information per user containing
* in addition to the minimum basic requirements (MBR), the full name of
* the user, his idle time and office location and phone number. The
* quick style output is UNIX who-like, giving only name, teletype and
* login time. Finally, the long style output give the same information
* as the short (in more legible format), the home directory and shell
* of the user, and, if it exits, a copy of the file .plan in the users
* home directory. Finger may be called with or without a list of people
* to finger -- if no list is given, all the people currently logged in
* The program is validly called by one of the following:
* finger {short form list of users}
* finger -l {long form list of users}
* finger -b {briefer long form list of users}
* finger -q {quick list of users}
* finger -i {quick list of users with idle times}
* finger namelist {long format list of specified users}
* finger -s namelist {short format list of specified users}
* finger -w namelist {narrow short format list of specified users}
* where 'namelist' is a list of users login names.
* The other options can all be given after one '-', or each can have its
* own '-'. The -f option disables the printing of headers for short and
* quick outputs. The -b option briefens long format outputs. The -p
* option turns off plans for long format outputs.
#define ASTERISK '*' /* ignore this in real name */
#define COMMA ',' /* separator in pw_gecos field */
#define COMMAND '-' /* command line flag char */
#define CORY 'C' /* cory hall office */
#define EVANS 'E' /* evans hall office */
#define SAMENAME '&' /* repeat login name in real name */
#define TALKABLE 0220 /* tty is writable if 220 mode */
#define NMAX sizeof(user.ut_name)
#define LMAX sizeof(user.ut_line)
#define HMAX sizeof(user.ut_host)
struct person
{ /* one for each person fingered */
char tty
[LMAX
+1]; /* null terminated tty line */
char host
[HMAX
+1]; /* null terminated remote host name */
long loginat
; /* time of (last) login */
long idletime
; /* how long idle (if logged in) */
char *realname
; /* pointer to full name */
char *office
; /* pointer to office name */
char *officephone
; /* pointer to office phone no. */
char *homephone
; /* pointer to home phone no. */
char *random
; /* for any random stuff in pw_gecos */
struct passwd
*pwd
; /* structure of /etc/passwd stuff */
char loggedin
; /* person is logged in */
char writable
; /* tty is writable */
char original
; /* this is not a duplicate entry */
struct person
*link
; /* link to next person */
char LASTLOG
[] = "/usr/adm/lastlog"; /* last login info */
char USERLOG
[] = "/etc/utmp"; /* who is logged in */
char PLAN
[] = "/.plan"; /* what plan file is */
char PROJ
[] = "/.project"; /* what project file */
int unbrief
= 1; /* -b option default */
int header
= 1; /* -f option default */
int hack
= 1; /* -h option default */
int idle
= 0; /* -i option default */
int large
= 0; /* -l option default */
int match
= 1; /* -m option default */
int plan
= 1; /* -p option default */
int unquick
= 1; /* -q option default */
int small
= 0; /* -s option default */
int wide
= 1; /* -w option default */
int lf
; /* LASTLOG file descriptor */
struct person
*person1
; /* list of people */
long tloc
; /* current time */
struct passwd
*pwdcopy();
/* parse command line for (optional) arguments */
while (*++argv
&& **argv
== COMMAND
)
for (s
= *argv
+ 1; *s
; s
++)
fprintf(stderr
, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n");
* *argv == 0 means no names given
register struct person
*p
;
register struct passwd
*pw
;
if ((uf
= open(USERLOG
, 0)) < 0) {
fprintf(stderr
, "finger: error opening %s\n", USERLOG
);
while (read(uf
, (char *)&user
, sizeof user
) == sizeof user
) {
if (user
.ut_name
[0] == 0)
p
= person1
= (struct person
*) malloc(sizeof *p
);
p
->link
= (struct person
*) malloc(sizeof *p
);
bcopy(user
.ut_name
, name
, NMAX
);
bcopy(user
.ut_line
, p
->tty
, LMAX
);
bcopy(user
.ut_host
, p
->host
, HMAX
);
p
->loginat
= user
.ut_time
;
if (unquick
&& (pw
= getpwnam(name
))) {
p
->name
= p
->pwd
->pw_name
;
p
->name
= strcpy(malloc(strlen(name
) + 1), name
);
printf("No one logged on\n");
register struct person
*p
;
register struct passwd
*pw
;
* get names from command line and check to see if they're
for (; *argv
!= 0; argv
++) {
p
= person1
= (struct person
*) malloc(sizeof *p
);
p
->link
= (struct person
*) malloc(sizeof *p
);
* if we are doing it, read /etc/passwd for the useful info
for (p
= person1
; p
!= 0; p
= p
->link
)
if (pw
= getpwnam(p
->name
))
} else while ((pw
= getpwent()) != 0) {
for (p
= person1
; p
!= 0; p
= p
->link
) {
if (strcmp(p
->name
, pw
->pw_name
) != 0 &&
!matchcmp(pw
->pw_gecos
, pw
->pw_name
, p
->name
))
* handle multiple login names, insert
* new "duplicate" entry behind
/* Now get login information */
if ((uf
= open(USERLOG
, 0)) < 0) {
fprintf(stderr
, "finger: error opening %s\n", USERLOG
);
while (read(uf
, (char *)&user
, sizeof user
) == sizeof user
) {
for (p
= person1
; p
!= 0; p
= p
->link
) {
if (strncmp(p
->pwd
? p
->pwd
->pw_name
: p
->name
,
user
.ut_name
, NMAX
) != 0)
bcopy(user
.ut_line
, p
->tty
, LMAX
);
bcopy(user
.ut_host
, p
->host
, HMAX
);
p
->loginat
= user
.ut_time
;
} else { /* p->loggedin == 1 */
new = (struct person
*) malloc(sizeof *new);
bcopy(user
.ut_line
, new->tty
, LMAX
);
bcopy(user
.ut_host
, new->host
, HMAX
);
new->loginat
= user
.ut_time
;
for (p
= person1
; p
!= 0; p
= p
->link
)
register struct person
*p
;
printf("Login Name TTY Idle When Office\n");
printf("Login TTY Idle When Office\n");
printf("Login TTY When");
for (p
= person1
; p
!= 0; p
= p
->link
) {
s
= malloc(strlen(p
->pwd
->pw_dir
) +
strcpy(s
, p
->pwd
->pw_dir
);
if ((fp
= fopen(s
, "r")) != 0) {
while ((c
= getc(fp
)) != EOF
) {
if (isprint(c
) || isspace(c
))
s
= malloc(strlen(p
->pwd
->pw_dir
) +
strcpy(s
, p
->pwd
->pw_dir
);
if ((fp
= fopen(s
, "r")) == 0)
while ((c
= getc(fp
)) != EOF
)
if (isprint(c
) || isspace(c
))
* Note: Only the useful things (what the program currently uses) are copied.
register struct passwd
*pfrom
;
register struct passwd
*pto
;
pto
= (struct passwd
*) malloc(sizeof *pto
);
#define savestr(s) strcpy(malloc(strlen(s) + 1), s)
pto
->pw_name
= savestr(pfrom
->pw_name
);
pto
->pw_uid
= pfrom
->pw_uid
;
pto
->pw_gecos
= savestr(pfrom
->pw_gecos
);
pto
->pw_dir
= savestr(pfrom
->pw_dir
);
pto
->pw_shell
= savestr(pfrom
->pw_shell
);
* print out information on quick format giving just name, tty, login time
* and idle time if idle is set.
register struct person
*pers
;
printf("%-*.*s ", NMAX
, NMAX
, pers
->name
);
printf("%c%-*s %-16.16s", pers
->writable
? ' ' : '*',
LMAX
, pers
->tty
, ctime(&pers
->loginat
));
ltimeprint(" ", &pers
->idletime
, "");
printf(" %-*s %-16.16s", LMAX
,
pers
->tty
, ctime(&pers
->loginat
));
printf(" Not Logged In\n");
* print out information in short format, giving login name, full name,
* tty, idle time, login time, office location and phone.
register struct person
*pers
;
printf("%-15s ???\n", pers
->name
);
printf("%-*s", NMAX
, pers
->pwd
->pw_name
);
printf(" %-20.20s", pers
->realname
);
if (pers
->loggedin
&& !pers
->writable
)
if (pers
->tty
[0] == 't' && pers
->tty
[1] == 't' &&
if (pers
->tty
[3] == 'd' && pers
->loggedin
)
printf("%-2.2s ", pers
->tty
+ 3);
printf("%-2.2s ", pers
->tty
);
p
= ctime(&pers
->loginat
);
stimeprint(&pers
->idletime
);
printf(" %3.3s %-5.5s ", p
, p
+ 11);
} else if (pers
->loginat
== 0)
else if (tloc
- pers
->loginat
>= 180 * 24 * 60 * 60)
printf(" <%-6.6s, %-4.4s>", p
+ 4, p
+ 20);
printf(" <%-12.12s>", p
+ 4);
if (dialup
&& pers
->homephone
)
printf(" %20s", pers
->homephone
);
printf(" %-11.11s", pers
->office
);
else if (pers
->officephone
|| pers
->homephone
)
printf(" %s", pers
->officephone
);
else if (pers
->homephone
)
printf(" %s", pers
->homephone
);
* print out a person in long format giving all possible information.
* directory and shell are inhibited if unbrief is clear.
register struct person
*pers
;
printf("Login name: %-10s\t\t\tIn real life: ???\n",
printf("Login name: %-10s", pers
->pwd
->pw_name
);
if (pers
->loggedin
&& !pers
->writable
)
printf(" (messages off) ");
printf("In real life: %s", pers
->realname
);
printf("\nOffice: %-.11s", pers
->office
);
printf(", %s", pers
->officephone
);
printf("\t\tHome phone: %s", pers
->homephone
);
printf("\t\t%s", pers
->random
);
printf("\t\t\tHome phone: %s", pers
->homephone
);
printf("\t\t\t%s", pers
->random
);
} else if (pers
->officephone
) {
printf("\nPhone: %s", pers
->officephone
);
printf(", %s", pers
->homephone
);
printf(", %s", pers
->random
);
} else if (pers
->homephone
) {
printf("\nPhone: %s", pers
->homephone
);
printf(", %s", pers
->random
);
printf("\n%s", pers
->random
);
printf("\nDirectory: %-25s", pers
->pwd
->pw_dir
);
if (*pers
->pwd
->pw_shell
)
printf("\tShell: %-s", pers
->pwd
->pw_shell
);
register char *ep
= ctime(&pers
->loginat
);
printf("\nOn since %15.15s on %s from %s",
&ep
[4], pers
->tty
, pers
->host
);
ltimeprint("\n", &pers
->idletime
, " Idle Time");
printf("\nOn since %15.15s on %-*s",
&ep
[4], LMAX
, pers
->tty
);
ltimeprint("\t", &pers
->idletime
, " Idle Time");
} else if (pers
->loginat
== 0)
printf("\nNever logged in.");
else if (tloc
- pers
->loginat
> 180 * 24 * 60 * 60) {
register char *ep
= ctime(&pers
->loginat
);
printf("\nLast login %10.10s, %4.4s on %s",
printf(" from %s", pers
->host
);
register char *ep
= ctime(&pers
->loginat
);
printf("\nLast login %16.16s on %s", ep
, pers
->tty
);
printf(" from %s", pers
->host
);
* very hacky section of code to format phone numbers. filled with
* magic constants like 4, 7 and 10.
register char *p
= fonebuf
;
return (strcpy(malloc(len
+ 1), s
));
return (strcpy(malloc(len
+ 1), s
));
return (strcpy(malloc(p
- fonebuf
), fonebuf
));
* decode the information in the gecos field of /etc/passwd
register struct person
*pers
;
register char *bp
, *gp
, *lp
;
gp
= pers
->pwd
->pw_gecos
;
while (*gp
&& *gp
!= COMMA
) /* name */
if ((len
= bp
- buffer
) > 1)
pers
->realname
= strcpy(malloc(len
), buffer
);
if (*gp
== COMMA
) { /* office */
while (*gp
&& *gp
!= COMMA
) {
/* leave 5 for Cory and Evans expansion */
if (bp
< buffer
+ sizeof buffer
- 6)
bp
--; /* point to last character */
if (hasspace
|| len
== 0)
} else if (*bp
== EVANS
) {
pers
->office
= strcpy(malloc(len
), buffer
);
if (*gp
== COMMA
) { /* office phone */
while (*gp
&& *gp
!= COMMA
) {
if (bp
< buffer
+ sizeof buffer
- 1)
pers
->officephone
= phone(buffer
, bp
- buffer
, alldigits
);
if (*gp
== COMMA
) { /* home phone */
while (*gp
&& *gp
!= COMMA
) {
if (bp
< buffer
+ sizeof buffer
- 1)
pers
->homephone
= phone(buffer
, bp
- buffer
, alldigits
);
* find the last log in of a user by checking the LASTLOG file.
* the entry is indexed by the uid, so this can only be done if
* the uid is known (which it isn't in quick mode)
if ((lf
= open(LASTLOG
, 0)) < 0)
fprintf(stderr
, "finger: %s open error\n", LASTLOG
);
register struct person
*pers
;
lseek(lf
, (long)pers
->pwd
->pw_uid
* sizeof ll
, 0);
if ((i
= read(lf
, (char *)&ll
, sizeof ll
)) == sizeof ll
) {
bcopy(ll
.ll_line
, pers
->tty
, LMAX
);
bcopy(ll
.ll_host
, pers
->host
, HMAX
);
pers
->loginat
= ll
.ll_time
;
fprintf(stderr
, "finger: %s read error\n",
* find the idle time of a user by doing a stat on /dev/tty??,
* where tty?? has been gotten from USERLOG, supposedly.
register struct person
*pers
;
static char buffer
[20] = "/dev/";
strcpy(buffer
+ TTYLEN
, pers
->tty
);
if (stat(buffer
, &ttystatus
) < 0) {
fprintf(stderr
, "finger: Can't stat %s\n", buffer
);
if (t
< ttystatus
.st_atime
)
pers
->idletime
= t
- ttystatus
.st_atime
;
pers
->writable
= (ttystatus
.st_mode
& TALKABLE
) == TALKABLE
;
* print idle time in short format; this program always prints 4 characters;
* if the idle time is zero, it prints 4 blanks.
register struct tm
*delta
;
printf(" %2d", delta
->tm_min
);
if (delta
->tm_hour
>= 10)
printf("%3d:", delta
->tm_hour
);
delta
->tm_hour
, delta
->tm_min
);
printf("%3dd", delta
->tm_yday
);
* print idle time in long format with care being taken not to pluralize
* 1 minutes or 1 hours or 1 days.
ltimeprint(before
, dt
, after
)
register struct tm
*delta
;
if (delta
->tm_yday
== 0 && delta
->tm_hour
== 0 && delta
->tm_min
== 0 &&
if (delta
->tm_yday
>= 10)
printf("%d days", delta
->tm_yday
);
else if (delta
->tm_yday
> 0)
printf("%d day%s %d hour%s",
delta
->tm_yday
, delta
->tm_yday
== 1 ? "" : "s",
delta
->tm_hour
, delta
->tm_hour
== 1 ? "" : "s");
if (delta
->tm_hour
>= 10)
printf("%d hours", delta
->tm_hour
);
else if (delta
->tm_hour
> 0)
printf("%d hour%s %d minute%s",
delta
->tm_hour
, delta
->tm_hour
== 1 ? "" : "s",
delta
->tm_min
, delta
->tm_min
== 1 ? "" : "s");
printf("%2d minutes", delta
->tm_min
);
else if (delta
->tm_min
== 0)
printf("%2d seconds", delta
->tm_sec
);
printf("%d minute%s %d second%s",
delta
->tm_min
== 1 ? "" : "s",
delta
->tm_sec
== 1 ? "" : "s");
matchcmp(gname
, login
, given
)
for (lp
= login
; bp
< buffer
+ sizeof buffer
if (namecmp(buffer
, given
))
if (c
== COMMA
|| c
== 0)
if (bp
< buffer
+ sizeof buffer
)
register char *name1
, *name2
;
for (name2
--; isdigit(*name2
); name2
++)
for (name1
--; isdigit(*name1
); name1
++)
host
= rindex(name
, '@');
hp
= gethostbyname(host
);
static struct hostent def
;
static struct in_addr defaddr
;
static char namebuf
[128];
defaddr
.s_addr
= inet_addr(host
);
if (defaddr
.s_addr
== -1) {
printf("unknown host: %s\n", host
);
def
.h_addr_list
= alist
, def
.h_addr
= (char *)&defaddr
;
def
.h_length
= sizeof (struct in_addr
);
def
.h_addrtype
= AF_INET
;
sp
= getservbyname("finger", "tcp");
printf("tcp/finger: unknown service\n");
sin
.sin_family
= hp
->h_addrtype
;
bcopy(hp
->h_addr
, (char *)&sin
.sin_addr
, hp
->h_length
);
sin
.sin_port
= sp
->s_port
;
s
= socket(hp
->h_addrtype
, SOCK_STREAM
, 0);
printf("[%s]", hp
->h_name
); fflush(stdout
);
if (connect(s
, (char *)&sin
, sizeof (sin
)) < 0) {
if (large
) write(s
, "/W ", 3);
write(s
, name
, strlen(name
));
while ((c
= getc(f
)) != EOF
) {
if (isprint(c
) || isspace(c
))