/*  nfsd - an Amiga NFS daemon

    ©1998, 1999 Joseph Walton

    This software is distributed under the terms of the GNU General Public
    License; either version 2 of the License, or (at your option) any
    later version.
*/

/*
    This is the main routine - it opens libraries, calls allocation routines,
    checks versions, and installs RPC dispatchers.
*/

/*
    28-Sep-1999 - Uses ReadArgs. Allows the user to set the GMT offset.
    30-Nov-1999 - Major changes to support new RPC code.
*/

#include "nfsd.h"
#include "nfs.h"
#include "mount.h"
#include "pcnfsd.h"

#include "service_nfsd.h" // service()

#include "rpc_register.h"
#include "handle_list.h"
#include "memory.h"
#include "config.h"
#include "timerdev.h"
#include "nfs_utils.h"

#include <stdio.h>
#include <signal.h>
#include <string.h>         /* strcmp */
#include <sys/socket.h>
#include <netinet/in.h>

#include <dos/dosextens.h>  /* struct Process */

#include <proto/socket.h>
#include <proto/exec.h>
#include <proto/dos.h>

/* Seems some SDKs don't follow Amiga conventions... */
#ifndef SOCKETNAME
#define SOCKETNAME "bsdsocket.library"
#endif

/* Are we verbose? If so, print copious output */
BOOL verbose = FALSE;

/* This changes how authentication behaves */
extern BOOL allow_root; /* in auth.c */

/* The offset of local time from GMT */
extern LONG amigaGMTOffset; /* in nfs_util.c */

/* I'm relying on DiceC to have opened these */
extern struct Library *SysBase;
extern struct DosLibrary *DOSBase;

struct Library *SocketBase = NULL;

/* This is a rather busy main, but what the hey... */
int main(int argc, char *argv[])
{
    /* Declarations first */

    /* Keep track of whether or not we need to deregister these */
    BOOL registered_nfsd = FALSE,
        registered_mount = FALSE,
        registered_pcnfsd = FALSE;

    /* Our future point of presence in IP space */
    int sock = -1;

    /* We need these to inhibit graphical requesters */
    struct Process *process;
    APTR vptr;

    /* Just a little beta-blaring... */
    puts("nfsd beta, ©1998, 1999 Joseph Walton <ver@etla.org>\n"
         "This version compiled " __DATE__ ".\n"
         "nfsd is free software, distributed under the terms of the GNU General Public License.\n"
         "It comes with ABSOLUTELY NO WARRANTY.");
    /* Moved from pre-pre- to just plain pre-...
        And now we're a plain beta... */

    /* We'll handle CTRL-C's ourselves */
    signal(SIGINT, SIG_IGN);

    /* We need some v39 exec.library functions - sorry */
    if (SysBase->lib_Version < 39) {
        puts("nfsd requires v39 exec.library");
        goto finish;
    }

    /* We do need some v39 dos.library functions - sorry */
    if (DOSBase->dl_lib.lib_Version < 39) {
        puts("nfsd requires v39 dos.library");
        goto finish;
    }

    /* We manually open timer.device, as SAS/C won't do it for us */
    if (!td_opentimer()) {
        puts("Unable to open " TIMERNAME);
        goto finish;
    }

    /* Oh, I might need some socket handling routines... */
    SocketBase = OpenLibrary(SOCKETNAME, 4);

    if (!SocketBase) {
        puts("Unable to open " SOCKETNAME " v4");
        goto finish;
    }

    /* Need to get memory to work with */
    if (!init_memory()) {
        puts("Unable to allocate handle list");
        goto finish;
    }

    /* Check the command line */
    {
        STRPTR template = "GMTOFFSET/K/N,ALLOWROOT/S,VERBOSE/S";
        LONG opts[3] = {0, 0, 0};
        struct RDArgs *argsptr;

        if (NULL == (argsptr = ReadArgs(template, opts, NULL))) {
            PrintFault(IoErr(), NULL);
            goto finish;
        }

        /* Now act on those arguments */
        if (opts[0]) {
            amigaGMTOffset = *(LONG *)opts[0];
        }

        if (opts[1]) {
            allow_root = TRUE;
            puts("Allowing remote accesses by root");
        }

        if (opts[2]) {
            verbose = TRUE;
            puts("Verbose mode enabled");
        }

        FreeArgs(argsptr);
    }

    /* And parse our configuration */
    if (!process_cfg("nfsd.config")) {
        puts("Unable to get configuration");
        goto finish;
    }

    /* Now fetch the inode base */
    persistent_inode_fetch();
    /* Any failure is dealt with internally, by starting afresh at 1 */

    /* We need a socket to communicate with */
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0) {
        printSocketError("Could not create a socket");
        goto finish;
    }

    /* nfsd needs to bind to a well-known port */
    {
        struct sockaddr_in addr = {
            sizeof(struct sockaddr_in),
            AF_INET,
            htons(NFS_PORT),
            {htonl(INADDR_ANY)}
        };

        /* Can I do this without a macro? */
        #define A(x) #x

        if (-1 == bind(sock, &addr, sizeof(addr))) {
            printSocketError("Unable to bind to port " A(NFS_PORT));
            goto finish;
        }

        #undef A
    }

    /*
        We don't try to get rid of any old mapping at the moment.
        It might well fail confusingly, and gazumping services like
        that is hardly polite.
    */

    /* Establish mappings. We bind everything to the same port. */
    if (pmap_set(sock, NFS_PROGRAM, NFS_VERSION, NFS_PORT)) {
        registered_nfsd = TRUE;
    } else {
        puts("Unable to register (NFS_PROGRAM, NFS_VERSION, NFS_PORT).");
        goto finish;
    }

    if (pmap_set(sock, MOUNT_PROGRAM, MOUNT_VERSION, NFS_PORT)) {
        registered_mount = TRUE;
    } else {
        puts("Unable to register (MOUNT_PROGRAM, MOUNT_VERSION, NFS_PORT).");
        goto finish;
    }

    if (pmap_set(sock, PCNFSD_PROGRAM, PCNFSD_VERSION, NFS_PORT)) {
        registered_pcnfsd = TRUE;
    } else {
        puts("Unable to register (PCNFSD_PROGRAM, PCNFSD_VERSION, NFS_PORT).");
        goto finish;
    }

    /* We don't want any requesters popped up */
    process = (struct Process *)FindTask(NULL);
    vptr = process->pr_WindowPtr;
    process->pr_WindowPtr = (APTR)-1L;

    puts("Serving.");
    service(sock); /* This where we jump to the Real Work */

    puts("Finished.");

    /* Now return things to normal */
    process->pr_WindowPtr = vptr;

    if (!persistent_inode_store())
        puts("Failed to store current inode");

    /* Now, free any resources and exit */
    finish:
    td_closetimer();

    if (SocketBase) {
        if (sock >= 0) {
            /* If any of these time out we should probably skip the
                rest, but we currently don't export that information
                from the registration process */
            if (registered_pcnfsd)
                if (!pmap_unset(sock, PCNFSD_PROGRAM, PCNFSD_VERSION))
                    puts("Unable to deregister the PCNFSD service.");
            if (registered_nfsd)
                if (!pmap_unset(sock, NFS_PROGRAM, NFS_VERSION))
                    puts("Unable to deregister the NFS service.");
            if (registered_mount)
                if (!pmap_unset(sock, MOUNT_PROGRAM, MOUNT_VERSION))
                    puts("Unable to deregister the mount service.");
            CloseSocket(sock);
        }
        CloseLibrary(SocketBase);
    }
    free_memory();
    return RETURN_OK;
}

