Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

v47i143: rfinger - reimplementation of Berkeley finger, Part01/01

5 views
Skip to first unread message

John DiMarco

unread,
Apr 16, 1995, 3:00:00 AM4/16/95
to
Submitted-by: j...@cdf.toronto.edu (John DiMarco)
Posting-number: Volume 47, Issue 143
Archive-name: rfinger/part01
Environment: UNIX

This is a reimplementation of Berkeley finger, with some features removed
and others added.

Features removed:
- Displaying home directories and login shell. Not usually very
useful to remote users, and potentially useful to system crackers.
- Displaying the last login and mail reading time. While this
is useful to help people keep track of others' activities, it also
lets system crackers know which users are inactive. Further,
last login time is hard to extend to machine clusters without
some sort of external support, and last mail reading time can be
misleading if something (eg. a cron job) touches a mailbox.
- indicator of terminal line "mesg" status.
- Support for the horrible commas-in-gecos-field kludge that Berkeley
finger uses.

Features added:
- Support for a central auxiliary information file for user
information (eg. a phone directory).
- Support for using rwho data in addition to (or instead of) utmp.
- Improved portability and configurability.
- Enhanced set of command-line options.

The latest version of this program is available for anonymous ftp
from ftp.cdf.toronto.edu in the directory /pub/rfinger.

Bug reports, comments, and enhancements should be sent to the author.

