finger.c revision 87229
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
372537Spst/*
382537Spst * Luke Mewburn <lm@rmit.edu.au> added the following on 940622:
392537Spst *    - mail status ("No Mail", "Mail read:...", or "New Mail ...,
402537Spst *	Unread since ...".)
412537Spst *    - 4 digit phone extensions (3210 is printed as x3210.)
422537Spst *    - host/office toggling in short format with -h & -o.
432537Spst *    - short day names (`Tue' printed instead of `Jun 21' if the
442537Spst *	login time is < 6 days.
452537Spst */
462537Spst
4787229Smarkm#include <sys/cdefs.h>
4887229Smarkm
4987229Smarkm__FBSDID("$FreeBSD: head/usr.bin/finger/finger.c 87229 2001-12-02 20:13:42Z markm $");
5087229Smarkm
511590Srgrimes#ifndef lint
5267467Srustatic const char copyright[] =
531590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
541590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
551590Srgrimes#endif /* not lint */
561590Srgrimes
571590Srgrimes#ifndef lint
5887229Smarkmstatic const char sccsid[] = "@(#)finger.c	8.5 (Berkeley) 5/4/95";
5972109Scharnier#endif
601590Srgrimes
611590Srgrimes/*
621590Srgrimes * Finger prints out information about users.  It is not portable since
631590Srgrimes * certain fields (e.g. the full user name, office, and phone numbers) are
641590Srgrimes * extracted from the gecos field of the passwd file which other UNIXes
651590Srgrimes * may not have or may use for other things.
661590Srgrimes *
671590Srgrimes * There are currently two output formats; the short format is one line
681590Srgrimes * per user and displays login name, tty, login time, real name, idle time,
692537Spst * and either remote host information (default) or office location/phone
702537Spst * number, depending on if -h or -o is used respectively.
712537Spst * The long format gives the same information (in a more legible format) as
722537Spst * well as home directory, shell, mail info, and .plan/.project files.
731590Srgrimes */
741590Srgrimes
7523693Speter#include <db.h>
7623693Speter#include <err.h>
771590Srgrimes#include <pwd.h>
781590Srgrimes#include <stdio.h>
791590Srgrimes#include <stdlib.h>
801590Srgrimes#include <string.h>
8123693Speter#include <time.h>
8223693Speter#include <unistd.h>
8323693Speter#include <utmp.h>
8411759Sache#include <locale.h>
8523693Speter
861590Srgrimes#include "finger.h"
8764775Sbrian#include "pathnames.h"
881590Srgrimes
891590SrgrimesDB *db;
901590Srgrimestime_t now;
9114631Solahint entries, lflag, mflag, pplan, sflag, oflag, Tflag;
9274586Sacheint d_first = -1;
931590Srgrimeschar tbuf[1024];
941590Srgrimes
951590Srgrimesstatic void loginlist __P((void));
9687229Smarkmstatic int option __P((int, char **));
9727169Scharnierstatic void usage __P((void));
981590Srgrimesstatic void userlist __P((int, char **));
991590Srgrimes
10087229Smarkmstatic int
1012589Spstoption(argc, argv)
1021590Srgrimes	int argc;
1031590Srgrimes	char **argv;
1041590Srgrimes{
1051590Srgrimes	int ch;
1061590Srgrimes
1072589Spst	optind = 1;		/* reset getopt */
1082537Spst
10924360Simp	while ((ch = getopt(argc, argv, "lmpshoT")) != -1)
1101590Srgrimes		switch(ch) {
1111590Srgrimes		case 'l':
1121590Srgrimes			lflag = 1;		/* long format */
1131590Srgrimes			break;
1141590Srgrimes		case 'm':
1151590Srgrimes			mflag = 1;		/* force exact match of names */
1161590Srgrimes			break;
1171590Srgrimes		case 'p':
1181590Srgrimes			pplan = 1;		/* don't show .plan/.project */
1191590Srgrimes			break;
1201590Srgrimes		case 's':
1211590Srgrimes			sflag = 1;		/* short format */
1221590Srgrimes			break;
1232537Spst		case 'h':
1242537Spst			oflag = 0;		/* remote host info */
1252537Spst			break;
1262537Spst		case 'o':
1272537Spst			oflag = 1;		/* office info */
1282537Spst			break;
12914631Solah		case 'T':
13014631Solah			Tflag = 1;		/* disable T/TCP */
13114631Solah			break;
1321590Srgrimes		case '?':
1331590Srgrimes		default:
13427169Scharnier			usage();
1351590Srgrimes		}
1361590Srgrimes
1372589Spst	return optind;
1382589Spst}
1392589Spst
14027169Scharnierstatic void
14127169Scharnierusage()
14227169Scharnier{
14327169Scharnier	(void)fprintf(stderr, "usage: finger [-lmpshoT] [login ...]\n");
14427169Scharnier	exit(1);
14527169Scharnier}
14627169Scharnier
14727169Scharnierint
1482589Spstmain(argc, argv)
1492589Spst	int argc;
1502589Spst	char **argv;
1512589Spst{
15227169Scharnier	int envargc, argcnt;
1532589Spst	char *envargv[3];
15446662Sobrien	struct passwd *pw;
15587229Smarkm	static char myname[] = "finger";
1562589Spst
15746662Sobrien	if (getuid() == 0 || geteuid() == 0) {
15846662Sobrien		if ((pw = getpwnam(UNPRIV_NAME)) && pw->pw_uid > 0) {
15946662Sobrien			 setgid(pw->pw_gid);
16046662Sobrien			 setuid(pw->pw_uid);
16146662Sobrien		} else {
16246662Sobrien			 setgid(UNPRIV_UGID);
16346662Sobrien			 setuid(UNPRIV_UGID);
16446662Sobrien		}
16546662Sobrien	}
16646662Sobrien
16711811Sache	(void) setlocale(LC_ALL, "");
16811759Sache
1692589Spst				/* remove this line to get remote host */
1702589Spst	oflag = 1;		/* default to old "office" behavior */
1712589Spst
1722589Spst	/*
1732589Spst	 * Process environment variables followed by command line arguments.
1742589Spst	 */
1752589Spst	if ((envargv[1] = getenv("FINGER"))) {
1762589Spst		envargc = 2;
17787229Smarkm		envargv[0] = myname;
1782589Spst		envargv[2] = NULL;
1792589Spst		(void) option(envargc, envargv);
1802589Spst	}
1812589Spst
1822589Spst	argcnt = option(argc, argv);
1832589Spst	argc -= argcnt;
1842589Spst	argv += argcnt;
1852589Spst
1861590Srgrimes	(void)time(&now);
1871590Srgrimes	setpassent(1);
1881590Srgrimes	if (!*argv) {
1891590Srgrimes		/*
1901590Srgrimes		 * Assign explicit "small" format if no names given and -l
1911590Srgrimes		 * not selected.  Force the -s BEFORE we get names so proper
1921590Srgrimes		 * screening will be done.
1931590Srgrimes		 */
1941590Srgrimes		if (!lflag)
1951590Srgrimes			sflag = 1;	/* if -l not explicit, force -s */
1961590Srgrimes		loginlist();
1971590Srgrimes		if (entries == 0)
1981590Srgrimes			(void)printf("No one logged on.\n");
1991590Srgrimes	} else {
2001590Srgrimes		userlist(argc, argv);
2011590Srgrimes		/*
2021590Srgrimes		 * Assign explicit "large" format if names given and -s not
2031590Srgrimes		 * explicitly stated.  Force the -l AFTER we get names so any
2041590Srgrimes		 * remote finger attempts specified won't be mishandled.
2051590Srgrimes		 */
2061590Srgrimes		if (!sflag)
2071590Srgrimes			lflag = 1;	/* if -s not explicit, force -l */
2081590Srgrimes	}
20948566Sbillf	if (entries) {
2101590Srgrimes		if (lflag)
2111590Srgrimes			lflag_print();
2121590Srgrimes		else
2131590Srgrimes			sflag_print();
21448566Sbillf	}
21523693Speter	return (0);
2161590Srgrimes}
2171590Srgrimes
2181590Srgrimesstatic void
2191590Srgrimesloginlist()
2201590Srgrimes{
22187229Smarkm	PERSON *pn;
2221590Srgrimes	DBT data, key;
2231590Srgrimes	struct passwd *pw;
2241590Srgrimes	struct utmp user;
22587229Smarkm	int r, sflag1;
2261590Srgrimes	char name[UT_NAMESIZE + 1];
2271590Srgrimes
2281590Srgrimes	if (!freopen(_PATH_UTMP, "r", stdin))
22923693Speter		err(1, "%s", _PATH_UTMP);
23027169Scharnier	name[UT_NAMESIZE] = '\0';
2311590Srgrimes	while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
2321590Srgrimes		if (!user.ut_name[0])
2331590Srgrimes			continue;
2341590Srgrimes		if ((pn = find_person(user.ut_name)) == NULL) {
2351590Srgrimes			bcopy(user.ut_name, name, UT_NAMESIZE);
2361590Srgrimes			if ((pw = getpwnam(name)) == NULL)
2371590Srgrimes				continue;
2385369Sjkh			if (hide(pw))
2395369Sjkh				continue;
2401590Srgrimes			pn = enter_person(pw);
2411590Srgrimes		}
2421590Srgrimes		enter_where(&user, pn);
2431590Srgrimes	}
2441590Srgrimes	if (db && lflag)
24587229Smarkm		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
24623693Speter			PERSON *tmp;
24723693Speter
24887229Smarkm			r = (*db->seq)(db, &key, &data, sflag1);
2491590Srgrimes			if (r == -1)
25023693Speter				err(1, "db seq");
2511590Srgrimes			if (r == 1)
2521590Srgrimes				break;
25323693Speter			memmove(&tmp, data.data, sizeof tmp);
25423693Speter			enter_lastlog(tmp);
2551590Srgrimes		}
2561590Srgrimes}
2571590Srgrimes
2581590Srgrimesstatic void
2591590Srgrimesuserlist(argc, argv)
26087229Smarkm	int argc;
26187229Smarkm	char **argv;
2621590Srgrimes{
26387229Smarkm	PERSON *pn;
2641590Srgrimes	DBT data, key;
2651590Srgrimes	struct utmp user;
2661590Srgrimes	struct passwd *pw;
26787229Smarkm	int r, sflag1, *used, *ip;
2681590Srgrimes	char **ap, **nargv, **np, **p;
26964775Sbrian	FILE *conf_fp;
27064775Sbrian	char conf_alias[LINE_MAX];
27164775Sbrian	char *conf_realname;
27264775Sbrian	int conf_length;
2731590Srgrimes
2741590Srgrimes	if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
2751590Srgrimes	    (used = calloc(argc, sizeof(int))) == NULL)
27623693Speter		err(1, NULL);
2771590Srgrimes
2781590Srgrimes	/* Pull out all network requests. */
2791590Srgrimes	for (ap = p = argv, np = nargv; *p; ++p)
2801590Srgrimes		if (index(*p, '@'))
2811590Srgrimes			*np++ = *p;
2821590Srgrimes		else
2831590Srgrimes			*ap++ = *p;
2841590Srgrimes
2851590Srgrimes	*np++ = NULL;
2861590Srgrimes	*ap++ = NULL;
2871590Srgrimes
2881590Srgrimes	if (!*argv)
2891590Srgrimes		goto net;
2901590Srgrimes
2911590Srgrimes	/*
29266563Sbrian	 * Mark any arguments beginning with '/' as invalid so that we
29366563Sbrian	 * don't accidently confuse them with expansions from finger.conf
29466563Sbrian	 */
29566563Sbrian	for (p = argv, ip = used; *p; ++p, ++ip)
29666563Sbrian	    if (**p == '/') {
29766563Sbrian		*ip = 1;
29866563Sbrian		warnx("%s: no such user", *p);
29966563Sbrian	    }
30066563Sbrian
30166563Sbrian	/*
30264775Sbrian	 * Traverse the finger alias configuration file of the form
30364775Sbrian	 * alias:(user|alias), ignoring comment lines beginning '#'.
30464775Sbrian	 */
30564775Sbrian	if ((conf_fp = fopen(_PATH_FINGERCONF, "r")) != NULL) {
30664775Sbrian	    while(fgets(conf_alias, sizeof(conf_alias), conf_fp) != NULL) {
30764775Sbrian		conf_length = strlen(conf_alias);
30864775Sbrian		if (*conf_alias == '#' || conf_alias[--conf_length] != '\n')
30964775Sbrian		    continue;
31064775Sbrian		conf_alias[conf_length] = '\0';      /* Remove trailing LF */
31164775Sbrian		if ((conf_realname = strchr(conf_alias, ':')) == NULL)
31264775Sbrian		    continue;
31364775Sbrian		*conf_realname = '\0';               /* Replace : with NUL */
31464775Sbrian		for (p = argv; *p; ++p) {
31564775Sbrian		    if (strcmp(*p, conf_alias) == NULL) {
31664775Sbrian			if ((*p = strdup(conf_realname+1)) == NULL) {
31764775Sbrian			    err(1, NULL);
31864775Sbrian			}
31964775Sbrian		    }
32064775Sbrian		}
32164775Sbrian	    }
32264775Sbrian	    (void)fclose(conf_fp);
32364775Sbrian	}
32464775Sbrian
32564775Sbrian	/*
3261590Srgrimes	 * Traverse the list of possible login names and check the login name
32765064Sbrian	 * and real name against the name specified by the user. If the name
32865064Sbrian	 * begins with a '/', try to read the file of that name instead of
32965064Sbrian	 * gathering the traditional finger information.
3301590Srgrimes	 */
3311590Srgrimes	if (mflag)
33266675Sru		for (p = argv, ip = used; *p; ++p, ++ip) {
33366675Sru			if (**p != '/' || *ip == 1 || !show_text("", *p, "")) {
33465064Sbrian				if (((pw = getpwnam(*p)) != NULL) && !hide(pw))
33565064Sbrian					enter_person(pw);
33666675Sru				else if (!*ip)
33765064Sbrian					warnx("%s: no such user", *p);
33865064Sbrian			}
33965064Sbrian		}
3401590Srgrimes	else {
34165787Sbrian		while ((pw = getpwent()) != NULL) {
3421590Srgrimes			for (p = argv, ip = used; *p; ++p, ++ip)
34365064Sbrian				if (**p == '/' && *ip != 1
34465787Sbrian				    && show_text("", *p, ""))
34565064Sbrian					*ip = 1;
34665787Sbrian				else if (match(pw, *p) && !hide(pw)) {
3471590Srgrimes					enter_person(pw);
3481590Srgrimes					*ip = 1;
3491590Srgrimes				}
3505369Sjkh		}
3511590Srgrimes		for (p = argv, ip = used; *p; ++p, ++ip)
3521590Srgrimes			if (!*ip)
35327169Scharnier				warnx("%s: no such user", *p);
3541590Srgrimes	}
3551590Srgrimes
3561590Srgrimes	/* Handle network requests. */
3574991Spstnet:	for (p = nargv; *p;) {
3581590Srgrimes		netfinger(*p++);
3594991Spst		if (*p || entries)
3604991Spst		    printf("\n");
3614991Spst	}
3621590Srgrimes
3631590Srgrimes	if (entries == 0)
3641590Srgrimes		return;
3651590Srgrimes
3661590Srgrimes	/*
3671590Srgrimes	 * Scan thru the list of users currently logged in, saving
3681590Srgrimes	 * appropriate data whenever a match occurs.
3691590Srgrimes	 */
3701590Srgrimes	if (!freopen(_PATH_UTMP, "r", stdin))
37123693Speter		err(1, "%s", _PATH_UTMP);
3721590Srgrimes	while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
3731590Srgrimes		if (!user.ut_name[0])
3741590Srgrimes			continue;
3751590Srgrimes		if ((pn = find_person(user.ut_name)) == NULL)
3761590Srgrimes			continue;
3771590Srgrimes		enter_where(&user, pn);
3781590Srgrimes	}
3791590Srgrimes	if (db)
38087229Smarkm		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
38123693Speter			PERSON *tmp;
38223693Speter
38387229Smarkm			r = (*db->seq)(db, &key, &data, sflag1);
3841590Srgrimes			if (r == -1)
38523693Speter				err(1, "db seq");
3861590Srgrimes			if (r == 1)
3871590Srgrimes				break;
38823693Speter			memmove(&tmp, data.data, sizeof tmp);
38923693Speter			enter_lastlog(tmp);
3901590Srgrimes		}
3911590Srgrimes}
392