/*
 *  GetRemoteDate
 *
 *  Setzt Datum und Uhrzeit des ausführenden Rechers
 *  auf Datum und Uhrzeit des angegebenen Rechners.
 *  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     "2.0"
#define DATE        "16.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/services_protos.h>
#include <pragmas/services_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;
static struct Library *ServicesBase;

/* 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
static long options[OPTN_COUNT];
#define HOSTNAME    ((unsigned char *)options[OPTN_HOSTNAME])

/*
 *  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); /* unter Kick 2.04 geht nichts! */

    if (NIPCBase = OpenLibrary("nipc.library", 39))
    {
        if (ServicesBase = OpenLibrary("services.library", 37))
        {
            /* Parameter auswerten */
            if (args = ReadArgs(TEMPLATE, options, NULL))
            {
                rc = remotesystemtime();
                FreeArgs(args);
            }
            else
            {
                /* Fehler bei den Parametern! */
                PrintFault(IoErr(), NULL);
                rc = RETURN_WARN;
            }

            CloseLibrary(ServicesBase);
        }
        else PutStr("Kann services.library ab V37 nicht öffnen!\n");

        CloseLibrary(NIPCBase);
    }
    else PutStr("Kann nipc.library V39 oder neuer nicht öffnen!\n");

    exit(rc);
}

static ULONG remotesystemtime(void)
{
    struct Entity *ausgangs_entity;
    struct Entity *ziel_entity;
    struct Transaction *ta;
    struct Transaction *rta;
    ULONG entitysigbit;
    ULONG error = ENVOYERR_NOERROR;
    ULONG rc = RETURN_ERROR;
    ULONG sigs;
    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 = FindService(HOSTNAME, "GPServer", ausgangs_entity,
                                      FSVC_Error, (ULONG)&error, TAG_END))
        {
            /*  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)
                            {
                                /* Unsere Transaction ist zurück ... */
                                quit = TRUE;
                            }
                            else
                            {
                                /* Das ist nicht unsere Transaction,
                                 * die wir losgeschickt haben; zur
                                 * Sicherheit schicken wie sie zurück
                                 */
                                ReplyTransaction(rta);
                            }
                        }
                    }

                    if (sigs & SIGBREAKF_CTRL_C)
                    {
                        /* Bei CTRL-C sollen wir abbrechen:
                         * dazu müsen wir die Transaction erst abbrechen.
                         */
                        AbortTransaction(ta);
                        WaitTransaction(ta);
                        quit = TRUE;
                    }
                }

                /* Ist ein Fehler aufgetreten? */
                if (ta->trans_Error == ENVOYERR_NOERROR)
                {
                    /* Hat die Antwort die richtige Länge? */
                    if (ta->trans_RespDataActual == sizeof(struct timeval))
                    {
                        /* Systemzeit setzen */
                        setsystemtime(ta->trans_ResponseData);
                        rc = RETURN_OK;
                    }
                    else PutStr("Antwortpaket hat falsche Länge!\n");
                }
                else
                {
                    Printf("Envoy-Fehler %lu!\n", ta->trans_Error);
                }

                /* Transaction wieder freigeben */
                FreeTransaction(ta);
            }
            /*  Kommunikationspfad freigeben */
            LoseService(ziel_entity);
        }
        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)
{
    #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);
}
