/*
 *  RemoteExecute
 *
 *  Führt eine Befehlszeile in einer Shell auf dem angegebenen Rechner aus.
 *  Außerdem kann die Ausgabedatei angegeben werden.
 *
 *  Template: HOSTNAME/A,COMMAND/A,OUTPUT/K
 *  Beispiel: RemoteExecute Amiga1 "dir c:" "ram:ausgabe"
 *
 *  Diese Version benutzt den GPServer.
 */

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

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

#include <envoy/envoy.h>
#include <envoy/errors.h>
#include <exec/memory.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 "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,COMMAND/A,OUTPUT/K"
#define OPTN_HOSTNAME   0
#define OPTN_COMMAND    1
#define OPTN_OUTPUT     2
#define OPTN_COUNT      3
static long options[OPTN_COUNT];
#define HOSTNAME    ((unsigned char *)options[OPTN_HOSTNAME])
#define COMMAND     ((unsigned char *)options[OPTN_COMMAND])
#define OUTPUT      ((unsigned char *)options[OPTN_OUTPUT])

/*
 *  Prototypen für Funktionen in dieser Datei
 */
static ULONG remoteexecute(void);
static BOOL makedatapacket(struct Transaction *ta);

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))
    {
        /* Parameter auswerten */
        if (args = ReadArgs(TEMPLATE, options, NULL))
        {
            rc = remoteexecute();
            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 remoteexecute(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 = FindEntity(HOSTNAME, "GPServer", ausgangs_entity, &error))
        {
            /*  Nun allozieren wir die Transaction */
            if (ta = AllocTransaction(TAG_END))
            {
                /*  Das Datenpaket für die Anfrage schnüren ... */
                if (makedatapacket(ta))
                {
                    /*  Die Transaction losschicken ... */
                    ta->trans_Command = TACMD_CMDLINE;
                    BeginTransaction(ziel_entity, ausgangs_entity, ta);

                    /*  Nun warten wir auf die Antwort ... */
                    while (!quit)
                    {
                        sigs = Wait((1 << entitysigbit) | SIGBREAKF_CTRL_C);

                        if (sigs & (1 << entitysigbit))
                        {
                            /*  Bei unserer Entity ist was angekommen ... */
                            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
                                     * Dieser Fall darf eigentlich nie auf-
                                     * treten.
                                     */
                                    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)
                    {
                        rc = RETURN_OK;
                    }
                    else
                    {
                        Printf("Envoy-Fehler %lu!\n", ta->trans_Error);
                    }

                    /* Datenbereich wieder freigeben */
                    FreeMem(ta->trans_RequestData, ta->trans_ReqDataLength);
                }
                /* Transaction freigeben */
                FreeTransaction(ta);
            }
            /*  Kommunikationspfad freigeben */
            LoseEntity(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 makedatapacket(struct Transaction *ta)
{
    /*  Diese Funktion erstellt aus den Kommandozeilenparametern,
     *  die uns der Benutzer angegeben hat, ein Datenpaket, wie es
     *  der Server versteht.
     */
    ULONG datalen;
    unsigned char *data;

    /*  Wieviel Bytes an Daten wollen wir an den Server schicken? */
    datalen = strlen(COMMAND) + 2;
    if (OUTPUT) datalen = datalen + strlen(OUTPUT);

    if (data = AllocMem(datalen, MEMF_ANY))
    {
        ta->trans_RequestData = data;
        ta->trans_ReqDataLength = datalen;
        ta->trans_ReqDataActual = datalen;

        /*  Nun müssen wir die Daten noch in der richtigen Form in den
         *  allozierten Speicher kopieren:
         *  Der GPServer erwartet zuerst den COMMAND-String,
         *  dann den OUTPUT-String; beide Strings sind Null-terminiert;
         */
        strcpy(data, COMMAND);              /* Kommando kopieren    */
        data += strlen(data) + 1;           /* Zeiger vor           */
        if (OUTPUT) strcpy(data, OUTPUT);   /* Output kopieren oder */
        else *data = '\0';                  /* noch ein Nullbyte    */

        return(TRUE);
    }
    return(FALSE);
}