John DiMarco <j...@cdf.toronto.edu> Office: EA201B
Computing Disciplines Facility Systems Manager Phone: 416-978-1928
University of Toronto Fax: 416-978-1931
http://www.cdf.toronto.edu/personal/jdd/jdd.html
-------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# Contents: README MANIFEST ANNOUNCEMENT COPYRIGHT Makefile config.h
# defs.h netfinger.c patchlevel.h rfinger.c rfinger.man utils.c
# utils.h
# Wrapped by kent@ftp on Sun Apr 16 18:16:30 1995
PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 1 (of 1)."'
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2094 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X$Id: README,v 1.6 1995/03/08 00:39:15 jdd Exp $
XMar 7, 1995 Version 1.1
X
XThis is a reimplementation of Berkeley finger, with some features removed
Xand others added.
X
XFeatures removed:
X - Displaying home directories and login shell. Not usually very
X useful to remote users, and potentially useful to system crackers.
X - Displaying the last login and mail reading time. While this
X is useful to help people keep track of others' activities, it also
X lets system crackers know which users are inactive. Further,
X last login time is hard to extend to machine clusters without
X some sort of external support, and last mail reading time can be
X misleading if something (eg. a cron job) touches a mailbox.
X - indicator of terminal line "mesg" status.
X - Support for the horrible commas-in-gecos-field kludge that Berkeley
X finger uses.
X
XFeatures added:
X - Support for a central auxiliary information file for user
X information (eg. a phone directory).
X - Support for using rwho data in addition to (or instead of) utmp.
X - Improved portability and configurability.
X - Enhanced set of command-line options.
X
XInstallation instructions:
X
X- Edit the Makefile according to the comments.
X- Edit config.h according to the comments (you can override the definitions
X in config.h from the Makefile if you wish).
X- If you've changed the default behaviour, you may wish to change the man page.
X- Type "make".
X- Type "make -n install" to see if you've got the installation paths correct.
X- If so, as root, type "make install" to install the program.
X- Type "make install.man" to install the manual page.
X
XThe latest version of this program is available for anonymous ftp
Xfrom ftp.cdf.toronto.edu in the directory /pub/rfinger.
X
XBug reports, comments, and enhancements should be sent to the author:
X
XJohn DiMarco <j...@cdf.toronto.edu> Office: EA201B
XComputing Disciplines Facility Systems Manager Phone: 416-978-1928
XUniversity of Toronto Fax: 416-978-1931
Xhttp://www.cdf.toronto.edu/personal/jdd/jdd.html
END_OF_FILE
if test 2094 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(426 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
XANNOUNCEMENT Announcement of rfinger
XCOPYRIGHT Copyright notice
XMANIFEST This file
XMakefile The makefile
XREADME READ THIS FILE FIRST
Xconfig.h Configuration header file
Xdefs.h Constant definitions header file
Xnetfinger.c support for fingering remote machines
Xpatchlevel.h Patch level/version
Xrfinger.man rfinger manual page
Xrfinger.c rfinger main source
Xutils.c misc utilities
Xutils.h header file for utils.c
END_OF_FILE
if test 426 -ne `wc -c <'MANIFEST'`; then
echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'ANNOUNCEMENT' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ANNOUNCEMENT'\"
else
echo shar: Extracting \"'ANNOUNCEMENT'\" \(1124 characters\)
sed "s/^X//" >'ANNOUNCEMENT' <<'END_OF_FILE'
XAnnouncing rfinger 1.1, a reimplementation of the Berkeley finger command.
X
XUnlike Berkeley finger, it is less revealing of user information.
XIn particular, it does not reveal:
X - user's home directory
X - user's login shell
X - time of last login
X - last time mailbox was accessed.
X
XFurther, it doesn't use the Berkeley finger's kludgy commas-in-gecos-field
Xconvention.
X
XFeatures provided by rfinger that are not found in Berkeley finger:
X - information about logged-in users on the local subnet is provided
X by making use of rwhod data files, if available.
X - additional per-user information from a central auxiliary information
X file can be made available.
X - improved portability and configurability.
X
XThe rfinger program is available for anonymous ftp from ftp.cdf.toronto.edu
Xas /pub/rfinger/rfinger-1.1.shar.gz.
X
XRegards,
X
XJohn
X--
XJohn DiMarco <j...@cdf.toronto.edu> Office: EA201B
XComputing Disciplines Facility Systems Manager Phone: 416-978-1928
XUniversity of Toronto Fax: 416-978-1931
Xhttp://www.cdf.toronto.edu/personal/jdd/jdd.html
END_OF_FILE
if test 1124 -ne `wc -c <'ANNOUNCEMENT'`; then
echo shar: \"'ANNOUNCEMENT'\" unpacked with wrong size!
fi
# end of 'ANNOUNCEMENT'
fi
if test -f 'COPYRIGHT' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'COPYRIGHT'\"
else
echo shar: Extracting \"'COPYRIGHT'\" \(917 characters\)
sed "s/^X//" >'COPYRIGHT' <<'END_OF_FILE'
X/*
X * Copyright University of Toronto 1994, 1995.
X * Written by John DiMarco
X *
X * Permission is granted to anyone to use this software for any purpose on
X * any computer system, and to alter it and redistribute it freely, subject
X * to the following restrictions:
X *
X * 1. The author and the University of Toronto are not responsible
X * for the consequences of use of this software, no matter how awful,
X * even if they arise from flaws in it.
X *
X * 2. The origin of this software must not be misrepresented, either by
X * explicit claim or by omission. Since few users ever read sources,
X * credits must appear in the documentation.
X *
X * 3. Altered versions must be plainly marked as such, and must not be
X * misrepresented as being the original software. Since few users
X * ever read sources, credits must appear in the documentation.
X *
X * 4. This notice may not be removed or altered.
X */
END_OF_FILE
if test 917 -ne `wc -c <'COPYRIGHT'`; then
echo shar: \"'COPYRIGHT'\" unpacked with wrong size!
fi
# end of 'COPYRIGHT'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(2359 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# rfinger Makefile
X# $Id: Makefile,v 1.4 1995/02/23 22:58:02 jdd Exp $
X
X# Your C compiler. eg. gcc -Wall
XCC = cc
X
X# Your rm command, for "make clean".
XRM = rm -f
X
X# Include directories. eg. -I/local/include. This is not normally required.
XINCLUDEDIRS =
X
X# Use -lsocket -lnsl for Solaris 2.x. Most other systems don't need
X# any special libraries here.
XLIBRARIES = -lsocket -lnsl
X
X# override definitions here, if desired, or in config.h. See config.h
X# for details.
XDEFS = -DINFOFILE=\"/etc/userlist\"
X
X# C compiler flags. You may wish to change the -g to a -O.
XCFLAGS = $(DEFS) -g $(INCLUDEDIRS)
X
X# Linker flags
XLDFLAGS =
X
X# Where your Berkeley install program lives. Try /usr/ucb/install for
X# Solaris 2.x, or /usr/bin/install for most Berkeley unixes.
XINSTALL = /usr/ucb/install
X
X# This is where the man page should be installed
XMANDIR = /local/man/man1
X
X# This is where the finger program should be installed
XUSRDIR = /local/bin
X
X# This is the name you want to call the installed finger program.
XINSTALLNAME = finger # or rfinger if you prefer
X
X# The mode you want the installed finger program to be.
XMODE = 0755
X
X# The man section you want the man page to reside in.
XMANSECTION = 1
X
X# The mode you want the installed man page to be
XMANMODE = 0644
X
X# The owner and group of the installed files.
XOWNER = root
XGROUP = staff
X
X# You shouldn't have to change any of the following definitions.
X
XLINTFLAGS = $(DEFS) -ha $(INCLUDEDIRS)
XSRCS = rfinger.c netfinger.c utils.c
XOBJS = $(SRCS:.c=.o)
X
Xall:: rfinger
X
Xrfinger: $(OBJS)
X $(RM) $@
X $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBRARIES)
X
X$(INSTALLNAME).1: rfinger.man
X sed "s/RFINGER/$(INSTALLNAME)/g" <rfinger.man >$(INSTALLNAME).1
X
Xlint:
X lint $(LINTFLAGS) $(SRCS)
Xlintport:
X lint -p $(LINTFLAGS) $(SRCS)
Xclean:
X $(RM) $(OBJS) rfinger $(INSTALLNAME).1
Xdepend:
X makedepend -- $(CFLAGS) -- $(SRCS)
X
Xinstall: rfinger
X $(INSTALL) -m $(MODE) -o $(OWNER) -g $(GROUP) rfinger $(USRDIR)/$(INSTALLNAME)
X
Xinstall.man: $(INSTALLNAME).1
X $(INSTALL) -m $(MANMODE) $(INSTALLNAME).1 $(MANDIR)/$(INSTALLNAME).$(MANSECTION)
X
Xshar:
X shar COPYRIGHT MANIFEST README ANNOUNCEMENT patchlevel.h Makefile \
X rfinger.man rfinger.c netfinger.c utils.c utils.h config.h \
X defs.h >rfinger.shar
X
X# DO NOT DELETE THIS LINE -- make depend depends on it.
X
Xrfinger.o: utils.h config.h defs.h
Xnetfinger.o: utils.h
Xutils.o: utils.h
END_OF_FILE
if test 2359 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'config.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'config.h'\"
else
echo shar: Extracting \"'config.h'\" \(3702 characters\)
sed "s/^X//" >'config.h' <<'END_OF_FILE'
X/*
X * config.h - configuration definitions
X *
X * Author: John DiMarco, University of Toronto, CDF
X * j...@cdf.toronto.edu
X *
X * $Id: config.h,v 1.1 1994/12/08 22:37:13 jdd Exp $
X */
X
X/* all the definitions here can be overridden in the Makefile instead. */
X
X/*
X * Whether or not the header should be printed for -s, -i, -q options
X */
X#ifndef SHOWHEADER
X#define SHOWHEADER TRUE
X#endif /* SHOWHEADER */
X
X/*
X * Whether or not the user's .project should be output
X */
X#ifndef SHOWPROJECT
X#define SHOWPROJECT TRUE
X#endif /* SHOWPROJECT */
X
X/*
X * Whether or not the user's .plan should be output
X */
X#ifndef SHOWPLAN
X#define SHOWPLAN TRUE
X#endif /* SHOWPLAN */
X
X/*
X * Default output format.
X * Allowable values: SHORT(-s), IDLE(-i), QUICK(-q), LONG(-l), UNKNOWN.
X * UNKNOWN means -s if no parameters, -l otherwise.
X */
X#ifndef FORMAT
X#define FORMAT UNKNOWN
X#endif /* FORMAT */
X
X/*
X * Whether or not the user name should be shown for short format.
X */
X#ifndef SHOWNAME
X#define SHOWNAME TRUE
X#endif /* SHOWNAME */
X
X/*
X * Whether or not the gecos field should be searched by default.
X * This can be very slow when NIS/NIS+ or the like is in use.
X */
X#ifndef MATCHGECOS
X#define MATCHGECOS TRUE
X#endif /* MATCHGECOS */
X
X/*
X * File for additional information. Lines in this file must be of the format
X * "login info". It is output only in long format.
X */
X#ifndef INFOFILE
X#define INFOFILE NULL
X#endif /* INFO */
X
X/*
X * Whether or not the password file should be grabbed and examined on
X * startup. finger will only grab it if necessary otherwise. Useful
X * if password files are small and NIS/NIS+ is not in use.
X */
X#ifndef GRABPASSWD
X#define GRABPASSWD FALSE
X#endif /* GRABPASSWD */
X
X/*
X * Where the information about current logins comes from.
X * Allowable values: NOTHING, UTMP (from utmp), RWHO (from rwho data), BOTH
X */
X#ifndef LOGININFO
X#define LOGININFO BOTH
X#endif /* LOGININFO */
X
X/*
X * Indicates what is reported for the local host name.
X * Allowable values: USELOCALHOST ("localhost"), USEHOSTNAME (nodename
X * as reported by uname), USENIL ("")
X */
X#ifndef LOCALHOSTNAME
X#define LOCALHOSTNAME USEHOSTNAME
X#endif /* LOCALHOSTNAME */
X
X/*
X * Directory where the rwho data files reside.
X */
X#ifndef RWHODIR
X#define RWHODIR "/usr/spool/rwho"
X#endif /* RWHODIR */
X
X/*
X * Prefix for the rwho data files. Adding RWHOPREFIX to the hostname should
X * indicate the name of the rwho data file corresponding to that host.
X */
X#ifndef RWHOPREFIX
X#define RWHOPREFIX "whod."
X#endif /* RWHOPREFIX */
X
X/*
X * Device prefix. Adding DEVPREFIX before the UTMP linename should point to
X * the device.
X */
X#ifndef DEVPREFIX
X#define DEVPREFIX "/dev"
X#endif /* DEVPREFIX */
X
X/*
X * "Down" interval. If the who file hasn't been updated in this number
X * of seconds, the machine is deemed to be down, and the file is ignored.
X */
X#ifndef DOWNIVL
X#define DOWNIVL 5*MINUTE
X#endif /* DOWNIVL */
X
X/*
X * Maximum finger specs. The maximum number of parameters to finger.
X */
X#ifndef MAXFSPEC
X#define MAXFSPEC 50
X#endif /* MAXFSPEC */
X
X/*
X * Size of the hash table. This should be a prime number bigger than the
X * number of users in your password file.
X */
X#ifndef HASHSIZE
X#define HASHSIZE 16127
X#endif /* HASHSIZE */
X
X/*
X * Name of the PLAN file
X */
X#ifndef PLAN
X#define PLAN ".plan"
X#endif /* PLAN */
X
X/*
X * Name of the PROJECT file
X */
X#ifndef PROJECT
X#define PROJECT ".project"
X#endif /* PROJECT */
X
X/*
X * Maximum line length in .plan and .project
X */
X#ifndef MAXLINELENGTH
X#define MAXLINELENGTH 1024
X#endif /* MAXLINELENGTH */
X
X/*
X * Maximum number of lines in .plan, 0 means no limit.
X */
X#ifndef MAXLINES
X#define MAXLINES 200
X#endif /* MAXLINES */
END_OF_FILE
if test 3702 -ne `wc -c <'config.h'`; then
echo shar: \"'config.h'\" unpacked with wrong size!
fi
# end of 'config.h'
fi
if test -f 'defs.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'defs.h'\"
else
echo shar: Extracting \"'defs.h'\" \(1691 characters\)
sed "s/^X//" >'defs.h' <<'END_OF_FILE'
X/*
X * defs.h - definitions
X *
X * Author: John DiMarco, University of Toronto, CDF
X * j...@cdf.toronto.edu
X *
X * $Id: defs.h,v 1.1 1994/12/08 22:37:19 jdd Exp $
X */
X
X/* defined formats */
X#define UNKNOWN -1
X#define SHORT 0 /* short: login, gecos, host tty, idle, logintime */
X#define LONG 1 /* long: everything */
X#define IDLE 2 /* idle: login, host, tty, logintime, idletime */
X#define QUICK 3 /* quick: login, host, tty, logintime */
X#define SHORT2 4 /* short format without gecos */
X
X/* values for logininfo */
X#define NOTHING 0 /* don't get current login information */
X#define RWHO 1 /* get current login information from rwho */
X#define UTMP 2 /* get current login information from utmp */
X#define BOTH 3 /* get current login information from rwho & utmp */
X
X/* output formats */
X#define LONGFORMAT "Login name: %-27.27s In real life: %s\n"
X#define INFOFORMAT "Additional Information: %s\n"
X#define IDLEFORMAT "%-8.8s %-20.20s %-8.8s %-16.16s %-22.22s\n"
X#define QUICKFORMAT "%-8.8s %-20.20s %-8.8s %-26.26s\n"
X#define SHORTFORMAT "%-8.8s %-24.24s %-20.20s %-8.8s %5.5s %-9.9s\n"
X#define SHORT2FORMAT "%-8.8s %-28.28s %-8.8s %5.5s %-26.26s\n"
X
X/* time periods in seconds */
X#define WEEK 604800
X#define DAY 86400
X#define HOUR 3600
X#define MINUTE 60
X
X/* values for localhostname */
X#define LOCALHOST "localhost"
X#define USENIL 0 /* output local hostname as "" */
X#define USELOCALHOST 1 /* output local hostname as LOCALHOST */
X#define USEHOSTNAME 2 /* output local hostname as its nodename */
X
X/* misc */
X#define WHITESPACE " ,\t\r\n" /* the comma here is my one concession to the
X * kludgy Berkeley gecos "name,office,phone"
X * convention. */
X#define EOL "\r\n"
END_OF_FILE
if test 1691 -ne `wc -c <'defs.h'`; then
echo shar: \"'defs.h'\" unpacked with wrong size!
fi
# end of 'defs.h'
fi
if test -f 'netfinger.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'netfinger.c'\"
else
echo shar: Extracting \"'netfinger.c'\" \(4031 characters\)
sed "s/^X//" >'netfinger.c' <<'END_OF_FILE'
X/*
X * netfinger.c - from berkeley finger 5.11 (Berkeley) 9/20/88
X *
X * Copyright (c) 1980 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley. The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifndef lint
Xstatic char rcsid[] = "$Id: netfinger.c,v 1.2 1994/12/08 22:38:32 jdd Exp $";
X#endif
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <ctype.h>
X#include <sys/param.h>
X#include <values.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <string.h>
X#include "utils.h"
X
Xextern int d;
X
Xint netfinger(name, large)
X char *name;
X int large;
X{
X char *host;
X struct hostent *hp;
X struct servent *sp;
X struct sockaddr_in sin;
X int ss;
X register FILE *f;
X register int c;
X register int lastc = '\0';
X
X if (name == NULL)
X return (0);
X host = strrchr(name, '@');
X if (host == NULL)
X return (0);
X *host++ = 0;
X hp = gethostbyname(host);
X if (hp == NULL) {
X static struct hostent def;
X static struct in_addr defaddr;
X static char *alist[1];
X static char namebuf[128];
X int inet_addr();
X
X defaddr.s_addr = inet_addr(host);
X if (defaddr.s_addr == (unsigned)-1) {
X (void)printf("unknown host: %s\n", host);
X return (1);
X }
X (void)strcpy(namebuf, host);
X def.h_name = namebuf;
X def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
X def.h_length = sizeof (struct in_addr);
X def.h_addrtype = AF_INET;
X def.h_aliases = 0;
X hp = &def;
X }
X sp = getservbyname("finger", "tcp");
X if (sp == 0) {
X (void)printf("tcp/finger: unknown service\n");
X return (1);
X }
X sin.sin_family = hp->h_addrtype;
X (void)memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
X sin.sin_port = sp->s_port;
X ss = socket(hp->h_addrtype, SOCK_STREAM, 0);
X if (ss < 0) {
X perror("socket");
X return (1);
X }
X (void)printf("[%s]\n", hp->h_name);
X (void)fflush(stdout);
X if (connect(ss, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
X perror("connect");
X (void)close(ss);
X return (1);
X }
X if (large) (void)write(ss, "/W ", 3);
X (void)write(ss, name, strlen(name));
X (void)write(ss, "\r\n", 2);
X f = fdopen(ss, "r");
X while ((c = getc(f)) != EOF) {
X switch(c) {
X case 0210:
X case 0211:
X case 0212:
X case 0214:
X c -= 0200;
X break;
X case 0215:
X c = '\n';
X break;
X }
X lastc = c;
X if (isprint(c) || isspace(c))
X (void)putchar(c);
X else
X (void)putchar(c ^ 100);
X }
X if (lastc != '\n')
X (void)putchar('\n');
X (void)fclose(f);
X return (1);
X}
END_OF_FILE
if test 4031 -ne `wc -c <'netfinger.c'`; then
echo shar: \"'netfinger.c'\" unpacked with wrong size!
fi
# end of 'netfinger.c'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(104 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
Xstatic char patchlevel[] = "rfinger Version 1.1 $Id: patchlevel.h,v 1.9 1995/03/08 00:40:46 jdd Exp $";
END_OF_FILE
if test 104 -ne `wc -c <'patchlevel.h'`; then
echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'rfinger.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rfinger.c'\"
else
echo shar: Extracting \"'rfinger.c'\" \(23926 characters\)
sed "s/^X//" >'rfinger.c' <<'END_OF_FILE'
X/*
X * rfinger.c - CDF finger
X *
X * Author: John DiMarco, University of Toronto, CDF
X * j...@cdf.toronto.edu
X */
X
X#ifndef lint
Xstatic char rcsid[] = "$Id: rfinger.c,v 1.13 1995/02/23 22:43:16 jdd Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/param.h>
X#include <values.h>
X#include <sys/types.h>
X#include <pwd.h>
X#include <sys/stat.h>
X#include <protocols/rwhod.h>
X#include <dirent.h>
X#include <fcntl.h>
X#include <search.h>
X#include <unistd.h>
X#include <string.h>
X#include <stdlib.h>
X#include <ctype.h>
X#include <time.h>
X#include <sys/utsname.h>
X#include <utmp.h>
X#ifndef USER_PROCESS
X#define ut_user ut_name
X#endif /* USER_PROCESS */
X#include <netdb.h>
X#include "utils.h"
X#include "patchlevel.h"
X#include "defs.h"
X#include "config.h"
X
X#ifndef hsearch
Xextern ENTRY *hsearch();
X#endif /* hsearch */
X
Xchar *progname;
Xint d;
X
Xstatic void finger();
Xextern int netfinger();
X
X#define PARAMETERS "[-I file] [-FHMPWfghilmnpqrsuw] [user|user@host]"
Xvoid usage()
X{
X (void)fprintf(stderr, "Usage: %s %s\n", progname, PARAMETERS);
X (void)exit(2);
X}
X
X/* option flags */
Xint showheader=SHOWHEADER; /* print a header for -q,-s,-i fmts */
Xint showproject=SHOWPROJECT; /* show the .project file */
Xint showplan=SHOWPLAN; /* show the .plan file */
Xint format=FORMAT; /* output format */
Xint showname=SHOWNAME; /* show the user's name (gecos) */
Xint matchgecos=MATCHGECOS; /* check arguments against gecos ? */
Xchar *infofile=INFOFILE; /* auxiliary information file */
Xint grabpasswd=GRABPASSWD; /* whether to precache password file */
Xint logininfo=LOGININFO; /* where login data is obtained */
Xint localhostname = USEHOSTNAME; /* how local hostname is printed */
Xstatic int minus_l=FALSE; /* Saw -l flag? */
X
X/* labour-saving flags */
Xint nopwlookup = FALSE; /* don't need password lookups */
Xint noidle = FALSE; /* don't need to compute idle time */
X
Xstatic struct utsname uts;
Xchar *hostname; /* name of the host we're running on */
X
X/* a logged-in user on a particular host:line */
Xstruct user {
X char *name; /* username */
X char *host; /* hostname */
X char *tty; /* ttyname */
X time_t login; /* login time */
X time_t idle; /* time idle */
X};
X
X/* user information stored in table */
Xstruct uinfo {
X char *name;
X char *gecos;
X int sawit;
X uid_t uid;
X char *dir;
X char *info;
X};
X
X/* user information list */
Xstruct ulist {
X struct uinfo *u;
X struct ulist *next;
X} *ulist = NUL(struct ulist *);
X
X
X/*
X * main - parse arguments and handle options
X */
Xint main(argc, argv)
Xint argc;
Xchar *argv[];
X{
X int c, i;
X int errflg = 0;
X extern int optind;
X int result = 0;
X extern char *optarg;
X char *fspec[MAXFSPEC+1];
X
X progname = argv[0];
X while ((c = getopt(argc, argv, "dI:mMgurnfFhHpPwWilqs")) != EOF)
X switch (c) {
X case 'd':
X ++d;
X break;
X case 'I':
X infofile=optarg;
X break;
X case 'm':
X matchgecos=FALSE;
X break;
X case 'M':
X matchgecos=TRUE;
X break;
X case 'g':
X grabpasswd=TRUE;
X break;
X
X /* determine where to get login information */
X case 'u':
X logininfo=UTMP;
X break;
X case 'r':
X logininfo=RWHO;
X break;
X case 'n':
X logininfo=NOTHING;
X break;
X
X /* determine what to show */
X case 'f':
X showheader=FALSE;
X break;
X case 'F':
X showheader=TRUE;
X break;
X case 'h':
X showproject=FALSE;
X break;
X case 'H':
X showproject=TRUE;
X break;
X case 'p':
X showplan=FALSE;
X break;
X case 'P':
X showplan=TRUE;
X break;
X case 'w':
X showname=FALSE;
X break;
X case 'W':
X showname=TRUE;
X break;
X
X /* select format type */
X case 'i':
X format=IDLE;
X break;
X case 'l':
X format=LONG;
X minus_l=TRUE;
X break;
X case 'q':
X format=QUICK;
X break;
X case 's':
X format=SHORT;
X break;
X
X default:
X errflg++;
X break;
X }
X if (errflg) {
X usage();
X }
X
X if(UNKNOWN==format){
X /* LONG if parameters used, SHORT otherwise */
X if(1<=argc-optind && *argv[optind]){
X format=LONG;
X } else {
X format=SHORT;
X }
X }
X
X if(!hcreate(HASHSIZE)){
X Error("Can't allocate enough memory");
X }
X
X if(!showname && SHORT==format) format=SHORT2;
X
X /*
X * Check the format to see if there's any work we can save.
X * If the output format doesn't report a particular datum, then why
X * compute it?
X */
X switch(format){
X case QUICK:
X noidle=TRUE;
X /* fall through */
X case IDLE:
X case SHORT2:
X nopwlookup=TRUE;
X break;
X }
X
X /*
X * Make sure showname can be used to distinguish between
X * SHORT and SHORT2 fmts
X */
X if(SHORT==format) showname=TRUE;
X if(SHORT2==format) showname=FALSE;
X
X uts.nodename[0]='\0';
X (void)uname(&uts);
X hostname=uts.nodename;
X
X if (optind >= argc){
X fspec[0]=NULL;
X finger(NUL(char **));
X } else {
X /*
X * Note: this code processes parameters in a different
X * order than berkeley finger: rather than doing them one
X * at a time as specified, it does all the network ones
X * first, then all the non-network ones.
X */
X for (i=0; optind < argc && i<MAXFSPEC; optind++){
X if(strchr(argv[optind], '@')){
X result += netfinger(argv[optind], minus_l);
X } else {
X fspec[i++]=argv[optind];
X }
X }
X fspec[i]=NULL;
X if(i)finger(fspec);
X }
X
X return(result);
X}
X
Xstatic char *printable_only(st)
Xchar *st;
X{
X unsigned int i;
X for(i=0;i<strlen(st);i++) if(!isprint(st[i])) st[i]=' ';
X return(st);
X}
X
Xstatic FILE *safeopen(uid, filename, type)
Xuid_t uid;
Xchar *filename, *type;
X{
X /*
X * Only accept files. No named pipes, no devices, no directories.
X * Otherwise some users will set up named pipes and attach programs
X * to them.
X */
X FILE *fp;
X struct stat sb;
X
X /* First stat is to prevent us from trying to open eg. a named pipe */
X if(0>stat(filename, &sb)) return(NUL(FILE *));
X if(!S_ISREG(sb.st_mode)) return(NUL(FILE *));
X
X if(NULL==(fp=fopen(filename, type))) return(NUL(FILE *));
X
X /*
X * Second stat (fstat) is to make sure things didn't change between
X * the first stat and the open.
X */
X if(0>fstat(fileno(fp), &sb)) { (void)fclose(fp); return(NUL(FILE *)); }
X if(!S_ISREG(sb.st_mode)) { (void)fclose(fp); return(NUL(FILE *)); }
X
X /*
X * Only accept files owned by the user being fingered. Otherwise
X * symlinking .plan/.project to a system file (eg /etc/password) will
X * give it to anyone who fingers that user.
X */
X if(uid!=sb.st_uid) { (void)fclose(fp); return(NUL(FILE *)); }
X
X return(fp);
X}
X
Xstatic void doproject(uid, homedir)
Xchar *homedir;
Xuid_t uid;
X{
X /* output the .project file */
X FILE *fp;
X char str[MAXLINELENGTH+1];
X
X if(NULL==(fp=safeopen(uid, cat(homedir, "/", PROJECT, NULL), "r"))){
X /* fail silently */
X return;
X }
X /* only one line of .project is printed */
X str[0]='\0';
X (void)fgets(str, sizeof(str), fp);
X (void)printf("Project: %s\n", printable_only(str));
X (void)fclose(fp);
X}
X
Xstatic void doplan(uid, homedir)
Xchar *homedir;
Xuid_t uid;
X{
X /* output the .plan file */
X FILE *fp;
X char str[MAXLINELENGTH+1];
X int linecount = 0;
X
X if(NULL==(fp=safeopen(uid, cat(homedir, "/", PLAN, NULL), "r"))){
X (void)printf("No Plan.\n");
X return;
X }
X (void)printf("Plan:\n");
X while(NULL!=fgets(str, sizeof(str), fp)){
X int l;
X l=strlen(str);
X if(l>1 && l<sizeof(str)-1) str[l-1]='\0'; /* zap trailing \n */
X (void)printf("%s\n", printable_only(str));
X linecount++;
X if(MAXLINES && linecount>=MAXLINES) break;
X }
X (void)fclose(fp);
X}
X
X/* indicate whether or not sub is found in full, case ignored. */
Xint foundin(sub, full)
Xchar *sub, *full;
X{
X char tmpbuf[1024];
X if(STREQ(sub, full)) {
X return(TRUE);
X } else {
X char *t;
X (void)strncpy(tmpbuf, full, sizeof(tmpbuf));
X tmpbuf[sizeof(tmpbuf)-1]='\0';
X for(t=strtok(tmpbuf, WHITESPACE);
X NULL!=t;
X t=strtok(NULL, WHITESPACE)){
X if(0==strcasecmp(sub, t)) return(TRUE);
X }
X }
X return(FALSE);
X}
X
X#ifndef USER_PROCESS
X#ifndef UTMP_FILE
X#define UTMP_FILE "/etc/utmp"
X#endif /* UTMP_FILE */
Xint utmpfd;
Xvoid setutent()
X{
X utmpfd=open(UTMP_FILE, O_RDONLY);
X}
Xstruct utmp *getutent()
X{
X int num, remaining;
X static struct utmp u;
X
X loop{
X remaining=sizeof(u);
X while(remaining && 0<(num=read(utmpfd, (char *)&u, remaining))){
X remaining -= num;
X }
X if(num<=0) return(NUL(struct utmp *));
X if(u.ut_name[0]) break;
X }
X return(&u);
X}
Xvoid endutent(){
X (void)close(utmpfd);
X}
X#endif /* !USER_PROCESS */
X
Xstatic struct user *getnextutmp(now)
Xtime_t now; /* should be the current time */
X{
X static struct user u;
X static struct utmp *ut = NUL(struct utmp *);
X static char linebuf[sizeof(ut->ut_line)+1];
X static char namebuf[sizeof(ut->ut_user)+1];
X static char hostbuf[MAXHOSTNAMELEN+1];
X static int all_done=FALSE;
X char tmpbuf[sizeof(DEVPREFIX)+sizeof(ut->ut_line)+2];
X
X if(all_done) return(NUL(struct user *));
X
X if(NULL==ut) {
X u.name=namebuf;
X u.tty=linebuf;
X u.host=hostbuf;
X switch(localhostname){
X case USELOCALHOST:
X (void)strcpy(hostbuf, LOCALHOST);
X break;
X case USEHOSTNAME:
X (void)strcpy(hostbuf, hostname);
X break;
X case USENIL:
X hostbuf[0]='\0';
X break;
X }
X
X setutent();
X }
X
X while(NULL!=(ut=getutent())){
X#ifdef USER_PROCESS
X if(USER_PROCESS == ut->ut_type) {
X#else /* !USER_PROCESS */
X {
X#endif /* !USER_PROCESS */
X struct stat sb;
X (void)strncpy(namebuf, ut->ut_user,
X sizeof(ut->ut_user));
X (void)strncpy(linebuf, ut->ut_line,
X sizeof(ut->ut_line));
X u.login = ut->ut_time;
X
X if(!noidle) {
X /* compute idle time by statting the line */
X (void)strcpy(tmpbuf, DEVPREFIX);
X (void)strcat(tmpbuf, "/");
X (void)strcat(tmpbuf, ut->ut_line);
X if(0>stat(tmpbuf, &sb)){
X u.idle = 0;
X } else {
X u.idle = now-sb.st_atime;
X if(u.idle<0) u.idle=0;
X }
X }
X
X return(&u);
X }
X }
X endutent();
X all_done=TRUE;
X return(NUL(struct user *));
X}
X
X/* Get the next user entry from whod. This is the whod version. */
Xstatic struct user *getnextwho(now)
Xtime_t now; /* should be the current time */
X{
X static DIR *dp = NUL(DIR *);
X static struct whod wd;
X static struct dirent *de;
X static struct user u;
X static int i = 0; /* index into whoent record */
X static char linebuf[sizeof(wd.wd_we[0].we_utmp.out_line)+1];
X static char namebuf[sizeof(wd.wd_we[0].we_utmp.out_name)+1];
X static char hostbuf[sizeof(wd.wd_hostname)+1];
X static int all_done = FALSE;
X char tmpbuf[sizeof(RWHODIR)+MAXNAMLEN+2];
X
X if(all_done) return(NUL(struct user *));
X
X if(NULL==dp) {
X /* directory hasn't been opened yet, so open it */
X if(NULL==(dp=opendir(RWHODIR))) return(NULL);
X u.name=namebuf;
X u.host=hostbuf;
X u.tty=linebuf;
X }
X if(NULL==de || !wd.wd_we[i].we_utmp.out_line[0]) {
X /*
X * Keep reading whod files until we find a good one, or
X * run out of them altogether.
X */
X i=0;
X loop{
X int fd;
X /* quit if we're out of directory entries */
X if(NULL==(de=readdir(dp))) {
X (void)closedir(dp);
X all_done=TRUE;
X return(NUL(struct user *));
X }
X
X if((UTMP&logininfo) &&
X STREQ(de->d_name+strlen(RWHOPREFIX), hostname)){
X /*
X * Skip this who file, which is the one
X * of the local host, because we're consulting
X * utmp, and utmp has a better opinion of
X * who is on the local host than rwho does.
X */
X continue;
X }
X
X /*
X * Open next whod file, read contents, and go on when
X * a "good" one is read. A "good" whod file is defined
X * as having the right whod version and type numbers,
X * plus at least one logged-in user, and it was
X * received less than DOWNIVL seconds ago.
X */
X (void)strcpy(tmpbuf, RWHODIR);
X (void)strcat(tmpbuf, "/");
X (void)strncat(tmpbuf, de->d_name, MAXNAMLEN);
X tmpbuf[sizeof(tmpbuf)-1]='\0';
X
X if(0<=(fd=open(tmpbuf, O_RDONLY))) {
X (void)memset(&wd, 0, sizeof(wd));
X if(read(fd, &wd, sizeof(wd))<sizeof(wd)){
X if(wd.wd_vers==WHODVERSION
X && wd.wd_type==WHODTYPE_STATUS
X && wd.wd_we[i].we_utmp.out_line[0]
X && wd.wd_recvtime >
X (now-DOWNIVL)){
X (void)close(fd);
X break;
X }
X }
X (void)close(fd);
X }
X }
X }
X /* This is a valid entry, return it.*/
X (void)strncpy(namebuf, wd.wd_we[i].we_utmp.out_name,
X sizeof(wd.wd_we[i].we_utmp.out_name));
X (void)strncpy(linebuf, wd.wd_we[i].we_utmp.out_line,
X sizeof(wd.wd_we[i].we_utmp.out_line));
X (void)strncpy(hostbuf, wd.wd_hostname, sizeof(wd.wd_hostname));
X u.login=(time_t)wd.wd_we[i].we_utmp.out_time;
X if(!noidle) {
X u.idle=(time_t)(wd.wd_we[i].we_idle/*+(now-wd.wd_recvtime)*/);
X }
X
X i++;
X if(i>=(1024/sizeof(struct whoent))) de=NUL(struct dirent *);
X return(&u);
X}
X
Xstatic struct user *getnextuser(now)
Xtime_t now; /* should be the current time */
X{
X struct user *u = NUL(struct user *);
X
X if(UTMP & logininfo) u=getnextutmp(now);
X if(u) return(u);
X if(RWHO & logininfo) u=getnextwho(now);
X return(u);
X}
X
X/* return string spelling out the time */
Xstatic char *verbosetime(l)
Xtime_t l;
X{
X static char timebuf[1024];
X
X timebuf[0]='\0';
X if(l/WEEK) {
X (void)sprintf(timebuf, "%ld week%s", l/WEEK, (l/WEEK)-1?"s":"");
X l-=WEEK*(l/WEEK);
X }
X if(l/DAY) {
X (void)sprintf(timebuf, "%s%s%ld day%s", timebuf,
X *timebuf?" ":"",
X l/DAY, (l/DAY)-1?"s":"");
X l-=DAY*(l/DAY);
X }
X if(l/HOUR) {
X (void)sprintf(timebuf, "%s%s%ld hour%s", timebuf,
X *timebuf?" ":"", l/HOUR, (l/HOUR)-1?"s":"");
X l-=HOUR*(l/HOUR);
X }
X if(l/MINUTE) {
X (void)sprintf(timebuf, "%s%s%ld minute%s", timebuf,
X *timebuf?" ":"", l/MINUTE, (l/MINUTE)-1?"s":"");
X l-=MINUTE*(l/MINUTE);
X }
X return(timebuf);
X}
X
Xvoid add_table(name, sawit, gecos, dir, uid, info)
Xchar *name, *gecos, *dir, *info;
Xuid_t uid;
Xint sawit;
X{
X ENTRY item, *found_item;
X struct uinfo *ui;
X struct ulist *ul;
X
X item.key = name;
X if(NULL!=(found_item = hsearch(item, FIND))){
X /*
X * It's already in the table. Just
X * override the appropriate data and
X * go on.
X */
X ui=(struct uinfo *)found_item->data;
X } else {
X /* initialize a new uinfo structure */
X ui = new(struct uinfo);
X ui->name = s(name);
X ui->gecos = NULL;
X ui->uid = (uid_t)0;
X ui->dir = NULL;
X ui->info = NULL;
X
X /* and a new ulist structure, and add it to the list */
X ul = new(struct ulist);
X ul->u = ui;
X ul->next = ulist;
X ulist=ul;
X
X /* finally, insert it into the hash table */
X item.key = ui->name;
X item.data = (char *)ui;
X (void)hsearch(item, ENTER);
X }
X ui->sawit = sawit;
X if(gecos) {
X if(NULL==ui->gecos || 0!=strcmp("???",gecos)){
X ui->gecos = s(gecos);
X }
X }
X if(uid) ui->uid = uid;
X if(dir) ui->dir = s(dir);
X if(info) ui->info = s(info);
X}
X
Xstruct uinfo *check_table(name)
Xchar *name;
X{
X ENTRY item, *found_item;
X
X item.key = name;
X if(NULL==(found_item = hsearch(item, FIND))){
X return(NUL(struct uinfo *));
X } else {
X return((struct uinfo *)found_item->data);
X }
X}
X
X/*
X * Determine if the specified login and/or gecos field matches. Returns
X * the index in fspec that matches, -1 if no match.
X */
Xint ismatch(fspec, login, gecos)
Xchar *fspec[], *login, *gecos;
X{
X int i;
X if(!fspec) return(0); /* trivially true if fspec is null */
X
X for(i=0;fspec[i];i++){
X if(login && STREQ(fspec[i], login)) return(i);
X if(!matchgecos || !gecos) continue;
X if(foundin(fspec[i], gecos)) return(i);
X }
X return(-1);
X}
X
X
X/* actually do the fingering of a particular user */
Xstatic void finger(fspec)
Xchar *fspec[]; /* NULL if all users */
X{
X struct passwd *pw;
X char *timestr, *tmp, tmpbuf1[1024], tmpbuf2[1024];
X long l;
X struct user *u;
X struct uinfo *ui;
X struct ulist *ul;
X time_t now;
X int i, firsttime=TRUE;
X
X now=time(NULL);
X
X if(LONG==format && infofile){
X /*
X * we're going to need to read the information file, so grab
X * and cache what we'll need. The file format is simple:
X * for each line, first token is the login name, the rest
X * is data.
X */
X FILE *fp;
X char line[1024], *login, *cname;
X if(NULL!=(fp=fopen(infofile, "r"))) {
X while(NULL!=fgets(line, sizeof(line)-1, fp)){
X if(NULL==(login=strtok(line, WHITESPACE))){
X continue; /* blank line */
X }
X if(NULL==(cname=strtok(NULL, EOL))){
X cname="???";
X }
X if(0>ismatch(fspec, login, cname)){
X continue;
X }
X add_table(login, FALSE, "???",
X NUL(char *), (uid_t)0, cname);
X }
X }
X }
X
X if(fspec) for(i=0;fspec[i];i++){
X /* Put ??? data in table in case no match can be made */
X add_table(fspec[i], FALSE, "???", NUL(char *),
X (uid_t)0, NUL(char *));
X }
X
X /*
X * Possibly precache (part of) the password file. There are four
X * interesting cases here.
X *
X * 1. grabpasswd is true. We _always_ precache the password file
X * if this is so. This is useful for sites with small password
X * files that resolve user names directly out of the password file.
X * It bloats finger dramatically for sites with large password
X * files in case 4 below, because the _whole_ password file will
X * be added to the table, and (additionally) slows it down on
X * sites that use NIS/NIS+ or the like, where gethostbynam is
X * much more efficient than getpwent'ing through the passwd file.
X *
X * 2. matchgecos is true, and arguments have been passed to the
X * finger program. Again, this requires us to compare the arguments
X * against the gecos field, which forces us to examine the entire
X * password map.
X *
X * 3. matchgecos is false, and arguments have been passed to finger.
X * Since they're logins, we can just look them up using
X * gethostbyname. Sites whose gethostbynam just looks through the
X * password file may want to set grabpasswd to force case 1, which
X * is usually more efficient when multiple arguments are passed to
X * finger.
X *
X * 4. matchgecos is false, and no arguments have been passed to the
X * finger program. In such a case, finger is only interested in
X * users who are logged in, and it'll do a gethostbynam on each
X * one, once. Again, sites whose gethostbynam just looks through
X * the password file may want to set grabpasswd.
X */
X
X if(grabpasswd || (matchgecos && fspec)) {
X /*
X * Case 1 or 2 above. Note: this slows down and/or bloats
X * finger if the password file is big or if something
X * like NIS or NIS+ is in use.
X */
X int tmp;
X setpwent();
X while(NULL!=(pw=getpwent())){
X if(0<=(tmp=ismatch(fspec, pw->pw_name, pw->pw_gecos))
X || NULL!=check_table(pw->pw_name) ){
X struct uinfo *ui;
X /*
X * Special case: if there's a match on the
X * gecos field, we don't want
X * the specified argument to
X * generate an "???" output
X * line.
X */
X if(fspec && tmp>=0
X && NULL!=(ui=check_table(fspec[tmp]))){
X if(ui->gecos && STREQ(ui->gecos, "???")
X && NULL==ui->info){
X add_table(fspec[tmp], TRUE,
X "???",
X NUL(char *),
X (uid_t)0,
X NUL(char *));
X }
X }
X add_table(pw->pw_name, FALSE, pw->pw_gecos,
X pw->pw_dir, pw->pw_uid, NUL(char *));
X }
X }
X endpwent();
X nopwlookup=TRUE; /* to tell later code not to gethostbynam */
X }
X if(!matchgecos && fspec && !nopwlookup){
X int i;
X /*
X * Case 3 above.
X */
X for(i=0;fspec[i];i++){
X if(NULL==(pw=getpwnam(fspec[i]))){
X add_table(fspec[i], FALSE, "???", NUL(char *),
X (uid_t)0, NUL(char *));
X } else {
X add_table(pw->pw_name, FALSE, pw->pw_gecos,
X pw->pw_dir, pw->pw_uid, NUL(char *));
X }
X }
X nopwlookup=TRUE; /* to tell later code not to gethostbynam */
X }
X
X /* print headers if applicable */
X if(showheader){
X switch(format){
X case SHORT:
X (void)printf(SHORTFORMAT, "Login", "Name", "Host",
X "TTY", "Idle", "LoginTime");
X break;
X case IDLE:
X (void)printf(IDLEFORMAT, "Login", "Host", "TTY",
X "LoginTime", "Idle");
X break;
X case QUICK:
X (void)printf(QUICKFORMAT, "Login", "Host", "TTY",
X "LoginTime");
X break;
X case SHORT2:
X (void)printf(SHORT2FORMAT, "Login", "Host", "TTY",
X "Idle", "LoginTime");
X break;
X }
X }
X
X /* look through the who records */
X while(NULL!=(u=getnextuser(now))){
X struct uinfo *ui;
X
X if(NULL==(ui=check_table(u->name))
X || NULL==ui->gecos){
X if(0<=ismatch(fspec, u->name, NUL(char *))){
X /*
X * Hmm, this user's password entry evidently
X * hasn't been seen yet, since either there's
X * no entry in the table, or the entry
X * doesn't have a gecos field.
X */
X if(nopwlookup) {
X /*
X * Either we've already snarfed the
X * password file, or we don't need
X * to look up the user at all.
X */
X add_table(u->name, FALSE, "???",
X ui?ui->dir:NULL,
X ui?ui->uid:0,
X ui?ui->info:NULL);
X } else {
X if(NULL==(pw=getpwnam(u->name))){
X add_table(u->name, FALSE, "???",
X ui?ui->dir:NULL,
X ui?ui->uid:0,
X ui?ui->info:NULL);
X } else {
X add_table(u->name, FALSE,
X pw->pw_gecos,
X pw->pw_dir,
X pw->pw_uid,
X ui?ui->info:NULL);
X }
X }
X if(NULL==(ui=check_table(u->name))){
X /*
X * Since it was just added, if it's
X * not there, there's something
X * wrong with the hash table.
X */
X Error(
X"The internal hash table size of %d is too small. \n\
XPlease recompile %s with a larger value for HASHSIZE.",
X HASHSIZE, progname);
X }
X } else {
X continue;
X }
X }
X
X /* if we've gotten this far, we're interested in this user */
X
X /* compute the user's login time */
X timestr=ctime((time_t *)&u->login);
X l=strlen(timestr)-1;
X if(timestr[l]=='\n') timestr[l]='\0';
X
X switch(format){
X case(LONG):
X switch(format){
X case LONG:
X if(firsttime) {
X firsttime=FALSE;
X } else {
X (void)printf("\n");
X }
X (void)printf(LONGFORMAT, u->name, ui->gecos);
X if(!ui->sawit && infofile && ui->info) {
X (void)printf(INFOFORMAT, ui->info);
X }
X if(!ui->sawit && NULL!=ui->dir){
X if(showproject) doproject(ui->uid,
X ui->dir);
X if(showplan) doplan(ui->uid, ui->dir);
X }
X break;
X }
X (void)printf("On since %s on %s%s%s\n",
X timestr, u->host, *u->host?":":"", u->tty);
X tmp=verbosetime(u->idle);
X if(*tmp) (void)printf("%s Idle Time\n", tmp);
X break;
X case(SHORT):
X case(SHORT2):
X if(u->idle > DAY){
X (void)sprintf(tmpbuf1, "%lud", u->idle/DAY);
X } else if(u->idle>HOUR){
X (void)sprintf(tmpbuf1, "%lu:%.2lu",
X u->idle/HOUR,
X (u->idle%HOUR)/MINUTE);
X } else if(u->idle>MINUTE){
X (void)sprintf(tmpbuf1, "%lu", u->idle/MINUTE);
X } else {
X tmpbuf1[0]='\0';
X }
X if(showname){
X (void)sprintf(tmpbuf2, "%c%c%c %c%c:%c%c",
X timestr[0], timestr[1],
X timestr[2],
X timestr[11], timestr[12],
X timestr[14], timestr[15]);
X (void)printf(SHORTFORMAT, u->name,
X ui->gecos, u->host,
X u->tty, tmpbuf1, tmpbuf2);
X } else {
X (void)printf(SHORT2FORMAT, u->name, u->host,
X u->tty, tmpbuf1, timestr);
X }
X break;
X case(IDLE):
X (void)printf(IDLEFORMAT, u->name, u->host, u->tty,
X timestr, verbosetime(u->idle));
X break;
X case(QUICK):
X (void)printf(QUICKFORMAT, u->name, u->host, u->tty,
X timestr);
X break;
X }
X ui->sawit = TRUE;
X }
X
X if(NULL==fspec) return; /* stop here if no user specified */
X
X /* finally deal with the specified users that aren't logged in */
X for(ul=ulist;NULL!=ul;ul=ul->next){
X ui=ul->u;
X if(ui->sawit) continue; /* don't bother with ones we've done */
X
X switch(format){
X case(LONG):
X if(firsttime) {
X firsttime=FALSE;
X } else {
X (void)printf("\n");
X }
X (void)printf(LONGFORMAT, ui->name, ui->gecos);
X if(infofile && ui->info) {
X (void)printf(INFOFORMAT, ui->info);
X }
X if(NULL!=ui->dir){
X if(showproject) doproject(ui->uid, ui->dir);
X if(showplan) doplan(ui->uid, ui->dir);
X }
X break;
X case(SHORT):
X case(SHORT2):
X if(showname) {
X (void)printf(SHORTFORMAT, ui->name, ui->gecos,
X "-", "-", "", "Not On");
X } else {
X (void)printf(SHORT2FORMAT, ui->name, "-",
X "-", "", "Not Logged On");
X }
X break;
X case(IDLE):
X (void)printf(IDLEFORMAT, ui->name, "-", "-",
X "Not Logged On", "");
X break;
X case(QUICK):
X (void)printf(QUICKFORMAT, ui->name, "-", "-",
X "Not Logged On");
X break;
X }
X }
X}
END_OF_FILE
if test 23926 -ne `wc -c <'rfinger.c'`; then
echo shar: \"'rfinger.c'\" unpacked with wrong size!
fi
# end of 'rfinger.c'
fi
if test -f 'rfinger.man' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rfinger.man'\"
else
echo shar: Extracting \"'rfinger.man'\" \(5686 characters\)
sed "s/^X//" >'rfinger.man' <<'END_OF_FILE'
X.TH RFINGER 1 "December 8, 1994"
X.SH RFINGER
XRFINGER \- display information about remote and local users
X.SH SYNOPSIS
X.B RFINGER
X[
X.B \-I
X.I file
X]
X[
X.B \-FHMPWfghilmnpqrsuw
X]
X[
X.I user[@host]
X]
X.SH DESCRIPTION
XThe
X.B RFINGER
Xcommand displays information about the users specified on the command
Xline, or if no users are specified, about users who are currently logged in
Xto the local machine and/or the local cluster.
XThis information can include the login name, user name (from the gecos field
Xin the password file), and the contents of the user's
X.I .plan
Xand
X.I .project
Xfiles in their home directory. For users that are logged in,
X.B RFINGER
Xcan report the name of the terminal line(s) being used, the login time,
Xand if applicable, the amount of time the user has been idle on each line.
X.PP
XInformation about logged-in users on the local machine is obtained from the
Xutmp file,
Xand information about logged-in users on other machines on the local network
Xis obtained from the rwho database. Information obtained from rwho can be
Xup to five minutes out of date.
X.PP
XThe
X.B RFINGER
Xprogram supports the use of a centralized information file for users. It
Xexamines this file for a line beginning with the user's login, and if present,
Xand if long format is in use, outputs this line as additional information.
XThe name of the information file can be specified on the command line.
X.PP
XIf the form
X.B user@host
Xis used, the program uses the
X.I finger user information protocol
Xto query the remote host for information about the user.
X.PP
X.SH OPTIONS
X.IP \fB\-F\fP
XPrint a header for modes that produce output in tabular format (all non-long
Xformats). This is normally the default.
X.IP \fB\-f\fP
XDisable printing the header for modes that produce output in tabular format
X(all non-long formats).
X.IP \fB\-H\fP
XIf long format is selected, show the project file. Only the first
Xline of the project file is shown. This is normally the default.
X.IP \fB\-h\fP
XDo not show the project file.
X.IP \fB\-P\fP
XIf long format is selected, show the plan file. This is normally the default.
X.IP \fB\-p\fP
XDo not show the plan file.
X.IP \fB\-M\fP
XMatch parameters against the gecos field (i.e. the "real name") in the password
Xfile as well as the login name. This is normally the default.
X.IP \fB\-m\fP
XDo not match parameters against the gecos field. This means that
X.B RFINGER
Xassumes that all specified parameters are login names. This can make RFINGER
Xrun faster (sometimes much faster) in some situations.
X.IP \fB\-W\fP
XIf short format is being used, show the "real name" (i.e. the gecos
Xfield). This is normally the default.
X.IP \fB\-w\fP
XIf short format is being used, do not show the "real name" (i.e. the gecos
Xfield). This can make RFINGER run faster in some situations.
X.IP \fB\-g\fP
XGrab the entire password map before doing anything else. This can speed
Xup
X.B RFINGER
Xif passwords are being resolved from a password file (rather than from
Xa network database of some sort).
X.IP \fB\-u\fP
XObtain information about active users from the utmp file. The utmp file only
Xprovides
Xinformation about users who are logged into the local machine. This is
Xnormally the default for local users.
X.IP \fB\-r\fP
XObtain information about active users from the rwho database. This provides
Xinformation about users on other machines on the local network, and uses
Xthe same data as the
X.B rwho
Xcommand. This is normally the default for users on other machines.
X.IP \fB\-n\fP
XDo not obtain information about logged-in users.
X.IP \fB\-l\fP
XOutput information in long format. Several lines are output for each user,
Xand for each login. This is normally the default when user logins or names
Xare specified.
X.IP \fB\-s\fP
XOutput information in short format. One line is printed per user login,
Xindicating the login name, real name (from the gecos field in the password
Xfile), the host name the user is logged in on, the terminal line on that
Xhost, the amount of time the user has been idle, if any, and the user's
Xlogin time.
X.IP \fB\-i\fP
XOutput information in idle format. This is similar to short format, except
Xthe user's real name is not printed, and the amount of time the user has
Xbeen idle is spelled out.
X.IP \fB\-q\fP
XOutput information in quick format. This is similar to idle format, except
Xthe amount of time the user has been idle is not printed. The
X.B RFINGER
Xprogram usually runs quickest when this format is selected.
X.IP \fB\-I\ file\fP
XUse the specified file as the centralized information file.
X.SH FILES
X/etc/utmp, /usr/spool/rwho, /etc/passwd, ~/.plan, ~/.project
X.SH SEE ALSO
Xpasswd(1), who(1), rwho(1), rwhod(8), fingerd(8)
X.SH HISTORY
XWritten by John DiMarco at the University of Toronto, CDF
X.SH LIMITATIONS
XOnly the first line of the .project file is printed.
X.PP
XThe
X.B finger user information protocol
Xpermits only the
X.B \-l
Xoption to be passed to a remote machine.
X.PP
XThe
X.B RFINGER
Xprogram will not be able to report the contents of the
X.I .plan
Xor
X.I .project
Xfiles unless it is running as a user with sufficient privileges to read those
Xfiles. As
X.B RFINGER
Xusually runs without privileges, these files should normally be readable by
X.I other
Xand the user's home directory should be accessible by
X.I other
X(see chmod(1)).
X.PP
XLogin information derived from rwho data may be up to five minutes out of date.
X.PP
XThe comma convention used by Berkeley finger to structure the gecos
Xfield in the password file is not supported.
X.PP
XUnlike other finger programs, (eg. Berkeley finger) this
Xprogram makes no attempt to figure out terminal line mesg status, or
Xindicate when users were last logged on or last read/received mail.
END_OF_FILE
if test 5686 -ne `wc -c <'rfinger.man'`; then
echo shar: \"'rfinger.man'\" unpacked with wrong size!
fi
# end of 'rfinger.man'
fi
if test -f 'utils.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'utils.c'\"
else
echo shar: Extracting \"'utils.c'\" \(5207 characters\)
sed "s/^X//" >'utils.c' <<'END_OF_FILE'
X/*
X * utils.c - Miscellaneous general-purpose utilities.
X *
X * Author: John DiMarco, University of Toronto, CDF
X * j...@cdf.toronto.edu
X */
X
X#ifndef lint
Xstatic char rcsid[] = "$Id: utils.c,v 1.1 1994/12/08 22:39:09 jdd Exp $";
X#endif
X
X#include <stdio.h>
X#include <varargs.h>
X#include <stdlib.h>
X#include <unistd.h>
X
X#ifndef SYSV
X/* trick for figuring out whether or not we're on SYSV or not */
X#ifdef FILENAME_MAX
X#define SYSV
X#endif /* FILENAME_MAX */
X#endif /* !SYSV */
X
X#ifdef SYSV
X#include <string.h>
X#else /* !SYSV */
X#include <strings.h>
X#endif /* SYSV */
X#include <errno.h>
X#include <time.h>
X#include "utils.h"
X
X#ifdef lint
X#undef va_arg
X#define va_arg(x,y) (y)NULL
X#endif
X
Xextern char *progname;
Xextern int d;
X
X/*
X * Error(): behaves like fprintf(stderr, ...) followed by exit(2), except
X * that the 'programname: ' preceeds the print, and a newline
X * follows it.
X */
X/*VARARGS*/
Xvoid Error(va_alist)
Xva_dcl
X{
X va_list args;
X char *format;
X
X va_start(args);
X format = va_arg(args, char *);
X (void)fprintf(stderr, "%s: ", progname);
X (void)vfprintf(stderr, format, args);
X (void)fprintf(stderr, "\n");
X va_end(args);
X (void)exit(2);
X}
X
X/*
X * dfprintf(): behaves like fprintf, except the first argument must be a
X * debugging level. The message will only be printed if "d"
X * is equal to or greater than the debugging level.
X */
X/*VARARGS*/
Xvoid dfprintf(va_alist)
Xva_dcl
X{
X va_list args;
X int debugLevel;
X FILE *stream;
X char *format;
X
X va_start(args);
X debugLevel = va_arg(args, int);
X stream = va_arg(args, FILE *);
X format = va_arg(args, char *);
X if(d >= debugLevel){
X (void)vfprintf(stream, format, args);
X }
X va_end(args);
X}
X
X/*
X * Warning(): behaves like Error, except returns rather than exits.
X */
X/*VARARGS*/
Xvoid Warning(va_alist)
Xva_dcl
X{
X va_list args;
X char *format;
X
X va_start(args);
X format = va_arg(args, char *);
X (void)fprintf(stderr, "%s: ", progname);
X (void)vfprintf(stderr, format, args);
X (void)fprintf(stderr, "\n");
X va_end(args);
X}
X
XFILE *efopen(file, mode)
Xchar *file, *mode;
X{
X FILE *fp;
X if (NULL!=(fp=fopen(file,mode)))
X return(fp);
X Error("can't open file \"%s\" mode \"%s\"", file, mode);
X return(NUL(FILE *));
X}
X
Xvoid efclose(f)
XFILE *f;
X{
X if(EOF==fclose(f))
X Error("can't close file");
X /*NOTREACHED*/
X}
X
X/*
X * mylib_malloc(): Checks if it gets a NULL pointer, calls Error if so.
X */
Xchar *mylib_malloc(size, file, line)
Xunsigned size;
Xchar *file;
Xint line;
X{
X char *result;
X
X result = malloc(size);
X if(NULL==result){
X Error("Out of memory at line %d in \"%s\".", line, file);
X }
X return(result);
X}
X
X/*
X * mylib_realloc(): Checks if it gets a NULL pointer, calls Error if so.
X */
Xchar *mylib_realloc(ptr, size, file, line)
Xchar *ptr, *file;
Xunsigned size;
Xint line;
X{
X char *result;
X
X result = realloc(ptr, size);
X if(NULL==result){
X Error("Out of memory at line %d in \"%s\".", line, file);
X }
X return(result);
X}
X
X/*
X * mylib_scopy(): Takes a string and creates a new physical copy of it.
X */
Xchar *mylib_scopy(string, file, line)
Xchar *string, *file;
Xint line;
X{
X char *result;
X
X result = malloc((unsigned)strlen(string)+1);
X
X if(NULL==result){
X Error("Out of memory at line %d in \"%s\".", line, file);
X }
X (void)strcpy(result, string);
X return(result);
X}
X
X/*
X * mylib_srcopy(): Reallocs first string to make room for second, copies it.
X */
Xchar *mylib_srcopy(s1, s2, file, line)
Xchar *s1, *s2, *file;
Xint line;
X{
X s1=mylib_realloc(s1, (unsigned)strlen(s2)+1, file, line);
X (void)strcpy(s1, s2);
X return(s1);
X}
X
X/*
X * cat(): Take a list of strings, followed by NULL, return their concatenation
X * in malloc'ed space.
X */
X/*VARARGS*/
Xchar *cat(va_alist)
Xva_dcl
X{
X va_list args;
X unsigned length=1;
X char *str, *newstr;
X
X /* get length */
X va_start(args);
X loop{
X str = va_arg(args, char *);
X if(NULL!=str){
X length+=strlen(str);
X } else {
X break;
X }
X }
X va_end(args);
X
X newstr=malloc(length);
X if(NULL==newstr) Error("Out of memory in cat()");
X
X newstr[0]=(char)0;
X
X /* create string */
X va_start(args);
X loop{
X str = va_arg(args, char *);
X if(NULL!=str) {
X (void)strcat(newstr, str);
X } else {
X break;
X }
X }
X va_end(args);
X#ifdef lint
X args=args; /* make lint shut up about "args set but not used" */
X#endif
X return(newstr);
X}
X
X/*
X * getstr(): read a string of arbitrary length from the given file
X * descriptor into malloc'ed memory, up to (but not including)
X * the next newline. Return a pointer to the string; NULL if EOF.
X */
Xchar *getstr(fd)
XFILE *fd;
X{
X int buffsize = 128; /* buffer size */
X char *result, *tmp;
X unsigned int length = 0; /* length of string read */
X int last;
X
X result = mem(buffsize+1);
X
X loop{
X tmp = result+length;
X if(NULL==fgets(tmp, buffsize+1, fd)){
X /* no more characters to read; EOF reached */
X if(tmp==result){
X /* we never read anything! */
X free(result);
X return(NULL);
X }
X break;
X } else {
X last = strlen(tmp);
X if('\n'==*(tmp+last-1)){
X /* found a newline */
X *(tmp+last-1)='\0';
X break;
X }
X /* still more to read */
X length += buffsize;
X result = rmem(result, length+buffsize);
X }
X }
X /* trim off excess buffer */
X result = rmem(result, strlen(result)+1);
X return(result);
X}
X
END_OF_FILE
if test 5207 -ne `wc -c <'utils.c'`; then
echo shar: \"'utils.c'\" unpacked with wrong size!
fi
# end of 'utils.c'
fi
if test -f 'utils.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'utils.h'\"
else
echo shar: Extracting \"'utils.h'\" \(2123 characters\)
sed "s/^X//" >'utils.h' <<'END_OF_FILE'
X/*
X * utils.h - header file for utils.c
X *
X * Author: John DiMarco, University of Toronto, CDF
X * j...@cdf.toronto.edu
X *
X * $Id: utils.h,v 1.2 1994/12/12 22:31:22 jdd Exp $
X */
X
X#ifndef __utils_h
X#define __utils_h
X
X#include <sys/syslog.h>
X
X/* new - mallocs any sized type */
X#ifndef lint
X#define new(t) (t *)mylib_malloc(sizeof(t),__FILE__,__LINE__)
X#else
X#define new(t) (t *)NULL
X#endif
X
X/* mem - mallocs a given number of bytes */
X#define mem(l) mylib_malloc((unsigned)l,__FILE__,__LINE__)
X
X/* rmem - reallocs a given number of bytes */
X#define rmem(c,l) mylib_realloc(c,(unsigned)l,__FILE__,__LINE__)
X
X/* s - returns a copy of a given string */
X#define s(c) mylib_scopy(c,__FILE__,__LINE__)
X
X/* rs - returns a copy of the second string in place of the first string,
X using realloc */
X#define rs(c,d) mylib_srcopy(c,strlen(d),__FILE__,__LINE__)
X
X/* assert - error message if statement is false */
X#ifdef DEBUG
X#define assert(c) if(!(c))(void)fprintf(stderr,"%s: %d: Assert false\n",__FILE__,__LINE__)
X#else
X#define assert(c) 0
X#endif
X
X/* STREQ - indicate whether two strings are equal */
X#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
X
X#define loop for(;;)
X#define NUL(x) (x)NULL
X#ifndef TRUE
X#define TRUE 1
X#endif
X#ifndef FALSE
X#define FALSE 0
X#endif
X#define YES 1
X#define NO 0
X
X/* Error - print like fprintf(stderr, ...) and die. Progname included at
X beginning of message, newline at end. */
Xextern void Error();
X/* Warning - like error, without dying */
Xextern void Warning();
X
X#define LOG_FILE -1
X#define LOG_STDOUT -2
X#define LOG_STDERR -3
X
X#ifndef LOG_PID
X#define LOG_PID 0x01
X#endif /* LOG_PID */
X
Xextern char *mylib_malloc();
Xextern char *mylib_realloc();
Xextern char *mylib_scopy();
Xextern char *mylib_srcopy();
Xextern char *cat();
Xextern char *getstr();
Xextern FILE *efopen();
Xextern void efclose();
Xextern void dfprintf();
Xextern int writeline();
Xextern char *readline();
Xextern void setlog();
Xextern void logmsg();
Xextern char *strsubst();
X
Xextern char *progname; /* application's name. Used by Error, Warning. */
Xextern int d; /* debug level */
X
X#endif /* __utils_h */
END_OF_FILE
if test 2123 -ne `wc -c <'utils.h'`; then
echo shar: \"'utils.h'\" unpacked with wrong size!
fi
# end of 'utils.h'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have the archive.
rm -f ark[1-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...

0 new messages