finger.c revision 11811
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
471590Srgrimes#ifndef lint
481590Srgrimesstatic char copyright[] =
491590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
501590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
511590Srgrimes#endif /* not lint */
521590Srgrimes
531590Srgrimes#ifndef lint
541590Srgrimesstatic char sccsid[] = "@(#)finger.c	8.2 (Berkeley) 9/30/93";
551590Srgrimes#endif /* not lint */
561590Srgrimes
571590Srgrimes/*
581590Srgrimes * Finger prints out information about users.  It is not portable since
591590Srgrimes * certain fields (e.g. the full user name, office, and phone numbers) are
601590Srgrimes * extracted from the gecos field of the passwd file which other UNIXes
611590Srgrimes * may not have or may use for other things.
621590Srgrimes *
631590Srgrimes * There are currently two output formats; the short format is one line
641590Srgrimes * per user and displays login name, tty, login time, real name, idle time,
652537Spst * and either remote host information (default) or office location/phone
662537Spst * number, depending on if -h or -o is used respectively.
672537Spst * The long format gives the same information (in a more legible format) as
682537Spst * well as home directory, shell, mail info, and .plan/.project files.
691590Srgrimes */
701590Srgrimes
711590Srgrimes#include <sys/param.h>
721590Srgrimes#include <fcntl.h>
731590Srgrimes#include <time.h>
741590Srgrimes#include <pwd.h>
751590Srgrimes#include <utmp.h>
761590Srgrimes#include <errno.h>
771590Srgrimes#include <stdio.h>
781590Srgrimes#include <stdlib.h>
791590Srgrimes#include <string.h>
801590Srgrimes#include <db.h>
8111759Sache#include <locale.h>
821590Srgrimes#include "finger.h"
831590Srgrimes
841590SrgrimesDB *db;
851590Srgrimestime_t now;
862537Spstint entries, lflag, mflag, pplan, sflag, oflag;
871590Srgrimeschar tbuf[1024];
881590Srgrimes
891590Srgrimesstatic void loginlist __P((void));
901590Srgrimesstatic void userlist __P((int, char **));
911590Srgrimes
922589Spstint
932589Spstoption(argc, argv)
941590Srgrimes	int argc;
951590Srgrimes	char **argv;
961590Srgrimes{
971590Srgrimes	int ch;
981590Srgrimes
992589Spst	optind = 1;		/* reset getopt */
1002537Spst
1012537Spst	while ((ch = getopt(argc, argv, "lmpsho")) != EOF)
1021590Srgrimes		switch(ch) {
1031590Srgrimes		case 'l':
1041590Srgrimes			lflag = 1;		/* long format */
1051590Srgrimes			break;
1061590Srgrimes		case 'm':
1071590Srgrimes			mflag = 1;		/* force exact match of names */
1081590Srgrimes			break;
1091590Srgrimes		case 'p':
1101590Srgrimes			pplan = 1;		/* don't show .plan/.project */
1111590Srgrimes			break;
1121590Srgrimes		case 's':
1131590Srgrimes			sflag = 1;		/* short format */
1141590Srgrimes			break;
1152537Spst		case 'h':
1162537Spst			oflag = 0;		/* remote host info */
1172537Spst			break;
1182537Spst		case 'o':
1192537Spst			oflag = 1;		/* office info */
1202537Spst			break;
1211590Srgrimes		case '?':
1221590Srgrimes		default:
1231590Srgrimes			(void)fprintf(stderr,
1242537Spst			    "usage: finger [-lmpsho] [login ...]\n");
1251590Srgrimes			exit(1);
1261590Srgrimes		}
1271590Srgrimes
1282589Spst	return optind;
1292589Spst}
1302589Spst
1312589Spstmain(argc, argv)
1322589Spst	int argc;
1332589Spst	char **argv;
1342589Spst{
1352589Spst	int ch, envargc, argcnt;
1362589Spst	char *envargv[3];
1372589Spst
13811811Sache	(void) setlocale(LC_ALL, "");
13911759Sache
1402589Spst				/* remove this line to get remote host */
1412589Spst	oflag = 1;		/* default to old "office" behavior */
1422589Spst
1432589Spst	/*
1442589Spst	 * Process environment variables followed by command line arguments.
1452589Spst	 */
1462589Spst	if ((envargv[1] = getenv("FINGER"))) {
1472589Spst		envargc = 2;
1482589Spst		envargv[0] = "finger";
1492589Spst		envargv[2] = NULL;
1502589Spst		(void) option(envargc, envargv);
1512589Spst	}
1522589Spst
1532589Spst	argcnt = option(argc, argv);
1542589Spst	argc -= argcnt;
1552589Spst	argv += argcnt;
1562589Spst
1571590Srgrimes	(void)time(&now);
1581590Srgrimes	setpassent(1);
1591590Srgrimes	if (!*argv) {
1601590Srgrimes		/*
1611590Srgrimes		 * Assign explicit "small" format if no names given and -l
1621590Srgrimes		 * not selected.  Force the -s BEFORE we get names so proper
1631590Srgrimes		 * screening will be done.
1641590Srgrimes		 */
1651590Srgrimes		if (!lflag)
1661590Srgrimes			sflag = 1;	/* if -l not explicit, force -s */
1671590Srgrimes		loginlist();
1681590Srgrimes		if (entries == 0)
1691590Srgrimes			(void)printf("No one logged on.\n");
1701590Srgrimes	} else {
1711590Srgrimes		userlist(argc, argv);
1721590Srgrimes		/*
1731590Srgrimes		 * Assign explicit "large" format if names given and -s not
1741590Srgrimes		 * explicitly stated.  Force the -l AFTER we get names so any
1751590Srgrimes		 * remote finger attempts specified won't be mishandled.
1761590Srgrimes		 */
1771590Srgrimes		if (!sflag)
1781590Srgrimes			lflag = 1;	/* if -s not explicit, force -l */
1791590Srgrimes	}
1801590Srgrimes	if (entries)
1811590Srgrimes		if (lflag)
1821590Srgrimes			lflag_print();
1831590Srgrimes		else
1841590Srgrimes			sflag_print();
1851590Srgrimes	exit(0);
1861590Srgrimes}
1871590Srgrimes
1881590Srgrimesstatic void
1891590Srgrimesloginlist()
1901590Srgrimes{
1911590Srgrimes	register PERSON *pn;
1921590Srgrimes	DBT data, key;
1931590Srgrimes	struct passwd *pw;
1941590Srgrimes	struct utmp user;
1951590Srgrimes	int r, sflag;
1961590Srgrimes	char name[UT_NAMESIZE + 1];
1971590Srgrimes
1981590Srgrimes	if (!freopen(_PATH_UTMP, "r", stdin))
1991590Srgrimes		err("%s: %s", _PATH_UTMP, strerror(errno));
2001590Srgrimes	name[UT_NAMESIZE] = NULL;
2011590Srgrimes	while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
2021590Srgrimes		if (!user.ut_name[0])
2031590Srgrimes			continue;
2041590Srgrimes		if ((pn = find_person(user.ut_name)) == NULL) {
2051590Srgrimes			bcopy(user.ut_name, name, UT_NAMESIZE);
2061590Srgrimes			if ((pw = getpwnam(name)) == NULL)
2071590Srgrimes				continue;
2085369Sjkh			if (hide(pw))
2095369Sjkh				continue;
2101590Srgrimes			pn = enter_person(pw);
2111590Srgrimes		}
2121590Srgrimes		enter_where(&user, pn);
2131590Srgrimes	}
2141590Srgrimes	if (db && lflag)
2151590Srgrimes		for (sflag = R_FIRST;; sflag = R_NEXT) {
2161590Srgrimes			r = (*db->seq)(db, &key, &data, sflag);
2171590Srgrimes			if (r == -1)
2181590Srgrimes				err("db seq: %s", strerror(errno));
2191590Srgrimes			if (r == 1)
2201590Srgrimes				break;
2211590Srgrimes			enter_lastlog(*(PERSON **)data.data);
2221590Srgrimes		}
2231590Srgrimes}
2241590Srgrimes
2251590Srgrimesstatic void
2261590Srgrimesuserlist(argc, argv)
2271590Srgrimes	register int argc;
2281590Srgrimes	register char **argv;
2291590Srgrimes{
2301590Srgrimes	register PERSON *pn;
2311590Srgrimes	DBT data, key;
2321590Srgrimes	struct utmp user;
2331590Srgrimes	struct passwd *pw;
2341590Srgrimes	int r, sflag, *used, *ip;
2351590Srgrimes	char **ap, **nargv, **np, **p;
2361590Srgrimes
2371590Srgrimes	if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
2381590Srgrimes	    (used = calloc(argc, sizeof(int))) == NULL)
2391590Srgrimes		err("%s", strerror(errno));
2401590Srgrimes
2411590Srgrimes	/* Pull out all network requests. */
2421590Srgrimes	for (ap = p = argv, np = nargv; *p; ++p)
2431590Srgrimes		if (index(*p, '@'))
2441590Srgrimes			*np++ = *p;
2451590Srgrimes		else
2461590Srgrimes			*ap++ = *p;
2471590Srgrimes
2481590Srgrimes	*np++ = NULL;
2491590Srgrimes	*ap++ = NULL;
2501590Srgrimes
2511590Srgrimes	if (!*argv)
2521590Srgrimes		goto net;
2531590Srgrimes
2541590Srgrimes	/*
2551590Srgrimes	 * Traverse the list of possible login names and check the login name
2561590Srgrimes	 * and real name against the name specified by the user.
2571590Srgrimes	 */
2581590Srgrimes	if (mflag)
2591590Srgrimes		for (p = argv; *p; ++p)
2605369Sjkh			if ((pw = getpwnam(*p)) && !hide(pw))
2611590Srgrimes				enter_person(pw);
2621590Srgrimes			else
2631590Srgrimes				(void)fprintf(stderr,
2641590Srgrimes				    "finger: %s: no such user\n", *p);
2651590Srgrimes	else {
2665369Sjkh		while (pw = getpwent()) {
2671590Srgrimes			for (p = argv, ip = used; *p; ++p, ++ip)
2689280Sache				if (match(pw, *p) && !hide(pw)) {
2691590Srgrimes					enter_person(pw);
2701590Srgrimes					*ip = 1;
2711590Srgrimes				}
2725369Sjkh		}
2731590Srgrimes		for (p = argv, ip = used; *p; ++p, ++ip)
2741590Srgrimes			if (!*ip)
2751590Srgrimes				(void)fprintf(stderr,
2761590Srgrimes				    "finger: %s: no such user\n", *p);
2771590Srgrimes	}
2781590Srgrimes
2791590Srgrimes	/* Handle network requests. */
2804991Spstnet:	for (p = nargv; *p;) {
2811590Srgrimes		netfinger(*p++);
2824991Spst		if (*p || entries)
2834991Spst		    printf("\n");
2844991Spst	}
2851590Srgrimes
2861590Srgrimes	if (entries == 0)
2871590Srgrimes		return;
2881590Srgrimes
2891590Srgrimes	/*
2901590Srgrimes	 * Scan thru the list of users currently logged in, saving
2911590Srgrimes	 * appropriate data whenever a match occurs.
2921590Srgrimes	 */
2931590Srgrimes	if (!freopen(_PATH_UTMP, "r", stdin))
2941590Srgrimes		err("%s: %s", _PATH_UTMP, strerror(errno));
2951590Srgrimes	while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
2961590Srgrimes		if (!user.ut_name[0])
2971590Srgrimes			continue;
2981590Srgrimes		if ((pn = find_person(user.ut_name)) == NULL)
2991590Srgrimes			continue;
3001590Srgrimes		enter_where(&user, pn);
3011590Srgrimes	}
3021590Srgrimes	if (db)
3031590Srgrimes		for (sflag = R_FIRST;; sflag = R_NEXT) {
3041590Srgrimes			r = (*db->seq)(db, &key, &data, sflag);
3051590Srgrimes			if (r == -1)
3061590Srgrimes				err("db seq: %s", strerror(errno));
3071590Srgrimes			if (r == 1)
3081590Srgrimes				break;
3091590Srgrimes			enter_lastlog(*(PERSON **)data.data);
3101590Srgrimes		}
3111590Srgrimes}
312