 af2d3b48c6
			
		
	
	
		af2d3b48c6
		
	
	
	
	
		
			
			The localtime function takes a time_t* argument. Passing the ut_time member of the utmp struct as this argument doesn't work on 64 bit systems because the lengths of the arguments don't agree. So use a temproary variable to store the ut_time value in a time_t variable and pass the address of the time_t variable to localtime. Signed-off-by: Clark Rawlins <clark@bit63.org>
		
			
				
	
	
		
			1624 lines
		
	
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1624 lines
		
	
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | ||
| 		"@(#) timeoutd.c 1.6 by Shane Alderton"
 | ||
| 			based on:
 | ||
| 		"@(#) autologout.c by David Dickson" 
 | ||
| 
 | ||
|     This program is free software; you can redistribute it and/or modify
 | ||
|     it under the terms of the GNU General Public License as published by
 | ||
|     the Free Software Foundation; either version 2 of the License, or
 | ||
|     (at your option) any later version.
 | ||
| 
 | ||
|     This program is distributed in the hope that it will be useful,
 | ||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||
|     GNU General Public License for more details.
 | ||
| 
 | ||
|     You should have received a copy of the GNU General Public License
 | ||
|     along with this program; if not, write to the Free Software
 | ||
|     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | ||
| 
 | ||
|     Thanks to:
 | ||
|     	David Dickson for writing the original autologout.c
 | ||
|     	programme upon which this programme was based.
 | ||
| 
 | ||
| */
 | ||
| /* #define DEBUG _DEBUG_ */
 | ||
| #include    <unistd.h>
 | ||
| #include    <stdlib.h>
 | ||
| #include    <sys/ioctl.h>
 | ||
| #include    <fcntl.h>
 | ||
| #include    <stdio.h>
 | ||
| #include    <signal.h>
 | ||
| #include    <string.h>
 | ||
| #include    <sys/types.h>
 | ||
| #include    <sys/wait.h>
 | ||
| #include    <sys/stat.h>
 | ||
| #include    <utmp.h>
 | ||
| #include    <pwd.h>
 | ||
| #include    <grp.h>
 | ||
| #include    <sys/syslog.h>
 | ||
| #include    <time.h>
 | ||
| #include    <ctype.h>
 | ||
| #include    <termios.h>
 | ||
| #include    <fcntl.h>
 | ||
| #include    <dirent.h>
 | ||
| 
 | ||
| #ifdef TIMEOUTDX11
 | ||
| #include <netdb.h>
 | ||
| #include <X11/Xlib.h>
 | ||
| #include <X11/extensions/scrnsaver.h>
 | ||
| 
 | ||
| #define TIMEOUTD_XSESSION_NONE 0
 | ||
| #define TIMEOUTD_XSESSION_LOCAL 1
 | ||
| #define TIMEOUTD_XSESSION_REMOTE 2
 | ||
| #endif
 | ||
| 
 | ||
| #define OPENLOG_FLAGS	LOG_CONS|LOG_PID
 | ||
| #define SYSLOG_DEBUG	LOG_DEBUG
 | ||
| 
 | ||
| /* For those systems (SUNOS) which don't define this: */
 | ||
| #ifndef WTMP_FILE
 | ||
| #define WTMP_FILE "/usr/adm/wtmp"
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef SUNOS
 | ||
| #define ut_pid ut_time
 | ||
| #define ut_user ut_name
 | ||
| #define SEEK_CUR 1
 | ||
| #define SEEK_END 2
 | ||
| #define SURE_KILL 1
 | ||
| 
 | ||
| FILE	*utfile = NULL;
 | ||
| #define NEED_UTMP_UTILS
 | ||
| #define NEED_STRSEP
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| #ifdef NEED_UTMP_UTILS
 | ||
| void setutent()
 | ||
