/*
   sdwarn.c --- example shutdown client.

   (c) Copyright 1995 SHW Wabnitz
   Written by Bernhard Fastenrath (fasten@shw.com)

   This file may be distributed under the terms
   of the GNU General Public License.
*/

#include <dos.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <exec/types.h>
#include <proto/exec.h>
#include <proto/dos.h>

#if defined (__GNUC__)
#include "halt_inline.h"
#else
#include "halt_pragmas.h"
#endif
#include "halt.h"
#include "ups.h"

void CloseTimerDevice (void);
int CheckCLISession (void);

struct Library *HaltBase = NULL;
struct MsgPort *prt = NULL;
struct timerequest *TimerReq = NULL;
struct MsgPort *TimerPort = NULL;
ULONG TimerMask = 0;
ULONG CliNumber = 0;
BPTR CliStdout = NULL;
struct Process *MyProc;
APTR WindowPtr;

char *optarg = NULL;

int
getopt (int argc, char *argv[], char *opts)
{
  static int pos = 0;
  char *c;

  while (++pos < argc && argv[pos][0] != '-');
  if (pos >= argc)
    return -1;
  if ((c = strchr (opts, (int) argv[pos][1])) == 0)
    return (int) '?';
  if (*(c+1) != ':')
    return (int) *c;
  if (strlen (argv[pos]) > 2)
    optarg = argv[pos] + 2;
  else if (pos+1 < argc)
    optarg = argv[pos+1];
  else
    return (int) '?';
  return (int) *c;
}

#if !defined (__GNUC__)
int
SigBreakHandler (void)
{
  struct Message *msg;

  if (HaltBase)
  {
    if (prt)
    {
      Forbid ();
      while (msg = GetMsg (prt))
        ReplyMsg (msg);
      RemShutdownPort (prt);
      DeleteMsgPort (prt);
      Permit ();
    }
    CloseTimerDevice ();
    CloseLibrary (HaltBase);
  }
  exit (EXIT_SUCCESS);
  return 0;
}
#endif

int
CheckCLISession ()
{
  struct Process *pr;
  int code = 0;

  if (!CliNumber)
    return 1;

  Forbid ();
  if (pr = FindCliProc (CliNumber))
  {
    /* We might have found a new CLI
       with the same number.
    */
    if (pr -> pr_COS == CliStdout)
      code = 1;
  }
  Permit ();
  return code;
}

void
CloseTimerDevice ()
{
  if (!TimerPort)
    return;

  AbortIO ((struct IORequest *) TimerReq);
  WaitIO ((struct IORequest *) TimerReq);
  CloseDevice ((struct IORequest *) TimerReq);
  DeleteIORequest (TimerReq);
  DeleteMsgPort (TimerPort);
  TimerPort = NULL;
}

void
StartTimer ()
{
  if (TimerPort)
  {
    TimerReq -> tr_node.io_Command = TR_ADDREQUEST;
    TimerReq -> tr_time.tv_secs  = 3;
    TimerReq -> tr_time.tv_micro = 0;
    SendIO ((struct IORequest *) TimerReq);
  }
}

ULONG
OpenTimerDevice ()
{
  if (!(TimerPort = CreateMsgPort ()))
    return 0;

  if (!(TimerReq = (struct timerequest *)
	CreateIORequest (TimerPort, sizeof (struct timerequest))))
  {
    DeleteMsgPort (TimerPort);
    return 0;
  }
  if (OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerReq, 0))
  {
    DeleteIORequest (TimerReq);
    DeleteMsgPort (TimerPort);
    return 0;
  }
  StartTimer ();
  return TimerMask = 1 << TimerPort -> mp_SigBit;
}

