/* * Copyright (c) 1999 Seth Kingsley. 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 Seth Kingsley and * contributors. * 4. Neither the author's name, nor any of the contributors names may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. * */ /* ** idle.c ** Displays a message as soon as another user is no ** longer idle. ** ** Author: ** Seth Kingsley ** ** Overview: ** Reads through the utmp for active logins that the ** specified user is on and then checks every REFRESH ** seconds to see if this user has accessed their ** terminal since it was last checked. ** ** Compile options: ** TRACE: If defined, print out ident and idle time ** each time a utmp entry is read. ** ** Usage: ** idle ** ** TODO: ** + Add option for variable refresh rate. ** + Port to *BSD. (done) */ #include #ifndef Linux #include #include #endif /* !Linux */ #include #include #include #include #ifndef lint static const char rcsid[] = "$Id: idle.c,v 1.3 2000/03/09 11:35:26 sethk Exp $"; #endif /* !lint */ #define REFRESH 20 /* Seconds */ #ifdef Linux #define ut_user ut_name #endif /* Linux */ /* Global data */ #ifndef Linux static FILE *_ufp; /* utmp file pointer */ #endif /* !Linux */ static struct utmp _ut_buf; /* utmp entry buffer */ static int _uto_sem = 0; /* utmp open semaphore: This exists so that * the utmp file is closed even if the * process is interrupted by a signal. */ static char *_prog = "idle"; /* Default, reset to argv[0] */ /******************** * Internal routines * ********************/ #ifndef Linux /* ** Replacements for Linux utmp functions */ static void endutent(void) { assert(_ufp != NULL); fclose(_ufp); _ufp = NULL; } static struct utmp *getutent(void) { if (_ufp == NULL) if ((_ufp = fopen(_PATH_UTMP, "r")) == NULL) return NULL; if (fread((char *)&_ut_buf, sizeof(struct utmp), 1, _ufp) != 1) return NULL; else return &_ut_buf; } #endif /* !Linux */ /* ** signal_handler ** Callback for clean handling of signals. ** ** Args: ** sig: The signal that was caught. ** ** Return: ** Nothing. */ static void signal_handler(int sig) { if (_uto_sem) endutent(); fprintf(stderr, "%s: exiting on signal %d\n", _prog, sig); exit(1); } /* ** is_logged_in ** Check utmp entry to see if it is for a logged in user. ** ** Args: ** ut: Pointer to utmp entry. ** ** Return: ** True/False if user is logged in */ static int is_logged_in(struct utmp *ut) { #ifdef Linux return (ut->ut_type == USER_PROCESS); #else return (*(ut->ut_line) != '\0'); #endif /* Linux */ } /*************** * Main routine * ***************/ int main(int argc, char *argv[]) { struct utmp *ut_ent; /* utmp entry */ struct stat dev_stat; /* Device status for the user's * terminal. */ char dev_fn[5 + UT_LINESIZE + 1]; /* "/dev/" "\0" */ time_t now_tm; _prog = argv[0]; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGQUIT, signal_handler); if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(1); } time(&now_tm); while (1) { while ((ut_ent = getutent()) != (struct utmp *)NULL) { if (!_uto_sem) _uto_sem = 1; #ifdef TRACE if (is_logged_in(ut_ent)) printf("%s on /dev/%s\n", ut_ent->ut_name, ut_ent->ut_line); #endif /* TRACE */ if (is_logged_in(ut_ent) && !strcmp(argv[1], ut_ent->ut_name)) { sprintf(dev_fn, "/dev/%s", ut_ent->ut_line); if (stat(dev_fn, &dev_stat) == -1) { perror(dev_fn); exit(2); } #ifdef TRACE printf("%lu [%lu]\n", dev_stat.st_atime, now_tm - dev_stat.st_atime); #endif /* TRACE */ if (now_tm - dev_stat.st_atime < REFRESH) { printf("%s:\a\n\n!!! %s is no longer idle on %s !!!\a\n\n", _prog, argv[1], dev_fn); exit(0); } } } endutent(); _uto_sem = 0; #ifdef TRACE puts("----------------------"); #endif /* TRACE */ sleep(REFRESH); } /* NOTREACHED */ return 0; }