finger.c revision 220971
10SN/A/* 2157SN/A * Copyright (c) 1989, 1993 30SN/A * The Regents of the University of California. All rights reserved. 40SN/A * 50SN/A * This code is derived from software contributed to Berkeley by 60SN/A * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. 7157SN/A * 80SN/A * Redistribution and use in source and binary forms, with or without 9157SN/A * modification, are permitted provided that the following conditions 100SN/A * are met: 110SN/A * 1. Redistributions of source code must retain the above copyright 120SN/A * notice, this list of conditions and the following disclaimer. 130SN/A * 2. Redistributions in binary form must reproduce the above copyright 140SN/A * notice, this list of conditions and the following disclaimer in the 150SN/A * documentation and/or other materials provided with the distribution. 160SN/A * 4. Neither the name of the University nor the names of its contributors 170SN/A * may be used to endorse or promote products derived from this software 180SN/A * without specific prior written permission. 190SN/A * 200SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21157SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22157SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23157SN/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 240SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 250SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 260SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 270SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 280SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 290SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 300SN/A * SUCH DAMAGE. 310SN/A */ 320SN/A 330SN/A/* 340SN/A * Luke Mewburn <lm@rmit.edu.au> added the following on 940622: 350SN/A * - mail status ("No Mail", "Mail read:...", or "New Mail ..., 360SN/A * Unread since ...".) 370SN/A * - 4 digit phone extensions (3210 is printed as x3210.) 380SN/A * - host/office toggling in short format with -h & -o. 390SN/A * - short day names (`Tue' printed instead of `Jun 21' if the 400SN/A * login time is < 6 days. 410SN/A */ 420SN/A 430SN/A#ifndef lint 440SN/Astatic const char copyright[] = 450SN/A"@(#) Copyright (c) 1989, 1993\n\ 460SN/A The Regents of the University of California. All rights reserved.\n"; 470SN/A#endif /* not lint */ 480SN/A 490SN/A#if 0 500SN/A#ifndef lint 510SN/Astatic char sccsid[] = "@(#)finger.c 8.5 (Berkeley) 5/4/95"; 520SN/A#endif 530SN/A#endif 540SN/A 550SN/A#include <sys/cdefs.h> 560SN/A__FBSDID("$FreeBSD: head/usr.bin/finger/finger.c 220971 2011-04-23 14:19:26Z simon $"); 570SN/A 580SN/A/* 590SN/A * Finger prints out information about users. It is not portable since 600SN/A * certain fields (e.g. the full user name, office, and phone numbers) are 610SN/A * extracted from the gecos field of the passwd file which other UNIXes 620SN/A * may not have or may use for other things. 630SN/A * 640SN/A * There are currently two output formats; the short format is one line 650SN/A * per user and displays login name, tty, login time, real name, idle time, 660SN/A * and either remote host information (default) or office location/phone 670SN/A * number, depending on if -h or -o is used respectively. 680SN/A * The long format gives the same information (in a more legible format) as 690SN/A * well as home directory, shell, mail info, and .plan/.project files. 700SN/A */ 710SN/A 720SN/A#include <sys/types.h> 730SN/A#include <sys/socket.h> 740SN/A#include <db.h> 750SN/A#include <err.h> 760SN/A#include <pwd.h> 770SN/A#include <stdio.h> 780SN/A#include <stdlib.h> 790SN/A#include <string.h> 800SN/A#include <time.h> 810SN/A#include <unistd.h> 820SN/A#include <utmpx.h> 830SN/A#include <locale.h> 840SN/A 850SN/A#include "finger.h" 860SN/A#include "pathnames.h" 870SN/A 880SN/ADB *db; 890SN/Atime_t now; 900SN/Aint entries, gflag, kflag, lflag, mflag, pplan, sflag, oflag; 910SN/Asa_family_t family = PF_UNSPEC; 920SN/Aint d_first = -1; 930SN/Achar tbuf[1024]; 940SN/Aint invoker_root = 0; 950SN/A 960SN/Astatic void loginlist(void); 970SN/Astatic int option(int, char **); 980SN/Astatic void usage(void); 990SN/Astatic void userlist(int, char **); 1000SN/A 1010SN/Astatic int 1020SN/Aoption(int argc, char **argv) 1030SN/A{ 1040SN/A int ch; 1050SN/A 1060SN/A optind = 1; /* reset getopt */ 1070SN/A 1080SN/A while ((ch = getopt(argc, argv, "46gklmpsho")) != -1) 1090SN/A switch(ch) { 1100SN/A case '4': 1110SN/A family = AF_INET; 1120SN/A break; 1130SN/A case '6': 1140SN/A family = AF_INET6; 1150SN/A break; 1160SN/A case 'g': 1170SN/A gflag = 1; 1180SN/A break; 1190SN/A case 'k': 1200SN/A kflag = 1; /* keep going without utmp */ 1210SN/A break; 1220SN/A case 'l': 1230SN/A lflag = 1; /* long format */ 1240SN/A break; 1250SN/A case 'm': 1260SN/A mflag = 1; /* force exact match of names */ 1270SN/A break; 1280SN/A case 'p': 1290SN/A pplan = 1; /* don't show .plan/.project */ 1300SN/A break; 1310SN/A case 's': 1320SN/A sflag = 1; /* short format */ 1330SN/A break; 1340SN/A case 'h': 1350SN/A oflag = 0; /* remote host info */ 1360SN/A break; 1370SN/A case 'o': 1380SN/A oflag = 1; /* office info */ 1390SN/A break; 1400SN/A case '?': 1410SN/A default: 1420SN/A usage(); 1430SN/A } 1440SN/A 1450SN/A return optind; 1460SN/A} 1470SN/A 1480SN/Astatic void 1490SN/Ausage(void) 1500SN/A{ 1510SN/A (void)fprintf(stderr, 1520SN/A "usage: finger [-46gklmpsho] [user ...] [user@host ...]\n"); 1530SN/A exit(1); 1540SN/A} 1550SN/A 1560SN/Aint 1570SN/Amain(int argc, char **argv) 1580SN/A{ 1590SN/A int envargc, argcnt; 1600SN/A char *envargv[3]; 1610SN/A struct passwd *pw; 1620SN/A static char myname[] = "finger"; 1630SN/A 1640SN/A if (getuid() == 0 || geteuid() == 0) { 1650SN/A invoker_root = 1; 1660SN/A if ((pw = getpwnam(UNPRIV_NAME)) && pw->pw_uid > 0) { 1670SN/A if (setgid(pw->pw_gid) != 0) 1680SN/A err(1, "setgid()"); 1690SN/A if (setuid(pw->pw_uid) != 0) 1700SN/A err(1, "setuid()"); 1710SN/A } else { 1720SN/A if (setgid(UNPRIV_UGID) != 0) 1730SN/A err(1, "setgid()"); 1740SN/A if (setuid(UNPRIV_UGID) != 0) 1750SN/A err(1, "setuid()"); 1760SN/A } 1770SN/A } 1780SN/A 1790SN/A (void) setlocale(LC_ALL, ""); 1800SN/A 1810SN/A /* remove this line to get remote host */ 1820SN/A oflag = 1; /* default to old "office" behavior */ 1830SN/A 1840SN/A /* 1850SN/A * Process environment variables followed by command line arguments. 1860SN/A */ 1870SN/A if ((envargv[1] = getenv("FINGER"))) { 1880SN/A envargc = 2; 1890SN/A envargv[0] = myname; 1900SN/A envargv[2] = NULL; 1910SN/A (void) option(envargc, envargv); 1920SN/A } 1930SN/A 1940SN/A argcnt = option(argc, argv); 1950SN/A argc -= argcnt; 1960SN/A argv += argcnt; 1970SN/A 1980SN/A (void)time(&now); 1990SN/A setpassent(1); 2000SN/A if (!*argv) { 2010SN/A /* 2020SN/A * Assign explicit "small" format if no names given and -l 2030SN/A * not selected. Force the -s BEFORE we get names so proper 2040SN/A * screening will be done. 2050SN/A */ 2060SN/A if (!lflag) 2070SN/A sflag = 1; /* if -l not explicit, force -s */ 2080SN/A loginlist(); 2090SN/A if (entries == 0) 2100SN/A (void)printf("No one logged on.\n"); 2110SN/A } else { 2120SN/A userlist(argc, argv); 2130SN/A /* 2140SN/A * Assign explicit "large" format if names given and -s not 2150SN/A * explicitly stated. Force the -l AFTER we get names so any 2160SN/A * remote finger attempts specified won't be mishandled. 2170SN/A */ 2180SN/A if (!sflag) 2190SN/A lflag = 1; /* if -s not explicit, force -l */ 2200SN/A } 2210SN/A if (entries) { 2220SN/A if (lflag) 2230SN/A lflag_print(); 2240SN/A else 2250SN/A sflag_print(); 2260SN/A } 2270SN/A return (0); 2280SN/A} 2290SN/A 2300SN/Astatic void 2310SN/Aloginlist(void) 2320SN/A{ 2330SN/A PERSON *pn; 2340SN/A DBT data, key; 2350SN/A struct passwd *pw; 2360SN/A struct utmpx *user; 2370SN/A int r, sflag1; 2380SN/A 2390SN/A if (kflag) 2400SN/A errx(1, "can't list logins without reading utmp"); 2410SN/A 2420SN/A setutxent(); 2430SN/A while ((user = getutxent()) != NULL) { 2440SN/A if (user->ut_type != USER_PROCESS) 2450SN/A continue; 2460SN/A if ((pn = find_person(user->ut_user)) == NULL) { 2470SN/A if ((pw = getpwnam(user->ut_user)) == NULL) 2480SN/A continue; 2490SN/A if (hide(pw)) 2500SN/A continue; 2510SN/A pn = enter_person(pw); 2520SN/A } 2530SN/A enter_where(user, pn); 2540SN/A } 2550SN/A endutxent(); 2560SN/A if (db && lflag) 2570SN/A for (sflag1 = R_FIRST;; sflag1 = R_NEXT) { 2580SN/A PERSON *tmp; 2590SN/A 2600SN/A r = (*db->seq)(db, &key, &data, sflag1); 2610SN/A if (r == -1) 2620SN/A err(1, "db seq"); 2630SN/A if (r == 1) 2640SN/A break; 2650SN/A memmove(&tmp, data.data, sizeof tmp); 2660SN/A enter_lastlog(tmp); 2670SN/A } 2680SN/A} 2690SN/A 2700SN/Astatic void 2710SN/Auserlist(int argc, char **argv) 2720SN/A{ 2730SN/A PERSON *pn; 2740SN/A DBT data, key; 2750SN/A struct utmpx *user; 2760SN/A struct passwd *pw; 2770SN/A int r, sflag1, *used, *ip; 2780SN/A char **ap, **nargv, **np, **p; 2790SN/A FILE *conf_fp; 2800SN/A char conf_alias[LINE_MAX]; 2810SN/A char *conf_realname; 2820SN/A int conf_length; 2830SN/A 2840SN/A if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL || 2850SN/A (used = calloc(argc, sizeof(int))) == NULL) 2860SN/A err(1, NULL); 2870SN/A 2880SN/A /* Pull out all network requests. */ 289 for (ap = p = argv, np = nargv; *p; ++p) 290 if (index(*p, '@')) 291 *np++ = *p; 292 else 293 *ap++ = *p; 294 295 *np++ = NULL; 296 *ap++ = NULL; 297 298 if (!*argv) 299 goto net; 300 301 /* 302 * Mark any arguments beginning with '/' as invalid so that we 303 * don't accidently confuse them with expansions from finger.conf 304 */ 305 for (p = argv, ip = used; *p; ++p, ++ip) 306 if (**p == '/') { 307 *ip = 1; 308 warnx("%s: no such user", *p); 309 } 310 311 /* 312 * Traverse the finger alias configuration file of the form 313 * alias:(user|alias), ignoring comment lines beginning '#'. 314 */ 315 if ((conf_fp = fopen(_PATH_FINGERCONF, "r")) != NULL) { 316 while(fgets(conf_alias, sizeof(conf_alias), conf_fp) != NULL) { 317 conf_length = strlen(conf_alias); 318 if (*conf_alias == '#' || conf_alias[--conf_length] != '\n') 319 continue; 320 conf_alias[conf_length] = '\0'; /* Remove trailing LF */ 321 if ((conf_realname = strchr(conf_alias, ':')) == NULL) 322 continue; 323 *conf_realname = '\0'; /* Replace : with NUL */ 324 for (p = argv; *p; ++p) { 325 if (strcmp(*p, conf_alias) == 0) { 326 if ((*p = strdup(conf_realname+1)) == NULL) { 327 err(1, NULL); 328 } 329 } 330 } 331 } 332 (void)fclose(conf_fp); 333 } 334 335 /* 336 * Traverse the list of possible login names and check the login name 337 * and real name against the name specified by the user. If the name 338 * begins with a '/', try to read the file of that name instead of 339 * gathering the traditional finger information. 340 */ 341 if (mflag) 342 for (p = argv, ip = used; *p; ++p, ++ip) { 343 if (**p != '/' || *ip == 1 || !show_text("", *p, "")) { 344 if (((pw = getpwnam(*p)) != NULL) && !hide(pw)) 345 enter_person(pw); 346 else if (!*ip) 347 warnx("%s: no such user", *p); 348 } 349 } 350 else { 351 while ((pw = getpwent()) != NULL) { 352 for (p = argv, ip = used; *p; ++p, ++ip) 353 if (**p == '/' && *ip != 1 354 && show_text("", *p, "")) 355 *ip = 1; 356 else if (match(pw, *p) && !hide(pw)) { 357 enter_person(pw); 358 *ip = 1; 359 } 360 } 361 for (p = argv, ip = used; *p; ++p, ++ip) 362 if (!*ip) 363 warnx("%s: no such user", *p); 364 } 365 366 /* 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