finger.c revision 72109
11722Sjkh/* 21722Sjkh * Copyright (c) 1989, 1993 31722Sjkh * The Regents of the University of California. All rights reserved. 41722Sjkh * 51722Sjkh * This code is derived from software contributed to Berkeley by 61722Sjkh * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. 71722Sjkh * 81722Sjkh * Redistribution and use in source and binary forms, with or without 91722Sjkh * modification, are permitted provided that the following conditions 101722Sjkh * are met: 111722Sjkh * 1. Redistributions of source code must retain the above copyright 121722Sjkh * notice, this list of conditions and the following disclaimer. 131722Sjkh * 2. Redistributions in binary form must reproduce the above copyright 141722Sjkh * notice, this list of conditions and the following disclaimer in the 151722Sjkh * documentation and/or other materials provided with the distribution. 161722Sjkh * 3. All advertising materials mentioning features or use of this software 171722Sjkh * must display the following acknowledgement: 181722Sjkh * This product includes software developed by the University of 191722Sjkh * California, Berkeley and its contributors. 201722Sjkh * 4. Neither the name of the University nor the names of its contributors 211722Sjkh * may be used to endorse or promote products derived from this software 221722Sjkh * without specific prior written permission. 231722Sjkh * 241722Sjkh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2556995Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2656995Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271722Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281722Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291722Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301722Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311722Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321722Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 338857Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341722Sjkh * SUCH DAMAGE. 3570884Sjoe */ 3670884Sjoe 3770884Sjoe/* 3870884Sjoe * Luke Mewburn <lm@rmit.edu.au> added the following on 940622: 3929453Scharnier * - mail status ("No Mail", "Mail read:...", or "New Mail ..., 4029453Scharnier * Unread since ...".) 4170124Sjoe * - 4 digit phone extensions (3210 is printed as x3210.) 4229453Scharnier * - host/office toggling in short format with -h & -o. 431722Sjkh * - short day names (`Tue' printed instead of `Jun 21' if the 4429453Scharnier * login time is < 6 days. 451722Sjkh */ 461722Sjkh 471722Sjkh#ifndef lint 481722Sjkhstatic const char copyright[] = 491722Sjkh"@(#) Copyright (c) 1989, 1993\n\ 501722Sjkh The Regents of the University of California. All rights reserved.\n"; 511722Sjkh#endif /* not lint */ 521722Sjkh 531722Sjkh#ifndef lint 541722Sjkh#if 0 551722Sjkhstatic char sccsid[] = "@(#)finger.c 8.5 (Berkeley) 5/4/95"; 561722Sjkh#endif 571722Sjkhstatic const char rcsid[] = 5870884Sjoe "$FreeBSD: head/usr.bin/finger/finger.c 72109 2001-02-06 20:13:48Z charnier $"; 5970884Sjoe#endif /* not lint */ 601722Sjkh 611722Sjkh/* 621722Sjkh * Finger prints out information about users. It is not portable since 631722Sjkh * certain fields (e.g. the full user name, office, and phone numbers) are 641722Sjkh * extracted from the gecos field of the passwd file which other UNIXes 6570884Sjoe * may not have or may use for other things. 6670884Sjoe * 6770884Sjoe * There are currently two output formats; the short format is one line 6870884Sjoe * per user and displays login name, tty, login time, real name, idle time, 6970884Sjoe * and either remote host information (default) or office location/phone 7070884Sjoe * number, depending on if -h or -o is used respectively. 7170884Sjoe * The long format gives the same information (in a more legible format) as 7270884Sjoe * well as home directory, shell, mail info, and .plan/.project files. 7370884Sjoe */ 7470884Sjoe 7570884Sjoe#include <db.h> 7693435Sluigi#include <err.h> 7770884Sjoe#include <pwd.h> 781722Sjkh#include <stdio.h> 791722Sjkh#include <stdlib.h> 801722Sjkh#include <string.h> 811722Sjkh#include <time.h> 821722Sjkh#include <unistd.h> 8368750Sjoe#include <utmp.h> 8470884Sjoe#include <locale.h> 8570884Sjoe 8670884Sjoe#include "finger.h" 871722Sjkh#include "pathnames.h" 881722Sjkh 891722SjkhDB *db; 901722Sjkhtime_t now; 9170884Sjoeint entries, lflag, mflag, pplan, sflag, oflag, Tflag; 9270884Sjoechar tbuf[1024]; 931722Sjkh 941722Sjkhstatic void loginlist __P((void)); 951722Sjkhstatic void usage __P((void)); 9670884Sjoestatic void userlist __P((int, char **)); 971722Sjkh 9870884Sjoeint 991722Sjkhoption(argc, argv) 1006696Sphk int argc; 1016696Sphk char **argv; 1021722Sjkh{ 1031722Sjkh int ch; 1041722Sjkh 1051722Sjkh optind = 1; /* reset getopt */ 1061722Sjkh 1071722Sjkh while ((ch = getopt(argc, argv, "lmpshoT")) != -1) 1081722Sjkh switch(ch) { 1091722Sjkh case 'l': 1101722Sjkh lflag = 1; /* long format */ 1111722Sjkh break; 1128857Srgrimes case 'm': 1131722Sjkh mflag = 1; /* force exact match of names */ 1141722Sjkh break; 1151722Sjkh case 'p': 1161722Sjkh pplan = 1; /* don't show .plan/.project */ 1171722Sjkh break; 1181722Sjkh case 's': 11970884Sjoe sflag = 1; /* short format */ 12070884Sjoe break; 1211722Sjkh case 'h': 12270884Sjoe oflag = 0; /* remote host info */ 12370884Sjoe break; 12470884Sjoe case 'o': 1258857Srgrimes oflag = 1; /* office info */ 12670884Sjoe break; 12770884Sjoe case 'T': 12870884Sjoe Tflag = 1; /* disable T/TCP */ 12970884Sjoe break; 13070884Sjoe case '?': 13170884Sjoe default: 13269413Sluigi usage(); 13370884Sjoe } 13470884Sjoe 13570884Sjoe return optind; 13670884Sjoe} 13770884Sjoe 13870884Sjoestatic void 13970884Sjoeusage() 14070884Sjoe{ 14170884Sjoe (void)fprintf(stderr, "usage: finger [-lmpshoT] [login ...]\n"); 14270884Sjoe exit(1); 14370884Sjoe} 1441722Sjkh 14570884Sjoeint 14670884Sjoemain(argc, argv) 14770884Sjoe int argc; 14870884Sjoe char **argv; 14970884Sjoe{ 15070113Sjoe int envargc, argcnt; 15170113Sjoe char *envargv[3]; 1521722Sjkh struct passwd *pw; 15370884Sjoe 15470884Sjoe if (getuid() == 0 || geteuid() == 0) { 15570884Sjoe if ((pw = getpwnam(UNPRIV_NAME)) && pw->pw_uid > 0) { 15670884Sjoe setgid(pw->pw_gid); 15770884Sjoe setuid(pw->pw_uid); 15870884Sjoe } else { 15970884Sjoe setgid(UNPRIV_UGID); 16070884Sjoe setuid(UNPRIV_UGID); 16170884Sjoe } 16270884Sjoe } 16370884Sjoe 16470884Sjoe (void) setlocale(LC_ALL, ""); 16570884Sjoe 16670884Sjoe /* remove this line to get remote host */ 16770884Sjoe oflag = 1; /* default to old "office" behavior */ 16870884Sjoe 16970884Sjoe /* 17070884Sjoe * Process environment variables followed by command line arguments. 17170884Sjoe */ 1721722Sjkh if ((envargv[1] = getenv("FINGER"))) { 1731722Sjkh envargc = 2; 17470884Sjoe envargv[0] = "finger"; 17570884Sjoe envargv[2] = NULL; 1761722Sjkh (void) option(envargc, envargv); 17770884Sjoe } 17870884Sjoe 1791722Sjkh argcnt = option(argc, argv); 18070884Sjoe argc -= argcnt; 18170884Sjoe argv += argcnt; 18270884Sjoe 1831722Sjkh (void)time(&now); 18470884Sjoe setpassent(1); 1851722Sjkh if (!*argv) { 18670884Sjoe /* 1871722Sjkh * Assign explicit "small" format if no names given and -l 18870884Sjoe * not selected. Force the -s BEFORE we get names so proper 18970884Sjoe * screening will be done. 19070884Sjoe */ 19170884Sjoe if (!lflag) 1921722Sjkh sflag = 1; /* if -l not explicit, force -s */ 19370884Sjoe loginlist(); 19470884Sjoe if (entries == 0) 1951722Sjkh (void)printf("No one logged on.\n"); 19670884Sjoe } else { 19770884Sjoe userlist(argc, argv); 19870884Sjoe /* 19970884Sjoe * Assign explicit "large" format if names given and -s not 20070884Sjoe * explicitly stated. Force the -l AFTER we get names so any 20170884Sjoe * remote finger attempts specified won't be mishandled. 20270884Sjoe */ 20370884Sjoe if (!sflag) 20470884Sjoe lflag = 1; /* if -s not explicit, force -l */ 20570124Sjoe } 2061722Sjkh if (entries) { 20770884Sjoe if (lflag) 20870884Sjoe lflag_print(); 20970884Sjoe else 2106696Sphk sflag_print(); 21170884Sjoe } 2121722Sjkh return (0); 21370884Sjoe} 2141722Sjkh 2151722Sjkhstatic void 2161722Sjkhloginlist() 2171722Sjkh{ 2181722Sjkh register PERSON *pn; 21970884Sjoe DBT data, key; 22070884Sjoe struct passwd *pw; 22170884Sjoe struct utmp user; 22270884Sjoe int r, sflag; 22370884Sjoe char name[UT_NAMESIZE + 1]; 2241722Sjkh 2251722Sjkh if (!freopen(_PATH_UTMP, "r", stdin)) 2261722Sjkh err(1, "%s", _PATH_UTMP); 2271722Sjkh name[UT_NAMESIZE] = '\0'; 2281722Sjkh while (fread((char *)&user, sizeof(user), 1, stdin) == 1) { 2291722Sjkh if (!user.ut_name[0]) 2301722Sjkh continue; 2311722Sjkh if ((pn = find_person(user.ut_name)) == NULL) { 2321722Sjkh bcopy(user.ut_name, name, UT_NAMESIZE); 2331722Sjkh if ((pw = getpwnam(name)) == NULL) 2341722Sjkh continue; 2351722Sjkh if (hide(pw)) 2368857Srgrimes continue; 2371722Sjkh pn = enter_person(pw); 2381722Sjkh } 2391722Sjkh enter_where(&user, pn); 2401722Sjkh } 24168750Sjoe if (db && lflag) 2421722Sjkh for (sflag = R_FIRST;; sflag = R_NEXT) { 2431722Sjkh PERSON *tmp; 2441722Sjkh 2451722Sjkh r = (*db->seq)(db, &key, &data, sflag); 2461722Sjkh if (r == -1) 2471722Sjkh err(1, "db seq"); 2481722Sjkh if (r == 1) 2491722Sjkh break; 25070884Sjoe memmove(&tmp, data.data, sizeof tmp); 25129453Scharnier enter_lastlog(tmp); 25270884Sjoe } 25370884Sjoe} 25470884Sjoe 25570884Sjoestatic void 25670884Sjoeuserlist(argc, argv) 25770884Sjoe register int argc; 2581722Sjkh register char **argv; 2591722Sjkh{ 2601722Sjkh register PERSON *pn; 2611722Sjkh DBT data, key; 2621722Sjkh struct utmp user; 26370884Sjoe struct passwd *pw; 26470884Sjoe int r, sflag, *used, *ip; 26570884Sjoe char **ap, **nargv, **np, **p; 26670884Sjoe FILE *conf_fp; 26770884Sjoe char conf_alias[LINE_MAX]; 2681722Sjkh char *conf_realname; 26970884Sjoe int conf_length; 27070884Sjoe 27170884Sjoe if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL || 2721722Sjkh (used = calloc(argc, sizeof(int))) == NULL) 27370884Sjoe err(1, NULL); 27470884Sjoe 27570884Sjoe /* Pull out all network requests. */ 27670884Sjoe for (ap = p = argv, np = nargv; *p; ++p) 27770884Sjoe if (index(*p, '@')) 2781722Sjkh *np++ = *p; 27970884Sjoe else 28070884Sjoe *ap++ = *p; 28170884Sjoe 28270884Sjoe *np++ = NULL; 28370884Sjoe *ap++ = NULL; 28470884Sjoe 28570884Sjoe if (!*argv) 28670884Sjoe goto net; 28770884Sjoe 28870884Sjoe /* 28970884Sjoe * Mark any arguments beginning with '/' as invalid so that we 29070884Sjoe * don't accidently confuse them with expansions from finger.conf 29170884Sjoe */ 29270884Sjoe for (p = argv, ip = used; *p; ++p, ++ip) 29370884Sjoe if (**p == '/') { 29470884Sjoe *ip = 1; 29570884Sjoe warnx("%s: no such user", *p); 29670884Sjoe } 29770884Sjoe 29870884Sjoe /* 29970884Sjoe * Traverse the finger alias configuration file of the form 30070884Sjoe * alias:(user|alias), ignoring comment lines beginning '#'. 30170884Sjoe */ 30270884Sjoe if ((conf_fp = fopen(_PATH_FINGERCONF, "r")) != NULL) { 30370884Sjoe while(fgets(conf_alias, sizeof(conf_alias), conf_fp) != NULL) { 30470884Sjoe conf_length = strlen(conf_alias); 30570884Sjoe if (*conf_alias == '#' || conf_alias[--conf_length] != '\n') 30670884Sjoe continue; 30770884Sjoe conf_alias[conf_length] = '\0'; /* Remove trailing LF */ 30870884Sjoe if ((conf_realname = strchr(conf_alias, ':')) == NULL) 30970884Sjoe continue; 31070884Sjoe *conf_realname = '\0'; /* Replace : with NUL */ 31170884Sjoe for (p = argv; *p; ++p) { 31270884Sjoe if (strcmp(*p, conf_alias) == NULL) { 31370884Sjoe if ((*p = strdup(conf_realname+1)) == NULL) { 31470884Sjoe err(1, NULL); 3151722Sjkh } 31670884Sjoe } 31770884Sjoe } 31870884Sjoe } 31970884Sjoe (void)fclose(conf_fp); 3201722Sjkh } 32170884Sjoe 3221722Sjkh /* 3231722Sjkh * Traverse the list of possible login names and check the login name 3241722Sjkh * and real name against the name specified by the user. If the name 3251722Sjkh * begins with a '/', try to read the file of that name instead of 3261722Sjkh * gathering the traditional finger information. 32770884Sjoe */ 3281722Sjkh if (mflag) 32970884Sjoe for (p = argv, ip = used; *p; ++p, ++ip) { 33070884Sjoe if (**p != '/' || *ip == 1 || !show_text("", *p, "")) { 3311722Sjkh if (((pw = getpwnam(*p)) != NULL) && !hide(pw)) 33270884Sjoe enter_person(pw); 33370884Sjoe else if (!*ip) 33470884Sjoe warnx("%s: no such user", *p); 33570884Sjoe } 33670884Sjoe } 33770884Sjoe else { 33870884Sjoe while ((pw = getpwent()) != NULL) { 33970884Sjoe for (p = argv, ip = used; *p; ++p, ++ip) 34070884Sjoe if (**p == '/' && *ip != 1 34170884Sjoe && show_text("", *p, "")) 34270884Sjoe *ip = 1; 34370884Sjoe else if (match(pw, *p) && !hide(pw)) { 34470884Sjoe enter_person(pw); 34570884Sjoe *ip = 1; 34670884Sjoe } 34770884Sjoe } 34870884Sjoe for (p = argv, ip = used; *p; ++p, ++ip) 34970884Sjoe if (!*ip) 35070884Sjoe warnx("%s: no such user", *p); 35170884Sjoe } 35270884Sjoe 3531722Sjkh /* Handle network requests. */ 3541722Sjkhnet: for (p = nargv; *p;) { 3551722Sjkh netfinger(*p++); 3561722Sjkh if (*p || entries) 3571722Sjkh printf("\n"); 35870884Sjoe } 3591722Sjkh 36070884Sjoe if (entries == 0) 36170884Sjoe return; 36270884Sjoe 36370884Sjoe /* 36470884Sjoe * Scan thru the list of users currently logged in, saving 36570884Sjoe * appropriate data whenever a match occurs. 36670884Sjoe */ 36770884Sjoe if (!freopen(_PATH_UTMP, "r", stdin)) 3681722Sjkh err(1, "%s", _PATH_UTMP); 3691722Sjkh while (fread((char *)&user, sizeof(user), 1, stdin) == 1) { 3701722Sjkh if (!user.ut_name[0]) 3711722Sjkh continue; 3721722Sjkh if ((pn = find_person(user.ut_name)) == NULL) 3731722Sjkh continue; 37470884Sjoe enter_where(&user, pn); 3751722Sjkh } 37670884Sjoe if (db) 37770884Sjoe for (sflag = R_FIRST;; sflag = R_NEXT) { 3781722Sjkh PERSON *tmp; 3791722Sjkh 3801722Sjkh r = (*db->seq)(db, &key, &data, sflag); 3811722Sjkh if (r == -1) 3821722Sjkh err(1, "db seq"); 38370884Sjoe if (r == 1) 3841722Sjkh break; 38570884Sjoe memmove(&tmp, data.data, sizeof tmp); 3861722Sjkh enter_lastlog(tmp); 38770884Sjoe } 38870884Sjoe} 38970884Sjoe