/*
* $Id: uwatch.c,v 1.3 2002/06/13 22:49:27 howardjp Exp $
*
* Copyright (c) 1999
* Arbornet, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Arbornet, Inc., and
* its contributors.
* 4. Neither the name of Arbornet, Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ARBORNET, INC. AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ARBORNET, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* watch logins and logouts
1994: jared wrote me.
jared guest 5042 Apr 19 1994 /usr/staff/jared/src/watch.c
1996: craig fixed some problem, created another.
root wheel 4174 Aug 16 1996 /usr/local/src/watch.c
1997: jor consolidated code, removing bugs and unused features
jp2 added features back in, what a hot dog
jor broke inline code into subroutines
5/8/97: jp2 started using dates in the comments, better for
keeping track of updates, added -r option to enable watch
file, changed the default file to .watchrc ... and hey
jor, those features I put in were both new and useful :P
11/1/99: James Howard started cleaning up the code and fixing
some of the dumber things in it.
*/
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
struct utsname lh;
int localh, port, where, t, quick, response;
char *watchfile;
#define MAXNAMES 150 /* note there's no table overflow checks */
#define MAXTTYS 64 /* a guess */
struct linerec { char name[10]; char line[20]; }
lines[MAXTTYS]; /* a record for each tty/line/port */
char bell, names[MAXNAMES][9]; /* table of names to watch for */
void notify(char *msg, struct utmp login) {
char tmpname[65] = "\0", tmphost[65] = "\0", tmpline[65] = "\0", tmptime
[65] = "\0";
strncpy(tmpname, login.ut_name, UT_NAMESIZE);
strncpy(tmphost, login.ut_host, UT_HOSTSIZE);
strncpy(tmpline, login.ut_line, UT_LINESIZE);
strftime(tmptime, 64, "%B %d, %H:%M", localtime(&login.ut_time));
printf("\a%s: %s", msg, tmpname);
if(localh)
printf("@%s", lh.nodename);
if(port)
printf(" Port: %s", tmpline);
if(where)
printf(" Host: %s", tmphost);
if(t)
printf(" Time: %s", tmptime);
printf("\n");
}
void read_watchlist()
{ FILE *wl; char name[10]; int n, l; n = 0;
wl = fopen(watchfile, "r");
if (wl == NULL) return;
while (fgets(name, 9, wl) != NULL) {
l = strlen(name) - 1;
if (name[l] == '\n') name[l] = '\0';
/* better way to kill newline char? */
/* I have a much better parser I'll add later -jp2 */
strcpy(names[n++], name);
}
fclose(wl);
}
void usage()
{
fprintf(stderr, "\
usage: watch [-f file] [-lpqrtw] [login ...]\n\
\t-f\t-- set response file (-r implied)\n\
\t-l\t-- list localhost\n\
\t-p\t-- list port\n\
\t-q\t-- list only who is logged in and terminate\n\
\t-r\t-- use response file (defualts to ~/.watchrc)\n\
\t-t\t-- list login/logout time\n\
\t-?\t-- print this message\n");
exit(0);
}
void processparams(argc,argv)
int argc;
char **argv;
{
int ch, x;
if (argc == 0) return;
while((ch = getopt(argc, argv, "f:lpqrtw?")) != EOF) {
switch(ch) {
case 'f':
watchfile = optarg;
case 'r':
response = 1;
break;
case 'l':
localh = 1;
uname(&lh);
break;
case 'p':
port = 1;
break;
case 'q':
quick = 1;
break;
case 't':
t = 1;
break;
case 'w':
where = 1;
break;
case '?':
usage();
}
}
for (x = 1; x < argc; x++)
{
if (argv[x][0] != '-')
{
int n;
for (n = 0; n < MAXNAMES; n++)
if (names[n][0] == 0) break;
strcpy(names[n], argv[x]);
} else {
if (argv[x][1] == 's') bell = 32;
}
}
return;
}
int inlist (char *logname)
{ int n;
if (logname[0] == '\0') return 0;
if (names[0][0] == 0 ) return 1;
for (n = 0 ; n < MAXNAMES ; n++)
if (strcmp(logname,names[n]) == 0) return 1;
return 0;
}
void read_utmp()
{ int fd, l; struct utmp u;
fd = open(_PATH_UTMP, O_RDONLY);
if (fd == -1) { perror(_PATH_UTMP); exit(1); }
while (read(fd, &u, sizeof (struct utmp)) == sizeof(struct utmp))
if (strcmp(u.ut_name, "") != 0)
{
if (inlist(u.ut_name)) notify("In", u);
for (l = 0; l < MAXTTYS; l++)
if (strcmp(lines[l].name, "") == 0)
{ strcpy(lines[l].name,u.ut_name);
strcpy(lines[l].line,u.ut_line);
break;
}
}
close(fd);
}
int open_wtmp()
{
int rc = open(_PATH_WTMP,O_RDONLY);
if (rc == -1)
perror(_PATH_WTMP);
else
lseek(rc, 0, SEEK_END);
return(rc);
}
void tail_wtmp(int wtfd)
{
int l; /*which line? */
struct utmp uin;
if (read(wtfd, &uin, sizeof(struct utmp)) != sizeof(struct utmp))
return;
for (l = 0; l < MAXTTYS ; l++)
if (strcmp(lines[l].line, uin.ut_line) == 0 ||
strcmp(lines[l].line, "") == 0)
break;
if (inlist(uin.ut_name))
notify("\r\nIn", uin);
if (inlist(lines[l].name)) {
strncpy(uin.ut_name, lines[l].name, UT_NAMESIZE);
notify("\r\nOut", uin);
uin.ut_name[0] = '\0';
}
strcpy(lines[l].name, uin.ut_name);
strcpy(lines[l].line, uin.ut_line);
}
char **rargv;
void rehash(int nil)
{
if(vfork() )
execvp(rargv[0], rargv);
else
exit(0);
}
void logout(int nil)
{
exit(EXIT_SUCCESS);
}
int main (argc,argv) int argc; char **argv;
{
struct kevent events[3]; /* SIGHUP handler, ppid dies, file to read */
if(!(watchfile = malloc(strlen(getenv("HOME"))+strlen("/.watchrc")+1)))
return(fprintf(stderr,"malloc failure\n"),EXIT_FAILURE);
else
watchfile = strcat(getenv("HOME"),"/.watchrc");
rargv = argv;
processparams(argc,argv); /* opts, more names */
if (response)
read_watchlist(); /* store in names[n] */
read_utmp(); /* store in lines[l] */
if (quick == 1)
return(EXIT_SUCCESS);
EV_SET(&events[0],SIGHUP,EVFILT_SIGNAL,EV_ADD,0,NULL,rehash);
EV_SET(&events[1],getppid(),EVFILT_PROC,EV_ADD,NOTE_EXIT,NULL,logout);
if (!fork())
{
int evtQ;
int wtmpFd;
struct kevent myEvt;
if((evtQ = kqueue()) == -1)
return(fprintf(stderr,"couldn't create kernel Q\n"),EXIT_FAILURE);
if((wtmpFd = open_wtmp()) == -1)
return(fprintf(stderr,"open_wtmp\n"),EXIT_FAILURE);
EV_SET(&events[2],wtmpFd,EVFILT_READ,EV_ADD,0,NULL,tail_wtmp);
if(kevent(evtQ,events,3,NULL,0,NULL) == -1)
return(fprintf(stderr,"couldn't insert events\n"),EXIT_FAILURE);
(void)sigblock(sigmask(SIGHUP));
for( ; ; )
{
if(kevent(evtQ,NULL,0,&myEvt,1,NULL) == -1)
return(fprintf(stderr,"evt read failure\n"),EXIT_FAILURE);
((void (*)(int))myEvt.udata)(myEvt.ident);
}
return(EXIT_SUCCESS);
}
return(EXIT_SUCCESS);
}
syntax highlighted by Code2HTML, v. 0.9.1