/* Login notify - informs you about login and logout of users :) */
/* Written and (C) 1997,2001 by Carsten Gross carsten@siski.de  */
/* This programm is free software and covered by the GPL */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utmp.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pwd.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>


#define DELAY 5				/* Delay between the calls to stat */
#define CONFIG_FILE ".logn"	/* Name of the configuration file  */
/* The next line should be defined by some include .. */
#define MAXPATHLEN 1024		/* As long as I do not know better */

char ** wanted;				/* Simple list of users    */
char ** ttys;				/* Simple list of available ttys */
							/* This allows logout control */

/* Test whether the user is in the "wanted" list */
/* If there is no list, the function returns "User not in list" */
int check_user(struct utmp *u) {
	int i;
	if  (wanted==NULL) return(0);
	for (i=0; wanted[i]!=NULL; i++) {
		if (strncmp(wanted[i], u->ut_user, 8) == 0) return(1);
	}
	return(0);
}

/* Get Homedirectory of the user by walking through the available users in the
 * password-DB and stopping at the correct UID
 */

char * GetHomeDir() {
	struct passwd *PasswordBuffer;
	uid_t uid;
	char * Home;
	uid = getuid();
	
	/* rewind password file */
	setpwent();	
	PasswordBuffer=NULL;
	do {
		PasswordBuffer = getpwent();
	} while (PasswordBuffer != NULL && ( PasswordBuffer->pw_uid != uid));

	endpwent();
	if (PasswordBuffer == NULL) {
		fprintf(stderr, "Cannout find user-id %d in passwd database.\n", uid);
		return NULL;
	}
	Home = PasswordBuffer->pw_dir;
	return Home;
}


/* Show who is logging in from where */
int show_login(int wtmp_fd) {
	struct utmp u;		/* A UTMP entry */
	struct hostent *h;	/* A host entry */
	char * UserName;	/* Store the passwd username in here */
	char date[26]; 		/* The date returned by ctime */
	
	if (lseek(wtmp_fd, - sizeof(struct utmp), SEEK_END)<0) {
		perror("lseek " WTMP_FILE); 
		exit(EXIT_FAILURE);
	}
	/* Rewind password file */
	setpwent();

	read(wtmp_fd, &u, sizeof(struct utmp));
	strcpy(date, ctime(&u.ut_time));
	date[20]='\0'; 	/* lösche Jahreszahl */
	if (u.ut_type == USER_PROCESS) {
		if ( (h=gethostbyaddr((char *) &u.ut_addr, 4, AF_INET)) <0 ) {
			herror("gethostbyname"); 
			exit(EXIT_FAILURE);
		}
		UserName = getpwnam((char *)&u.ut_user)->pw_gecos;
		if (UserName != NULL) {
			/* Cut of trash in GECOS field */
			UserName = strtok(UserName, ",");
			if (check_user(&u)) 
				printf("\a");
			if (h != NULL) {
				printf("LOGIN: %s (%.8s) on %s from %.20s at %s\n", \
					UserName, (char *)&u.ut_user, (char *)&u.ut_line, h->h_name, \
					&(date[11]));
				fflush(stdout);
			} else {
				printf("LOGIN: %s (%.8s) on %s from %.16s at %s\n", \
					UserName, (char *)&u.ut_user, (char *)&u.ut_line, (char *)&u.ut_host, \
					&(date[11]));
				fflush(stdout);
			}
		} else {
				printf("LOGIN: %.8s from %.16s at %s\n", \
					(char *)&u.ut_user, (char *)&u.ut_host, \
					&(date[11]));
				fflush(stdout);
		}
	} else /* (u.ut_type == USER_PROCESS) */
		if (u.ut_type == DEAD_PROCESS) {
			/* Does not work as expected */
#if 0
		  	printf("LOGOUT: pid %u, tty %.12s,", \
	  		u.ut_pid, u.ut_line);
	  	printf(" time: %s, user: %.8s, host: %.15s, IP 0x%x\n",
	  		&(date[11]), u.ut_user, u.ut_host, u.ut_addr);
#endif 
	}
	/* Close password file */
	endpwent();
	return(0);
}	

/*	You can store interesting users in a "." file in your home directory.
	The file is only searched in your home directory. The home directory
	is found with GetHomeDir (getpwent) 
*/

int ReadConfigFile() {
	FILE *fd; 
	int filedes=-1;
	char buffer[10];
	char home_path[MAXPATHLEN];
	char *home;
	int ii,i=0;
	
	/* Get Homedirectory */
	home = GetHomeDir();
	if (home != NULL) {
		strncpy(home_path, home, MAXPATHLEN);
	} else {
		/* Fatal error: User does not exist */
		exit(EXIT_FAILURE);
	}
	strcat(home_path, "/" CONFIG_FILE);
   	if ( (fd=fopen(home_path, "r")) > 0) 
		filedes=fileno(fd);
    if (filedes<0) {
       	wanted=NULL;
    	printf("No configuration file found, showing all logins.\n");
		return(0);
   	} else {
   		printf("Using configuration file %s\n", home_path);
	}

	lseek(filedes, 0, SEEK_SET);
	wanted=(void *) malloc(4*50); 
	wanted[i]=(char *)malloc(sizeof(buffer));
	while (fscanf(fd, "%s", wanted[i])>=0) {
		i++;
		if (i>50) {
			fprintf(stderr, "to many users (a maximum of 50 is allowed)\n");
			exit(EXIT_FAILURE);
		}
		wanted[i]=(char *)malloc(sizeof(buffer));
	}
	wanted[i]=NULL;
	printf("A login of one of the following users will be announced by a `ding'.\n");
	for (ii=0;ii<i;ii++) {
		printf("%s\t", wanted[ii]);
		if ( ((ii+1) & 7) == 0) printf("\n");
	}
	printf("\n");
	return(0);
}	       		

/*	The main loop asks all "DELAY" seconds for the status of the wtmp 
	file. If the modification time changed we scan for the last entry
	in wtmp */

int main() {
	int wtmp_fd;					/* Filedeskriptor for wtmp */
	struct stat stat_buffer;        /* Buffer for stat info */
	struct stat stat_buffer2;		/* another buffer */ 
	int zerocounter=0;				/* Zählt ca. 10 minuten aus */
	if ((wtmp_fd=open(WTMP_FILE, O_RDONLY))<0) {
		perror("open " WTMP_FILE);
		exit(1);
	}
	if (fstat(wtmp_fd, &stat_buffer)<0) {
		perror("stat " WTMP_FILE);
		exit(1);
	}
	printf("Login-Notifier (C)1997,2001 by Carsten Groß\n");
	ReadConfigFile();
	while (1) {
		if (isatty(STDOUT_FILENO) == 0)
			exit(EXIT_FAILURE);
		sleep(DELAY);
		if (fstat(wtmp_fd, &stat_buffer2)<0) {
			perror("stat " WTMP_FILE);
			exit(1);
		}
		if (stat_buffer2.st_mtime != stat_buffer.st_mtime) {
			show_login(wtmp_fd);
		}
		stat_buffer=stat_buffer2;
		zerocounter++;
		/* To disable connection resets by ssh */
		if (zerocounter > 100) { /* ~ 8 Minuten! */
			zerocounter=0;
			write(STDOUT_FILENO, "\0", 1);
		}
	}
	exit(EXIT_SUCCESS); /* We won't reach this point (normally)! */
}