int
main (int argc, char *argv[])
{
  ShutdownMessage *sm;
  UpsInfo *ups;
  char *UpsEvents[] = UPS_EV_NAMES;
  int abort = 0;
  int verbose = 0;
  ULONG mask, rmask;
  int opt;

#if !defined (__GNUC__)
  onbreak (SigBreakHandler);
#endif

  MyProc = (struct Process *) FindTask (0);
  WindowPtr = MyProc -> pr_WindowPtr;
  MyProc -> pr_WindowPtr = (void *) -1;

  while ((opt = getopt (argc, argv, "ac:v")) != -1)
    switch (opt)
    {
      case 'a': abort = 1; break;
      case 'v': verbose = 1; break;
      case 'c':
        if (CliNumber = atoi (optarg))
        {
          struct Process *pr;

          Forbid ();
          if (pr = FindCliProc (CliNumber))
	    CliStdout = pr -> pr_COS;
          Permit ();
          if (!CliStdout)
          {
	    printf ("Invalid CLI.\n", CliNumber);
	    exit ( EXIT_FAILURE );
          }
        }
	break;
      default:
        printf ("Usage: %s [-av] [-c <cli>].\n", argv[0]);
	exit (EXIT_FAILURE);
    }

  if (!(HaltBase = OpenLibrary ("halt.library", 0)))
  {
    printf ("Failed to open halt.library.\n");
    return EXIT_FAILURE;
  }
  if (CliNumber)
  {
    if (!OpenTimerDevice ())
    {
      printf ("Failed to open timer.device.\n");
      CloseLibrary (HaltBase);
      return EXIT_FAILURE;    
    }
  }
  if (prt = CreateMsgPort ())
  {
    mask = 1 << prt -> mp_SigBit;
    mask |= SIGBREAKF_CTRL_C;
    mask |= TimerMask;

    if (AddShutdownPort (prt))
    {
      while (1)
      {
	rmask = Wait (mask);
	if (rmask & TimerMask)
	  StartTimer ();

	if (sm = (ShutdownMessage *) GetMsg (prt))
	{
	  switch (sm -> sm_Status)
	  {
	    case SHUTDOWN_WARN:
	      printf ("The system is going down in %d seconds.\n", sm -> sm_TimeLeft);
	      break;
	    case SHUTDOWN_INFO:
	      if (verbose)
              {
	        if (sm -> sm_Info == SDMI_UPS_MONITOR)
                {
	          printf ("Shutdown info received:\n");
                  ups = sm -> sm_Extra;
                  if (ups -> ui_Event > UPS_EV_MAX)
                    printf ("Event = %d (unknown)\n", ups -> ui_Event);
                  else
                    printf ("Event = %s.\n", UpsEvents[ups -> ui_Event]);
	          printf ("Charge = %d.\n", ups -> ui_Charge);
	          printf ("Remaining time = %d.\n", ups -> ui_Time);	        
                }
	        else
	          printf ("Shutdown info received.\n");
              }
	      break;
	    case SHUTDOWN_ABORT:
	      printf ("Shutdown has been aborted.\n");
#if 0
	      CloseTimerDevice ();
	      RemShutdownPort (prt);
	      ReplyMsg ((struct Message *) sm);
	      while (sm = GetMsg (prt))
	        ReplyMsg ((struct Message *) sm);
	      DeleteMsgPort (prt);
	      CloseLibrary (HaltBase);
	      exit (EXIT_SUCCESS);
#else
	      break;
#endif
	    case SHUTDOWN_UMOUNT:
	      printf ("The system is going down, unmounting filesystems.\n");
	      break;
	    case SHUTDOWN_NOW:
	      printf ("The system is going down in 5 seconds.\n");
	      break;
	    case SHUTDOWN_HALT:
	      printf ("The system is halted.\n");
	      break;
	    default:
	      printf ("Unknown message.\n");
	      break;
	  }
          if (abort)
            sm -> sm_Flags |= SDMF_CANCEL;
	  ReplyMsg ((struct Message *) sm);
	}
        if (rmask & SIGBREAKF_CTRL_C || !CheckCLISession ())
        {
	  if (rmask & SIGBREAKF_CTRL_C)
	    printf ("CTRL-C\n");
	  else
	    printf ("\n%s: End of session.\n", argv[0]);
	  RemShutdownPort (prt);
	  break;
        }
      }
    }
    else
      printf ("AddShutdownPort() failed.\n");
    while (sm = GetMsg (prt))
      ReplyMsg ((struct Message *) sm);
    DeleteMsgPort (prt);
  }
  else
    printf ("Can't allocate message port.\n");
  CloseTimerDevice ();
  CloseLibrary (HaltBase);
  MyProc -> pr_WindowPtr = WindowPtr;
  exit ( EXIT_SUCCESS );
}
