home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume41
/
ison
/
part01
next >
Wrap
Text File
|
1993-12-13
|
59KB
|
2,225 lines
Newsgroups: comp.sources.misc
From: mgleason@cse.unl.edu (Mike Gleason)
Subject: v41i031: ison - Network user logon monitor, Part01/01
Message-ID: <1993Dec14.042025.3559@sparky.sterling.com>
X-Md4-Signature: 0b1c189ea02d39d78e8740738e61edb2
Sender: kent@sparky.sterling.com (Kent Landfield)
Organization: NCEMRSoft
Date: Tue, 14 Dec 1993 04:20:25 GMT
Approved: kent@sparky.sterling.com
Submitted-by: mgleason@cse.unl.edu (Mike Gleason)
Posting-number: Volume 41, Issue 31
Archive-name: ison/part01
Environment: UNIX, finger, optionally rpcsvc
Supersedes: ison: Volume 34, Issue 4
Version 5.0 changes:
+ Complete rewrite; numerous minor changes.
+ RPC support added, drastically improving efficiency.
+ Can detect idle time (even for Finger), and can poll until not-idle.
+ Multiple hosts, Multiple users!
+ Monitor mode, where ison prints logon and logoff messages
+ Commands can contain %flags that are expanded to the appropriate value
before the command is run, i.e. "write %u %t" executes
"write <username> <tty>"
+ Powerful user files can be used, to use one ison process to do
different things with different users.
+ If the information is available, ison can tell you the actual
logon time and tty of remote (and local) users.
+ More detailed output printed.
+ Manual page rewritten.
IsOn's primary purpose is to let you know when someone logs on. You could
always sit there at your terminal typing 'finger' or 'who' every 5 minutes,
but that's boring and unproductive. IsOn makes this easy. If you wanted to
know the instant I logged on, all it would take is a simple:
ison mgleason@cse.unl.edu
When I do log on, ison would respond:
** IsOn: mgleason@cse.unl.edu logged on to ttyq28 since 8:41 PM
and is not idle.
IsOn lowers it's priority automatically, so it takes very little CPU,
and spares you the trouble of remembering to use 'nice.' It also puts
itself in the background automatically. For remote addresses (those in
dude@machine.domain format) the rusers function from the rpcsvc library
is used, or the 'finger' utility if it has to, and for a user on the same
machine that you are on, IsOn will simply walk the 'utmp' file.
Enjoy!
--mike gleason = mgleason@cse.unl.edu
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: INSTALL patchlevel.h ison.1 config.h ison.h Makefile
# ison.c
# Wrapped by mgleason@cse on Thu Dec 9 23:37:31 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'INSTALL' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'INSTALL'\"
else
echo shar: Extracting \"'INSTALL'\" \(701 characters\)
sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
XIf you're lazy, just type "make" and see if compiles. Otherwise read
Xand edit config.h, then the Makefile.
X
XThe two biggest problems I could imagine would be (1) the variable-arguments
Xpart (you MUST have one of <stdarg.h> or <varargs.h>), and (2) Getting the
Xcode to link with your RPC libraries. config.h lets you choose for (1),
Xbut for (2) you MUST have the rpcsvc library, and you MAY need an additional
Xlibrary or two that the rpcsvc library requires to link. For my system I
Xneed both the "rpcsvc" library and the "sun" library. The program can
Xlive without RPC, but it is MUCH more efficient net-friendly, so I
Xhighly recommend it. config.h will let you try compiling without it,
Xthough.
END_OF_FILE
if test 701 -ne `wc -c <'INSTALL'`; then
echo shar: \"'INSTALL'\" unpacked with wrong size!
fi
# end of 'INSTALL'
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'\" \(795 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X/*
X
Xv5.0.0 -- December 7, 1993. Initial release of version 5.
X + Complete rewrite; numerous minor changes.
X + RPC support added, drastically improving efficiency.
X + Can detect idle time (even for Finger), and can poll until not-idle.
X + Multiple hosts, Multiple users!
X + Monitor mode, where ison prints logon and logoff messages
X + Commands can contain %flags that are expanded to the appropriate value
X before the command is run, i.e. "write %u %t" executes
X "write <username> <tty>"
X + Powerful user files can be used, to use one ison process to do
X different things with different users.
X + If the information is available, ison can tell you the actual
X logon time and tty of remote (and local) users.
X + More detailed output printed.
X + Manual page rewritten.
X
X*/
END_OF_FILE
if test 795 -ne `wc -c <'patchlevel.h'`; then
echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'ison.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ison.1'\"
else
echo shar: Extracting \"'ison.1'\" \(7904 characters\)
sed "s/^X//" >'ison.1' <<'END_OF_FILE'
X.\" IsOn
X.\"
X.\" Dd distance to space vertically before a "display"
X.\" These are what n/troff use for interparagraph distance
X.\"-------
X.if t .nr Dd .4v
X.if n .nr Dd 1v
X.\"-------
X.\" Sp space down the interparagraph distance
X.\"-------
X.de Sp
X.sp \\n(Ddu
X..
X.\"-------
X.\" Ds begin a display, indented .5 inches from the surrounding text.
X.\"
X.\" Note that uses of Ds and De may NOT be nested.
X.\"-------
X.de Ds
X.Sp
X.in +0.5i
X.nf
X..
X.\"-------
X.\" De end a display (no trailing vertical spacing)
X.\"-------
X.de De
X.fi
X.in
X..
X.TH IsOn 1 "" NCEMRSoft
X.\"-------
X.SH "NAME"
X.\"-------
XIsOn \(em Network user logon monitor
X.\"-------
X.SH "SYNOPSIS"
X.\"-------
X.HP
X.B ison
X.RI [ "\-bdFIjLmoq" ]
X.RI [ "\-p" "\ "\c
X.BR seconds ]
X.RI [ "\-i" "\ "\c
X.BR num ]
X.RI [ "\-o" "\ "\c
X.BR "outfile" ]
X.RI [ "\-c" "\ "\c
X.BR "command" ]
X.RI [ "\-f" "\ "\c
X.BR "userlist" ]
X.RI [ "usernames..." ]
X.\"-------
X.SH "DESCRIPTION"
X.\"-------
X.PP
XYou can use
X.I IsOn
Xto detect when a certain user logs on, and optionally run another program
Xor shell script in response.
X.I IsOn
Xcan detect when a user becomes active (i.e. has no idle time) or logs off.
XYou can use
X.I IsOn
Xto continually monitor a user's logons and logoffs, if you just want to
Xknow when someone is online.
X.PP
X.I IsOn
Xcan do all of this with multiple users and multiple remote or local hosts.
XFor the local machine,
X.I IsOn
Xmonitors the
X.I utmp
Xfile for optimal performance. For the remote hosts,
X.I IsOn
Xtries to use the
X.I rusers
Xfunction from the
X.I Remote Procedure Call Services
Xlibrary if it is available for optimal perfomance and minimal network
Xtraffic. Otherwise it can use the
X.I finger
Xprogram as plan B.
X.PP
X.I IsOn
Xlowers it's priority automatically, so it takes very little CPU time, and
Xspares you the trouble of remembering to use
X.IR "nice" "(1)."
XIt also puts itself in the background automatically.
X.\"-------
X.SH "OPTIONS"
X.\"-------
X.TP
X.B \-b
XToggles beeping on important messages the program prints.
X.TP
X.B \-d
XPrints debugging information during operation.
X.TP
X.B \-F
XTells the program not to bother trying
X.I rusers
Xfor the given remote hosts and try
X.I finger
Xinstead. By default the program tries
X.IR rusers,
Xand if the remote host does not support
X.IR RPC,
Xit then falls back to
X.IR finger.
X.TP
X.BI \-i " x"
XSets the maximum number of iterations before giving up to
X.I x.
XBy default the program doesn't give up until all users have been detected
Xor you log out.
X.TP
X.B \-I
XTells the program to keep polling the given users until they have no idle
Xtime. If you supply this option,
X.I IsOn
Xwill print a message when the person logs on, and print another message
Xlater when the user becomes active.
X.TP
X.B \-j
XThis toggles daemon behavior. Depending on how the program was compiled, this
Xwill allow you do the opposite. If the default is to go into the background,
Xsupplying this option tells the program not to go into the background, and
Xvice-versa.
X.TP
X.B \-L
XThis tells the program not to exit when you log off. Normally the program
Xterminates when all the given users have logged on, or you log off, since you
Xwould not be online to read the logon notices. This option is usually used
Xwith the
X.B "\-c"
Xoption, described below.
X.TP
X.B \-m
XSupplying this option makes the program keep monitoring all users, telling
Xyou each time any of them log on or off. This feature is similar to the
X.I tcsh
Xshell's
X.I watch
Xvariable, except that
X.I IsOn
Xactually works (but
X.I tcsh
Xis a great shell nonetheless).
X.TP
X.BI \-o " x"
XSpecifies that output should be written to the file
X.I x
Xinstead of to your screen.
X.TP
X.BI \-p " x"
XSets the pause between iterations to
X.I x
Xseconds.
X.TP
X.B \-q
XQuiet mode, no output at all will be written. Usually used with the
X.B "\-c"
Xoption, described below.
X.TP
X.BI \-c " command"
XThis tells the program to execute
X.I command
Xeach time a user logs on. In addition, the program also expands certain
Xflags within
X.I command
Xbefore executing:
X.RS
X.TP
X.BR "%h" " inserts the name of the user's host machine."
X.TP
X.BR "%i" " inserts user's idletime, or zero if not idle."
X.TP
X.BR "%m" " inserts which was polling method was used."
X.TP
X.BR "%t" " inserts the user's tty name."
X.TP
X.BR "%u" " inserts the user's username."
X.TP
X.BR "%U" " inserts the user's complete user@host address."
X.RE
X.TP
X.BI \-f " file"
XThis tells the program to read
X.I file
Xto get the list of users. Besides convenience, as an added benefit you can
Xhave the program perform differently for each user, instead of having the
Xcommand line options apply to all users.
X.RS
X.PP
XEach line in the file should contain one username, a set of user flags, and
Xthe command to execute when this user logs on. The exact format is a username
Xfollowed by whitespace, followed by the user flags, followed by whitespace,
Xand the command which continues until the end of the line. The command itself
Xcan contain whitespace and percent flags to be expanded. You may omit either
Xthe user flags, command, or both if you wish.
X.PP
XThere are only three user flags as of this writing, and
Xthey are
X.BR "\-F" ", " "\-I" ", and "\-m"
Xwhich perform as described above, except that they apply only to the
Xspecifed user. If you do not want to use any user flags,
Xjust use a plain ``\-'' to signify no flags at all. See below for an
Xexample which will explain everything.
X.RE
X.PP
XIf you do not supply a userfile, you can type the users on the command
Xline along with the options, or do neither and you will be prompted for
Xa single user by the program (handy if you don't want that to show up
Xin
X.IR "ps" " or " "w" ")."
X.PP
XThe usernames themselves can be in two formats. If you give a complete
X``username@remote.host'' type specification, the program assumes that is not
Xthe machine you are on and tries the remote methods. Otherwise you can
Xjust give ``username'' to tell the program to search the same machine you
Xare on.
X.\"-------
X.SH "SAMPLE USER FILE"
X.\"-------
X.nf
Xtmcmille@scott.skidmore.edu \-IF date >> /users/me/Tara.log
Xpdietz \-m /usr/bsd/w | fgrep %u | mail me
Xshari@cse.unl.edu \-
Xspyros \- write %u %t < /users/me/memo
X.fi
X.\"-------
X.SH "EXAMPLES"
X.\"-------
X.PP
X.nf
Xison mgleason
Xison -I mgleason
Xison -dF -i 100 -p 60 tmcmille@scott.skidmore.edu
Xison -c "echo Tara is on %t." tmcmille@scott.skidmore.edu
Xison -f userfile
Xison -o pdietz humphrey brooke hgleason pnellor enellor kera bretn
X.fi
X.PP
XNote that it doesn't matter how many users you track, but the number of
Xhosts does. The more hosts, the more network traffic. Also, the longer
Xyou set the iteration delay, the better.
X.\"-------
X.SH "BUGS"
X.\"-------
X.PP
XIt is possible that a user could both log on and off while
X.I IsOn
Xis sleeping between tries. Similarly, it is also possible when you've
Xspecified to poll users until they become active, to have an idle user
Xsuddenly activate just long enough to log out while
X.I IsOn
Xsleeps.
X.PP
XWhen
X.I IsOn
Xis using
X.I finger
Xas it's remote polling method, sometimes it will not be able to determine
Xa user's idle time or TTY because of a non-standard remote
X.I finger
Xserver.
X.\"-------
X.SH "FILES"
X.\"-------
X.PP
X.I /etc/utmp
X.\"-------
X.SH "AUTHORS"
X.\"-------
X.PP
XThe original concept and first and second versions by
X.IR "Phil Dietz" " of " "NCEMRSoft"
X(pdietz@cse.unl.edu);
X.PP
XAll subsequent versions, including this one, by
X.IR "Mike Gleason" " of " "NCEMRSoft"
X(mgleason@cse.unl.edu);
X.PP
X.RI "Copyright 1990\-94 by " "NCEMRSoft" "."
X.\"-------
X.SH "SEE ALSO"
X.\"-------
X.HP
X.IR "RFC 1288" ", ``The Finger User Information Protocol''"
X.HP
X.IR "RFC 1057" ", ``RPC: Remote Procedure Call Protocol Specification: Version 2''"
X.HP
X.IR "rusers" "(3),"
X.IR "finger" "(1),"
X.IR "rusers" "(1),"
X.IR "nice" "(1),"
X.IR "nice" "(2),"
X.IR "ps" "(1),"
X.IR "w" "(1),"
X.IR "who" "(1),"
X.IR "write" "(1),"
X.IR "talk" "(1),"
X.IR "tcsh" "(1),"
X.IR "ncftp" "(1)."
X.\" end of file
END_OF_FILE
if test 7904 -ne `wc -c <'ison.1'`; then
echo shar: \"'ison.1'\" unpacked with wrong size!
fi
# end of 'ison.1'
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'\" \(4199 characters\)
sed "s/^X//" >'config.h' <<'END_OF_FILE'
X/* IsOn's config.h */
X
X/* Change what you like. Everything is surrounded in #ifndef blocks,
X * so you can just add them to the command line if you like, such
X * as -DBEEP=0. Note that you should not comment out the whole #define
X * line. The code uses #if's and not #ifdef's so just change a 1 to a
X * 0, etc., instead of #undef'ing the symbol.
X *
X * After browsing this whole file, and making the changes needed for your
X * system, it should compile cleanly. If not, re-edit this file, and
X * make sure you have your compiler and flags set up correctly in the
X * Makefile.
X */
X
X/* Define this 1 if you want to use RPC's rusers() call (highly
X * recommended). Otherwise 0.
X */
X#ifndef RPC
X#define RPC 1 /* 1 or 0 */
X#endif
X
X/* For best results, define FINGER to the full path leading to your
X * finger executable. If you don't, we'll have to hope that the path
X * is in the user's PATH.
X */
X#ifndef FINGER
X#define FINGER "finger"
X#endif
X
X/* If you define DAEMON to be 1, then by default, ison is a daemon,
X * and the user must use -j to prevent ison from going into the back-
X * ground by default. If you define DAEMON to 0, then the user must
X * supply -j to have ison go into the background by itself, and runs
X * as a normal program.
X */
X#ifndef DAEMON
X#define DAEMON 1 /* Daemon by default? (1 or 0) */
X#endif
X
X/* Do you want ison processes to nice themselves?
X * I recommend it, since ison is usually a less important process.
X */
X#ifndef NICE
X#define NICE 1 /* 1 or 0 */
X#endif
X
X/* Do you mind if the program uses ^G's by default to get your attention?
X * The user can use -b to not do the default of what you choose here. */
X#ifndef BEEP
X#define BEEP 1 /* 1 or 0 */
X#endif
X
X/* Do you have strchr()? More than likely you do, unless you're running
X * an older BSD system that still only has index() (the BSD equivalent)
X * and not strchr too
X */
X#ifndef HAVE_STRCHR
X#define HAVE_STRCHR 1
X#endif
X
X/* You should only define this to 0 if you don't have the strstr()
X * call. We can do without it, but you won't be able to detect
X * the idle time when using Finger.
X */
X#ifndef HAVE_STRSTR
X#define HAVE_STRSTR 1 /* 1 or 0 */
X#endif
X
X/* Similarly, if you don't have both strftime() AND localtime()
X * stick a 0 here. We can work around this too.
X */
X#ifndef HAVE_STRFTIME
X#define HAVE_STRFTIME 1
X#endif
X
X/* Do you have <stdlib.h>? Most recent systems do. */
X#ifndef HAVE_STDLIBH
X#define HAVE_STDLIBH 1
X#endif
X
X/* Similarly, do you have <stdarg.h>? If you define HAVE_STDARGH to 0,
X * we'll try <varargs.h> instead. Older versions of SunOS will need
X * <varargs.h>
X */
X#ifndef HAVE_STDARGH
X#define HAVE_STDARGH 1
X#endif
X
X/* You can enable some extra debugging messages if you set this to
X * 1, or save a few bytes and leave it 0.
X */
X#ifndef DEBUG
X#define DEBUG 0 /* 0 or 1 */
X#endif
X
X/* These are the two checks ison tries to see if you've logged off so ison
X * can kill itself. I usually use both of them, but if one or both don't
X * work correctly use undef here.
X */
X#ifndef CHECK_PARENT
X#define CHECK_PARENT 1 /* (1 or 0) check to see if our parent is alive */
X#endif
X
X#ifndef CHECK_STDERR
X#define CHECK_STDERR 1 /* (1 or 0) check to see if stderr is a tty */
X#endif
X
X#ifndef DEFAULT_LOCAL_SLEEP
X#define DEFAULT_LOCAL_SLEEP 10 /* seconds to sleep between Utmp()'s */
X#endif
X
X#ifndef DEFAULT_REMOTE_SLEEP
X#define DEFAULT_REMOTE_SLEEP 45 /* secs to sleep between Finger/RUsers */
X#endif
X
X/* You can put an actual number for this one, but I recommend leaving
X * it a -1L. This way it will poll forever until you logoff, it finds
X * everyone, or the user supplies the max iterations.
X */
X#ifndef MAXITER
X#define MAXITER (-1L) /* Default is -1L */
X#endif
X
X/* You can define a default command, but I don't recommend it. This
X * command will be executed each time a user is found, unless the
X * user who ran the program supplied their own command instead.
X */
X#ifndef COMMAND
X#define COMMAND NULL /* Default is NULL */
X#endif
X
X/* If you're debugging the program (then you're probably me), you
X * may want to use a debugging malloc library to help out.
X */
X#ifndef DBMALLOC
X#define DBMALLOC 0 /* (0 or 1) Linking w/ a debugging malloc library? */
X#endif
X
X/* eof config.h */
END_OF_FILE
if test 4199 -ne `wc -c <'config.h'`; then
echo shar: \"'config.h'\" unpacked with wrong size!
fi
# end of 'config.h'
fi
if test -f 'ison.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ison.h'\"
else
echo shar: Extracting \"'ison.h'\" \(5825 characters\)
sed "s/^X//" >'ison.h' <<'END_OF_FILE'
X/* ison.h */
X
X#define VERSION_STR "Version 5.0 (December 7, 1993)"
X
X/* IsOn is Copyright 1990-1994 by NCEMRSoft. */
X
X#define SZ(expr) ((size_t) (expr))
X
X/* Various ways to tweak PrintF(): */
X#define DEBUG_MSG 001
X#define NO_HEADER_MSG 002
X#define NO_BEEP_MSG 004
X#define FLUSH_MSG 010
X
X/* Arbitrary "hostname" for this machine. But it usually works if you use
X * it as a hostname anyway.
X */
X#define ISLOCALHOST "localhost"
X
X#define NOT_IDLE (0) /* User is active, and has no idletime. */
X#define IDLETIME_UNKNOWN (-1) /* We can't determine if idle or not. */
X#define IS_IDLE (-2) /* User is idle, but don't know exact #minutes. */
X
X/* This is used to tell the program there is no maximum number of
X * iterations.
X */
X#define NO_LIMIT (-1L)
X
X/* You can have comment lines in the data file. Lines starting with
X * this character are skipped.
X */
X#define COMMENT_CHAR '#'
X
X/* Return codes for exit(): */
X#define EXIT_SUCCESSFUL 0
X#define EXIT_MAX_ITERATIONS 1
X#define EXIT_NOT_LOGGED_IN 2
X#define EXIT_USAGE 3
X#define EXIT_PARENT 4
X#define EXIT_FATAL_ERROR (-1)
X
X#ifndef UTMP_FILE /* Most define this in utmp.h; SunOS
X * 4.1.1 doesn't. */
X# define UTMP_FILE "/etc/utmp"
X#endif
X
X#ifndef INDEX
X# if HAVE_STRCHR
X# define INDEX strchr /* ANSI, System V */
X# define RINDEX strrchr
X# else
X# define INDEX index /* BSD */
X# define RINDEX rindex
X# endif
X#endif
X
X#define DONEWITHHOST(hp) hp->usersDone = hp->nUsers
X#define CALLOC1(siz) calloc((size_t)1, siz)
X
Xtypedef struct User *UserPtr;
Xtypedef struct User {
X UserPtr next;
X int idlemsg; /* Only say this user is idle one time. */
X time_t tyme; /* This user's login date, if we know it. */
X int idletime; /* Idle time of user, in minutes (maybe). */
X int wasOn; /* If the user was on before the current iter. */
X int wasIdle; /* If user was idle for current iter. */
X int isOn; /* If this user is on for the current iter. */
X int logons; /* Mostly for debugging. */
X int pollUntilActive; /* Wait until user is not idle? */
X int detectLogoffs; /* Monitor logoffs too? */
X char *cmd; /* What to do if this person is on. */
X char username[12]; /* This user's login name. */
X char dev[12]; /* This user's tty. */
X char idlestr[8]; /* only used by Finger. */
X} User;
X
Xtypedef struct Host *HostPtr;
X
X/* This may cause some compilation problems :-( */
X#ifdef ansi
Xtypedef void (*pollproc_t)(IsonParams *g, HostPtr hp);
X#else
Xtypedef int (*pollproc_t)();
X#endif
X
Xtypedef struct Host {
X HostPtr next;
X char *hostname; /* FQDN name of the this host, if remote. */
X pollproc_t pollproc; /* Which polling routine in use. */
X UserPtr firstUser; /* List of users on this machine to look for. */
X UserPtr lastUser;
X size_t nUsers; /* Number of users in list. */
X size_t usersDone; /* Number we've detected already. */
X int idleCol; /* For Finger(); where to look for idle time. */
X int ttyCol; /* Same, but for the TTY. */
X} Host;
X
X/* This program started out by not using any global variables. I kept
X * the tradition alive by using this trick. We keep one of the following
X * structures on the stack, and then pass a pointer to it around to the
X * various sub-routines. That way this program is re-entrant if you
X * know how to compile it that way. Of course the tradeoff is that there
X * are going to be plenty of extra pointer dereferences.
X */
Xtypedef struct IsonParams {
X FILE *outfile; /* Stream to print messages on. */
X char *progname; /* Short name of this program. */
X long maxIters; /* An upperbound, if any, on iterations. */
X long iter; /* The current iteration number. */
X int sleep; /* How long to delay between iterations. */
X int daemon; /* Are we a background process? */
X int debug; /* Printing diagnostic output? */
X int canBeep; /* Printing ^G's with important messages? */
X int memInUse; /* Dynamic memory in use; used to detect leaks. */
X int pid; /* PID of this process. */
X int parentPid; /* The parent PID of ison. */
X int stdinatty; /* Were we run from a shell script? */
X int pollmsg; /* Print startup message? */
X int autodie; /* exit() when you logoff? */
X int detectLogoffs; /* Staying on, reporting log ons/offs? */
X FILE *utmpfp; /* Used by Utmp() for localhost's utmp file. */
X int nHosts; /* How many hosts we're polling. */
X int nRemoteHosts; /* How many of them are remote, if any. */
X int hostsDone; /* How many are totally finished. */
X HostPtr firstHost; /* The host list. */
X HostPtr lastHost;
X} IsonParams;
X
Xextern int optind; /* getopt() stuff */
Xextern char *optarg;
X
Xextern int errno;
X
X#if defined(__STDC__) || defined(__cplusplus) || defined(__EXTENSIONS__)
X#define Pr(s) s
X#define ansi 1
X#include <unistd.h> /* just for prototypes... can be omitted. */
X#ifndef __EXTENSIONS__
Xextern FILE * popen(const char *, const char *);
X#endif
X#else
X#define Pr(s) ()
X#define ansi 0
Xextern FILE *popen();
X#endif
X
X/* Function prototypes... */
Xint Strncasecmp Pr((char *a, char *b, size_t n));
Xvoid DoneWithUser Pr((IsonParams *g, HostPtr hp, UserPtr up));
Xvoid RunCommand Pr((HostPtr hp, UserPtr up));
Xvoid MakeSureIAmLoggedIn Pr((IsonParams *g));
Xchar *UserAddress Pr((char *buf, HostPtr hp, UserPtr up));
Xchar *TimeStr Pr((char *buf, size_t siz, time_t t));
Xvoid Delay Pr((IsonParams *g));
Xvoid IterationDebugMsg Pr((IsonParams *g, char *whichproc, HostPtr hp));
Xvoid UserIsIdle Pr((IsonParams *g, HostPtr hp, UserPtr up));
Xvoid Utmp Pr((IsonParams *g, HostPtr hp));
Xvoid Finger Pr((IsonParams *g, HostPtr hp));
Xvoid RUsers Pr((IsonParams *g, HostPtr hp));
Xint AddUser Pr((IsonParams *g, char *username, int F, int noIdle, int mon, char *cmd));
Xvoid Poll Pr((IsonParams *g));
Xvoid ReadDataFile Pr((IsonParams *g, char *fname));
Xvoid Usage Pr((IsonParams *g));
X
X/* eof ison.h */
END_OF_FILE
if test 5825 -ne `wc -c <'ison.h'`; then
echo shar: \"'ison.h'\" unpacked with wrong size!
fi
# end of 'ison.h'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(2144 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# IsOn's Makefile
X#
X# As usual comment lines are those beginning with a # character; I've
X# left some extra comments in there to give more example values for some
X# Make variables.
X
X# Your favorite C Compiler, and flags.
XCC=cc
X#CC=gcc
X
XCFLAGS=-O
X#CFLAGS=-O2
X#CFLAGS=-g
X#CFLAGS=-ansi -fullwarn -woff 211,269,270,303,309 -g -I.. -L..
X#CFLAGS=-g -Wall -Wshadow -Wconversion -Wstrict-prototypes -Wmissing-prototypes
X
X
X# Link flags -- add the RPC library(s) here if you want to use it.
X#
X# For SunOS, you just need -lrpcsvc. That may do it for most systems.
X#
X# My system needs to libraries, because the librpcsvc library relies on
X# functions not included in libc (it needs the XDR routines).
X# So I add -lrpcsvc -lsun here to get it to link correctly.
X#
X# If you aren't using RPC, you can just set LFLAGS to empty.
X
X
XLFLAGS=-lrpcsvc
X#LFLAGS=-lrpcsvc -lsun
X#LFLAGS=-lrpcsvc -lsun -lc_s
X#LFLAGS=
X
X
X# Set STRIP to -s if you want a much smaller binary (stripped of debugging
X# symbols).
X
XSTRIP=-s
X#STRIP=
X
X
X# Definitions. EDIT config.h! PLEASE read through that whole file,
X# and make the necessary changes!
X
XDEFS= #-DHAVE_STDARGH=0 -DRPC=1 -DDEBUG=1
X
X
X
X
X### SHOULD NOT NEED TO EDIT BEYOND THIS POINT ###
X
X# Source file name minus .c, and compiled exectuable's pathname.
XPROG=ison
XOFILE=ison
XVERS=5.0
XTARNAME=$(PROG)$(VERS).tar
X
XBLURB=Blurb
XPACKAGE=INSTALL patchlevel.h $(PROG).1 config.h ison.h Makefile $(PROG).c
X
Xall: $(PROG)
X
X$(PROG): $(PROG).c config.h ison.h
X $(CC) $(CFLAGS) $(DEFS) $(PROG).c -o $(OFILE) $(LFLAGS) $(STRIP)
X -@ls -l $(OFILE)
X
X# For my system:
Xirix:
X cc -O3 -DRPC=1 $(PROG).c -o $(OFILE) -lrpcsvc -lsun -lc_s -s
X @-rm -f *.[ou]
X @-chmod 755 $(OFILE)
X @ls -l $(OFILE)
X mv ./$(OFILE) $(HOME)/bin
X
Xdebug:
X cc -g -DRPC=1 -DDEBUG=1 $(PROG).c -o $(OFILE) -lrpcsvc -lsun
X
Xshar:
X shar $(PACKAGE) | cat $(BLURB) - > $(PROG).shar
X
Xtar:
X ( cd .. ; tar cvf ./$(PROG).tar ison )
X mv ../$(TARNAME) .
X -@ls -l $(TARNAME)
X
Xtar2:
X tar cvf $(PROG).tar $(PACKAGE)
X
Xgz: tar
X gzip -v $(TARNAME)
X -@ls -l $(TARNAME).gz
X
Xfinst: gz
X cp $(TARNAME).gz /usr/people/ftp/pub/mgleason
X
Xclean:
X rm -f core $(OFILE)
X
Xclobber: clean
X rm -i $(PACKAGE)
END_OF_FILE
if test 2144 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'ison.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ison.c'\"
else
echo shar: Extracting \"'ison.c'\" \(30133 characters\)
sed "s/^X//" >'ison.c' <<'END_OF_FILE'
X/* IsOn... Copyright 1990-94 NCEMRSoft. Use at your own risk.
X * This version by Mike Gleason, NCEMRSoft (mgleason@cse.unl.edu).
X * Original version by Phil Dietz, NCEMRSoft (pdietz@cse.unl.edu).
X */
X
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/stat.h>
X#include <utmp.h>
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include <signal.h>
X
X#include "config.h"
X#include "ison.h"
X
X#if HAVE_STDLIBH
X#include <stdlib.h>
X#else
Xextern void *malloc(), *calloc();
X#endif
X
X#if HAVE_STDARGH
X#include <stdarg.h>
X#else
X#include <varargs.h>
X#endif
X
X#if RPC
X#include <sys/socket.h>
X#include <rpc/rpc.h>
X#include <rpcsvc/rusers.h>
X#endif /* RPC */
X
X#if DBMALLOC
X#include <malloc.h>
X#endif
X
X#if ansi
Xint Strncasecmp(register char *a, register char *b, register size_t n)
X#else
Xint Strncasecmp(a, b, n)
X register char *a, *b;
X register size_t n;
X#endif
X{
X register int A, B;
X register int fullcompare;
X
X /* You can supply a 0 to mean just do a regular Strcasecmp. */
X fullcompare = (n == (size_t) 0);
X while ((fullcompare) || (n-- > (size_t) 0)) {
X A = islower(*a) ? tolower((int) *a++) : *a++;
X B = islower(*b) ? tolower((int) *b++) : *b++;
X if (A > B)
X return (A - B);
X if (B > A)
X return (B - A);
X if (A == 0 && B == 0)
X return (0);
X }
X return (0); /* equal to n characters if we get
X * here */
X} /* Strncasecmp */
X
X
X
X
X#if ansi
Xchar *TimeStr(char *buf, size_t siz, time_t t)
X#else
Xchar *TimeStr(buf, siz, t)
X char *buf;
X size_t siz;
X time_t t;
X#endif
X{
X#if HAVE_STRFTIME
X char buf2[128];
X
X if (t == (time_t)0)
X time(&t);
X (void) strftime(buf2, SZ(127), "%I:%M %p", localtime(&t));
X if (buf2[0] == '0' && buf[1] == '0') {
X buf2[0] = '1';
X buf2[1] = '2';
X }
X (void) strncpy(buf, buf2[0] == '0' ? buf2 + 1 : buf2 , siz);
X#else
X if (t == (time_t)0)
X time(&t);
X (void) strncpy(buf, ctime(&t), siz);
X if (t == (time_t)0)
X time(&t);
X buf[strlen(buf) - 1] = '\0'; /* Get rid of the \n. */
X#endif
X return (buf);
X} /* TimeStr */
X
X
X
X
X#if ansi && HAVE_STDARGH
Xstatic void PrintF(IsonParams *g0, int flags0, char *fmt0, ...)
X#else
X#if HAVE_STDARGH
Xstatic void PrintF(g0, flags0, fmt0, ...)
X IsonParams *g0;
X int flags0;
X char *fmt0;
X#else
Xstatic void PrintF(va_alist)
X va_dcl
X#endif /* !HAVE_STDARGH*/
X#endif /* !ansi */
X{
X va_list ap;
X char tmstr[40];
X IsonParams *g;
X int flags;
X char *fmt;
X
X#if (HAVE_STDARGH == 0)
X va_start(ap);
X g = va_arg(ap, IsonParams *);
X flags = va_arg(ap, int);
X fmt = va_arg(ap, char *);
X#else
X va_start(ap, fmt0);
X g = g0; flags = flags0; fmt = fmt0;
X#endif
X
X if (g->outfile == NULL)
X return;
X#if DEBUG
X if (((flags & DEBUG_MSG) != 0) && (!g->debug))
X return;
X#endif
X if ((flags & FLUSH_MSG) != 0) {
X (void) fflush(g->outfile);
X return;
X }
X
X if ((flags & NO_HEADER_MSG) == 0) {
X tmstr[0] = '\0';
X if (g->outfile != stderr) { /* Log file. */
X tmstr[0] = ' ';
X (void) TimeStr(tmstr+1, sizeof(tmstr)-1, (time_t) 0);
X }
X (void) fprintf(g->outfile, "\r\n%s%s%s IsOn: ",
X (((flags & (NO_BEEP_MSG|DEBUG_MSG)) != 0) ||
X (g->canBeep == 0)) ? "" : "\007",
X ((flags & DEBUG_MSG) != 0) ? "#DB#" : "**",
X tmstr
X );
X }
X
X (void) vfprintf(g->outfile, fmt, ap);
X va_end(ap);
X} /* PrintF */
X
X
X#if ansi
Xvoid MakeSureIAmLoggedIn(IsonParams *g)
X#else
Xvoid MakeSureIAmLoggedIn(g)
X IsonParams *g;
X#endif
X{
X if (g->autodie) {
X#if CHECK_PARENT
X /* Don't kill ourself if stdin was not a tty in the first place,
X * which means we were probably called from a shell script, and
X * the shell will exit before we finish.
X */
X if (g->stdinatty && kill(g->parentPid, 0)) {
X /* we've lost our shell! */
X PrintF(g, 0, "Lost our parent!\n");
X exit(EXIT_NOT_LOGGED_IN);
X }
X#endif
X#if CHECK_STDERR
X if (!isatty(2)) {
X /* Hmm... wonder where this is going? */
X PrintF(g, 0, "Stderr is not a tty!\n");
X exit(EXIT_NOT_LOGGED_IN);
X }
X#endif
X }
X} /* MakeSureIAmLoggedIn */
X
X
X
X#if ansi
Xvoid Delay(IsonParams *g)
X#else
Xvoid Delay(g)
X IsonParams *g;
X#endif
X{
X unsigned int sl;
X
X /* Kill some time so more important processes can run. */
X g->iter++;
X#if DEBUG
X#if DBMALLOC
X {
X int inuse = (int) malloc_inuse(NULL);
X if (inuse > g->memInUse) {
X g->memInUse = inuse;
X PrintF(g, DEBUG_MSG, "malloc_inuse: %d\n", inuse);
X }
X }
X#endif
X#endif
X if ((g->maxIters > 0L) && (g->iter > g->maxIters)) {
X PrintF(g, 0, "Giving up after %ld iterations.\n", g->maxIters);
X exit(EXIT_MAX_ITERATIONS);
X }
X if (g->iter == 1L) {
X if (g->stdinatty && g->pollmsg) {
X /* Print a message if you ran us from an interactive shell. */
X PrintF(g, NO_BEEP_MSG, "Polling.");
X if (g->daemon)
X PrintF(g, NO_HEADER_MSG, " Type \"kill %d\" to terminate.\n", g->pid);
X else
X PrintF(g, NO_HEADER_MSG, "..\n");
X }
X } else {
X if (g->sleep > 0) {
X /* The user supplied a value to give to sleep. */
X sl = (unsigned int) g->sleep;
X } else if (g->sleep < 0) {
X /* Use one of the defaults. If you supplied a remote host,
X * wait a little longer, otherwise use the local delay
X * which is shorter.
X */
X sl = (unsigned int) ((g->nRemoteHosts > 0) ? DEFAULT_REMOTE_SLEEP :
X DEFAULT_LOCAL_SLEEP);
X }
X#if DEBUG
X PrintF(g, DEBUG_MSG, "Sleeping %3d%s",
X sl,
X (g->daemon == 0) ? ": " : "...\r\n"
X );
X PrintF(g, FLUSH_MSG, "");
X#endif
X (void) sleep(sl);
X#if DEBUG
X PrintF(g, DEBUG_MSG|NO_HEADER_MSG, "done.\n");
X#endif
X }
X} /* Delay */
X
X
X
X#if DEBUG
X/* If we have debug mode on, we can print a message to the screen telling
X * the user that we are getting ready to do another check.
X */
X#if ansi
Xvoid IterationDebugMsg(IsonParams *g, char *whichproc, HostPtr hp)
X#else
Xvoid IterationDebugMsg(g, whichproc, hp)
X IsonParams *g;
X char *whichproc;
X HostPtr hp;
X#endif
X{
X char tmstr[40];
X
X PrintF(g, DEBUG_MSG, "Checking %s on %s (try #%ld) at %s\n",
X whichproc, hp->hostname, g->iter,
X TimeStr(tmstr, sizeof(tmstr), (time_t)0)
X );
X} /* IterationDebugMsg */
X#else
X#define IterationDebugMsg(a,b,c)
X#endif /* DEBUG */
X
X
X
X#if ansi
Xvoid RunCommand(HostPtr hp, UserPtr up)
X#else
Xvoid RunCommand(hp, up)
X HostPtr hp;
X UserPtr up;
X#endif
X{
X char buf[256];
X char str[64];
X char *catstr;
X char *cp, *dp;
X size_t n;
X
X if (up->cmd != NULL) {
X for (dp = buf, cp = up->cmd, n = sizeof(buf) - 1; *cp != '\0'; cp++) {
X if (*cp == '%') {
X ++cp;
X switch (*cp) {
X case '\0':
X --cp;
X break;
X case 'h':
X catstr = hp->hostname;
X goto cat;
X case 'i':
X if (up->idletime != IS_IDLE) {
X (void) sprintf(str, "%d",
X up->idletime == IS_IDLE ? 0 : up->idletime);
X catstr = str;
X } else catstr = up->idlestr;
X goto cat;
X case 'm':
X if (hp->pollproc == (pollproc_t) Utmp)
X catstr = "utmp";
X else if (hp->pollproc == (pollproc_t) Finger)
X catstr = "finger";
X else
X catstr = "rusers";
X goto cat;
X case 'n':
X case 'u':
X catstr = up->username;
X goto cat;
X case 'N':
X case 'U':
X if (hp->hostname != ISLOCALHOST) {
X (void) sprintf(str, "%s@%s", up->username,
X hp->hostname);
X catstr = str;
X } else
X catstr = up->username;
X goto cat;
X case 't':
X catstr = up->dev;
X goto cat;
X cat:
X for (; (n > 0) && (*catstr != '\0'); --n)
X *dp++ = *catstr++;
X break;
X default:
X if (n > 0) {
X --n;
X *dp++ = *cp;
X }
X }
X } else if (n > 0) {
X --n;
X *dp++ = *cp;
X }
X }
X *dp = 0;
X
X if (fork() == 0) {
X (void) sleep(1);
X (void) execlp("/bin/sh", "sh", "-c", buf, NULL);
X (void) perror(buf);
X exit(EXIT_FATAL_ERROR); /* Rarely reached... */
X }
X }
X} /* RunCommand */
X
X
X
X
X#if ansi
Xchar *UserAddress(char *buf, HostPtr hp, UserPtr up)
X#else
Xchar *UserAddress(buf, hp, up)
X char *buf;
X HostPtr hp;
X UserPtr up;
X#endif
X{
X (void) strcpy(buf, up->username);
X if (Strncasecmp(hp->hostname, ISLOCALHOST, SZ(0)) != 0) {
X (void) strcat(buf, "@");
X (void) strcat(buf, hp->hostname);
X }
X return (buf);
X} /* UserAddress */
X
X
X
X
X#if ansi
Xvoid UserIsIdle(IsonParams *g, HostPtr hp, UserPtr up)
X#else
Xvoid UserIsIdle(g, hp, up)
X IsonParams *g;
X HostPtr hp;
X UserPtr up;
X#endif
X{
X char uabuf[128];
X
X up->wasIdle = 1;
X if (++up->idlemsg == 1) {
X PrintF(g, 0, "%s is logged in,\r\n but has been idle ",
X UserAddress(uabuf, hp, up)
X );
X if (up->idletime > 0)
X PrintF(g, NO_HEADER_MSG, "%d minute%s.\n\n",
X up->idletime,
X (up->idletime > 1 ? "s" : "")
X );
X else
X PrintF(g, NO_HEADER_MSG, "%s.\n\n", up->idlestr);
X }
X} /* UserIsIdle */
X
X
X
X
X#if ansi
Xvoid DoneWithUser(IsonParams *g, HostPtr hp, UserPtr up)
X#else
Xvoid DoneWithUser(g, hp, up)
X IsonParams *g;
X HostPtr hp;
X UserPtr up;
X#endif
X{
X char tmstr[40];
X char useraddr[128];
X char TTY[32];
X char since[10];
X
X up->isOn = 1;
X if (up->wasOn) /* Yeah, we know already. */
X return;
X
X up->logons++;
X up->wasOn = 1;
X
X (void) UserAddress(useraddr, hp, up);
X
X if (up->tyme == (time_t)0) {
X since[0] = '\0';
X /* tmstr will contain the time _here_ after the TimeStr() then. */
X } else {
X (void) strcpy(since, " since ");
X }
X (void) TimeStr(tmstr, sizeof(tmstr), up->tyme);
X
X TTY[0] = '\0';
X if (up->dev[0] != '\0')
X (void) sprintf(TTY, " to %s", up->dev);
X
X /* We don't want to print the idle message after we print this
X * next message.
X */
X ++up->idlemsg;
X if (up->idletime > 0) {
X PrintF(g, 0,
X "%s logged on%s%s%s,\r\n but has been idle %d min.\n",
X useraddr,
X TTY,
X since,
X since[0] == '\0' ? since : tmstr,
X up->idletime
X );
X } else if (up->idletime == NOT_IDLE) {
X up->wasIdle = 0;
X PrintF(g, 0,
X "%s logged on%s%s%s\r\n and is not idle.\n",
X useraddr,
X TTY,
X since,
X since[0] == '\0' ? since : tmstr
X );
X } else if (up->idletime == IDLETIME_UNKNOWN) {
X /* Probably using Finger. Since not all finger servers spew
X * their output in the same format, we may not be able to find
X * out if the user is idle or not.
X */
X up->wasIdle = 0;
X PrintF(g, 0,
X "Detected login of %s at %s.\n",
X useraddr,
X tmstr
X );
X } else if (up->idletime == IS_IDLE) {
X /* Again, from Finger. We don't bother trying to figure out
X * the exact number of minutes since there are various ways
X * it prints that figure. We just know it's there is some
X * idletime, which is good enough. Hopefully we won't be
X * using Finger very much anyway. We just tell them the
X * exact same thing Finger told us.
X */
X PrintF(g, 0,
X "Detected login of %s%s at %s,\r\n but has been idle %s.\n",
X useraddr,
X TTY,
X tmstr,
X up->idlestr
X );
X }
X
X if (!up->detectLogoffs) {
X /* If we are running detect logoffs mode, we want to run forever,
X * so we don't want to increment the done-users counter. If we
X * did that, we would exit once all users had logged on at least
X * once. With this mode, we don't want to exit.
X */
X if (++hp->usersDone == hp->nUsers) {
X ++g->hostsDone;
X }
X }
X
X /* Run a command (script) if the user requested to. */
X RunCommand(hp, up);
X} /* DoneWithUser */
X
X
X
X
X#if ansi
Xvoid Utmp(IsonParams *g, HostPtr hp)
X#else
Xvoid Utmp(g, hp)
X IsonParams *g;
X HostPtr hp;
X#endif
X{
X struct utmp info;
X int idletime;
X struct stat st;
X char ttypath[128];
X UserPtr up;
X
X /* Open the utmp file, which is a list of all logged on users. */
X if ((g->utmpfp == NULL) && ((g->utmpfp = fopen(UTMP_FILE, "r")) == NULL)) {
X (void) perror(UTMP_FILE);
X DONEWITHHOST(hp);
X }
X
X /* Reset the utmp file and re-read it. */
X (void) rewind(g->utmpfp);
X
X IterationDebugMsg(g, "Utmp", hp);
X
X /* Cycle through all 'users' logged in. */
X while (fread(&info, SZ(sizeof(info)), SZ(1), g->utmpfp) == SZ(1)) {
X /* See if this guy matches any of the users we are looking for. */
X for (up = hp->firstUser; up != NULL; up = up->next) {
X if (Strncasecmp(up->username, info.ut_name, SZ(8)) == 0) {
X /* This user is logged on. But is the user active? */
X (void) time(&up->tyme);
X
X up->isOn = 1;
X (void) strcat(strcpy(ttypath, "/dev/"), info.ut_line);
X idletime = IDLETIME_UNKNOWN;
X if (stat(ttypath, &st) == 0) {
X idletime = (int) (up->tyme - st.st_mtime) - 0;
X if (idletime < 0) idletime = NOT_IDLE;
X else idletime /= 60;
X }
X
X up->idletime = idletime;
X if (up->pollUntilActive && idletime > 0) {
X UserIsIdle(g, hp, up);
X } else {
X /* The user is not idle; use the real login time. */
X up->tyme = info.ut_time;
X
X /* Note the tty the user is on. */
X (void) strcpy(up->dev, info.ut_line);
X
X DoneWithUser(g, hp, up);
X }
X }
X }
X }
X} /* Utmp */
X
X
X
X
X#if ansi
Xvoid Finger(IsonParams *g, HostPtr hp)
X#else
Xvoid Finger(g, hp)
X IsonParams *g;
X HostPtr hp;
X#endif
X{
X FILE *in;
X register char *cp;
X int piperesult, pipelines, i;
X char buf[160], pipename[128];
X UserPtr up;
X
X (void) strcat(strcpy(pipename, FINGER), " @");
X (void) strcat(pipename, hp->hostname);
X
X if ((in = popen(pipename, "r")) == NULL) {
X perror(FINGER);
X exit(EXIT_FATAL_ERROR);
X }
X
X IterationDebugMsg(g, "Finger", hp);
X
X /* Cycle through all 'users' logged in. */
X pipelines = 0;
X while (fgets(buf, (int) sizeof(buf), in) != NULL) {
X pipelines++;
X
X#if HAVE_STRSTR
X /* We would like to determine if a user is idle if possible.
X * Since not all finger daemons format their output the same
X * way, we may not be able to get the idle time. We can
X * find it if the other side prints a column header line.
X * From my experience almost all of them being with "Login" as
X * the very first column, and if there is an idle time column,
X * the word "Idle" will appear on that header line. If that's
X * the case, we remember the offset into the line where the
X * word "Idle" occurred. Later when we find a user on our list,
X * we look at the offset into the line. If a user is idle,
X * some sort of text will appear usually a number. If not, it
X * should be just whitespace.
X */
X if (Strncasecmp("Login", buf, SZ(5)) == 0) {
X if (hp->idleCol == 0) {
X cp = strstr(buf, "Idle");
X if (cp != NULL)
X hp->idleCol = (int) (cp - buf);
X }
X
X /* Same thing for the TTY. */
X if (hp->ttyCol == 0) {
X cp = strstr(buf, "TTY");
X if (cp != NULL)
X hp->ttyCol = (int) (cp - buf);
X }
X }
X#endif /* HAVE_STRSTR */
X
X /* put a \0 in the first space after the username for Strncasecmp */
X cp = buf;
X while (*cp && isspace(*cp) == 0)
X cp++;
X *cp = '\0';
X
X /* See if this guy matches any of the users we are looking for. */
X for (up = hp->firstUser; up != NULL; up = up->next) {
X if (Strncasecmp(up->username, buf, SZ(8)) == 0) {
X up->tyme = (time_t)0; /* Don't know login time. */
X up->idletime = IDLETIME_UNKNOWN;
X if (hp->idleCol != 0) {
X cp = buf + hp->idleCol;
X cp[4] = 0;
X up->idletime = NOT_IDLE;
X for (i=0; i<4; i++)
X if (!isspace(cp[i])) {
X up->idletime = IS_IDLE;
X /* Well, just save the same string finger
X * said for later use.
X */
X while (isspace(*cp) && *cp != '\0')
X cp++;
X (void) strcpy(up->idlestr, cp);
X }
X }
X if (hp->ttyCol != 0) {
X cp = buf + hp->ttyCol;
X cp[3] = 0;
X while (isspace(*cp) && *cp != '\0')
X cp++;
X if (Strncasecmp(cp, "co", SZ(2)) == 0)
X (void) strcpy(up->dev, "console");
X else
X (void) strcat(strcpy(up->dev, "tty"), cp);
X }
X up->isOn = 1;
X if (up->pollUntilActive && up->idletime == IS_IDLE) {
X UserIsIdle(g, hp, up);
X } else {
X DoneWithUser(g, hp, up);
X }
X }
X }
X }
X
X piperesult = pclose(in); /* close pipe */
X if (piperesult) {
X PrintF(g, 0,
X "%sFinger unsuccessful with %s, so no users from it can be polled.\n",
X hp->hostname
X );
X DONEWITHHOST(hp);
X }
X if (pipelines <= 1) {
X /* finger probably puked */
X PrintF(g, 0,
X "%s did not supply any Finger output, so no users from it can be polled.\n",
X hp->hostname
X );
X DONEWITHHOST(hp);
X }
X} /* Finger */
X
X
X
X
X#if RPC
X
X#if ansi
Xvoid RUsers(IsonParams *g, HostPtr hp)
X#else
Xvoid RUsers(g, hp)
X IsonParams *g;
X HostPtr hp;
X#endif
X{
X struct utmpidlearr uti;
X UserPtr up;
X int i;
X
X IterationDebugMsg(g, "RUsers", hp);
X uti.uia_cnt = 0; uti.uia_arr = 0;
X
X if (rusers(hp->hostname, &uti) != 0) {
X if (g->iter == 1) {
X /* This remote site probably doesn't support RPC at all, so
X * try finger instead.
X */
X#if DEBUG
X PrintF(g, DEBUG_MSG, "RPC failed with %s, will try Finger next time.\n",
X hp->hostname);
X#endif
X } else {
X /* We were able to use RPC at least once, but it isn't working
X * anymore. We can still try using finger, and if that doesn't
X * work, we'll have to give up on this host.
X */
X PrintF(g, 0, "RPC not reliable with %s, falling back to Finger for this host.\n",
X hp->hostname);
X }
X hp->pollproc = (pollproc_t) Finger;
X Finger(g, hp);
X return;
X }
X
X/* I prefer to use my own temp variable, but the internal structure names
X * differ across different versions of RPCSVC :-(
X */
X#define R_UTMP(a) ((*uti.uia_arr[i]).ui_utmp)
X
X /* Cycle through each entry in the remote utmp list, and see if any
X * of the entries match the users we are looking for.
X */
X for (i=0; i<uti.uia_cnt; i++) {
X for (up = hp->firstUser; up != NULL; up = up->next) {
X if (Strncasecmp(up->username, R_UTMP(uti).ut_name,
X SZ(8)) == 0)
X {
X /* We have found one of our users. */
X
X /* We can print exact login time, unlike Finger. */
X up->tyme = R_UTMP(uti).ut_time;
X
X /* We can also get the exact idletime for this user,
X * without any hassle.
X */
X up->idletime = (int) uti.uia_arr[i]->ui_idle;
X
X up->isOn = 1;
X if (up->pollUntilActive && up->idletime > 0) {
X UserIsIdle(g, hp, up);
X } else {
X /* Note the tty the user is on. */
X (void) strcpy(up->dev, R_UTMP(uti).ut_line);
X DoneWithUser(g, hp, up);
X }
X }
X }
X }
X
X /* We're done with this chunk, so get rid of it and get a new one
X * at the next iteration.
X */
X xdr_free(xdr_utmpidlearr, &uti);
X} /* RUsers */
X#endif /* RPC */
X
X
X
X
X#if ansi
Xint AddUser(IsonParams *g, char *username, int F, int noIdle, int mon, char *cmd)
X#else
Xint AddUser(g, username, F, noIdle, mon, cmd)
X IsonParams *g;
X char *username, *cmd;
X int F, mon, noIdle;
X#endif
X{
X HostPtr hp;
X UserPtr up;
X int isLocal;
X char *cp;
X char *hostname;
X char userandhost[128];
X
X (void) strcpy(userandhost, username); /* Don't modify original. */
X
X /*
X * Check the username for an @, which would suggest that it is a
X * domain-style address.
X */
X if ((cp = INDEX(userandhost, '@')) != NULL) {
X *cp = 0; /* now will contain only the username. */
X hostname = cp + 1; /* points to the part after the @. */
X isLocal = 0;
X } else {
X isLocal = 1;
X hostname = ISLOCALHOST; /* Give it an arbitrary name, so we can
X * group all the local users together.
X */
X }
X
X for (hp=g->firstHost; hp != NULL; hp = hp->next) {
X if (Strncasecmp(hostname, hp->hostname, 0) == 0) {
X /* We already have a host in the list by this name. */
X break;
X }
X }
X
X if (hp == NULL) {
X /* If we didn't have the hostname in question in our host list,
X * add a new one.
X */
X hp = (HostPtr) CALLOC1(sizeof(Host));
X if (hp == NULL) goto memerr;
X hp->hostname = (char *) malloc(strlen(hostname) + 1);
X if (hp->hostname == NULL) goto memerr;
X (void) strcpy(hp->hostname, hostname);
X
X /* Attach hp to the host list. */
X if (g->firstHost == NULL)
X g->firstHost = g->lastHost = hp;
X else {
X g->lastHost->next = hp;
X g->lastHost = hp;
X }
X
X hp->nUsers = 0;
X hp->firstUser = hp->lastUser = NULL;
X g->nHosts++;
X }
X
X if (isLocal)
X hp->pollproc = (pollproc_t) Utmp;
X else {
X g->nRemoteHosts++;
X#if RPC
X /* Try RPC first, unless you said not to. */
X hp->pollproc = (pollproc_t) (F ? Finger : RUsers);
X#else
X hp->pollproc = (pollproc_t) Finger;
X#endif
X }
X
X up = (UserPtr) CALLOC1(sizeof(User));
X if (up == NULL) goto memerr;
X up->next = NULL;
X (void) strncpy(up->username, userandhost, sizeof(up->username) - 1);
X up->cmd = NULL;
X if (cmd != NULL) {
X up->cmd = (char *) malloc(strlen(cmd) + 1);
X if (up->cmd == NULL) goto memerr;
X (void) strcpy(up->cmd, cmd);
X }
X up->pollUntilActive = noIdle;
X up->detectLogoffs = mon;
X
X if (hp->lastUser == NULL)
X hp->firstUser = hp->lastUser = up;
X else {
X hp->lastUser->next = up;
X hp->lastUser = up;
X }
X hp->nUsers++;
X
X return (0);
X
Xmemerr:
X return (-1);
X} /* AddUser */
X
X
X
X
X#if ansi
Xvoid Poll(IsonParams *g)
X#else
Xvoid Poll(g)
X IsonParams *g;
X#endif
X{
X HostPtr hp;
X UserPtr up;
X char uabuf[128], tmstr[40];
X
X do {
X Delay(g); /* Delay a little so we won't hog the CPU */
X MakeSureIAmLoggedIn(g);
X
X#if DEBUG
X if (g->debug) {
X PrintF(g, NO_HEADER_MSG,
X "\r\nIsOn's PID: %d; Parent: %d.\n", g->pid, g->parentPid);
X
X for (hp=g->firstHost; hp != NULL; hp = hp->next) {
X PrintF(g, NO_HEADER_MSG, "\r\nHost: %-40s Mode: %c\n",
X hp->hostname,
X hp->pollproc == (pollproc_t) Finger ? 'F'
X : (hp->pollproc == (pollproc_t) Utmp ? 'U' : 'R')
X );
X for (up = hp->firstUser; up != NULL; up = up->next) {
X PrintF(g, NO_HEADER_MSG,
X "\r %-8s %c %-3s logons=%-2d idle=%-2d",
X up->username,
X up->pollUntilActive ? 'I' : ' ',
X up->isOn ? "ON" : "off",
X up->logons,
X up->idletime
X );
X if (up->cmd != NULL)
X PrintF(g, NO_HEADER_MSG, " cmd='%s'", up->cmd);
X PrintF(g, NO_HEADER_MSG, "\n");
X }
X }
X }
X#endif /* DEBUG */
X
X for (hp=g->firstHost; hp != NULL; hp = hp->next) {
X if (hp->nUsers > hp->usersDone) {
X for (up = hp->firstUser; up != NULL; up = up->next)
X up->isOn = 0;
X (*hp->pollproc)(g, hp);
X
X /* Now go through and see if any users were logged on
X * the last time, but were no longer detected on.
X * If not, we can print a msg saying that this user
X * logged out.
X *
X * Note that we always do this, no matter what
X * up->detectLogoffs is set to, because we want to
X * report the rare instance where a user has been
X * idle, but sneaks on and logs off during ison's
X * sleep period.
X *
X * Basically we print the message if the user was
X * already on but idle then logs off before we
X * noticed that she wasn't idle, OR you said to
X * report logoffs.
X */
X for (up = hp->firstUser; up != NULL; up = up->next) {
X if ((up->wasOn || up->wasIdle) && (!up->isOn) &&
X (up->detectLogoffs || up->logons == 0)) {
X PrintF(g, 0,
X "Detected log off of %s at %s.\n",
X UserAddress(uabuf, hp, up),
X TimeStr(tmstr, sizeof(tmstr), (time_t)0)
X );
X up->wasOn = up->wasIdle = 0;
X up->idletime = 0;
X }
X }
X }
X }
X } while (g->nHosts > g->hostsDone);
X
X if (g->utmpfp != NULL)
X (void) fclose(g->utmpfp);
X PrintF(g, NO_BEEP_MSG, "Done!\n");
X} /* Poll */
X
X
X
X
X#if ansi
Xvoid ReadDataFile(IsonParams *g, char *fname)
X#else
Xvoid ReadDataFile(g, fname)
X IsonParams *g;
X char *fname;
X#endif
X{
X FILE *fp;
X int usersAdded, linenum;
X int fingerOnly, pollUntilActive, detectLogoffs;
X char buf[256], username[64], *cp, *dp, *cmd;
X
X if ((fp = fopen(fname, "r")) == NULL) {
X perror(fname);
X exit(EXIT_FATAL_ERROR);
X }
X
X usersAdded = linenum = 0;
X while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
X ++linenum;
X for (cp = buf; ; cp++) { /* Skip whitespace. */
X if (*cp == '\0') goto skip; /* Blank line? */
X if (!isspace(*cp)) break;
X }
X if (*cp == COMMENT_CHAR) goto skip;
X for (dp = cp; ; dp++) {
X if (*dp == '\0') goto syntax;
X if (isspace(*dp)) break;
X }
X *dp++ = 0;
X (void) strcpy(username, cp);
X
X for (; ; dp++) { /* Skip whitespace. */
X if (*dp == '\0') goto addUsr; /* No flags or cmd. */
X if (!isspace(*dp)) break;
X }
X
X fingerOnly = pollUntilActive = detectLogoffs = 0;
X cmd = NULL;
X
X if (*dp == '-') {
X /* Collect options for this user. */
X for (++dp; ; dp++) {
X if (*dp == '\0') goto addUsr; /* no EOLN? */
X if (isspace(*dp)) break;
X
X /* Check options. */
X if (*dp == 'I')
X pollUntilActive = 1;
X else if (*dp == 'F')
X fingerOnly = 1;
X else if (*dp == 'm')
X detectLogoffs = 1;
X else {
X PrintF(g, 0, "Illegal option '%c' on line %d.\n",
X (int) *dp, linenum);
X goto skip;
X }
X }
X }
X
X for (; ; dp++) { /* Skip whitespace. */
X if (*dp == '\0') goto addUsr; /* No command given. */
X if (!isspace(*dp)) break;
X }
X
X /* The command is what's left, if any. */
X cmd = dp;
X
X /* But let's strip off the EOLN. */
X cp = dp + strlen(dp) - 1;
X if (isspace(*cp))
X *cp-- = '\0';
X if (isspace(*cp))
X *cp = '\0';
X
XaddUsr:
X if (AddUser(g, username, fingerOnly, pollUntilActive,
X detectLogoffs, cmd) < 0) {
X PrintF(g, 0, "Too many users, out of memory!\n");
X break;
X }
X ++usersAdded;
X continue;
Xsyntax:
X PrintF(g, 0, "Syntax error on line %d.\n", linenum);
Xskip:
X continue;
X }
X if (usersAdded == 0) {
X PrintF(g, 0, "No users in data file.\n");
X exit(EXIT_USAGE);
X }
X} /* ReadDataFile */
X
X
X
X
X
X#if ansi
Xvoid Usage(IsonParams *g)
X#else
Xvoid Usage(g)
X IsonParams *g;
X#endif
X{
X (void) fprintf(stderr, "\n\
XIsOn Usage:\n\
X %s [-bq%s%sILmP] [-p N] [-i N] [-o outfile] [-c cmd] [-f userfile] [users]%s\n\
XUsernames:\n\
X They can be in the form \"user@remote.host\" to poll remote hosts, or\n\
X just \"user\" for the local host. You can also use the -f option to\n\
X supply a data file with many users (see the manual for the format).\n",
X g->progname,
X#if RPC
X "F",
X#else
X "",
X#endif
X#if DEBUG
X "d",
X#else
X "",
X#endif
X#if DAEMON
X ""
X#else
X " &"
X#endif
X );
X
X (void) fprintf(stderr, "\
XFlags:\n\
X -I : Poll until users are both logged on and not idle.\n\
X -L : Live (don't exit) when you log off. Usually used with -c.\n%s%s\
X -q : Don't print any output at all.\n\
X -P : Don't print 'Polling...' message.\n\
X -b : %seep when IsOn prints an important message.\n\
X -j : %sly.\n",
X#if RPC
X " -F : Use finger right away, don't bother with RPC.\n",
X#else
X "",
X#endif
X#if DEBUG
X " -d : Print debugging information while running.\n",
X#else
X "",
X#endif
X#if BEEP
X "Don't b",
X#else
X "B",
X#endif
X#if DAEMON
X "Don't go into the background automatical"
X#else
X "Go into the background immediate"
X#endif
X );
X
X (void) fprintf(stderr, "\
X -m : Report logons and logoffs of specified users forever.\n\
X -p N : Seconds between iterations (defaults: local=%d, remote=%d).\n\
X -i N : Give up after 'N' iterations (default is ",
X DEFAULT_LOCAL_SLEEP,
X DEFAULT_REMOTE_SLEEP
X );
X#if (MAXITER == NO_LIMIT)
X (void) fprintf(stderr, "infinity");
X#else
X (void) fprintf(stderr, "%ld", MAXITER);
X#endif
X
X (void) fprintf(stderr, ").\n\
X -o fil : Send output to 'fil' instead of the screen.\n\
X -c cmd : Command to run for each user found (e.g. \"write %%u %%t <msg\").\
X\n%s by Phil Dietz & Mike Gleason, NCEMRSoft.\n",
X VERSION_STR);
X
X exit(EXIT_USAGE);
X} /* Usage */
X
X
X
X#if ansi
Xvoid main(int argc, char **argv)
X#else
Xmain(argc, argv)
X int argc;
X char **argv;
X#endif
X{
X IsonParams g;
X int flag;
X char *cp, promptedName[64];
X char validOpts[32];
X char *cmd = COMMAND;
X int fingerOnly = 0;
X int pollUntilActive = 0;
X int dataFileUsed = 0;
X int detectLogoffs = 0;
X int i;
X
X g.sleep = -1;
X g.progname = argv[0];
X if ((cp = RINDEX(g.progname, '/')) != NULL)
X g.progname = cp + 1;
X g.daemon = DAEMON;
X g.debug = 0;
X g.nRemoteHosts = 0;
X g.memInUse = 0;
X g.autodie = 1;
X g.maxIters = MAXITER;
X g.iter = 0;
X g.pollmsg = 1;
X g.utmpfp = NULL;
X g.parentPid = getppid();
X g.stdinatty = isatty(0);
X g.nHosts = g.nRemoteHosts = g.hostsDone = 0;
X g.firstHost = g.lastHost = NULL;
X g.canBeep = BEEP;
X g.outfile = stderr;
X
X (void) strcpy(validOpts, "bIjqPmLc:o:p:f:i:");
X#if RPC
X (void) strcat(validOpts, "F");
X#endif
X#if DEBUG
X (void) strcat(validOpts, "d");
X#endif
X
X while ((flag = getopt(argc, argv, validOpts)) != EOF) {
X switch (flag) {
X case 'b':
X g.canBeep = !g.canBeep;
X break;
X case 'f':
X ReadDataFile(&g, optarg);
X dataFileUsed = 1;
X break;
X case 'F':
X fingerOnly++;
X break;
X case 'd':
X g.debug++;
X break;
X case 'j':
X g.daemon = !g.daemon;
X break;
X case 'm':
X detectLogoffs = 1;
X break;
X case 'o':
X if ((g.outfile = fopen(optarg, "a")) == NULL) {
X perror(optarg);
X exit(EXIT_FATAL_ERROR);
X }
X break;
X case 'q':
X g.outfile = NULL;
X break;
X case 'L':
X g.autodie = 0;
X break;
X case 'c':
X cmd = optarg;
X break;
X case 'P':
X g.pollmsg = !g.pollmsg;
X break;
X case 'p':
X g.sleep = atoi(optarg);
X break;
X case 'i':
X g.maxIters = atol(optarg);
X break;
X case 'I':
X pollUntilActive = 1;
X break;
X default:
X Usage(&g);
X }
X }
X
X if ((argv[optind] == NULL) && (dataFileUsed == 0)) {
X /* No users supplied on the command line. */
X if (g.stdinatty) {
X (void) fprintf(stderr, "User to poll: ");
X (void) fgets(promptedName, sizeof(promptedName), stdin);
X promptedName[strlen(promptedName) - 1] = '\0';
X if (promptedName[0] == '\0')
X Usage(&g); /* They just hit return. */
X (void) AddUser(&g, promptedName, fingerOnly, pollUntilActive,
X detectLogoffs, cmd);
X } else
X Usage(&g); /* Can't prompt from a shell script, etc. */
X } else {
X for (i=optind; i<argc; i++) {
X if (AddUser(&g, argv[i], fingerOnly, pollUntilActive,
X detectLogoffs, cmd) < 0) {
X PrintF(&g, 0, "Too many users, out of memory!\n");
X break;
X }
X }
X }
X
X
X /* Print a warning if the user chose a scenario where we know we
X * will never exit!
X */
X if (detectLogoffs && !g.autodie) {
X PrintF(&g, 0,
X"NOTE: since you enabled continuous log on/off detection and turned off\n\
Xself-termination when you logoff, ison will run forever! You will have to\n\
Xuse 'ps -u yourname' or some such to kill it later.");
X }
X
X /* Don't leave ^G's in actual files. */
X if (g.outfile != stderr)
X g.canBeep = 0;
X
X#if NICE
X /* lower our process' priority (nice) */
X (void) nice(20);
X#endif
X
X if (g.daemon) {
X if (fork()) /* automatically puts this task in
X * background! */
X exit(EXIT_PARENT);
X
X (void) signal(SIGINT, SIG_IGN);
X (void) signal(SIGQUIT, SIG_IGN);
X }
X
X (void) sleep(1); /* wait for your shell prompt to catch up. */
X (void) signal(SIGHUP, SIG_DFL);
X
X g.pid = getpid();
X
X Poll(&g); /* Finally, get down to business. */
X
X exit(EXIT_SUCCESSFUL);
X} /* main */
X
X/* eof ison.c */
END_OF_FILE
if test 30133 -ne `wc -c <'ison.c'`; then
echo shar: \"'ison.c'\" unpacked with wrong size!
fi
# end of 'ison.c'
fi
echo shar: End of shell archive.
exit 0
--
--mg mgleason@cse.unl.edu
exit 0 # Just in case...