finger.c revision 229403
12490Sjkh/*
22490Sjkh * Copyright (c) 1989, 1993
32490Sjkh *	The Regents of the University of California.  All rights reserved.
42490Sjkh *
52490Sjkh * This code is derived from software contributed to Berkeley by
62490Sjkh * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
72490Sjkh *
82490Sjkh * Redistribution and use in source and binary forms, with or without
92490Sjkh * modification, are permitted provided that the following conditions
102490Sjkh * are met:
112490Sjkh * 1. Redistributions of source code must retain the above copyright
122490Sjkh *    notice, this list of conditions and the following disclaimer.
132490Sjkh * 2. Redistributions in binary form must reproduce the above copyright
142490Sjkh *    notice, this list of conditions and the following disclaimer in the
152490Sjkh *    documentation and/or other materials provided with the distribution.
162490Sjkh * 4. Neither the name of the University nor the names of its contributors
172490Sjkh *    may be used to endorse or promote products derived from this software
182490Sjkh *    without specific prior written permission.
192490Sjkh *
202490Sjkh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
212490Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
222490Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
232490Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
242490Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
252490Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
262490Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
272490Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
282490Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
292490Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
302490Sjkh * SUCH DAMAGE.
312490Sjkh */
322490Sjkh
332490Sjkh/*
3410352Sjoerg * Luke Mewburn <lm@rmit.edu.au> added the following on 940622:
3510352Sjoerg *    - mail status ("No Mail", "Mail read:...", or "New Mail ...,
3610352Sjoerg *	Unread since ...".)
3710352Sjoerg *    - 4 digit phone extensions (3210 is printed as x3210.)
3810352Sjoerg *    - host/office toggling in short format with -h & -o.
392490Sjkh *    - short day names (`Tue' printed instead of `Jun 21' if the
402490Sjkh *	login time is < 6 days.
412490Sjkh */
422490Sjkh
432490Sjkh#ifndef lint
442490Sjkhstatic const char copyright[] =
452490Sjkh"@(#) Copyright (c) 1989, 1993\n\
462490Sjkh	The Regents of the University of California.  All rights reserved.\n";
472490Sjkh#endif /* not lint */
482490Sjkh
492490Sjkh#if 0
502490Sjkh#ifndef lint
5110352Sjoergstatic char sccsid[] = "@(#)finger.c	8.5 (Berkeley) 5/4/95";
5210352Sjoerg#endif
532490Sjkh#endif
5410352Sjoerg
5510352Sjoerg#include <sys/cdefs.h>
5610352Sjoerg__FBSDID("$FreeBSD: head/usr.bin/finger/finger.c 229403 2012-01-03 18:51:58Z ed $");
5710352Sjoerg
5810352Sjoerg/*
5910352Sjoerg * Finger prints out information about users.  It is not portable since
6010352Sjoerg * certain fields (e.g. the full user name, office, and phone numbers) are
6110352Sjoerg * extracted from the gecos field of the passwd file which other UNIXes
622490Sjkh * may not have or may use for other things.
632490Sjkh *
6410352Sjoerg * There are currently two output formats; the short format is one line
652490Sjkh * per user and displays login name, tty, login time, real name, idle time,
6610352Sjoerg * and either remote host information (default) or office location/phone
6710352Sjoerg * number, depending on if -h or -o is used respectively.
6810352Sjoerg * The long format gives the same information (in a more legible format) as
6910352Sjoerg * well as home directory, shell, mail info, and .plan/.project files.
7010352Sjoerg */
7110352Sjoerg
7210352Sjoerg#include <sys/types.h>
7310352Sjoerg#include <sys/socket.h>
7410352Sjoerg#include <db.h>
7510352Sjoerg#include <err.h>
7610352Sjoerg#include <pwd.h>
7710352Sjoerg#include <stdio.h>
7810352Sjoerg#include <stdlib.h>
7910352Sjoerg#include <string.h>
8010352Sjoerg#include <time.h>
8110352Sjoerg#include <unistd.h>
8210352Sjoerg#include <utmpx.h>
8310352Sjoerg#include <locale.h>
8410352Sjoerg
8510352Sjoerg#include "finger.h"
8610352Sjoerg#include "pathnames.h"
8710352Sjoerg
8810352SjoergDB *db;
8910352Sjoergtime_t now;
9010352Sjoergint entries, gflag, kflag, lflag, mflag, pplan, sflag, oflag;
9110352Sjoergsa_family_t family = PF_UNSPEC;
9210352Sjoergint d_first = -1;
9310352Sjoergchar tbuf[1024];
9410352Sjoergint invoker_root = 0;
9510352Sjoerg
9610352Sjoergstatic void loginlist(void);
9710352Sjoergstatic int option(int, char **);
9810352Sjoergstatic void usage(void);
9910352Sjoergstatic void userlist(int, char **);
10010352Sjoerg
10110352Sjoergstatic int
10210352Sjoergoption(int argc, char **argv)
10310352Sjoerg{
10410352Sjoerg	int ch;
10510352Sjoerg
10610352Sjoerg	optind = 1;		/* reset getopt */
10710352Sjoerg
10810352Sjoerg	while ((ch = getopt(argc, argv, "46gklmpsho")) != -1)
10910352Sjoerg		switch(ch) {
11010352Sjoerg		case '4':
11110352Sjoerg			family = AF_INET;
11210352Sjoerg			break;
11310352Sjoerg		case '6':
11410352Sjoerg			family = AF_INET6;
11510352Sjoerg			break;
11610352Sjoerg		case 'g':
11710352Sjoerg			gflag = 1;
11810352Sjoerg			break;
11910352Sjoerg		case 'k':
12010352Sjoerg			kflag = 1;		/* keep going without utmp */
12110352Sjoerg			break;
12210352Sjoerg		case 'l':
12310352Sjoerg			lflag = 1;		/* long format */
12410352Sjoerg			break;
12510352Sjoerg		case 'm':
12610352Sjoerg			mflag = 1;		/* force exact match of names */
12710352Sjoerg			break;
12810352Sjoerg		case 'p':
12910352Sjoerg			pplan = 1;		/* don't show .plan/.project */
13010352Sjoerg			break;
13110352Sjoerg		case 's':
13210352Sjoerg			sflag = 1;		/* short format */
13310352Sjoerg			break;
13410352Sjoerg		case 'h':
13510352Sjoerg			oflag = 0;		/* remote host info */
13610352Sjoerg			break;
13710352Sjoerg		case 'o':
13810352Sjoerg			oflag = 1;		/* office info */
13910352Sjoerg			break;
14010352Sjoerg		case '?':
14110352Sjoerg		default:
14210352Sjoerg			usage();
14310352Sjoerg		}
14410352Sjoerg
14510352Sjoerg	return optind;
14610352Sjoerg}
14710352Sjoerg
14810352Sjoergstatic void
14910352Sjoergusage(void)
15010352Sjoerg{
15110352Sjoerg	(void)fprintf(stderr,
15210352Sjoerg	    "usage: finger [-46gklmpsho] [user ...] [user@host ...]\n");
15310352Sjoerg	exit(1);
15410352Sjoerg}
15510352Sjoerg
15610352Sjoergint
15710352Sjoergmain(int argc, char **argv)
15810352Sjoerg{
15910352Sjoerg	int envargc, argcnt;
16029018Sache	char *envargv[3];
16110352Sjoerg	struct passwd *pw;
16210352Sjoerg	static char myname[] = "finger";
16310352Sjoerg
16410352Sjoerg	if (getuid() == 0 || geteuid() == 0) {
16510352Sjoerg		invoker_root = 1;
16610352Sjoerg		if ((pw = getpwnam(UNPRIV_NAME)) && pw->pw_uid > 0) {
16710352Sjoerg			if (setgid(pw->pw_gid) != 0)
16810352Sjoerg				err(1, "setgid()");
16910352Sjoerg			if (setuid(pw->pw_uid) != 0)
17010352Sjoerg				err(1, "setuid()");
17110352Sjoerg		} else {
17210352Sjoerg			if (setgid(UNPRIV_UGID) != 0)
17310352Sjoerg				err(1, "setgid()");
17410352Sjoerg			if (setuid(UNPRIV_UGID) != 0)
17510352Sjoerg				err(1, "setuid()");
17610352Sjoerg		}
17710352Sjoerg	}
17810352Sjoerg
17910352Sjoerg	(void) setlocale(LC_ALL, "");
18010352Sjoerg
18110352Sjoerg				/* remove this line to get remote host */
18210352Sjoerg	oflag = 1;		/* default to old "office" behavior */
18310352Sjoerg
18410352Sjoerg	/*
18510352Sjoerg	 * Process environment variables followed by command line arguments.
18610352Sjoerg	 */
18710352Sjoerg	if ((envargv[1] = getenv("FINGER"))) {
18810352Sjoerg		envargc = 2;
18910352Sjoerg		envargv[0] = myname;
19010352Sjoerg		envargv[2] = NULL;
19110352Sjoerg		(void) option(envargc, envargv);
19210352Sjoerg	}
19310352Sjoerg
19410352Sjoerg	argcnt = option(argc, argv);
19510352Sjoerg	argc -= argcnt;
19610352Sjoerg	argv += argcnt;
19710352Sjoerg
19810352Sjoerg	(void)time(&now);
19910352Sjoerg	setpassent(1);
20010352Sjoerg	if (!*argv) {
20110352Sjoerg		/*
20210352Sjoerg		 * Assign explicit "small" format if no names given and -l
20310352Sjoerg		 * not selected.  Force the -s BEFORE we get names so proper
20410352Sjoerg		 * screening will be done.
20510352Sjoerg		 */
20610352Sjoerg		if (!lflag)
20710352Sjoerg			sflag = 1;	/* if -l not explicit, force -s */
20810352Sjoerg		loginlist();
20910352Sjoerg		if (entries == 0)
2102490Sjkh			(void)printf("No one logged on.\n");
21110352Sjoerg	} else {
21210352Sjoerg		userlist(argc, argv);
21310352Sjoerg		/*
21410352Sjoerg		 * Assign explicit "large" format if names given and -s not
2152490Sjkh		 * explicitly stated.  Force the -l AFTER we get names so any
21610352Sjoerg		 * remote finger attempts specified won't be mishandled.
21710352Sjoerg		 */
21810352Sjoerg		if (!sflag)
21910352Sjoerg			lflag = 1;	/* if -s not explicit, force -l */
22010352Sjoerg	}
22110352Sjoerg	if (entries) {
22210352Sjoerg		if (lflag)
22310352Sjoerg			lflag_print();
2242490Sjkh		else
2252490Sjkh			sflag_print();
2262490Sjkh	}
22710352Sjoerg	return (0);
22810352Sjoerg}
22910352Sjoerg
2302490Sjkhstatic void
2312490Sjkhloginlist(void)
23210352Sjoerg{
2332490Sjkh	PERSON *pn;
2342490Sjkh	DBT data, key;
23510352Sjoerg	struct passwd *pw;
23610352Sjoerg	struct utmpx *user;
23710352Sjoerg	int r, sflag1;
23810352Sjoerg
23910352Sjoerg	if (kflag)
24010352Sjoerg		errx(1, "can't list logins without reading utmp");
24110352Sjoerg
24210352Sjoerg	setutxent();
24310352Sjoerg	while ((user = getutxent()) != NULL) {
24410352Sjoerg		if (user->ut_type != USER_PROCESS)
24510352Sjoerg			continue;
24610352Sjoerg		if ((pn = find_person(user->ut_user)) == NULL) {
24710352Sjoerg			if ((pw = getpwnam(user->ut_user)) == NULL)
24810352Sjoerg				continue;
24910352Sjoerg			if (hide(pw))
25010352Sjoerg				continue;
25110352Sjoerg			pn = enter_person(pw);
25210352Sjoerg		}
25310352Sjoerg		enter_where(user, pn);
25410352Sjoerg	}
25510352Sjoerg	endutxent();
25610352Sjoerg	if (db && lflag)
25710352Sjoerg		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
25810352Sjoerg			PERSON *tmp;
2592490Sjkh
2602490Sjkh			r = (*db->seq)(db, &key, &data, sflag1);
2612490Sjkh			if (r == -1)
26229018Sache				err(1, "db seq");
26329018Sache			if (r == 1)
26429018Sache				break;
26529018Sache			memmove(&tmp, data.data, sizeof tmp);
26629018Sache			enter_lastlog(tmp);
26710352Sjoerg		}
26810352Sjoerg}
26929018Sache
27010352Sjoergstatic void
27110352Sjoerguserlist(int argc, char **argv)
2722490Sjkh{
27310352Sjoerg	PERSON *pn;
27429018Sache	DBT data, key;
27510352Sjoerg	struct utmpx *user;
27629018Sache	struct passwd *pw;
2772490Sjkh	int r, sflag1, *used, *ip;
27810352Sjoerg	char **ap, **nargv, **np, **p;
27910352Sjoerg	FILE *conf_fp;
28010352Sjoerg	char conf_alias[LINE_MAX];
28110352Sjoerg	char *conf_realname;
28210352Sjoerg	int conf_length;
2832490Sjkh
2842490Sjkh	if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
28510352Sjoerg	    (used = calloc(argc, sizeof(int))) == NULL)
28610352Sjoerg		err(1, NULL);
2872490Sjkh
28810352Sjoerg	/* Pull out all network requests. */
28910352Sjoerg	for (ap = p = argv, np = nargv; *p; ++p)
29029018Sache		if (strchr(*p, '@'))
29129018Sache			*np++ = *p;
29210352Sjoerg		else
29310352Sjoerg			*ap++ = *p;
29410352Sjoerg
29510352Sjoerg	*np++ = NULL;
29610352Sjoerg	*ap++ = NULL;
29710352Sjoerg
29810352Sjoerg	if (!*argv)
29910352Sjoerg		goto net;
30010352Sjoerg
30110352Sjoerg	/*
30210352Sjoerg	 * Mark any arguments beginning with '/' as invalid so that we
30310352Sjoerg	 * don't accidentally confuse them with expansions from finger.conf
30410352Sjoerg	 */
30510352Sjoerg	for (p = argv, ip = used; *p; ++p, ++ip)
30610352Sjoerg	    if (**p == '/') {
30710352Sjoerg		*ip = 1;
30810352Sjoerg		warnx("%s: no such user", *p);
30910352Sjoerg	    }
31010352Sjoerg
31110352Sjoerg	/*
31210352Sjoerg	 * Traverse the finger alias configuration file of the form
3132490Sjkh	 * alias:(user|alias), ignoring comment lines beginning '#'.
3142490Sjkh	 */
31510352Sjoerg	if ((conf_fp = fopen(_PATH_FINGERCONF, "r")) != NULL) {
31610352Sjoerg	    while(fgets(conf_alias, sizeof(conf_alias), conf_fp) != NULL) {
3172490Sjkh		conf_length = strlen(conf_alias);
3182490Sjkh		if (*conf_alias == '#' || conf_alias[--conf_length] != '\n')
3192490Sjkh		    continue;
32010352Sjoerg		conf_alias[conf_length] = '\0';      /* Remove trailing LF */
32110352Sjoerg		if ((conf_realname = strchr(conf_alias, ':')) == NULL)
32210352Sjoerg		    continue;
32310352Sjoerg		*conf_realname = '\0';               /* Replace : with NUL */
3242490Sjkh		for (p = argv; *p; ++p) {
32510352Sjoerg		    if (strcmp(*p, conf_alias) == 0) {
32610352Sjoerg			if ((*p = strdup(conf_realname+1)) == NULL) {
32710352Sjoerg			    err(1, NULL);
32810352Sjoerg			}
32910352Sjoerg		    }
33010352Sjoerg		}
33110352Sjoerg	    }
33210352Sjoerg	    (void)fclose(conf_fp);
33329018Sache	}
33410352Sjoerg
33510352Sjoerg	/*
33610352Sjoerg	 * Traverse the list of possible login names and check the login name
33710352Sjoerg	 * and real name against the name specified by the user. If the name
33810352Sjoerg	 * begins with a '/', try to read the file of that name instead of
33910352Sjoerg	 * gathering the traditional finger information.
34010352Sjoerg	 */
34110352Sjoerg	if (mflag)
34210352Sjoerg		for (p = argv, ip = used; *p; ++p, ++ip) {
34310352Sjoerg			if (**p != '/' || *ip == 1 || !show_text("", *p, "")) {
34410352Sjoerg				if (((pw = getpwnam(*p)) != NULL) && !hide(pw))
34510352Sjoerg					enter_person(pw);
34610352Sjoerg				else if (!*ip)
34710352Sjoerg					warnx("%s: no such user", *p);
34810352Sjoerg			}
34910352Sjoerg		}
35010352Sjoerg	else {
35110352Sjoerg		while ((pw = getpwent()) != NULL) {
35210352Sjoerg			for (p = argv, ip = used; *p; ++p, ++ip)
35310352Sjoerg				if (**p == '/' && *ip != 1
35410352Sjoerg				    && show_text("", *p, ""))
35510352Sjoerg					*ip = 1;
35610352Sjoerg				else if (match(pw, *p) && !hide(pw)) {
35710352Sjoerg					enter_person(pw);
35810352Sjoerg					*ip = 1;
35910352Sjoerg				}
36010352Sjoerg		}
36110352Sjoerg		for (p = argv, ip = used; *p; ++p, ++ip)
36210352Sjoerg			if (!*ip)
36310352Sjoerg				warnx("%s: no such user", *p);
36410352Sjoerg	}
36510352Sjoerg
36610352Sjoerg	/* Handle network requests. */
367net:	for (p = nargv; *p;) {
368		netfinger(*p++);
369		if (*p || entries)
370		    printf("\n");
371	}
372
373	free(used);
374	if (entries == 0)
375		return;
376
377	if (kflag)
378		return;
379
380	/*
381	 * Scan thru the list of users currently logged in, saving
382	 * appropriate data whenever a match occurs.
383	 */
384	setutxent();
385	while ((user = getutxent()) != NULL) {
386		if (user->ut_type != USER_PROCESS)
387			continue;
388		if ((pn = find_person(user->ut_user)) == NULL)
389			continue;
390		enter_where(user, pn);
391	}
392	endutxent();
393	if (db)
394		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
395			PERSON *tmp;
396
397			r = (*db->seq)(db, &key, &data, sflag1);
398			if (r == -1)
399				err(1, "db seq");
400			if (r == 1)
401				break;
402			memmove(&tmp, data.data, sizeof tmp);
403			enter_lastlog(tmp);
404		}
405}
406