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