[alt.sources] Daily Login Usage Restrictor

jfh@rpp386.Dallas.TX.US (John F. Haugh II) (07/31/89)

I whipped this up as it became apparent I might wind up with
my dial-up users getting busy signals because of each other.

How this works ...

You start by adding a line either to your users .profile or
to the system-wide startup file.  The line should be of the
form

/etc/timer 3600

where 3600 is the number of seconds in an hour, or however
many minutes you want them to be logged in per day.  I use
a four hour limit, so obviously I use something like

/etc/timer 14400

The /etc/timer program then notes whether or not it is being
exec'd by a login shell, as told by getpgrp() and goes off into
the background to hide.

Once a minute it checks for the existence of its parent and
updates its usage counters.  When the daily usage reaches the
limit on the command line, out you go!  It is smart enough
to detect multiple logins and charge for each in a cummulative
fashion [ which explains some of the goings on ]

The user is told their initial time remaining when they first
login, and is then warned at 10 minutes remaining and at each
minute between five and zero.  If they choose to ignore your
warnings, it's SIGKILL for the kiddies!

Several clever methods of defeat have been checked for,
including forking the bugger into the background and leaving
init as the parent [ which would be real nasty, sending init
a SIGKILL ... ]

Let me know what you think, other than that I'm a fascist
bastard.  Included is timer.c, which should be compiled to
become /etc/timer, mode 4711 owner root, and usage.c which
should be compiled to become /etc/usage, mode 700, owner
root.  [ or mode 4711 to allow users to check on their own
usage ]

Documentation?  Ha!  Read the source you luser!
--
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	timer.c
#	usage.c
# This archive created: Sun Jul 30 13:55:37 1989
# By:	John F. Haugh II (River Parishes Programming, Plano TX)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'timer.c'
then
	echo shar: "will not over-write existing file 'timer.c'"
else
cat << \SHAR_EOF > 'timer.c'
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>

#define	TIMER_LOG	"/usr/adm/timer"
#define	INTERVAL	60

struct	timer {
	long	today;
	long	usage;
};

void	update_usage();
void	print_remaining();

main (argc, argv)
int	argc;
char	**argv;
{
	char	*cp;
	int	uid = getuid ();
	int	parent = getppid ();
	int	fd;
	int	minutes;
	long	usage;
	long	start;
	long	strtol();
	struct	timer	timer;
	void	catch();

	if (parent != getpgrp () || parent <= 1 || argc != 2)
		exit (1);

	usage = strtol (argv[1], &cp, 10);
	if (cp == argv[1])
		exit (2);

	if ((fd = open (TIMER_LOG, O_RDWR|O_CREAT, 0600)) < 0)
		exit (3);

	(void) lseek (fd, (long) uid * sizeof timer, 0);
	if (read (fd, &timer, sizeof timer) != sizeof timer)
		memset (&timer, 0, sizeof timer);

	(void) time (&start);

	update_usage (fd, &timer, uid, 0);
	print_remaining (usage - timer.usage);	

	if (usage - timer.usage <= 0)
		kill (- parent, SIGKILL);

	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGTERM, SIG_IGN);

	if (fork () != 0)
		exit (0);

	setpgrp ();
	setuid (geteuid ());

	while (usage > timer.usage) {
		sleep (INTERVAL);

		if (kill (parent, 0) == -1)
			exit (0);

		update_usage (fd, &timer, uid, INTERVAL);

		minutes = (usage - timer.usage) / 60;	

		if (minutes == 10 || minutes <= 5)
			print_remaining (usage - timer.usage);
	}
	sleep (15);
	kill (- parent, SIGKILL);
}

void
update_usage (fd, timer, id, delta)
int	fd;
struct	timer 	*timer;
int	id;
long	delta;
{
	long	today = time (0) / (24*3600);

	(void) lseek (fd, (long) id * sizeof *timer, 0);
	if (read (fd, timer, sizeof *timer) != sizeof *timer)
		memset (timer, 0, sizeof *timer);

	if (today != timer->today) {
		timer->today = today;
		timer->usage = 0;
	}
	timer->usage += delta;

	(void) lseek (fd, (long) id * sizeof *timer, 0);
	(void) write (fd, timer, sizeof *timer);
}

void
print_remaining (remaining)
long	remaining;
{
	char	buf[BUFSIZ];
	int	hours = remaining / 3600;
	int	minutes = (remaining % 3600) / 60;

	if (remaining <= 60)
		strcpy (buf, "no time");
	else if (remaining < 3600)
		sprintf (buf, "%d %s",
			minutes, minutes != 1 ? "minutes":"minute");
	else {
		if (minutes == 0)
			sprintf (buf, "%d %s",
				hours, hours != 1 ? "hours":"hour");
		else
			sprintf (buf, "%d %s, %d %s",
				hours, hours != 1 ? "hours":"hour",
				minutes, minutes != 1 ? "minutes":"minute");
	}
	printf ("You have %s remaining.\n", buf);
}
SHAR_EOF
fi
if test -f 'usage.c'
then
	echo shar: "will not over-write existing file 'usage.c'"
else
cat << \SHAR_EOF > 'usage.c'
#include <sys/types.h>
#include <pwd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>

#define	TIMER_LOG	"/usr/adm/timer"

struct	timer {
	long	today;
	long	usage;
};

print_usage (id, time)
int	id;
long	time;
{
	int	hours;
	int	minutes;
	struct	passwd	*pwd;

	hours = time / 3600;
	minutes = (time % 3600) / 60;

	if (pwd = getpwuid (id))
		printf ("%8.8s    %02d:%02d\n", pwd->pw_name, hours, minutes);
	else
		printf ("%8d    %02d:%02d\n", id, hours, minutes);
}

main (argc, argv)
int	argc;
char	**argv;
{
	int	fd;
	int	uid;
	int	all = 0;
	long	today = time (0) / (24*3600L);
	struct	timer	timer;

	if (argc == 2 && strcmp (argv[1], "all") == 0)
		all = 1;

	if ((fd = open (TIMER_LOG, O_RDONLY)) < 0) {
		perror (TIMER_LOG);
		exit (1);
	}
	uid = 0;
	while (read (fd, &timer, sizeof timer) == sizeof timer) {
		if (timer.today == today || (timer.today && all))
			print_usage (uid, timer.usage);

		uid++;
	}
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
John F. Haugh II                        +-Quote of the month club: ------------
VoiceNet: (512) 832-8832   Data: -8835  | "Computer security is to information
InterNet: jfh@rpp386.cactus.org         |  control as a chastity belt is to
UucpNet : <backbone>!bigtex!rpp386!jfh  +- birth control"    -- Doug Steves  --