| {
 | ||
| 	if (utfile == NULL)
 | ||
| 	{
 | ||
| 		if ((utfile = fopen("/etc/utmp", "r")) == NULL)
 | ||
| 		{
 | ||
| 			openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 			syslog(LOG_ERR, "Could not open /etc/utmp");
 | ||
| 			closelog();
 | ||
| 			exit(1);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	else fseek(utfile, 0L, 0);
 | ||
| }
 | ||
| 
 | ||
| struct utmp *getutent()    /* returns next utmp file entry */
 | ||
| {
 | ||
| 	static struct utmp	uent;
 | ||
| 
 | ||
| 	while (fread(&uent, sizeof(struct utmp), 1, utfile) == 1)
 | ||
| 	{
 | ||
| 		if (uent.ut_line[0] != 0 && uent.ut_name[0] != 0) 
 | ||
| 			return &uent;
 | ||
| 	}
 | ||
| 	return (struct utmp *) NULL;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| #ifndef linux
 | ||
| #define N_TTY 1
 | ||
| #define N_SLIP 2
 | ||
| #endif
 | ||
| 
 | ||
| #ifndef CONFIG
 | ||
| #define CONFIG "/etc/timeouts"
 | ||
| #endif
 | ||
| 
 | ||
| #define MAXLINES 512
 | ||
| #define max(a,b) ((a)>(b)?(a):(b))
 | ||
| 
 | ||
| #define ACTIVE		1
 | ||
| #define IDLEMAX		2
 | ||
| #define SESSMAX		3
 | ||
| #define DAYMAX		4
 | ||
| #define NOLOGIN		5
 | ||
| /*#define XSESSION	6*/
 | ||
| #define	IDLEMSG		0
 | ||
| #define	SESSMSG		1
 | ||
| #define	DAYMSG		2
 | ||
| #define	NOLOGINMSG	3
 | ||
| 
 | ||
| #define KWAIT		5  /* Time to wait after sending a kill signal */
 | ||
| 
 | ||
| char	*limit_names[] = {"idle", "session", "daily", "nologin"};
 | ||
| 
 | ||
| char	*daynames[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA", "WK", "AL", NULL};
 | ||
| char	daynums[] = {   1  ,  2  ,  4  ,  8  ,  16 ,  32 ,  64 ,  62 ,  127, 0};
 | ||
| 
 | ||
| struct utmp *utmpp;         /* pointer to utmp file entry */
 | ||
| char        *ctime();       /* returns pointer to time string */
 | ||
| struct utmp *getutent();    /* returns next utmp file entry */
 | ||
| void	    shut_down();
 | ||
| void	    read_config();
 | ||
| void	    reread_config();
 | ||
| void        reapchild();
 | ||
| void        free_wtmp();
 | ||
| void        check_idle();
 | ||
| void        read_wtmp();
 | ||
| void        bailout();
 | ||
| char	    chk_timeout();
 | ||
| void	    logoff_msg();
 | ||
| void	    killit();
 | ||
| int	    getdisc();
 | ||
| int	    chk_xsession(); /* seppy: is it a X-Session? */
 | ||
| void	    killit_xsession(); /* seppy: kill the X-Session*/
 | ||
| int 	    chk_ssh(pid_t pid); /* seppy: check if user is logged in via ssh (we have to
 | ||
| handle that different... ;( */
 | ||
| char	    *getusr(pid_t pid); /*seppy: get the owner of a running process */
 | ||
| void	    segfault(); /* seppy: catch segfault and log them */
 | ||
| int	    chk_xterm(); /* seppy: is it a xterm? */
 | ||
| pid_t	    getcpid(); /* seppy: get the child's pid. Needed for ssh */
 | ||
| 
 | ||
| #ifdef TIMEOUTDX11
 | ||
| Time	    get_xidle(); /* seppy: how long is user idle? (user,display)*/
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| struct ut_list {
 | ||
| 	struct utmp	elem;
 | ||
| 	struct ut_list	*next;
 | ||
| };
 | ||
| 
 | ||
| struct ut_list	*wtmplist = (struct ut_list *) NULL;
 | ||
| struct ut_list	*ut_list_p;
 | ||
| 
 | ||
| struct time_ent {
 | ||
| 	int	days;
 | ||
| 	int	starttime;
 | ||
| 	int	endtime;
 | ||
| };
 | ||
| 
 | ||
| struct config_ent {
 | ||
| 	struct time_ent	*times;
 | ||
| 	char	*ttys;
 | ||
| 	char	*users;
 | ||
| 	char	*groups;
 | ||
| 	char	login_allowed;
 | ||
| 	int	idlemax;
 | ||
| 	int	sessmax;
 | ||
| 	int	daymax;
 | ||
| 	int	warntime;
 | ||
| 	char	*messages[10];
 | ||
| };
 | ||
| 
 | ||
| struct config_ent	*config[MAXLINES + 1];
 | ||
| char	errmsg[256];
 | ||
| char	dev[sizeof(utmpp->ut_line)];
 | ||
| unsigned char	limit_type;
 | ||
| int	configline = 0;
 | ||
| int	pending_reread = 0;
 | ||
| int	allow_reread = 0;
 | ||
| time_t	time_now;
 | ||
| struct tm	now;
 | ||
| int		now_hhmm;
 | ||
| int	daytime = 0;	/* Amount of time a user has been on in current day */
 | ||
| char path[255]; /*seppy*/
 | ||
| FILE *proc_file;/*seppy*/
 | ||
| char comm[16]; /*seppy; to save the command of a pid*/
 | ||
| 
 | ||
| #ifdef NEED_STRCASECMP
 | ||
| int strcasecmp(char *s1, char *s2)
 | ||
| {
 | ||
| 	while (*s1 && *s2)
 | ||
| 	{
 | ||
| 	  if (tolower(*s1) < tolower(*s2))
 | ||
| 	  	return -1;
 | ||
| 	  else if (tolower(*s1) > tolower(*s2))
 | ||
| 	  	return 1;
 | ||
| 	  s1++;
 | ||
| 	  s2++;
 | ||
| 	}
 | ||
| 	if (*s1)
 | ||
| 		return -1;
 | ||
| 	if (*s2)
 | ||
| 		return 1;
 | ||
| 	return 0;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef NEED_STRSEP
 | ||
| char *strsep (stringp, delim)
 | ||
| char **stringp;
 | ||
| char *delim;
 | ||
| {
 | ||
| 	char	*retp = *stringp;
 | ||
| 	char	*p;
 | ||
| 
 | ||
| 	if (!**stringp) return NULL;
 | ||
| 
 | ||
| 	while (**stringp)
 | ||
| 	{
 | ||
| 		p = delim;
 | ||
| 		while (*p)
 | ||
| 		{
 | ||
| 		  if (*p == **stringp)
 | ||
| 		  {
 | ||
| 		    **stringp = '\0';
 | ||
| 		    (*stringp)++;
 | ||
| 		    return retp;
 | ||
| 		  }
 | ||
| 		  p++;
 | ||
| 	  	}
 | ||
| 	  	(*stringp)++;
 | ||
| 	}
 | ||
| 	return retp;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| int main(argc, argv)
 | ||
| int	argc;
 | ||
| char	*argv[];
 | ||
| {
 | ||
|     signal(SIGTERM, shut_down);
 | ||
|     signal(SIGHUP, reread_config);
 | ||
|     signal(SIGCHLD, reapchild);
 | ||
|     signal(SIGINT, SIG_IGN);
 | ||
|     signal(SIGQUIT, SIG_IGN);
 | ||
|     signal(SIGSEGV, segfault);
 | ||
| 
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 
 | ||
| /* The only valid invocations are "timeoutd" or "timeoutd user tty" */
 | ||
|     if (argc != 1 && argc != 3)
 | ||
|     {
 | ||
|       openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|       syslog(LOG_ERR, "Incorrect invocation of timeoutd (argc=%d) by UID %d.", argc, getuid());
 | ||
|       closelog();
 | ||
|       exit(5);
 | ||
|     }
 | ||
| 
 | ||
|     /* read config file into memory */
 | ||
|     read_config();
 | ||
| 
 | ||
| /* Change into the root filesystem to avoid "device busy" errors when the
 | ||
|  * filesystem from which we were started is unmounted.  /dev is convenient as
 | ||
|  * ut_line fields are relative to it.
 | ||
|  */
 | ||
|     if (chdir("/dev"))
 | ||
|     {
 | ||
|       openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|       syslog(LOG_ERR, "Could not change working directory to /dev!");
 | ||
|       closelog();
 | ||
|       exit(1);
 | ||
|     }
 | ||
| 
 | ||
| /* Handle the "timeoutd user tty" invocation */
 | ||
| /* This is a bit of a shameless hack, but, well, it works. */
 | ||
|     if (argc == 3)
 | ||
|     {
 | ||
| #ifdef DEBUG
 | ||
|         openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(SYSLOG_DEBUG, "Running in user check mode.  Checking user %s on %s.", argv[1], argv[2]);
 | ||
|         closelog();
 | ||
| #endif
 | ||
|     	strncpy(dev, argv[2], sizeof(dev) - 1);
 | ||
|     	dev[sizeof(dev) - 1] = '\0';
 | ||
|         time_now = time((time_t *)0);  /* get current time */
 | ||
|         now = *(localtime(&time_now));  /* Break it into bits */
 | ||
|         now_hhmm = now.tm_hour * 100 + now.tm_min;
 | ||
|         allow_reread = 0;
 | ||
|         read_wtmp(); /* Read in today's wtmp entries */
 | ||
|         switch(chk_timeout(argv[1], dev, "",  0, 0))
 | ||
|         {
 | ||
|             case DAYMAX:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_NOTICE,
 | ||
| 		       "User %s on %s exceeded maximum daily limit (%d minutes).  Login check failed.",
 | ||
| 		       argv[1], argv[2], config[configline]->daymax);
 | ||
|     		closelog();
 | ||
|     		/*
 | ||
|     		printf("\r\nLogin not permitted.  You have exceeded your maximum daily limit.\r\n");
 | ||
|     		printf("Please try again tomorrow.\r\n");
 | ||
|     		*/
 | ||
|     		logoff_msg(1);
 | ||
|     		exit(10);
 | ||
|             case NOLOGIN:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_NOTICE,
 | ||
| 		       "User %s not allowed to login on %s at this time.  Login check failed.",
 | ||
| 		       argv[1], argv[2]);
 | ||
|     		closelog();
 | ||
|     		/*
 | ||
|     		printf("\r\nLogin not permitted at this time.  Please try again later.\r\n");
 | ||
|     		*/
 | ||
|     		logoff_msg(1);
 | ||
|     		exit(20);
 | ||
|             case ACTIVE:
 | ||
| #ifdef DEBUG
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(SYSLOG_DEBUG, "User %s on %s passed login check.", argv[1], argv[2]);
 | ||
|     		closelog();
 | ||
| #endif
 | ||
| 		free_wtmp();
 | ||
| 		exit(0);
 | ||
|             default:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_ERR, "Internal error checking user %s on %s - unexpected return from chk_timeout",
 | ||
| 			argv[1], argv[2]);
 | ||
|     		closelog();
 | ||
|     		exit(30);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /* If running in daemon mode (no parameters) */
 | ||
|     if (fork())             /* the parent process */
 | ||
|         exit(0);            /* exits */
 | ||
| 
 | ||
|     close(0);
 | ||
|     close(1);
 | ||
|     close(2);
 | ||
|     setsid();
 | ||
| 
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(LOG_NOTICE, "Daemon started.");
 | ||
|     closelog();
 | ||
| 
 | ||
|     /* the child processes all utmp file entries: */
 | ||
|     while (1)
 | ||
|     {
 | ||
|         /* Record time at which we woke up & started checking */
 | ||
|         time_now = time((time_t *)0);  /* get current time */
 | ||
|         now = *(localtime(&time_now));  /* Break it into bits */
 | ||
|         now_hhmm = now.tm_hour * 100 + now.tm_min;
 | ||
|         allow_reread = 0;
 | ||
|         read_wtmp(); /* Read in today's wtmp entries */
 | ||
|     	setutent();
 | ||
| #ifdef DEBUG
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     	syslog(SYSLOG_DEBUG, "Time to check utmp for exceeded limits.");
 | ||
|     	closelog();
 | ||
| #endif
 | ||
|         while ((utmpp = getutent()) != (struct utmp *) NULL)
 | ||
|             check_idle();
 | ||
|         free_wtmp();  /* Free up memory used by today's wtmp entries */
 | ||
|         allow_reread = 1;
 | ||
|         if (pending_reread)
 | ||
|            reread_config(SIGHUP);
 | ||
| #ifdef DEBUG
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(SYSLOG_DEBUG, "Finished checking utmp... sleeping for 1 minute.");
 | ||
| 	closelog();
 | ||
| #endif
 | ||
|         sleep(60);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Read in today's wtmp entries */
 | ||
| 
 | ||
| void read_wtmp()
 | ||
| {
 | ||
|     FILE	*fp;
 | ||
|     struct utmp	ut;
 | ||
|     struct tm	*tm;
 | ||
|     time_t time;
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Reading today's wtmp entries.");
 | ||
|     closelog();
 | ||
| #endif
 | ||
| 
 | ||
|     if ((fp = fopen(WTMP_FILE, "r")) == NULL)
 | ||
|       bailout("Could not open wtmp file!", 1);
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Seek to end of wtmp");
 | ||
|     closelog();
 | ||
| #endif
 | ||
|     /* Go to end of file minus one structure */
 | ||
|     fseek(fp, -1L * sizeof(struct utmp), SEEK_END);
 | ||
| 
 | ||
|     while (fread(&ut, sizeof(struct utmp), 1, fp) == 1)
 | ||
|     {
 | ||
|       /* On 64 bit systems time_t is a 64 bit integer
 | ||
|          while ut_time is a 32 bit (unsigned) integer.
 | ||
| 	 Copy the value to a time_t value so the pointer
 | ||
|          types agree in the call to localtime.
 | ||
|        */
 | ||
|       time = ut.ut_time;
 | ||
|       tm = localtime(&time);
 | ||
| 
 | ||
|       if (tm->tm_year != now.tm_year || tm->tm_yday != now.tm_yday)
 | ||
|         break;
 | ||
| 
 | ||
| #ifndef SUNOS
 | ||
|       if (ut.ut_type == USER_PROCESS ||
 | ||
|           ut.ut_type == DEAD_PROCESS ||
 | ||
|           ut.ut_type == UT_UNKNOWN ||	/* SA 19940703 */
 | ||
| 	  ut.ut_type == LOGIN_PROCESS ||
 | ||
|           ut.ut_type == BOOT_TIME)
 | ||
| #endif
 | ||
|       {
 | ||
|         if ((ut_list_p = (struct ut_list *) malloc(sizeof(struct ut_list))) == NULL)
 | ||
|           bailout("Out of memory in read_wtmp.", 1);
 | ||
|         ut_list_p->elem = ut;
 | ||
|         ut_list_p->next = wtmplist;
 | ||
|         wtmplist = ut_list_p;
 | ||
|       }
 | ||
| 
 | ||
|       /* Position the file pointer 2 structures back */
 | ||
|       if (fseek(fp, -2 * sizeof(struct utmp), SEEK_CUR) < 0) break;
 | ||
|     }
 | ||
|     fclose(fp);
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Finished reading today's wtmp entries.");
 | ||
|     closelog();
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| /* Free up memory used by today's wtmp entries */
 | ||
| 
 | ||
| void free_wtmp()
 | ||
| {
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Freeing list of today's wtmp entries.");
 | ||
|     closelog();
 | ||
| #endif
 | ||
| 
 | ||
|     while (wtmplist)
 | ||
|     {
 | ||
| #ifdef DEBUG_WTMP
 | ||
|     struct tm	*tm;
 | ||
|     tm = localtime(&(wtmplist->elem.ut_time));
 | ||
|     printf("%d:%d %s %s %s\n", 
 | ||
|     	tm->tm_hour,tm->tm_min, wtmplist->elem.ut_line,
 | ||
|     	wtmplist->elem.ut_user,
 | ||
| #ifndef SUNOS
 | ||
|     	wtmplist->elem.ut_type == LOGIN_PROCESS?"login":wtmplist->elem.ut_type == BOOT_TIME?"reboot":"logoff"
 | ||
| #else
 | ||
| 	""
 | ||
| #endif
 | ||
| 	);
 | ||
| #endif
 | ||
|         ut_list_p = wtmplist;
 | ||
|         wtmplist = wtmplist->next;
 | ||
|         free(ut_list_p);
 | ||
|     }
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Finished freeing list of today's wtmp entries.");
 | ||
|     closelog();
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| void	store_times(t, time_str)
 | ||
| struct time_ent **t;
 | ||
| char *time_str;
 | ||
| {
 | ||
|     int	i = 0;
 | ||
|     int	ar_size = 2;
 | ||
|     char	*p;
 | ||
|     struct time_ent	*te;
 | ||
| 
 | ||
|     while (time_str[i])
 | ||
|       if (time_str[i++] == ',')
 | ||
|         ar_size++;
 | ||
| 
 | ||
|     if ((*t = (struct time_ent *) malloc (ar_size * sizeof(struct time_ent))) == NULL)
 | ||
| 	bailout("Out of memory", 1);
 | ||
|     te = *t;
 | ||
| 
 | ||
|     p = strtok(time_str, ",");
 | ||
| /* For each day/timerange set, */
 | ||
|     while (p)
 | ||
|     {
 | ||
| /* Store valid days */
 | ||
|       te->days = 0;
 | ||
|       while (isalpha(*p))
 | ||
|       {
 | ||
|         if (!p[1] || !isalpha(p[1]))
 | ||
|         {
 | ||
| 	  openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|           syslog(LOG_ERR, "Malformed day name (%c%c) in time field of config file (%s).  Entry ignored.", p[0], p[1], CONFIG);
 | ||
|           closelog();
 | ||
|           (*t)->days = 0;
 | ||
|           return;
 | ||
|         }
 | ||
|         *p = toupper(*p);
 | ||
|         p[1] = toupper(p[1]);
 | ||
| 
 | ||
|       	i = 0;
 | ||
|       	while (daynames[i])
 | ||
|       	{
 | ||
|       	  if (!strncmp(daynames[i], p, 2))
 | ||
|       	  {
 | ||
|       	    te->days |= daynums[i];
 | ||
|       	    break;
 | ||
|       	  }
 | ||
|       	  i++;
 | ||
|       	}
 | ||
|       	if (!daynames[i])
 | ||
|       	{
 | ||
| 	  openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|           syslog(LOG_ERR, "Malformed day name (%c%c) in time field of config file (%s).  Entry ignored.", p[0], p[1], CONFIG);
 | ||
|           closelog();
 | ||
|           (*t)->days = 0;
 | ||
|           return;
 | ||
|       	}
 | ||
|       	p += 2;
 | ||
|       }
 | ||
| 
 | ||
| /* Store start and end times */
 | ||
|       if (*p)
 | ||
|       {
 | ||
|         if (strlen(p) != 9 || p[4] != '-')
 | ||
|         {
 | ||
| 	  openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|           syslog(LOG_ERR, "Malformed time (%s) in time field of config file (%s).  Entry ignored.", p, CONFIG);
 | ||
|           closelog();
 | ||
|           (*t)->days = 0;
 | ||
|           return;
 | ||
|         }
 | ||
|         te->starttime = atoi(p);
 | ||
|         te->endtime = atoi(p+5);
 | ||
|         if ((te->starttime == 0 && strncmp(p, "0000-", 5)) || (te->endtime == 0 && strcmp(p+5, "0000")))
 | ||
|         {
 | ||
| 	  openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|           syslog(LOG_ERR, "Invalid range (%s) in time field of config file (%s).  Entry ignored.", p, CONFIG);
 | ||
|           closelog();
 | ||
|           (*t)->days = 0;
 | ||
|           return;
 | ||
|         }
 | ||
|       }
 | ||
|       else
 | ||
|       {
 | ||
|       	te->starttime = 0;
 | ||
|       	te->endtime = 2359;
 | ||
|       }
 | ||
|       p = strtok(NULL, ",");
 | ||
|       te++;
 | ||
|     }
 | ||
|     te->days = 0;
 | ||
| }
 | ||
| 
 | ||
| void alloc_cp(a, b)
 | ||
| char **a;
 | ||
| char *b;
 | ||
| {
 | ||
| 	if ((*a = (char *) malloc(strlen(b)+1)) == NULL)
 | ||
| 		bailout("Out of memory", 1);
 | ||
| 	else	strcpy(*a, b);
 | ||
| }
 | ||
| 
 | ||
| void read_config()
 | ||
| {
 | ||
|     FILE	*config_file;
 | ||
|     char	*p;
 | ||
|     char	*lstart;
 | ||
|     int		i = 0;
 | ||
| #ifdef DEBUG
 | ||
|     int		j = 0;
 | ||
| #endif
 | ||
|     char	line[256];
 | ||
|     char	*tok;
 | ||
|     int		linenum = 0;
 | ||
| 
 | ||
|     if ((config_file = fopen(CONFIG, "r")) == NULL)
 | ||
|       bailout("Cannot open config file", 1);
 | ||
| 
 | ||
|     while (fgets(line, 256, config_file) != NULL)
 | ||
|     {
 | ||
|       linenum++;
 | ||
|       p = line;
 | ||
|       while  (*p && (*p == ' ' || *p == '\t'))
 | ||
|         p++;
 | ||
|       lstart = p;
 | ||
|       while (*p && *p != '#' && *p != '\n')
 | ||
|         p++;
 | ||
|       *p = '\0';
 | ||
|       if (*lstart)
 | ||
|       {
 | ||
|       	if (i == MAXLINES)
 | ||
|       	  bailout("Too many lines in timeouts config file.", 1);
 | ||
|         if ((config[i] = (struct config_ent *) malloc(sizeof(struct config_ent))) == NULL)
 | ||
|           bailout("Out of memory", 1);
 | ||
|   	config[i]->times = NULL;
 | ||
|   	config[i]->ttys = NULL;
 | ||
|   	config[i]->users = NULL;
 | ||
|   	config[i]->groups = NULL;
 | ||
|   	config[i]->login_allowed = 1;
 | ||
|   	config[i]->idlemax = -1;
 | ||
|   	config[i]->sessmax = -1;
 | ||
|   	config[i]->daymax = -1;
 | ||
|   	config[i]->warntime = 5;
 | ||
|   	config[i]->messages[IDLEMSG] = NULL;
 | ||
|   	config[i]->messages[SESSMSG] = NULL;
 | ||
|   	config[i]->messages[DAYMSG] = NULL;
 | ||
|   	config[i]->messages[NOLOGINMSG] = NULL;
 | ||
| 	if ((tok = strsep(&lstart, ":")) != NULL) store_times(&config[i]->times, tok);
 | ||
| 	if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->ttys, tok);
 | ||
| 	if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->users, tok);
 | ||
| 	if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->groups, tok);
 | ||
| 	tok = strsep(&lstart, ":");
 | ||
| 	if (tok != NULL && !strncasecmp(tok, "NOLOGIN", 7))
 | ||
| 	{
 | ||
| 		config[i]->login_allowed=0;
 | ||
| 		if (tok[7] == ';') alloc_cp(&config[i]->messages[NOLOGINMSG], tok+8);
 | ||
| 		else if ((tok = strsep(&lstart, ":")) != NULL) alloc_cp(&config[i]->messages[NOLOGINMSG], tok);
 | ||
| 	}
 | ||
| 	else
 | ||
| 	if (tok != NULL && !strcasecmp(tok, "LOGIN")) config[i]->login_allowed=1;
 | ||
| 	else
 | ||
| 	{
 | ||
| 		if (tok != NULL)
 | ||
| 		{
 | ||
| 		    config[i]->idlemax = atoi(tok);
 | ||
| 		    if ((p = strchr(tok, ';')) != NULL) alloc_cp(&config[i]->messages[IDLEMSG], p+1);
 | ||
| 		}
 | ||
| 		if ((tok = strsep(&lstart, ":")) != NULL)
 | ||
| 		{
 | ||
| 		    config[i]->sessmax = atoi(tok);
 | ||
| 		    if ((p = strchr(tok, ';')) != NULL) alloc_cp(&config[i]->messages[SESSMSG], p+1);
 | ||
| 	    	}
 | ||
| 		if ((tok = strsep(&lstart, ":")) != NULL)
 | ||
| 		{
 | ||
| 		    config[i]->daymax = atoi(tok);
 | ||
| 		    if ((p = strchr(tok, ';')) != NULL) alloc_cp(&config[i]->messages[DAYMSG], p+1);
 | ||
| 		}
 | ||
| 		if ((tok = strsep(&lstart, ":")) != NULL)
 | ||
| 		{
 | ||
| 		    config[i]->warntime = atoi(tok);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	if (!config[i]->times || !config[i]->ttys  ||
 | ||
| 	    !config[i]->users || !config[i]->groups)
 | ||
|         {
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         	syslog(LOG_ERR,
 | ||
|         		"Error on line %d of config file (%s).  Line ignored.",
 | ||
|         		linenum, CONFIG);
 | ||
|         	closelog();
 | ||
| 	}
 | ||
| 	else i++;
 | ||
|       }
 | ||
|     }
 | ||
|     config[i] = NULL;
 | ||
| 
 | ||
|     if (fclose(config_file) == EOF)
 | ||
|       bailout("Cannot close config file", 1);
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
| 	i = 0;
 | ||
| 	while (config[i])
 | ||
| 	{
 | ||
| 		printf("line %d: ", i);
 | ||
| 		j = 0;
 | ||
| 	  	while (config[i]->times[j].days)
 | ||
| 		  printf("%d(%d-%d):", config[i]->times[j].days,
 | ||
| 		  			config[i]->times[j].starttime,
 | ||
| 		  			config[i]->times[j].endtime),j++;
 | ||
| 		printf("%s:%s:%s:%s:%d;%s:%d;%s:%d;%s:%d\n",
 | ||
| 			config[i]->ttys,
 | ||
| 			config[i]->users,
 | ||
| 			config[i]->groups,
 | ||
| 			config[i]->login_allowed?"LOGIN":"NOLOGIN",
 | ||
| 			config[i]->idlemax,
 | ||
| 			config[i]->messages[IDLEMSG] == NULL?"builtin":config[i]->messages[IDLEMSG],
 | ||
| 			config[i]->sessmax,
 | ||
| 			config[i]->messages[SESSMSG] == NULL?"builtin":config[i]->messages[SESSMSG],
 | ||
| 			config[i]->daymax,
 | ||
| 			config[i]->messages[DAYMSG] == NULL?"builtin":config[i]->messages[DAYMSG],
 | ||
| 			config[i]->warntime
 | ||
| 			),i++;
 | ||
| 	}
 | ||
| printf("End debug output.\n");
 | ||
| #endif /* DEBUG */
 | ||
| }
 | ||
| 
 | ||
| char chktimes(te)
 | ||
| struct time_ent *te;
 | ||
| {
 | ||
|     while (te->days)
 | ||
|     {
 | ||
|         if (daynums[now.tm_wday] & te->days &&	/* Date within range */
 | ||
|               ((te->starttime <= te->endtime &&	/* Time within range */
 | ||
|                now_hhmm >= te->starttime &&	
 | ||
|                now_hhmm <= te->endtime)
 | ||
|                ||
 | ||
|                (te->starttime > te->endtime &&
 | ||
|                (now_hhmm >= te->starttime ||
 | ||
|                 now_hhmm <= te->endtime))
 | ||
|               )
 | ||
|            )
 | ||
|                return 1;
 | ||
|         te++;
 | ||
|     }
 | ||
|     return 0;
 | ||
| }
 | ||
| 
 | ||
| char chkmatch(element, in_set)
 | ||
| char *element;
 | ||
| char *in_set;
 | ||
| {
 | ||
|     char	*t;
 | ||
|     char	*set = (char *) malloc(strlen(in_set) + 1);
 | ||
| 
 | ||
|     if (set == NULL) bailout("Out of memory", 1);
 | ||
|     else strcpy(set, in_set);
 | ||
| 
 | ||
|     t = strtok(set, " ,");
 | ||
|     while (t)
 | ||
|     {
 | ||
|         if (t[strlen(t)-1] == '*')
 | ||
|         {
 | ||
|             if (!strncmp(t, element, strlen(t) - 1))
 | ||
|             {
 | ||
|               free(set);
 | ||
|               return 1;
 | ||
| 	    }
 | ||
| 	}
 | ||
|         else if (!strcmp(t, element))
 | ||
|         {
 | ||
|           free(set);
 | ||
|           return 1;
 | ||
| 	}
 | ||
|         t = strtok(NULL, " ,");
 | ||
|     }
 | ||
|     free(set);
 | ||
|     return 0;
 | ||
| }
 | ||
| 
 | ||
| /* Return the number of minutes which user has been logged in for on
 | ||
|  * any of the ttys specified in config[configline] during the current day.
 | ||
|  */
 | ||
| 
 | ||
| void get_day_time(user)
 | ||
| char *user;
 | ||
| {
 | ||
|     struct ut_list	*login_p;
 | ||
|     struct ut_list	*logout_p;
 | ||
|     struct ut_list	*prev_p;
 | ||
| 
 | ||
|     daytime = 0;
 | ||
|     login_p = wtmplist;
 | ||
|     while (login_p)
 | ||
|     {
 | ||
|         /* For each login on a matching tty find its logout */
 | ||
|         if (
 | ||
| #ifndef SUNOS
 | ||
| 	    login_p->elem.ut_type == USER_PROCESS &&
 | ||
| #endif
 | ||
|             !strncmp(login_p->elem.ut_user, user, 8) &&
 | ||
|             chkmatch(login_p->elem.ut_line, config[configline]->ttys))
 | ||
|         {
 | ||
| #ifdef DEBUG_WTMP
 | ||
|     struct tm	*tm;
 | ||
|     tm = localtime(&(login_p->elem.ut_time));
 | ||
|     fprintf(stderr, "%d:%d %s %s %s\n", 
 | ||
|     	tm->tm_hour,tm->tm_min, login_p->elem.ut_line,
 | ||
|     	login_p->elem.ut_user,
 | ||
|     	"login");
 | ||
| #endif
 | ||
|             prev_p = logout_p = login_p->next;
 | ||
|             while (logout_p)
 | ||
|             {
 | ||
| /*
 | ||
|  * SA19931128
 | ||
|  * If there has been a crash, then be reasonably fair and use the
 | ||
|  * last recorded login/logout as the user's logout time.  This will
 | ||
|  * potentially allow them slightly more online time than usual,
 | ||
|  * but is better than marking them as logged in for the time the machine
 | ||
|  * was down.
 | ||
|  */
 | ||
| #ifndef SUNOS
 | ||
|                 if (logout_p->elem.ut_type == BOOT_TIME)
 | ||
|                 {
 | ||
|                       logout_p = prev_p;
 | ||
|                       break;
 | ||
| 	        }
 | ||
| #endif
 | ||
|                 if (/*logout_p->elem.ut_type == DEAD_PROCESS &&*/
 | ||
|                   !strcmp(login_p->elem.ut_line, logout_p->elem.ut_line))
 | ||
|                       break;
 | ||
|                 prev_p = logout_p;
 | ||
|                 logout_p = logout_p->next;
 | ||
|             }
 | ||
| 
 | ||
| #ifdef DEBUG_WTMP
 | ||
|     if (logout_p)
 | ||
|     {
 | ||
|       tm = localtime(&(logout_p->elem.ut_time));
 | ||
|       fprintf(stderr, "%d:%d %s %s %s\n", 
 | ||
|     	tm->tm_hour,tm->tm_min, logout_p->elem.ut_line,
 | ||
|     	logout_p->elem.ut_user, "logout");
 | ||
| fprintf(stderr, "%s %d minutes\n", user, ((logout_p?logout_p->elem.ut_time:time_now) - login_p->elem.ut_time)/60);
 | ||
|     }
 | ||
| #endif
 | ||
|             daytime += (logout_p?logout_p->elem.ut_time:time_now) - login_p->elem.ut_time;
 | ||
|         }
 | ||
|         login_p = login_p->next;
 | ||
|     }
 | ||
|     daytime /= 60;
 | ||
| #ifdef DEBUG
 | ||
| fprintf(stderr, "%s has been logged in for %d minutes today.\n", user, daytime);
 | ||
| #endif
 | ||
|     return;
 | ||
| }
 | ||
| 
 | ||
| void warnpending(tty, time_remaining, user, host)
 | ||
| char *tty;
 | ||
| int time_remaining;
 | ||
| char *user;
 | ||
| char *host;
 | ||
| {
 | ||
|     int		fd;
 | ||
|     FILE	*ttyf;
 | ||
|     char 	cmdbuf[1024];
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Warning %s@%s on %s of pending logoff in %d minutes.",
 | ||
|     	user, host, tty, time_remaining);
 | ||
|     closelog();
 | ||
| #endif
 | ||
| 
 | ||
|     if(chk_xsession(tty, host)) {
 | ||
|     	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     	syslog(SYSLOG_DEBUG, "Warning %s running X on %s for pending logout! (%d min%s left)", user, tty, time_remaining, time_remaining==1?"":"s");
 | ||
|     	closelog();
 | ||
| 
 | ||
|   	/* then send the message using xmessage */
 | ||
|   	/* well, this is not really clean: */
 | ||
|   	sprintf(cmdbuf, "su %s -c \"xmessage -display %s -center 'WARNING: You will be logged out in %d minute%s when your %s limit expires.'&\"", user, host, time_remaining, time_remaining==1?"":"s", limit_names[limit_type]);
 | ||
|   	system(cmdbuf);
 | ||
|   	/*#ifdef DEBUG*/
 | ||
| 	    openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|             syslog(LOG_DEBUG, "cmdbuf=%s", cmdbuf);
 | ||
|             closelog();
 | ||
|   	/*#endif*/
 | ||
|   	sleep(KWAIT); /* and give the user some time to read the message ;) */
 | ||
| 	return;
 | ||
|     }
 | ||
|     
 | ||
|     if ((fd = open(tty, O_WRONLY|O_NOCTTY|O_NONBLOCK)) < 0 ||
 | ||
| 	(ttyf = fdopen(fd, "w")) == NULL)
 | ||
|     {
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(LOG_ERR, "Could not open %s to warn of impending logoff.\n",tty);
 | ||
|         closelog();
 | ||
|         return;
 | ||
|     }
 | ||
|     fprintf(ttyf, "\r\nWARNING:\r\nYou will be logged out in %d minute%s when your %s limit expires.\r\n",
 | ||
|     	time_remaining, time_remaining==1?"":"s", limit_names[limit_type]);
 | ||
|     fclose(ttyf);
 | ||
| }
 | ||
| 
 | ||
| char chk_timeout(user, dev, host, idle, session)
 | ||
| char *user;
 | ||
| char *dev;
 | ||
| char *host;
 | ||
| int idle;
 | ||
| int session;
 | ||
| {
 | ||
|     struct passwd	*pw;
 | ||
|     struct group	*gr;
 | ||
|     struct group	*secgr;
 | ||
|     char	timematch = 0;
 | ||
|     char	ttymatch = 0;
 | ||
|     char	usermatch = 0;
 | ||
|     char	groupmatch = 0;
 | ||
|     char	*tty = dev;
 | ||
|     char	**p;
 | ||
|     int		disc;
 | ||
| 
 | ||
|     configline = 0;
 | ||
| 
 | ||
| /* Find primary group for specified user */
 | ||
|     if ((pw = getpwnam(user)) == NULL)
 | ||
|     {
 | ||
|       openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|       syslog(LOG_ERR, "Could not get password entry for %s.", user);
 | ||
|       closelog();
 | ||
|       return 0;
 | ||
|     }
 | ||
|     if ((gr = getgrgid(pw->pw_gid)) == NULL)
 | ||
|     {
 | ||
|       openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|       syslog(LOG_ERR, "Could not get group name for %s.", user);
 | ||
|       closelog();
 | ||
|       return 0;
 | ||
|     }
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "Checking user %s group %s tty %s.", user, gr->gr_name, tty);
 | ||
|     closelog();
 | ||
| #endif
 | ||
| 
 | ||
| /* Check to see if current user matches any entry based on tty/user/group */
 | ||
|     while (config[configline])
 | ||
|     {
 | ||
|     	timematch = chktimes(config[configline]->times);
 | ||
|         ttymatch = chkmatch(tty, config[configline]->ttys);
 | ||
|         usermatch = chkmatch(user, config[configline]->users);
 | ||
|         groupmatch = chkmatch(gr->gr_name, config[configline]->groups);
 | ||
| /* If the primary group doesn't match this entry, check secondaries */
 | ||
| 	setgrent();
 | ||
| 	while (!groupmatch && (secgr = getgrent()) != NULL)
 | ||
| 	{
 | ||
| 	    p = secgr->gr_mem;
 | ||
| 	    while (*p && !groupmatch)
 | ||
| 	    {
 | ||
| /*
 | ||
| printf("Group %s member %s\n", secgr->gr_name, *p);
 | ||
| */
 | ||
| 	    	if (!strcmp(*p, user))
 | ||
| 	    	    groupmatch = chkmatch(secgr->gr_name, config[configline]->groups);
 | ||
| 	    	p++;
 | ||
| 	    }
 | ||
| /*
 | ||
| 	    free(gr);
 | ||
| */
 | ||
| 	}
 | ||
| /*
 | ||
| 	endgrent();
 | ||
| */
 | ||
| /* If so, then check their idle, daily and session times in turn */
 | ||
|         if (timematch && ttymatch && usermatch && groupmatch)
 | ||
|         {
 | ||
|           get_day_time(user);
 | ||
| #ifdef DEBUG
 | ||
| 	  openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 	  syslog(SYSLOG_DEBUG, "Matched entry %d", configline);
 | ||
| 	  syslog(SYSLOG_DEBUG, "Idle=%d (max=%d) Sess=%d (max=%d) Daily=%d (max=%d) warntime=%d", idle, config[configline]->idlemax, session, config[configline]->sessmax, daytime, config[configline]->daymax, config[configline]->warntime);
 | ||
| 	  closelog();
 | ||
| #endif
 | ||
| 	  disc = getdisc(dev, host);
 | ||
| 
 | ||
| 	  limit_type = NOLOGINMSG;
 | ||
| 	  if (!config[configline]->login_allowed)
 | ||
| 	  	return NOLOGIN;
 | ||
| 
 | ||
| 	  limit_type = IDLEMSG;
 | ||
| 	  if (disc == N_TTY && config[configline]->idlemax > 0 && idle >= config[configline]->idlemax)
 | ||
| 	  	return IDLEMAX;
 | ||
| 
 | ||
| 	  limit_type = SESSMSG;
 | ||
| 	  if (config[configline]->sessmax > 0 && session >= config[configline]->sessmax)
 | ||
| 	  	return SESSMAX;
 | ||
| 
 | ||
| 	  limit_type = DAYMSG;
 | ||
| 	  if (config[configline]->daymax > 0 && daytime >= config[configline]->daymax)
 | ||
| 	  	return DAYMAX;
 | ||
| 
 | ||
| /* If none of those have been exceeded, then warn users of upcoming logouts */
 | ||
| 	  limit_type = DAYMSG;
 | ||
| 	  if (config[configline]->daymax > 0 && daytime >= config[configline]->daymax - config[configline]->warntime)
 | ||
| 	  	warnpending(dev, config[configline]->daymax - daytime, user, host);
 | ||
| 	  else
 | ||
| 	  {
 | ||
| 	    limit_type = SESSMSG;
 | ||
| 	    if (config[configline]->sessmax > 0 && session >= config[configline]->sessmax - config[configline]->warntime)
 | ||
| 		warnpending(dev, config[configline]->sessmax - session, user, host);
 | ||
| 	  }
 | ||
| 
 | ||
| /* Otherwise, leave the poor net addict alone */
 | ||
|           return ACTIVE;
 | ||
|         }
 | ||
|         configline++;
 | ||
|     }
 | ||
| 
 | ||
| /* If they do not match any entries, then they can stay on forever */
 | ||
|     return ACTIVE;
 | ||
| }
 | ||
| 
 | ||
| void check_idle()    /* Check for exceeded time limits & logoff exceeders */
 | ||
| {
 | ||
|     char        user[sizeof(utmpp->ut_user)];
 | ||
|     char	host[sizeof(utmpp->ut_host)];
 | ||
|     struct stat status, *pstat;
 | ||
|     time_t	idle, sesstime, time();
 | ||
|     short aktconfigline = -1; /* -1 if user is in config; >0 if he's not in config, * is handled in an other way */
 | ||
| 
 | ||
|     pstat = &status;    /* point to status structure */
 | ||
| #ifndef SUNOS
 | ||
|     sprintf(path, "/proc/%d", utmpp->ut_pid);
 | ||
|     if (utmpp->ut_type != USER_PROCESS || !utmpp->ut_user[0] || /* if not user process */
 | ||
| 	stat(path, pstat))					/* or if proc doesn't exist */
 | ||
|         return;                      /* skip the utmp entry */
 | ||
| #endif
 | ||
|     strncpy(user, utmpp->ut_user, sizeof(user) - 1);   /* get user name */
 | ||
|     user[sizeof(user) - 1] = '\0';   /* null terminate user name string */
 | ||
| 
 | ||
| 
 | ||
|     /* Only check user if he is mentioned in the config */
 | ||
|     
 | ||
|     if(!config[0])
 | ||
|     	return; /* no entries in config */
 | ||
|     while(config[++aktconfigline] && aktconfigline >= 0)
 | ||
|     	if(strcmp(config[aktconfigline]->users, user) == 0 || config[aktconfigline]->users[0] == '*') {
 | ||
|     		aktconfigline = -2; /* we found user or * in config, so he/they has/have restrictions */
 | ||
| 		break;
 | ||
| 	}
 | ||
| 	
 | ||
|     if(aktconfigline > 0) { /* > 0 if user is not in config */
 | ||
| #ifdef DEBUG
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 	syslog(SYSLOG_DEBUG, "User %s or * not in config -> No restrictions. Not checking %s on %s", user, user, dev);
 | ||
| 	closelog();
 | ||
| #endif
 | ||
| 	return; /* now, we return because the user beeing checked is not in config, so he has no restrictions */
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
|     strncpy(host, utmpp->ut_host, sizeof(host) - 1);	/* get host name */
 | ||
|     host[sizeof(host) - 1] = '\0';
 | ||
|     strncpy(dev, utmpp->ut_line, sizeof(dev) - 1);    /* get device name */
 | ||
|     dev[sizeof(dev) - 1] = '\0';
 | ||
|     sprintf(path, "/dev/%s", dev);
 | ||
|     if (stat(path, pstat) && !chk_xsession(dev, host) == TIMEOUTD_XSESSION_LOCAL)   /* if can't get status for 
 | ||
|     port && if it's not a local Xsession */
 | ||
|     {
 | ||
|         sprintf(errmsg, "Can't get status of user %s's terminal (%s)\n",
 | ||
|         	user, dev);
 | ||
| 	/* bailout(errmsg, 1); MOH: is there a reason to exit here? */
 | ||
| 	return; 
 | ||
|     }
 | ||
|     /* idle time is the lesser of:
 | ||
|      * current time less last access time OR
 | ||
|      * current time less last modified time
 | ||
|      */
 | ||
| #ifdef TIMEOUTDX11	
 | ||
|     if(chk_xsession(dev, host) && !chk_xterm(dev, host)) { /* check idle for Xsession, but not for xterm */
 | ||
|     	idle = get_xidle(user, host) / 1000 / 60; /* get_xidle returns millisecs, we need mins */
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 	syslog(SYSLOG_DEBUG, "get_xidle(%s,%s) returned %d mins idle for %s.", dev, host, (int)idle, user);
 | ||
| 	closelog();
 | ||
|     }
 | ||
|     else if (chk_xterm(dev, host)) return;
 | ||
|     else
 | ||
| #endif
 | ||
|     	idle = (time_now - max(pstat->st_atime, pstat->st_mtime)) / 60;
 | ||
| 
 | ||
|     sesstime = (time_now - utmpp->ut_time) / 60;
 | ||
|     switch(chk_timeout(user, dev, host, idle, sesstime))
 | ||
|     {
 | ||
|     	case ACTIVE:
 | ||
| #ifdef DEBUG
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(SYSLOG_DEBUG, "User %s is active.", user);
 | ||
| 		closelog();
 | ||
| #endif
 | ||
|     		break;
 | ||
|     	case IDLEMAX:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     		syslog(LOG_NOTICE,
 | ||
|     		       "User %s exceeded idle limit (idle for %ld minutes, max=%d).\n",
 | ||
|     		       user, idle, config[configline]->idlemax);
 | ||
|     		closelog();
 | ||
| 		killit(utmpp->ut_pid, user, dev, host);
 | ||
|     		break;
 | ||
|     	case SESSMAX:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_NOTICE,
 | ||
| 		       "User %s exceeded maximum session limit at %s (on for %ld minutes, max=%d).\n",
 | ||
| 		       user, dev, sesstime, config[configline]->sessmax);
 | ||
|     		closelog();
 | ||
| 		killit(utmpp->ut_pid, user, dev, host);
 | ||
|     		break;
 | ||
|     	case DAYMAX:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_NOTICE,
 | ||
| 		       "User %s exceeded maximum daily limit (on for %d minutes, max=%d).\n",
 | ||
| 		       user, daytime, config[configline]->daymax);
 | ||
|     		closelog();
 | ||
| 		killit(utmpp->ut_pid, user, dev, host);
 | ||
|     		break;
 | ||
| 	case NOLOGIN:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		#ifdef DEBUG
 | ||
| 		syslog(LOG_NOTICE, "NOLOGIN period reached for user %s@%s. (pid %d)", user, host, utmpp->ut_pid);
 | ||
| 		#else
 | ||
| 		syslog(LOG_NOTICE, "NOLOGIN period reached for user %s %s", user, host);
 | ||
| 		#endif
 | ||
| 		closelog();
 | ||
| 		killit(utmpp->ut_pid, user, dev, host);
 | ||
| 		break;
 | ||
| 	default:
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_ERR, "Internal error - unexpected return from chk_timeout");
 | ||
|     		closelog();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| void bailout(message, status) /* display error message and exit */
 | ||
| int     status;     /* exit status */
 | ||
| char    *message;   /* pointer to the error message */
 | ||
| {
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(LOG_ERR, "Exiting - %s", message);
 | ||
|     closelog();
 | ||
|     exit(status);
 | ||
| }
 | ||
| 
 | ||
| void shut_down(signum)
 | ||
| int signum;
 | ||
| {
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(LOG_NOTICE, "Received SIGTERM.. exiting.");
 | ||
|     closelog();
 | ||
|     exit(0);
 | ||
| }
 | ||
| 
 | ||
| void segfault(signum)
 | ||
| int signum;
 | ||
| {
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(LOG_NOTICE, "Received SIGSEGV.. Something went wrong! Exiting!");
 | ||
|     closelog();
 | ||
|     exit(0);
 | ||
| }
 | ||
| 
 | ||
| void logoff_msg(tty)
 | ||
| int tty;
 | ||
| {
 | ||
|     FILE	*msgfile = NULL;
 | ||
|     char	msgbuf[1024];
 | ||
|     int		cnt;
 | ||
| 
 | ||
|     if (config[configline]->messages[limit_type])
 | ||
|     	msgfile = fopen(config[configline]->messages[limit_type], "r");
 | ||
| 
 | ||
|     if (msgfile)
 | ||
|     {
 | ||
|     	while ((cnt = read(tty, msgbuf, 1024)) > 0)
 | ||
|     	    write(tty, msgbuf, cnt);
 | ||
|     	fclose(msgfile);
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|     	if (limit_type == NOLOGINMSG)
 | ||
|     	    sprintf(msgbuf, "\r\n\r\nLogins not allowed at this time.  Please try again later.\r\n");
 | ||
|     	else
 | ||
|     	    sprintf(msgbuf, "\r\n\r\nYou have exceeded your %s time limit.  Logging you off now.\r\n\r\n", limit_names[limit_type]);
 | ||
|     	write(tty, msgbuf, strlen(msgbuf));
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* terminate process using SIGHUP, then SIGKILL */
 | ||
| void killit(pid, user, dev, host)
 | ||
| int pid;
 | ||
| char *user;
 | ||
| char *dev;
 | ||
| char *host;
 | ||
| {
 | ||
|     int	tty;
 | ||
|     pid_t cpid;
 | ||
| #ifdef SUNOS
 | ||
|    struct passwd	*pw;
 | ||
| #endif
 | ||
| 
 | ||
|     if(chk_xsession(dev, host) && !chk_xterm(dev, host)) {
 | ||
| 	killit_xsession(utmpp->ut_pid, user, host);
 | ||
|     	return;
 | ||
|     }
 | ||
| /* Tell user which limit they have exceeded and that they will be logged off */
 | ||
|     if ((tty = open(dev, O_WRONLY|O_NOCTTY|O_NONBLOCK)) < 0)
 | ||
|     {
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(LOG_ERR, "Could not write logoff message to %s.", dev);
 | ||
|         closelog();
 | ||
| 	return;
 | ||
|     }
 | ||
| 	
 | ||
| 	
 | ||
| 	/* check if the pid is sshd. If so, get PID of the child process (another ssh, owned by the user).
 | ||
| 	   Test reverse if this child process is also ssh and owned by the user we want to log out.
 | ||
| 	   (because we don't want to slay another user ;) */
 | ||
| 	cpid = getcpid(pid);
 | ||
| #ifdef DEBUG
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 	syslog(LOG_NOTICE, "I am at killit() pid=%d user=%s child=%d line %d", pid, user, cpid, __LINE__);
 | ||
| 	closelog();
 | ||
| #endif
 | ||
| 	
 | ||
| 	if(chk_ssh(pid) && chk_ssh(cpid) && !strcmp(getusr(cpid), user)) {
 | ||
| #ifdef DEBUG
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         	syslog(LOG_NOTICE, "User %s (pid:%d, cpid:%d) logged in via ssh from %s.", user, pid, cpid, host);
 | ||
|         	closelog();
 | ||
| #endif
 | ||
| 		pid = cpid;
 | ||
| 	}
 | ||
| 
 | ||
|     logoff_msg(tty);
 | ||
|     sleep (KWAIT); /*make sure msg does not get lost, again (esp. ssh)*/
 | ||
|     close(tty);
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(LOG_NOTICE, "Would normally kill pid %d user %s on %s",pid,user,dev);
 | ||
|     closelog();
 | ||
|     return;
 | ||
| #endif
 | ||
| 
 | ||
|     if (fork())             /* the parent process */
 | ||
|         return;            /* returns */
 | ||
| 
 | ||
| /* Wait a little while in case the above message gets lost during logout */
 | ||
| #ifdef SURE_KILL
 | ||
|     signal(SIGHUP, SIG_IGN);
 | ||
|     if ((pw = getpwnam(user)) == NULL)
 | ||
|     {
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(LOG_ERR, "Could not log user %s off line %s - unable to determine uid.", user, dev);
 | ||
|         closelog();
 | ||
|     }
 | ||
|     if (setuid(pw->pw_uid))
 | ||
|     {
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(LOG_ERR, "Could not log user %s off line %s - unable to setuid(%d).", user, dev, pw->pw_uid);
 | ||
|         closelog();
 | ||
|     }
 | ||
|     kill(-1, SIGHUP);
 | ||
|     sleep(KWAIT);
 | ||
|     kill(-1, SIGKILL);
 | ||
| #else
 | ||
|     kill(pid, SIGHUP);  /* first send "hangup" signal */
 | ||
|     sleep(KWAIT);
 | ||
|     if (!kill(pid, 0)) {    /* SIGHUP might be ignored */
 | ||
|         kill(pid, SIGKILL); /* then send sure "kill" signal */
 | ||
|         sleep(KWAIT);
 | ||
|         if (!kill(pid, 0))
 | ||
|         {
 | ||
| 	    openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|             syslog(LOG_ERR, "Could not log user %s off line %s.", user, dev);
 | ||
|             closelog();
 | ||
|         }
 | ||
|     }
 | ||
| #endif
 | ||
|     exit(0);
 | ||
| }
 | ||
| 
 | ||
| void reread_config(signum)
 | ||
| int signum;
 | ||
| {
 | ||
|     int i = 0;
 | ||
| 
 | ||
|     if (!allow_reread)
 | ||
|         pending_reread = 1;
 | ||
|     else
 | ||
|     {
 | ||
|         pending_reread = 0;
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         syslog(LOG_NOTICE, "Re-reading configuration file.");
 | ||
|         closelog();
 | ||
|         while (config[i])
 | ||
|         {
 | ||
|             free(config[i]->times);
 | ||
|             free(config[i]->ttys);
 | ||
|             free(config[i]->users);
 | ||
|             free(config[i]->groups);
 | ||
|             if (config[i]->messages[IDLEMSG]) free(config[i]->messages[IDLEMSG]);
 | ||
|             if (config[i]->messages[DAYMSG]) free(config[i]->messages[DAYMSG]);
 | ||
|             if (config[i]->messages[SESSMSG]) free(config[i]->messages[SESSMSG]);
 | ||
|             if (config[i]->messages[NOLOGINMSG]) free(config[i]->messages[NOLOGINMSG]);
 | ||
|             free(config[i]);
 | ||
|             i++;
 | ||
|         }
 | ||
|         read_config();
 | ||
|     }
 | ||
|     signal(SIGHUP, reread_config);
 | ||
| }
 | ||
| 
 | ||
| void reapchild(signum)
 | ||
| int signum;
 | ||
| {
 | ||
|     int st;
 | ||
| 
 | ||
|     wait(&st);
 | ||
|     signal(SIGCHLD, reapchild);
 | ||
| }
 | ||
| 
 | ||
| int getdisc(d, host)
 | ||
| char *d;
 | ||
| char *host;
 | ||
| {
 | ||
|     int	fd;
 | ||
|     int	disc;
 | ||
| 
 | ||
| #ifdef linux
 | ||
|     if(chk_xsession(d, host) || chk_xterm(d, host))
 | ||
|     	return N_TTY;
 | ||
| 	
 | ||
|     if ((fd = open(d, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0)
 | ||
|     {
 | ||
|       openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|       syslog(LOG_WARNING, "Could not open %s for checking line discipline - idle limits will be enforced.", d);
 | ||
|       closelog();
 | ||
|       return N_TTY;
 | ||
|     }
 | ||
| 
 | ||
|     if (ioctl(fd, TIOCGETD, &disc) < 0)
 | ||
|     {
 | ||
|       close(fd);
 | ||
|       openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|       syslog(LOG_WARNING, "Could not get line discipline for %s - idle limits will be enforced.", d);
 | ||
|       closelog();
 | ||
|       return N_TTY;
 | ||
|     }
 | ||
| 
 | ||
|     close(fd);
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
|     openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|     syslog(SYSLOG_DEBUG, "TTY %s: Discipline=%s.",d,disc==N_SLIP?"SLIP":disc==N_TTY?"TTY":disc==N_PPP?"PPP":disc==N_MOUSE?"MOUSE":"UNKNOWN");
 | ||
|     closelog();
 | ||
| #endif
 | ||
| 
 | ||
|     return disc;
 | ||
| #else
 | ||
|     return N_TTY;
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| int chk_xsession(dev, host) /* returns TIMEOUTD_XSESSION_{REMOTE,LOCAL,NONE} when dev and host seem to be a xSession. */
 | ||
| char *dev,*host;
 | ||
| {
 | ||
|     if( strncmp(host, ":0", 1) == 0 ) {
 | ||
|       /* Look here, how we check if it's a Xsession but no telnet or whatever.
 | ||
|        * The problem is that a xterm running on :0 has the device pts/?.  But if we ignore
 | ||
|        * all pts/?, ssh users won't be restricted.  
 | ||
|        * So, if (tty="pts/?" OR tty=":*") AND host = ":*", we have a Xsession:
 | ||
|        * 
 | ||
|        * seppy@schleptop:~$ w
 | ||
|        * 20:06:33 up 18 min,  6 users,  load average: 0.14, 0.16, 0.12
 | ||
|        * USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU  WHAT
 | ||
|        * dennis   :0       -                19:48   ?xdm?   0.00s   ?     -
 | ||
|        * dennis   pts/1    :0.0             20:00    4:12   0.03s  0.03s  bash 
 | ||
|        * dennis   pts/2    :0.0             20:01    0.00s  0.18s  0.16s  ssh localhost 
 | ||
|        * dennis   pts/3    localhost        20:01    0.00s  0.01s  0.00s  w
 | ||
|        */       
 | ||
|       #ifdef DEBUG
 | ||
|          openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|          syslog(LOG_DEBUG, "LOCAL Xsession detected. device=%s host=%s", dev, host);
 | ||
|          closelog();
 | ||
|       #endif
 | ||
| 	 return TIMEOUTD_XSESSION_LOCAL;
 | ||
|     }
 | ||
|     else if (strstr(dev, ":") && strlen(host) > 1 && gethostbyname(host)) {
 | ||
|       /* What about remote XDMCP sessions?
 | ||
|        * USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
 | ||
|        * mark     pts/3    mercury           Sat11    0.00s 10.99s  0.04s w
 | ||
|        * rebecca  ap:10    ap                10:32    0.00s  0.00s  1.28s x-session-manager
 | ||
|        */
 | ||
| 
 | ||
|       #ifdef DEBUG
 | ||
|          openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|          syslog(LOG_DEBUG, "REMOTE Xsession detected. device=%s host=%s", dev, host);
 | ||
|          closelog();
 | ||
|       #endif
 | ||
|       return TIMEOUTD_XSESSION_REMOTE;
 | ||
|     }
 | ||
|     else {
 | ||
|       #ifdef DEBUG
 | ||
|          openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|          syslog(LOG_DEBUG, "NO xsession detected. device=%s host=%s", dev, host);
 | ||
|          closelog();
 | ||
|       #endif
 | ||
|       return TIMEOUTD_XSESSION_NONE;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* We have to handle Xterms(pts/?) and Xsessions (:0) different:
 | ||
|    - Check Xsession for idle, but not a XTERM
 | ||
|    - Send message for pending logoff to X, but not to XTERM
 | ||
|      -> Don't check XTERM at all
 | ||
|    - but: check ssh (pts/?) but no XTERM (again)
 | ||
| */
 | ||
| int chk_xterm(dev, host) /* returns 1 when dev and host seem to be a xTERM. */
 | ||
| char *dev,*host;
 | ||
| {
 | ||
|     if(strncmp(dev, "pts/0", 3) == 0 && strncmp(host, ":0", 1) == 0 ) {
 | ||
|       #ifdef DEBUG
 | ||
|          openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|          syslog(LOG_DEBUG, "XTERM detected. device=%s host=%s Ignoring.", dev, host);
 | ||
|          closelog();
 | ||
|       #endif
 | ||
|     	return 1;
 | ||
|     }
 | ||
|     else
 | ||
|     	return 0;
 | ||
| } /* chk_xterm(dev,host) */
 | ||
| 
 | ||
| 
 | ||
| void killit_xsession(pid, user, host) /* returns 1 when host seems to be a xSession. */
 | ||
| int pid;
 | ||
| char *host, *user;
 | ||
| {
 | ||
|     char	msgbuf[1024], cmdbuf[1024];
 | ||
|   /* first, get the message into msgbuf */
 | ||
|     	if (limit_type == NOLOGINMSG) {
 | ||
|     	    sprintf(msgbuf, "Logins not allowed at this time.  Please try again later.");
 | ||
| 	} else {
 | ||
|     	    sprintf(msgbuf, "You have exceeded your %s time limit.  Logging you off now.", limit_names[limit_type]);
 | ||
|         }
 | ||
| 
 | ||
|   /* then send the message using xmessage */
 | ||
|   /* well, this is not really clean: */
 | ||
|   sprintf(cmdbuf, "su %s -c \"xmessage -display %s -center '%s'&\"", user, host, msgbuf);
 | ||
|   system(cmdbuf);
 | ||
|   #ifdef DEBUG
 | ||
| 	    openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|             syslog(LOG_DEBUG, "cmdbuf=%s", cmdbuf);
 | ||
|             closelog();
 | ||
|   #endif
 | ||
|   sleep(KWAIT); /* and give the user some time to read the message ;) */
 | ||
| 	    
 | ||
| 
 | ||
|   #ifndef DEBUG	
 | ||
|   /* kill pid here */
 | ||
|     kill(pid, SIGTERM);  /* otherwise, X crashes */
 | ||
|     sleep(KWAIT);
 | ||
|     if (!kill(pid, 0)) {    /* SIGHUP might be ignored */
 | ||
|         kill(pid, SIGKILL); /* then send sure "kill" signal */
 | ||
|         sleep(KWAIT);
 | ||
|         if (!kill(pid, 0))
 | ||
|         {
 | ||
| 	    openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|             syslog(LOG_ERR, "Could not log user %s off line %s. (running X)", user, host);
 | ||
|             closelog();
 | ||
|         }
 | ||
|     }
 | ||
|   #else
 | ||
|   openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|   syslog(LOG_ERR, "Would normally logoff user %s running X (kill PID %d)", user, pid);
 | ||
|   closelog();
 | ||
|   #endif
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| int chk_ssh(pid)/* seppy; returns true if pid is sshd, otherwise it returns false */
 | ||
| pid_t pid;
 | ||
| {
 | ||
| 	sprintf(path, "/proc/%d/stat", pid);
 | ||
| 	proc_file = fopen(path, "r");
 | ||
| 	if(!proc_file) {
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         	syslog(LOG_WARNING, "chk_ssh(): PID %d does not exist. Something went wrong. Ignoring.", pid);
 | ||
|         	closelog();
 | ||
| 		return 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	fscanf (proc_file, "%*d (%[^)]", comm);
 | ||
| 	fclose(proc_file);
 | ||
| 	
 | ||
| 	if(!strcmp(comm, "sshd"))
 | ||
| 		return 1;
 | ||
| 	else
 | ||
| 		return 0;
 | ||
| } 
 | ||
| 
 | ||
| char *getusr(pid) /*seppy; returns the name of the user owning process with the Process ID pid */
 | ||
| pid_t pid;
 | ||
| {
 | ||
| 	char uid[99];
 | ||
| 	sprintf(path, "/proc/%d/status", pid);
 | ||
| 	proc_file = fopen(path, "r");
 | ||
| 	if(!proc_file) {
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
|         	syslog(LOG_NOTICE, "getusr(): PID %d does not exist. Ignoring.", pid);
 | ||
|         	closelog();
 | ||
| 		return "unknown";
 | ||
| 	}
 | ||
| 	while(!fscanf(proc_file, "Uid:    %s", uid))
 | ||
|                 fgets(uid, 98, proc_file);
 | ||
| 	fclose(proc_file);
 | ||
| 	return getpwuid(atoi(uid))->pw_name;
 | ||
| }
 | ||
| 
 | ||
| #ifdef TIMEOUTDX11
 | ||
| Time get_xidle(user, display) /*seppy; returns millicecs since last input event */
 | ||
| char *user;
 | ||
| char *display;
 | ||
| {
 | ||
| 	Display* dpy;
 | ||
| 	static XScreenSaverInfo* mitInfo = 0; 
 | ||
| 	struct passwd *pwEntry;
 | ||
| 	char homedir[50]; /*50 should be enough*/
 | ||
| 	char oldhomedir[50];
 | ||
| 	uid_t oldeuid;
 | ||
| 	Time retval = 0;
 | ||
| 
 | ||
| 	pwEntry = getpwnam(user);
 | ||
| 	if(!pwEntry) {
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_ERR, "Could not get passwd-entry for user %s", user);
 | ||
| 		closelog();
 | ||
| 	}
 | ||
| 
 | ||
| #ifdef DEBUG
 | ||
| 	openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 	syslog(LOG_DEBUG, "su-ing to %s(%d) and connecting to X", user, pwEntry->pw_uid);
 | ||
| 	closelog();
 | ||
| #endif
 | ||
| 
 | ||
| 	/*change into the user running x. we need that to connect to X*/
 | ||
| 	/*setregid(1000, 1000); We don't need this*/
 | ||
| 
 | ||
| 	/*save old, to come back*/
 | ||
| 	oldeuid = geteuid();
 | ||
| 	sprintf(oldhomedir, "HOME=%s", getenv("HOME"));
 | ||
| 
 | ||
| 	/*become user*/
 | ||
| 	if(seteuid(pwEntry->pw_uid) == -1) {
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_ERR, "Could not seteuid(%d).", pwEntry->pw_uid);
 | ||
| 		closelog();
 | ||
| 	}
 | ||
| 
 | ||
| 	sprintf(homedir, "HOME=%s", pwEntry->pw_dir);
 | ||
| 	putenv(homedir);
 | ||
| 
 | ||
| 	/* First, check if there is a xserver.. */ 
 | ||
| 	if ((dpy = XOpenDisplay (display)) == NULL) { /* = intended */
 | ||
| 		openlog("timeoutd", OPENLOG_FLAGS, LOG_DAEMON);
 | ||
| 		syslog(LOG_NOTICE, "Could not connect to %s to query idle-time for %s. Ignoring.", display, user);
 | ||
| 		closelog();
 | ||
| 	} else {
 | ||
| 	if (!mitInfo) 
 | ||
| 	  mitInfo = XScreenSaverAllocInfo ();
 | ||
| 	XScreenSaverQueryInfo (dpy, DefaultRootWindow (dpy), mitInfo);
 | ||
| 	retval = mitInfo->idle;
 | ||
| 	XCloseDisplay(dpy);
 | ||
| 	}
 | ||
| 	/*go back again*/
 | ||
| 	putenv(oldhomedir);
 | ||
| 	setuid(oldeuid);
 | ||
| 
 | ||
|     	return retval;
 | ||
| } /* get_xidle(user) */
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| /* seppy; getchild()
 | ||
|           returns the pid of the first child-process found. 
 | ||
|           - 1 if a error occured, 
 | ||
| 	  - 0 if none found
 | ||
| 	  
 | ||
| 	  We need this because utmp returns a process owned by 
 | ||
| 	  root when a user is connected via ssh. If we kill its
 | ||
| 	  child (owned by the user) he/she gets logged off */
 | ||
| pid_t getcpid(ppid) 
 | ||
| pid_t ppid;
 | ||
| {
 | ||
| 	DIR *proc;
 | ||
| 	FILE *proc_file;
 | ||
| 	struct dirent *cont;
 | ||
| 	char akt_pid[99];
 | ||
| 	char path[256];
 | ||
| 	
 | ||
| 	proc = opendir("/proc/");
 | ||
| 	if(proc == NULL) {
 | ||
| 		printf("error opening directory\n");
 | ||
| 		return -1; /* error */
 | ||
| 	}
 | ||
| 		
 | ||
| 	while((cont = readdir(proc)) != NULL)
 | ||
| 		if(cont->d_type == 4 && isdigit(cont->d_name[0])) { /* check only PIDs */						
 | ||
| 			sprintf(path, "/proc/%s/status", cont->d_name);
 | ||
| 			proc_file = fopen(path, "r");
 | ||
| 			if(!proc_file)
 | ||
| 				printf("error opening proc status file %s\n", path);
 | ||
| 				
 | ||
| 			while(!fscanf(proc_file, "PPid:    %s", akt_pid))
 | ||
|                 		fgets(akt_pid, 10, proc_file);
 | ||
| 			
 | ||
| 			if(atoi(akt_pid) == ppid)
 | ||
| 				return (pid_t)atoi(cont->d_name); /* return pid of child */
 | ||
| 		} /* if(cont->d_type == 4) */
 | ||
| 		
 | ||
| 	return 0; /* no child found */	
 | ||
| } /* getchild(ppid) */
 |