/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* This is a finger program. It prints out useful information about users
* by digging it up from various system files.
*
* 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 user's full name,
* idle time and location.
* 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
* are fingered.
*
* 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 -m {matches arguments against only username}
* finger -f {suppress header in non-long form}
* finger -p {suppress printing of .plan file}
* finger -h {suppress printing of .project file}
* finger -i {forces "idle" output format}
* 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.
*/
#include <utmpx.h>
#include <pwd.h>
#include <stdio.h>
#include <lastlog.h>
#include <ctype.h>
#include <time.h>
#include <netdb.h>
#include <locale.h>
#include <stdlib.h>
#include <strings.h>
#include <fcntl.h>
#include <curses.h>
#include <unctrl.h>
#include <maillock.h>
#include <deflt.h>
#include <unistd.h>
#include <macros.h>
};
/*
* RFC 1288 says that system administrators should have the option of
* separately allowing ASCII characters less than 32 or greater than
* 126. The termpass variable keeps track of this.
*/
char *termopts[] = {
#define TERM_LOW 0
"low",
"high",
(char *)NULL
};
int unshort;
"finger [-bfhilmpqsw] [name1 [name2 ...] ]\n";
void doall(void);
void fwclose(void);
void fwopen(void);
void initscreening(void);
void print(void);
void sort_by_username(void);
int
{
int c;
/* parse command line for (optional) arguments */
switch (c) {
case 'b':
unbrief = 0;
break;
case 'f':
header = 0;
break;
case 'h':
hack = 0;
break;
case 'i':
idle = 1;
unquick = 0;
break;
case 'l':
large = 1;
break;
case 'm':
match = 0;
break;
case 'p':
plan = 0;
break;
case 'q':
unquick = 0;
break;
case 's':
small = 1;
break;
case 'w':
wide = 0;
break;
default:
exit(1);
}
/* find out what filtering on .plan/.project files we should do */
/*
* optind == argc means no names given
*/
doall();
else
if (nperson > 0)
print();
return (0);
/* NOTREACHED */
}
void
doall(void)
{
struct person *p;
struct utmpx *u;
setutxent();
if (unquick) {
setpwent();
fwopen();
}
if (u->ut_name[0] == 0 ||
nonuserx(*u) ||
u->ut_type != USER_PROCESS)
continue;
else {
p = p->link;
}
p->loggedin = 1;
decode(p);
} else
nperson++;
}
if (unquick) {
fwclose();
endpwent();
}
endutxent();
if (nperson == 0) {
(void) printf("No one logged on\n");
return;
}
}
void
{
struct person *p;
struct utmpx *u;
/*
* get names from command line and check to see if they're
* logged in
*/
continue;
else {
p = p->link;
}
p->loggedin = 0;
p->original = 1;
nperson++;
}
if (nperson == 0)
return;
/*
*/
if (unquick) {
setpwent();
if (!match) {
}
} else {
if (!p->original)
continue;
p->name)) {
continue;
}
} else {
/*
* Handle multiple login names.
* Insert new "duplicate" entry
* behind.
*/
p->original = 0;
p = new;
nperson++;
}
}
}
}
endpwent();
}
/* Now get login information */
setutxent();
continue;
if (p->loggedin == 2)
continue;
continue;
if (p->loggedin == 0) {
p->loggedin = 1;
} else { /* p->loggedin == 1 */
p->loggedin = 2;
p = new;
nperson++;
}
}
}
endutxent();
if (unquick) {
fwopen();
decode(p);
fwclose();
}
}
void
print(void)
{
struct person *p;
char *s;
/*
* print out what we got
*/
if (header) {
if (unquick) {
if (!unshort) {
if (wide) {
(void) printf("Login "
"Name TTY "
"Idle When Where\n");
} else {
(void) printf("Login TTY Idle "
"When Where\n");
}
}
} else {
(void) printf("Login TTY When");
if (idle)
(void) printf(" Idle");
(void) putchar('\n');
}
}
if (!unquick) {
quickprint(p);
continue;
}
if (!unshort) {
shortprint(p);
continue;
}
personprint(p);
if (hack) {
sizeof (PROJ));
if (s != NULL) {
(void) printf("Project: ");
(void) putchar('\n');
}
free(s);
}
}
if (plan) {
sizeof (PLAN));
if (s != NULL) {
(void) printf("No Plan.\n");
else {
(void) printf("Plan:\n");
}
free(s);
}
}
}
(void) putchar('\n');
}
}
/*
* Duplicate a pwd entry.
* Note: Only the useful things (what the program currently uses) are copied.
*/
struct passwd *
{
return (pto);
}
/*
* print out information on quick format giving just name, tty, login time
* and idle time if idle is set.
*/
void
{
if (idle) {
(void) printf("%c%-12s %-16.16s",
} else {
(void) printf(" %-12s %-16.16s",
}
(void) putchar('\n');
} else {
(void) printf(" Not Logged In\n");
}
}
/*
* print out information in short format, giving login name, full name,
* tty, idle time, login time, and host.
*/
void
{
char *p;
return;
}
if (wide) {
} else {
(void) printf(" ??? ");
}
}
(void) putchar(' ');
(void) putchar('*');
} else {
(void) putchar(' ');
}
} else {
}
(void) printf(" < . . . . >");
} else {
}
} else {
}
(void) putchar('\n');
}
/*
* print out a person in long format giving all possible information.
* directory and shell are inhibited if unbrief is clear.
*/
void
{
(void) printf("Login name: %-10s\t\t\tIn real life: ???\n",
return;
}
(void) printf(" (messages off) ");
} else {
(void) printf(" ");
}
}
if (unbrief) {
}
(void) printf("\nOn since %15.15s on %s from %s",
} else {
(void) printf("\nOn since %15.15s on %-12s",
}
(void) printf("\nNever logged in.");
(void) printf("\nLast login %10.10s, %4.4s on %s",
}
} else {
}
}
(void) putchar('\n');
}
/*
*/
void
{
return;
if (gecos_ignore_c != '\0' &&
*gp == gecos_ignore_c) {
gp++;
}
while (*gp != '\0' &&
if (*gp == gecos_samename) {
;
bp--;
gp++;
} else {
}
}
*bp++ = 0;
else
}
/*
* 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)
*/
void
fwopen(void)
{
}
void
{
SEEK_SET) == 0) {
} else {
"finger: %s read error\n", LASTLOG);
}
} else {
LASTLOG);
}
} else {
}
}
void
fwclose(void)
{
}
/*
* where tty?? has been gotten from UTMPX_FILE, supposedly.
*/
void
{
time_t t;
exit(4);
}
/*
* On the console, the user may be running a window system; if
* so, their activity will show up in the last-access times of
* as the idle time.
*/
}
}
}
if (t < lastinputtime)
else
}
/*
* print idle time in short format; this program always prints 4 characters;
* if the idle time is zero, it prints 4 blanks.
*/
void
{
(void) printf(" ");
else
else
else
(void) printf("%1d:%02d",
else
}
/*
* print idle time in long format with care being taken not to pluralize
* 1 minutes or 1 hours or 1 days.
* print "prefix" first.
*/
void
{
return;
(void) printf("%d day%s %d hour%s",
else
(void) printf("%d hour%s %d minute%s",
else
else
(void) printf("%d minute%s %d second%s",
}
/*
* The grammar of the pw_gecos field is sufficiently complex that the
* best way to parse it is by using an explicit finite-state machine,
* in which a table defines the rules of interpretation.
*
* Some special rules are necessary to handle the fact that names
* may contain certain punctuation characters. At this writing,
* the possible punctuation characters are '.', '-', and '_'.
*
* Other rules are needed to account for characters that require special
* processing when they appear in the pw_gecos field. At present, there
* are three such characters, with these default values and effects:
*
* gecos_ignore_c '*' This character is ignored.
* gecos_sep_c ',' Delimits displayed and nondisplayed contents.
* gecos_samename '&' Copies the login name into the output.
*
* As the program examines each successive character in the returned
* pw_gecos value, it fetches (from the table) the FSM rule applicable
* for that character in the current machine state, and thus determines
* the next state.
*
* The possible states are:
* S0 start
* S1 in a word
* S2 not in a word
* S3 copy login name into output
* S4 end of GECOS field
*
* Here follows a depiction of the state transitions.
*
*
* gecos_ignore_c OR isspace OR any other character
* +--+
* | |
* | V
* +-----+
* NULL OR | S0 | isalpha OR isdigit
* +---------------|start|------------------------+
* | gecos_sep_c +-----+ | isalpha OR isdigit
* | | | | +---------------------+
* | | | | | OR '.' '-' '_' |
* | | |isspace | | |
* | | +-------+ V V |
* | | | +-----------+ |
* | | | | S1 |<--+ |
* | | | | in a word | | isalpha OR |
* | | | +-----------+ | isdigit OR |
* | | | | | | | | '.' '-' '_' |
* | | +----- ---------------+ | | +-----+ |
* | | | | | | |
* | | | | gecos_ignore_c | | |
* | | | | isspace | | |
* | | | | any other char | | |
* | | | | +---------------+ | |
* | | | | | |NULL OR gecos_sep_c |
* | | | | | +------------------+ |
* | gecos_samename| | V V | |
* | +-------------+ | +---------------+ | |
* | | | | S2 | isspace OR '.' '-' '_' | |
* | | gecos_samename | | not in a word |<---------------------+ | |
* | | +---------------+ +---------------+ OR gecos_ignore_c | | |
* | | | | ^ | | OR ispunct OR other | | |
* | | | | | | | | | |
* | | | gecos_samename | | | +-----------------------+ | |
* | | | +---------------------+ | | | |
* | | | | | | | |
* | | | | gecos_ignore_c| | NULL OR gecos_sep_c | |
* | | | | gecos_samename| +-----------------------+ | |
* | V V V isspace | | | |
* | +-----------------+ any other char| | | |
* | | S3 |---------------+ isalpha OR isdigit OR | | |
* | |insert login name|------------------------------------------ ----- ---+
* | +-----------------+ '.' '-' '_' | |
* | | NULL OR gecos_sep_c | |
* | +------------------------------------------+ | |
* | | | |
* | V V V
* | +------------+
* | NULL OR gecos_sep_c | S4 |
* +-------------------------------------------------------->|end of gecos|<--+
* +------------+ |
* | all |
* +-----+
*
*
* The transitions from the above diagram are summarized in
* the following table of target states, which is implemented
* in code as the gecos_fsm array.
*
* Input:
* +--gecos_ignore_c
* | +--gecos_sep_c
* | | +--gecos_samename
* | | | +--isalpha
* | | | | +--isdigit
* | | | | | +--isspace
* | | | | | | +--punctuation possible in name
* | | | | | | | +--other punctuation
* | | | | | | | | +--NULL character
* | | | | | | | | | +--any other character
* | | | | | | | | | |
* V V V V V V V V V V
* From: ---------------------------------------------------
* S0 | S0 | S4 | S3 | S1 | S1 | S0 | S1 | S2 | S4 | S0 |
* S1 | S2 | S4 | S3 | S1 | S1 | S2 | S1 | S2 | S4 | S2 |
* S2 | S2 | S4 | S3 | S1 | S1 | S2 | S2 | S2 | S4 | S2 |
* S3 | S2 | S4 | S2 | S1 | S1 | S2 | S1 | S2 | S4 | S2 |
* S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 |
*
*/
/*
* Data types and structures for scanning the pw_gecos field.
*/
typedef enum gecos_state {
};
/*
* Scan the pw_gecos field according to defined state table;
* return the next state according the the rules.
*/
{
if (ch == gecos_ignore_c) {
} else if (ch == gecos_sep_c) {
} else if (ch == gecos_samename) {
} else if (ch == '\0') {
}
}
/*
* Compare the given argument, which is taken to be a username, with
* the login name and with strings in the the pw_gecos field.
*/
int
{
return (1);
do {
switch (kstate_next) {
case S0:
gp++;
break;
case S1:
}
break;
case S2:
*bp++ = ' ';
}
gp++;
break;
case S3:
do {
bp--;
break;
case S4:
*bp++ = '\0';
break;
default:
*bp++ = '\0';
break;
}
return (1);
}
}
return (0);
}
/*
* Perform the character-by-character comparison.
* It is intended that "finger foo" should match "foo2", but an argument
* consisting entirely of digits should not be matched too broadly.
* Also, we do not want "finger foo123" to match "Mr. Foo" in the gecos.
*/
int
{
for (;;) {
break;
if (c1 == '\0')
return (1);
}
if (!c1) {
;
return (1);
}
} else if (!c2) {
;
return (1);
}
}
return (0);
}
int
{
char *host;
int s;
FILE *f;
int c;
int lastc;
int error_num;
return (0);
return (0);
*host++ = 0;
"unknown host: %s (try again later)\n", host);
} else {
}
return (1);
}
/*
* If hp->h_name is a IPv4-mapped IPv6 literal, we'll convert it to
* IPv4 literal address.
*/
sizeof (abuf)));
} else {
}
if (s < 0) {
perror("socket");
return (1);
}
hp->h_addr_list++;
(void) close(s);
if (s < 0) {
perror("socket");
return (0);
}
continue;
}
perror("connect");
(void) close(s);
return (1);
}
(void) printf("\n");
if (large)
f = fdopen(s, "r");
lastc = '\n';
/* map CRLF -> newline */
/* print out saved CR */
(void) putchar('\r');
lastc = c;
if (c == '\r')
continue;
(void) putchar(c);
}
if (lastc != '\n')
(void) putchar('\n');
(void) fclose(f);
return (1);
}
/*
* AnyMail - takes a username (string pointer thereto), and
* prints on standard output whether there is any unread mail,
* and if so, how old it is. (JCM@Shasta 15 March 80)
*/
void
{
char *timestr;
return;
/* Mailbox is empty or nonexistent */
(void) printf("No unread mail\n");
} else {
/*
* No new mail since the last time the user read it.
*/
(void) printf("Mail last read ");
/*
* New mail has definitely arrived since the last time
* mail was read. mtime is the time the most recent
* message arrived; atime is either the time the oldest
* unread message arrived, or the last time the mail
* was read.
*/
(void) printf("New mail received ");
(void) printf(";\n unread since ");
} else {
/*
* There is something in mailbox, but we can't really
* be sure whether it is mail held there by the user
* or a (single) new message that was placed in a newly
* recreated mailbox, so punt and call it "unread mail."
*/
(void) printf("Unread mail since ");
}
}
}
/*
* if not, enter this uid into table (so this function has a side-effect.)
*/
int
{
int i = 0;
while (i++ < PPIndex) {
if (PlanPrinted[i] == uid)
return (1);
}
if (i < PPMAX) {
PlanPrinted[i] = uid;
PPIndex++;
}
return (0);
}
/* BEGIN CSTYLED */
#define PRINT_CHAR(c) \
( \
|| \
(isascii((int)c) && \
) \
|| \
)
/* END CSTYLED */
void
{
int fd;
if (fd != -1) {
timerclear(&tv);
int nread;
if (nread > 0) {
unsigned char *p;
if (trunc_at_nl && *p == '\n')
goto out;
if (PRINT_CHAR(*p))
(void) putchar((int)*p);
else if (isascii(*p))
stdout);
}
} else
break;
}
out:
}
} else {
int c;
if (fp) {
if (trunc_at_nl && c == '\n')
break;
if (PRINT_CHAR(c))
(void) putchar((int)c);
else
if (isascii(c))
}
}
}
}
void
initscreening(void)
{
if (defopen(defaultfile) == 0) {
char *cp;
int flags;
/*
* ignore case
*/
while (*options != '\0')
case TERM_LOW:
break;
case TERM_HIGH:
break;
}
}
}
}
int
{
int r;
/*
* Sort by username.
*/
if (r != 0)
return (r);
/*
* If usernames are the same, sort by idle time.
*/
return (r);
}
void
{
size_t i;
return;
}
for (i = 1; i < nperson; i++)
}