/*
 * Very simple APM daemon.
 * Original by David Hinds <dhinds@allegro.stanford.edu>
 *
 * $Id: apmd.c,v 1.2 1994/12/19 07:27:26 sfr Exp $
 */
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/apm_bios.h>
#include <linux/fs.h>

/*
 * Find the major number for the device with a given name.
 */

int lookup_dev(char *name)
{
	FILE *	f;
	int 	n;
	char	s[80];
	char	t[80];

	f = fopen("/proc/devices", "r");
	if (f == NULL)
		return -1;
	while (fgets(s, sizeof(s), f) != NULL) {
		if ((sscanf(s, "%d %s", &n, t) == 2)
		    && (strcmp(name, t) == 0)) {
			fclose(f);
			return n;
		}
	}
	fclose(f);
	return -1;
}

/*
 * Open a device givan its dev_t
 */

int open_dev(dev_t dev)
{
	char *	fn;
	int	fd;

	if ((fn = tmpnam(NULL)) == NULL)
		return -1;
	if (mknod(fn, S_IFCHR | S_IREAD | S_IWRITE, dev) != 0)
		return -1;
	fd = open(fn, O_RDWR);
	unlink(fn);
	return fd;
}

void done(void)
{
    syslog(LOG_INFO, "exiting");
}

#define MAX_EVENTS 8

void main(void)
{
	int		i;
	int		major;
	int		fd;
	int		ret;
	apm_event_t	events[MAX_EVENTS];
	fd_set		fds;
    
	openlog("apmd", LOG_PID | LOG_CONS, LOG_DAEMON);

	if ((ret = fork()) > 0)
		exit(0);

	if (ret == -1)
		syslog(LOG_INFO, "forking: %m");
	if (setsid() < 0)
		syslog(LOG_INFO, "detaching from tty: %m");

	syslog(LOG_INFO, "starting");
	atexit(&done);

	major = lookup_dev("apm_bios");
	if (major < 0) {
		syslog(LOG_INFO, "no apm_bios driver in /proc/devices");
		exit(EXIT_FAILURE);
	}
	fd = open_dev(MKDEV(major, 0));
	if (fd < 0) {
		syslog(LOG_INFO, "open_dev() failed: %m");
		exit(EXIT_FAILURE);
	}

	for (;;) {
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		while ((ret = select(5, &fds, NULL, NULL, NULL)) < 0) {
			if (errno == EINTR)
				/* handle_signal() */ ;
			else {
				syslog(LOG_INFO, "select(): %m");
				exit(EXIT_FAILURE);
			}
		}
		ret = read(fd, events, sizeof(events));
		if ((ret == -1) && (errno != EAGAIN))
			syslog(LOG_INFO, "read(): %m");
		for (i = 0; i < ret; i += sizeof(apm_event_t)) {
			syslog(LOG_INFO, "event: %#4.4x", events[i]);
			switch (events[i]) {
			case APM_SYS_STANDBY:
				ioctl(fd, APM_IOC_STANDBY, NULL);
				break;

			case APM_SYS_SUSPEND:
				ioctl(fd, APM_IOC_SUSPEND, NULL);
				break;
			}
		}
	}
}
