/*
 *  GetRemoteDate
 *
 *  Setzt Datum und Uhrzeit des ausführenden Rechers
 *  auf Datum und Uhrzeit des angegebenen Rechners.

//+ "Kommentare, Includes, globale Daten"
 *  Das Datum und die Uhrzeit wird dabei nur als SystemZeit gesetzt
 *  und nicht einer batteriegepufferten Uhr gespeichert. Dazu bei
 *  Bedarf "SetClock save" benutzen.
 *
 *  Template: HOSTNAME/A
 *  Beispiel: GetRemoteDate Amiga1

 *  Diese Version benutzt den GP.service.
 */

#define VERSION     "1.0"
#define DATE        "12.9.94"
#define PROGNAME    "GetRemoteDate"

/*
 *  Include-Dateien
 */
#include <stdlib.h>
#define USE_BUILTIN_MATH
#include <string.h>

#include <envoy/envoy.h>
#include <envoy/errors.h>

#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/nipc_protos.h>
#include <pragmas/nipc_pragmas.h>
#include <clib/timer_protos.h>
#include <pragmas/timer_pragmas.h>

#include "GPServer.h"

/*
 *  globale Datan
 */

/* libraries */
extern struct Library *DOSBase;
extern struct Library *SysBase;
static struct Library *NIPCBase;

//-
/* version string */
static const char version[] = "\0$VER: " PROGNAME " " VERSION " (" DATE ")";

/* Daten & Definitionen für die Parameter */
#define TEMPLATE "HOSTNAME/A"
#define OPTN_HOSTNAME   0
#define OPTN_COUNT      1
#define HOSTNAME    ((unsigned char *)options[OPTN_HOSTNAME])
static long options[OPTN_COUNT];

//+ "/ Prototypen für Funktionen in dieser Datei /"
/* Prototypen für Funktionen in dieser Datei */
static BOOL setsystemtime(struct timeval *tv);
static ULONG remotesystemtime(void);
//-

void main()
{   struct RDArgs *args;
    ULONG rc = RETURN_OK;

    if (SysBase->lib_Version < 37) exit(RETURN_ERROR);
    if (NIPCBase = OpenLibrary("nipc.library", 39))
    {   /* Parameter auswerten */
        if (args = ReadArgs(TEMPLATE, options, NULL))
        {   rc = remotesystemtime();
            FreeArgs(args);
        }
        else
        {   /* Fehler bei den Parametern! */
            PrintFault(IoErr(), NULL); rc = RETURN_WARN;
        }
        CloseLibrary(NIPCBase);
    }
    else PutStr("Kann nipc.library V39 oder neuer nicht öffnen!\n");
    exit(rc);
}

static ULONG remotesystemtime(void)
{
    struct Entity *ausgangs_entity, *ziel_entity;
    struct Transaction *ta, *rta;
    ULONG entitysigbit;
    ULONG sigs;
    ULONG error = ENVOYERR_NOERROR;
    ULONG rc    = RETURN_ERROR;
    BOOL quit   = FALSE;

    /*  Private Entity anlegen. Dabei lassen wir für die Entity gleich ein
     *  SignalBit belegen. Dieses wird in entitysignal abgelegt. */
    if (ausgangs_entity = CreateEntity(ENT_AllocSignal, (ULONG)&entitysigbit,
                                       TAG_END))
    {   /*  Kommunikationspfad errichten */
        if (ziel_entity = FindEntity(HOSTNAME, "GPServer", ausgangs_entity, &error))
        {   /*  Nun allozieren wir die Transaction; den Puffer für die
             *  Antwort lassen wir gleich mitallozieren */
            if (ta = AllocTransaction(TRN_AllocRespBuffer, sizeof(struct timeval), TAG_END))
            {   ta->trans_RequestData = NULL;
                ta->trans_ReqDataLength = 0;
                ta->trans_ReqDataActual = 0;
                ta->trans_Command = TACMD_GETTIME;
                BeginTransaction(ziel_entity, ausgangs_entity, ta);

                while (!quit)
                {   sigs = Wait((1 << entitysigbit) | SIGBREAKF_CTRL_C);
                    if (sigs & (1 << entitysigbit))
                    {   while (rta = GetTransaction(ausgangs_entity))
                        {   if (rta->trans_Type == TYPE_RESPONSE)
                            {   quit = TRUE; } /* sie ist zurück ! */
                            else
                            {   /* Das ist nicht unsere Transaction,
                                 * zur Sicherheit zurückschicken */
                                ReplyTransaction(rta);
                    }   }   }

                    if (sigs & SIGBREAKF_CTRL_C) /* Bei CTRL-C abbrechen */
                    {   AbortTransaction(ta);
                        WaitTransaction(ta);
                        quit = TRUE;
                    }
                }
                if (ta->trans_Error == ENVOYERR_NOERROR) /* Fehler? */
                {   /* Hat die Antwort die richtige Länge? */
                    if (ta->trans_RespDataActual == sizeof(struct timeval))
                    {   setsystemtime(ta->trans_ResponseData);
                        rc = RETURN_OK;  /* Zeit setzen und OK*/
                    }
                    else PutStr("Antwortpaket hat falsche Länge!\n");
                }
                else Printf("Envoy-Fehler %lu!\n", ta->trans_Error);

                FreeTransaction(ta); /* Transaction wieder freigeben */
            }
            LoseEntity(ziel_entity); /*  Kommunikationspfad freigeben */
        }
        else
        { Printf("GPServer auf Rechner '%s' nicht erreichbar:\n", HOSTNAME);
          Printf("Envoy-Fehler %lu\n", error);
        }
        DeleteEntity(ausgangs_entity);
    }
    else PutStr("Kann Entity nicht erzeugen!\n");

    return(rc);
}

//+ "static BOOL setsystemtime(struct timeval tv)"
static BOOL setsystemtime(struct timeval *tv)
{
    #define TimerBase ((struct Library *)timerio->tr_node.io_Device)
    struct MsgPort *timerport;
    struct timerequest *timerio;
    BOOL ok = FALSE;

    if (timerport = CreateMsgPort())
    {
        if (timerio = CreateIORequest(timerport, sizeof(struct timerequest)))
        {
            if (!OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timerio, 0))
            {
                timerio->tr_node.io_Command = TR_SETSYSTIME;
                timerio->tr_time = *tv;
                DoIO((struct IORequest *)timerio);

                ok = TRUE;
                CloseDevice((struct IORequest *)timerio);
            }
            DeleteIORequest(timerio);
        }
        DeleteMsgPort(timerport);
    }
    return(ok);
}
//-

