/*$Id: wakeup.c,v 1.4 2003/09/16 02:07:17 talon Exp $*/
/* Copyright 2003 Bill Schaub. talon@fingers.shocking.com 
This is Free software, you can do whatever you like with it as long as
you dont hold the author liable for any damages and you
leave this copyright/license message intact in the source code. */
  
/*
   This program sets the autowakeup timer on sun4u platforms that support it.
   Sun workstations use an alarm on the TOD chip to perform a sheduled power on
   There are undocumented ioctl calls to the /dev/tod driver in sys/todio.h
   using these undocumented ioctls will allow you to use this functionality
   without having to resort to powerd and its cracked out suspend-resume shit.

   This program is intended to be used in /sbin/rc5 just before the system 
   unmounts all filesystems. It might work earlier than that but you never 
   know if somthing else might be clearing the TOD alarm otherwise.

   *************************************
   *DANGER DANGER DANGER DANGER DANGER *
   *************************************
   I do not work for sun and i have only done very very basic testing.
   if you somehow manage to kill your box with this dont come crying to me.
   i doubt it can do serious damage but i would still advise that you think 
   twice before using this code on a production box of any kind 
   (which isnt likely to need to be powerd off in the first place any way)
   */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/todio.h>
#include <time.h>
#include <errno.h>
#define MAXBUF 300

int clear_alarm(int fd); /* clear TOD alarm */
int set_alarm(int fd, time_t val); /* set TOD alarm */
int main(int argc, char **argv)
{
	int tod; /* fd for Time of day clock */
	struct tm local_tm; /* tm structure containing local time, modified by tm_wake members */
	struct tm wake_tm; /* tm structure containing the time to wake up at. */
	time_t wake; /* the seconds from now that the alarm should trigger */
	time_t now; /* the current time */
	
	tod = open("/dev/tod",O_RDWR); /* open TOD */

	if(tod == -1)
	{
		perror("Cant open /dev/tod");	
		return 1;
	}
	
	now = time(NULL); /*Get current time */

	(void) localtime_r(&now,&local_tm); /* fill out the tm struct.*/

	/* parse command line */
	if(argc > 1)
	{
		char *tmp; /* pointer for strptime */
		char cat[MAXBUF]; /* buffer to hold argv[1] and argv[2] if needed */

		/* if two arguments are passed cat them togeather for strptime */
		if(argc > 2 ) 
		{
			/* initialize buffer to 0 */
			memset(cat,0,MAXBUF);
			/* copy argv[1] to cat */
			strncpy(cat,argv[1],MAXBUF-1);
			/* append argv[2] to cat */
			strncat(cat,argv[2], MAXBUF - 1 - strlen(argv[1]));
			tmp = strptime(cat,"%m/%d/%Y %H:%M",&wake_tm);
		}
		/* otherwise keep going and parse just the time instead */
		else
		tmp = strptime(argv[1],"%H:%M",&wake_tm); /* parse time argument */
		
		if(tmp == NULL )
		{
			printf("Invalid time spec use HH:MM\n");
			printf("or month/day/year HH:MM\n");
			return 1;
		}

		/* if the date was parsed we need to copy the whole
		   wake_tm structure over. not just the hours and minutes */
		if(argc > 2)
			local_tm = wake_tm;
		else
		{
			local_tm.tm_sec=0;
			local_tm.tm_min = wake_tm.tm_min;
			local_tm.tm_hour = wake_tm.tm_hour;
		}
	}
	else
	{
		printf("No time specified, using default of 8:00\n");
		local_tm.tm_sec=0;
		local_tm.tm_min=0;
		local_tm.tm_hour=8;
	}
	

	wake = mktime(&local_tm); /* take time1 and generate seconds. */
	
	/* If the wake time is in the past make it wake us tomorrow at that
	   time instead */
	if(wake < now)
	{
		printf("Time set in the past! setting alarm for tomorrow.\n");
		wake = wake + ( 24 * 60 * 60 );
	}

	/* dont allow wakeup alarms that are too early in the future */
	if((wake - now) < 300)
	{
		printf("Wake time has to be > 5 minutes in the future!\n");
		exit(1);
	}


	if(clear_alarm(tod) == -1)
		return 1;

	/* Probably not needed, just in case clearing
	   the alarm and setting it too soon after might be a problem. */
	sleep(2); 

	/* Finally we perform the ioctl on TOD to set the alarm */
	if(set_alarm(tod,wake) == -1)
		return 1;

	/* if we get here everything went ok */
	/* Print status and return */
		now = time(NULL);
		printf("Alarm set to %ld \n",wake);
		printf("thats %ld seconds from now\n",wake - now);
		printf("now = %ld \n",now);
		return 0;
}

int clear_alarm(int fd)
{
	int ret;
	printf("Clearing the alarm just to be safe....");
	if((ret = ioctl(fd,TOD_CLEAR_ALARM,NULL)) == -1)
	{
		printf(" Failed! :%s\n",strerror(errno));
		return ret;
		
	} 

	printf(" Success!\n");
	return ret;
}

int set_alarm(int fd, time_t val)
{
	int ret;
	/* Finally we perform the ioctl on TOD to set the alarm */
	printf("Setting TOD alarm....");
	if((ret = ioctl(fd,TOD_SET_ALARM,&val)) == -1)
	{
		printf(" Failed!\n");
		perror("Unable to set the TOD alarm, sorry! ");
		(void) clear_alarm(fd);
		return ret;
	}

	printf(" Success!\n");
	return ret;
}

