/*************************************************************************\
* Copyright (c) 2002 The University of Saskatchewan
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
 * RTEMS startup task for EPICS
 *      Author: W. Eric Norum
 *              eric.norum@usask.ca
 *              (306) 966-5394
 */

/*
 * This is the first implementation with posix and rtems5.
 *  Currently only dhcp is supported with libbsd. The reading of the
 *  environment variables from e.g. u-boot environment is still missing.
 * Extensive tests so far only with qemu.
 */

/* trigger sys/syslog.h to emit prioritynames[] */
#define SYSLOG_NAMES

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <rtems.h>
#include <rtems/malloc.h>
#include <rtems/error.h>
#include <rtems/stackchk.h>
#include <rtems/imfs.h>
#include <rtems/shell.h>
#include <pthread.h>
#include <signal.h>
#include <sched.h>
#include <rtems/libio.h>
#include <rtems/rtc.h>
#include <time.h>
#include <sys/unistd.h>

#if __RTEMS_MAJOR__ > 4
#include <rtems/tod.h>
#else
#include <rtems/score/tod.h>
#endif

#ifdef RTEMS_LEGACY_STACK
#include <rtems/rtems_bsdnet.h>
#include <librtemsNfs.h>
#else // libbsd stack
#include <rtems/bsd/bsd.h>
#include <rtems/bsd/modules.h>
#include <rtems/dhcpcd.h>
#include <machine/rtems-bsd-commands.h>
#endif

#include <rtems/telnetd.h>
#if __RTEMS_MAJOR__ > 4
#include <rtems/printer.h>
#endif
#include "epicsVersion.h"
#include "epicsThread.h"
#include "epicsTime.h"
#include "epicsExit.h"
#include "envDefs.h"
#include "errlog.h"
#include "logClient.h"
#include "osiUnistd.h"
#include "iocsh.h"
#include "osdTime.h"
#include "epicsMemFs.h"
#include "epicsEvent.h"
#include "errlog.h"

#include "epicsRtemsInitHooks.h"

#ifndef RTEMS_LEGACY_STACK
// setup own ntp-client to get time
#include "epicsNtp.h"
epicsEventId 	dhcpDone;
#endif

/* these settings are needed by the rtems startup
 * may provide by dhcp/bootp
 * or environments from the "BIOS" like u-boot, motboot etc.
 */
char rtemsInit_NTP_server_ip[16] = "";
char bootp_server_name_init[128] = "1001.1001@10.0.5.1:/epics";
char bootp_boot_file_name_init[128] = "/epics/myExample/bin/RTEMS-beatnik/myExample.boot";
char bootp_cmdline_init[128] = "/epics/myExample/iocBoot/iocmyExample/st.cmd";

struct in_addr rtems_bsdnet_bootp_server_address;
/* TODO check rtems_bsdnet_bootp_cmdline */
#ifndef RTEMS_LEGACY_STACK
char *rtems_bsdnet_bootp_server_name = bootp_server_name_init;
char *rtems_bsdnet_bootp_boot_file_name = bootp_boot_file_name_init;
char *rtems_bsdnet_bootp_cmdline = bootp_cmdline_init;
#endif // not LEGACY Stack

/*
 * Prototypes for some functions not in header files
 */
//void tzset(void);
int fileno(FILE *);
int main(int argc, char **argv);

static void
logReset (void)
{
    void rtems_bsp_reset_cause(char *buf, size_t capacity) __attribute__((weak));
    void (*fp)(char *buf, size_t capacity) = rtems_bsp_reset_cause;

    if (fp) {
        char buf[80];
        fp(buf, sizeof buf);
        errlogPrintf ("Startup after %s.\n", buf);
    }
    else {
        errlogPrintf ("Startup.\n");
    }
}

/*
 ***********************************************************************
 *                         FATAL ERROR REPORTING                       *
 ***********************************************************************
 */
/*
 * Delay for a while, then terminate
 */
static void
delayedPanic (const char *msg)
{
    rtems_task_wake_after (rtems_clock_get_ticks_per_second());
    rtems_task_wake_after (rtems_clock_get_ticks_per_second());
    rtems_panic ("%s", msg);
}

/*
 * Log error and terminate
 */
void
LogFatal (const char *msg, ...)
{
    va_list ap;

    va_start (ap, msg);
    errlogVprintf (msg, ap);
    va_end (ap);
    delayedPanic (msg);
}

/*
 * Log RTEMS error and terminate
 */
void
LogRtemsFatal (const char *msg, rtems_status_code sc)
{
    errlogPrintf ("%s: %s\n", msg, rtems_status_text (sc));
    delayedPanic (msg);
}

/*
 * Log network error and terminate
 */
void
LogNetFatal (const char *msg, int err)
{
    errlogPrintf ("%s: %d\n", msg, err);
    delayedPanic (msg);
}

void *
mustMalloc(int size, const char *msg)
{
    void *p;

    if ((p = malloc (size)) == NULL)
        LogFatal ("Can't allocate space for %s.\n", msg);
    return p;
}

/*
 ***********************************************************************
 *                         REMOTE FILE ACCESS                          *
 ***********************************************************************
 */
#ifdef OMIT_NFS_SUPPORT
# include <rtems/tftp.h>
#endif

const epicsMemFS *epicsRtemsFSImage __attribute__((weak));
const epicsMemFS *epicsRtemsFSImage = (void*)&epicsRtemsFSImage;

/* hook to allow app specific FS setup */
int
epicsRtemsMountLocalFilesystem(char **argv) __attribute__((weak));
int
epicsRtemsMountLocalFilesystem(char **argv)
{
    if(epicsRtemsFSImage==(void*)&epicsRtemsFSImage)
        return -1; /* no FS image provided. */
    else if(epicsRtemsFSImage==NULL)
        return 0; /* no FS image provided, but none is needed. */
    else {
        printf("***** Using compiled in file data *****\n");
        if (epicsMemFsLoad(epicsRtemsFSImage) != 0) {
            printf("Can't unpack tar filesystem\n");
            return -1;
        } else {
            argv[1] = "/";
            return 0;
        }
    }
}

static int
initialize_local_filesystem(char **argv)
{
    extern char _DownloadLocation[] __attribute__((weak));
    extern char _FlashBase[] __attribute__((weak));
    extern char _FlashSize[]  __attribute__((weak));

    argv[0] = rtems_bsdnet_bootp_boot_file_name;
    if (epicsRtemsMountLocalFilesystem(argv)==0) {
        return 1; /* FS setup successful */
#ifdef RTEMS_LEGACY_STACK
    } else if (_FlashSize && (_DownloadLocation || _FlashBase)) {
        extern char _edata[];
        size_t flashIndex = _edata - _DownloadLocation;
        char *header = _FlashBase + flashIndex;

        if (memcmp(header + 257, "ustar  ", 8) == 0) {
            int fd;
            printf ("***** Unpack in-memory file system (IMFS) *****\n");
            if (rtems_tarfs_load("/", (unsigned char *)header, (size_t)_FlashSize - flashIndex) != 0) {
                printf("Can't unpack tar filesystem\n");
                return 0;
            }
            if ((fd = open(rtems_bsdnet_bootp_cmdline, 0)) >= 0) {
                close(fd);
                printf ("***** Found startup script (%s) in IMFS *****\n", rtems_bsdnet_bootp_cmdline);
                argv[1] = rtems_bsdnet_bootp_cmdline;
                return 1;
            }
            printf ("***** Startup script (%s) not in IMFS *****\n", rtems_bsdnet_bootp_cmdline);
        }
#endif /* only with old stack, check check libbsd dependency! */
    }
    return 0;
}

#ifndef OMIT_NFS_SUPPORT
int
nfsMount(char *uidhost, char *path, char *mntpoint)
{
    int   devl = strlen(uidhost) + strlen(path) + 2;
    char *dev;
    int   rval = -1;

    if ((dev = malloc(devl)) == NULL) {
        fprintf(stderr,"nfsMount: out of memory\n");
        return -1;
    }
    sprintf(dev, "%s:%s", uidhost, path);
    printf("Mount %s on %s\n", dev, mntpoint);
    rval = mount_and_make_target_path (
        dev, mntpoint, RTEMS_FILESYSTEM_TYPE_NFS,
        RTEMS_FILESYSTEM_READ_WRITE, NULL );
   if(rval)
      perror("mount failed");
    free(dev);
    return rval;
}
#define NFS_INIT
#endif


static void
initialize_remote_filesystem(char **argv, int hasLocalFilesystem)
{
#ifdef OMIT_NFS_SUPPORT
    printf ("***** Initializing TFTP *****\n");
    mount_and_make_target_path(NULL,
                               "/TFTP",
                               RTEMS_FILESYSTEM_TYPE_TFTPFS,
                               RTEMS_FILESYSTEM_READ_WRITE,
                               NULL);
    if (!hasLocalFilesystem) {
        char *path;
        int pathsize = 200;
        int l;

        path = mustMalloc(pathsize, "Command path name ");
        strcpy (path, "/TFTP/BOOTP_HOST/epics/");
        l = strlen (path);
        if (gethostname (&path[l], pathsize - l - 10) || (path[l] == '\0'))
        {
            LogFatal ("Can't get host name");
        }
        strcat (path, "/st.cmd");
        argv[1] = path;
    }
#else
    char *server_name;
    char *server_path;
    char *mount_point;
    char *cp;
    int l = 0;

    printf ("***** Initializing NFS *****\n");
    NFS_INIT
    if (env_nfsServer && env_nfsPath && env_nfsMountPoint) {
        server_name = env_nfsServer;
        server_path = env_nfsPath;
        mount_point = env_nfsMountPoint;
        cp = mount_point;
        while ((cp = strchr(cp+1, '/')) != NULL) {
            *cp = '\0';
            if ((mkdir (mount_point, 0755) != 0)
             && (errno != EEXIST))
                LogFatal("Can't create directory \"%s\": %s.\n",
                                                mount_point, strerror(errno));
            *cp = '/';
        }
        argv[1] = rtems_bsdnet_bootp_cmdline;
    }
    else if (hasLocalFilesystem) {
        return;
    }
    else {
        /*
         * Use first component of nvram/bootp command line pathname
         * to set up initial NFS mount.  A "/tftpboot/" is prepended
         * if the pathname does not begin with a '/'.  This allows
         * NFS and TFTP to have a similar view of the remote system.
         */
        if (rtems_bsdnet_bootp_cmdline[0] == '/')
            cp = rtems_bsdnet_bootp_cmdline + 1;
        else
            cp = rtems_bsdnet_bootp_cmdline;
        cp = strchr(cp, '/');
        if ((cp == NULL)
         || ((l = cp - rtems_bsdnet_bootp_cmdline) == 0))
            LogFatal("\"%s\" is not a valid command pathname.\n", rtems_bsdnet_bootp_cmdline);
        cp = mustMalloc(l + 20, "NFS mount paths");
        server_path = cp;
        server_name = rtems_bsdnet_bootp_server_name;
        if (rtems_bsdnet_bootp_cmdline[0] == '/') {
            mount_point = server_path;
            strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l);
            mount_point[l] = '\0';
            argv[1] = rtems_bsdnet_bootp_cmdline;
            /*
             * Its probably common to embed the mount point in the server
             * name so, when this is occurring, don't clobber the mount point
             * by appending the first node from the command path. This allows
             * the mount point to be a different path then the server's mount
             * path.
             *
             * This allows for example a line similar to as follows the DHCP
             * configuration file.
             *
             * server-name "159.233@192.168.0.123:/vol/vol0/bootRTEMS";
             */
            if ( server_name ) {
                const size_t allocSize = strlen ( server_name ) + 2;
                char * const pServerName = mustMalloc( allocSize,
                                                        "NFS mount paths");
                char * const pServerPath = mustMalloc ( allocSize,
                                                        "NFS mount paths");
                const int scanfStatus = sscanf (
                                          server_name,
                                          "%[^:] : / %s",
                                          pServerName,
                                          pServerPath + 1u );
                if ( scanfStatus ==  2 ) {
                    pServerPath[0u]= '/';
                    server_name = pServerName;
                    // is this a general solution?
                    server_path = mount_point = pServerPath;
                }
                else {
                    free ( pServerName );
                    free ( pServerPath );
                }
            }
        }
        else {
            char *abspath = mustMalloc(strlen(rtems_bsdnet_bootp_cmdline)+2,"Absolute command path");
            strcpy(server_path, "/tftpboot/");
            mount_point = server_path + strlen(server_path);
            strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l);
            mount_point[l] = '\0';
            mount_point--;
            strcpy(abspath, "/");
            strcat(abspath, rtems_bsdnet_bootp_cmdline);
            argv[1] = abspath;
        }
    }
    errlogPrintf("nfsMount(\"%s\", \"%s\", \"%s\")\n",
                 server_name, server_path, mount_point);
    nfsMount(server_name, server_path, mount_point);
#endif
}

static
char rtems_etc_hosts[] = "127.0.0.1       localhost\n";

/* If it doesn't already exist, create /etc/hosts with an entry for 'localhost' */
static
void fixup_hosts(void)
{
    FILE *fp;
    int ret;
    struct stat STAT;

    ret=stat("/etc/hosts", &STAT);
    if(ret==0)
    {
        return; /* already exists, assume file */
    } else if(errno!=ENOENT) {
        perror("error: fixup_hosts stat /etc/hosts");
        return;
    }

    ret = mkdir("/etc", 0775);
    if(ret!=0 && errno!=EEXIST)
    {
        perror("error: fixup_hosts create /etc");
        return;
    }

    if((fp=fopen("/etc/hosts", "w"))==NULL)
    {
        perror("error: fixup_hosts create /etc/hosts");
    }

    if(fwrite(rtems_etc_hosts, 1, sizeof(rtems_etc_hosts)-1, fp)!=sizeof(rtems_etc_hosts)-1)
    {
        perror("error: failed to write /etc/hosts");
    }

    fclose(fp);
}

/*
 * Get to the startup script directory
 * The TFTP filesystem requires a trailing '/' on chdir arguments.
 */
static void
set_directory (const char *commandline)
{
    const char *cp;
    char *directoryPath;
    int l;

    cp = strrchr(commandline, '/');
    if (cp == NULL) {
        l = 0;
        cp = "/";
    }
    else {
        l = cp - commandline;
        cp = commandline;
    }
    directoryPath = mustMalloc(l + 2, "Command path directory ");
    strncpy(directoryPath, cp, l);
    directoryPath[l] = '/';
    directoryPath[l+1] = '\0';
    if (chdir (directoryPath) < 0)
        LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno));
    else
        errlogPrintf("chdir(\"%s\")\n", directoryPath);
    free(directoryPath);
}

/*
 ***********************************************************************
 *                         RTEMS/EPICS COMMANDS                        *
 ***********************************************************************
 */

static const iocshArg rtshellArg0 = { "cmd", iocshArgString};
static const iocshArg rtshellArg1 = { "args", iocshArgArgv};
static const iocshArg * rtshellArgs[2] = { &rtshellArg0, &rtshellArg1};
static const iocshFuncDef rtshellFuncDef = { "rt",2, rtshellArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
                                            , "run rtems shell command"
#endif
                                           };

static void rtshellCallFunc(const iocshArgBuf *args)
{
    rtems_shell_cmd_t *cmd = rtems_shell_lookup_cmd(args[0].sval);
    int ret;
    
    if (!cmd) {
        fprintf(stderr, "ERR: No such command\n");
    
    } else {
        fflush(stdout);
        fflush(stderr);
        ret = (*cmd->command)(args[1].aval.ac,args[1].aval.av);
        fflush(stdout);
        fflush(stderr);
        if(ret)
            fprintf(stderr, "ERR: %d\n",ret);
    }
}

/*
 * RTEMS status
 */
static void
rtems_netstat (unsigned int level)
{
#ifndef RTEMS_LEGACY_STACK
    printf ("***** Sorry not implemented yet with the new network stack (bsdlib)\n");
#else
    rtems_bsdnet_show_if_stats ();
    rtems_bsdnet_show_mbuf_stats ();
    if (level >= 1) {
        rtems_bsdnet_show_inet_routes ();
    }
    if (level >= 2) {
        rtems_bsdnet_show_ip_stats ();
        rtems_bsdnet_show_icmp_stats ();
        rtems_bsdnet_show_udp_stats ();
        rtems_bsdnet_show_tcp_stats ();
    }
#endif
}

static const iocshArg netStatArg0 = { "level",iocshArgInt};
static const iocshArg * const netStatArgs[1] = {&netStatArg0};
static const iocshFuncDef netStatFuncDef = {"netstat",1,netStatArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
                                            , "show network status"
#endif
                                           };
static void netStatCallFunc(const iocshArgBuf *args)
{
    rtems_netstat(args[0].ival);
}

static const iocshFuncDef heapSpaceFuncDef = {"heapSpace",0,NULL
#ifdef IOCSHFUNCDEF_HAS_USAGE
                                              , "show malloc statistic"
#endif
                                             };
static void heapSpaceCallFunc(const iocshArgBuf *args)
{
#if __RTEMS_MAJOR__ > 4
    Heap_Information_block info;
    double x;

    malloc_info (&info);
    x = info.Stats.size - (unsigned long)
        (info.Stats.lifetime_allocated - info.Stats.lifetime_freed);
    if (x >= 1024*1024)
        printf("Heap space: %.1f MB\n", x / (1024 * 1024));
    else
        printf("Heap space: %.1f kB\n", x / 1024);
#else
    rtems_malloc_statistics_t s;
    double x;

    malloc_get_statistics(&s);
    x = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed);
    if (x >= 1024*1024)
        printf("Heap space: %.1f MB\n", x / (1024 * 1024));
    else
        printf("Heap space: %.1f kB\n", x / 1024);
#endif // RTEMS < 5
}

#ifndef OMIT_NFS_SUPPORT
static const iocshArg nfsMountArg0 = { "[uid.gid@]host",iocshArgString};
static const iocshArg nfsMountArg1 = { "server path",iocshArgString};
static const iocshArg nfsMountArg2 = { "mount point",iocshArgString};
static const iocshArg * const nfsMountArgs[3] = {&nfsMountArg0,&nfsMountArg1,
                                                 &nfsMountArg2};
static const iocshFuncDef nfsMountFuncDef = {"nfsMount",3,nfsMountArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
                                             , "mount nfs drive"
#endif
                                            };
static void nfsMountCallFunc(const iocshArgBuf *args)
{
    char *cp = args[2].sval;
    while ((cp = strchr(cp+1, '/')) != NULL) {
        *cp = '\0';
        if ((mkdir (args[2].sval, 0755) != 0) && (errno != EEXIST)) {
            printf("Can't create directory \"%s\": %s.\n",
                                            args[2].sval, strerror(errno));
            return;
        }
        *cp = '/';
    }
    nfsMount(args[0].sval, args[1].sval, args[2].sval);
}
#endif


void zoneset(const char *zone)
{
    if(zone)
        setenv("TZ", zone, 1);
    else
        unsetenv("TZ");
    tzset();
}

static const iocshArg zonesetArg0 = {"zone string", iocshArgString};
static const iocshArg * const zonesetArgs[1] = {&zonesetArg0};
static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs
#ifdef IOCSHFUNCDEF_HAS_USAGE
                                           , "set timezone (obsolete?)"
#endif
                                           };
static void zonesetCallFunc(const iocshArgBuf *args)
{
    zoneset(args[0].sval);
}

#ifndef RTEMS_LEGACY_STACK
static int bsd_log_to_erl(int level, const char *fmt, va_list ap)
{
    return errlogVprintf(fmt, ap);
}
#endif // RTEMS_LEGACY_STACK

static void setlogmaskCallFunc(const iocshArgBuf *args)
{
    const char* name = args[0].sval;
    const CODE* cur;
    if(!name) {
        printf("Usage: setlogmask <level>\n"
               "\n"
               "  Level names:\n");
        for(cur = prioritynames; cur->c_name; cur++) {
            printf("    %s\n", cur->c_name);
        }
    } else {
        for(cur = prioritynames; cur->c_name; cur++) {
            if(strcmp(name, cur->c_name)!=0)
                continue;

            (void)setlogmask(LOG_MASK(cur->c_val));
#ifndef RTEMS_LEGACY_STACK
            rtems_bsd_setlogpriority(name);
#endif
            return;
        }
        printf("Error: unknown log level.\n");
    }
}
static const iocshArg setlogmaskArg0 = {"level name", iocshArgString};
static const iocshArg * const setlogmaskArgs[1] = {&setlogmaskArg0};
static const iocshFuncDef setlogmaskFuncDef = {"setlogmask",1,setlogmaskArgs,
                                            "Set syslog() threshold level"};

/*
 * Register RTEMS-specific commands
 */
static void iocshRegisterRTEMS (void)
{
    iocshRegister(&netStatFuncDef, netStatCallFunc);
    iocshRegister(&heapSpaceFuncDef, heapSpaceCallFunc);
#ifndef OMIT_NFS_SUPPORT
    iocshRegister(&nfsMountFuncDef, nfsMountCallFunc);
#endif
    iocshRegister(&zonesetFuncDef, &zonesetCallFunc);
    iocshRegister(&rtshellFuncDef, &rtshellCallFunc);
    iocshRegister(&setlogmaskFuncDef, &setlogmaskCallFunc);
#if __RTEMS_MAJOR__ > 4
    rtems_shell_init_environment();
#endif
}

/*
 * Set up the console serial line (no handshaking)
 */
static void
initConsole (void)
{
    struct termios t;

printf("\n initConsole --- Info ---\n");
printf("stdin: fileno: %d, ttyname: %s\n", fileno(stdin), ttyname(fileno(stdin)));
printf("stdout: fileno: %d, ttyname: %s\n", fileno(stdout), ttyname(fileno(stdout)));
printf("stderr: fileno: %d, ttyname: %s\n", fileno(stderr), ttyname(fileno(stderr)));

    if (tcgetattr (fileno (stdin), &t) < 0) {
        printf ("tcgetattr failed: %s\n", strerror (errno));
        return;
    }
    t.c_iflag &= ~(IXOFF | IXON | IXANY);
    if (tcsetattr (fileno (stdin), TCSANOW, &t) < 0) {
        printf ("tcsetattr failed: %s\n", strerror (errno));
        return;
    }
}

/*
 * Hook to ensure that BSP cleanup code gets run on exit
 */
static void
exitHandler(void)
{
    rtems_shutdown_executive(0);
}

#ifndef RTEMS_LEGACY_STACK
static void
dhcpcd_hook_handler(rtems_dhcpcd_hook *hook, char *const *env)
{
    int bound = 0;
    char interface[16] = "?";
    char reason[16] = "?";
    char new_host_name[32] = "RTEMShost";
    struct dhcp_vars_t {
        const char * const name;
        char* var;
        size_t varsize;
    } dhcp_vars[] = {
#define DEFVAR(N) { #N, N, sizeof(N) }
        DEFVAR(interface),
        DEFVAR(reason),
        DEFVAR(new_host_name),
#undef DEFVAR
#define DEFVAR(N, V) { N, V, sizeof(V) }
        DEFVAR("new_ntp_servers", rtemsInit_NTP_server_ip),
        DEFVAR("new_tftp_server_name", bootp_server_name_init),
        DEFVAR("new_bootfile_name", bootp_boot_file_name_init),
        DEFVAR("new_rtems_cmdline", bootp_cmdline_init),
#undef DEFVAR
        {NULL}
    };
    (void)hook;

    printf("\n");
    for (;NULL != *env;++env) {
        printf("dhcpcd ---> '%s'\n", *env);
        
        for(const struct dhcp_vars_t* var = dhcp_vars; var->name; var++) {
            size_t namelen = strlen(var->name);

            // assume "KEY=value" with no whitespace around '='
            if(strncmp(*env, var->name, namelen)!=0 || (*env)[namelen]!='=')
                continue;

            const char* value = *env + namelen+1;
            size_t valuelen = strlen(value);

            // require ordering of keys:
            // interface=
            // reason=
            // all others...
            if(var->var!=reason && var->var!=interface && !bound) {
                // ignore others if !BOUND

            } else if(valuelen >= var->varsize) {
                printf("  Not enough space for value string.  Expand buffer and recompile.\n");

            } else {
                memcpy(var->var, value, valuelen);
                var->var[valuelen] = '\0';
                printf("  '%s' = '%s'\n", var->name, var->var);

                if(var->var==reason && strcmp(reason, "BOUND")==0) {
                    printf("  is BOUND\n");
                    bound = 1;
                }
            }
            break;
        }
    }

    if(bound) {
        sethostname (new_host_name, strlen (new_host_name));
        epicsEventSignal(dhcpDone);
        printf("dhcpd BOUND\n");
    }
}

static rtems_dhcpcd_hook dhcpcd_hook = {
	.name = "ioc boot",
	.handler = dhcpcd_hook_handler
};

static void
default_network_on_exit(int exit_code, void *arg)
{
        rtems_printer printer;

        (void)arg;

        rtems_print_printer_printf(&printer);
        rtems_stack_checker_report_usage_with_plugin(&printer);
}

static void
default_network_set_self_prio(rtems_task_priority prio)
{
        rtems_status_code sc;

        sc = rtems_task_set_priority(RTEMS_SELF, prio, &prio);
        assert(sc == RTEMS_SUCCESSFUL);
}

static void
default_network_dhcpcd(void)
{
    static const char default_cfg[] = "clientid test client\n";
    rtems_status_code sc;
    int fd;
    int rv;
    ssize_t n;
    struct stat statbuf;

    if (ENOENT == stat("/etc/dhcpcd.conf", &statbuf)) {
        fd = open("/etc/dhcpcd.conf", O_CREAT | O_WRONLY,
                  S_IRWXU | S_IRWXG | S_IRWXO);
        assert(fd >= 0);

        n = write(fd, default_cfg, sizeof(default_cfg) - 1);
        assert(n == (ssize_t) sizeof(default_cfg) - 1);

        static const char fhi_cfg[] =
            "nodhcp6\n"
            "ipv4only\n"
            "option ntp-servers\n"
            "option rtems_cmdline\n"
            "option tftp-server-name\n"
            "option bootfile-name\n"
            "define 129 string rtems_cmdline\n"
            "timeout 0";

        n = write(fd, fhi_cfg, sizeof(fhi_cfg) - 1);
        assert(n == (ssize_t) sizeof(fhi_cfg) - 1);

        rv = close(fd);
        assert(rv == 0);
    }
    
    sc = rtems_dhcpcd_start(NULL);
    assert(sc == RTEMS_SUCCESSFUL);
}
#endif // not RTEMS_LEGACY_STACK

#if __RTEMS_MAJOR__>4
/*
 ***********************************************************************
 *                         TELNET DAEMON                               *
 ***********************************************************************
 */
#define LINE_SIZE 256
static void
telnet_pseudoIocsh(char *name, __attribute__((unused))void *arg)
{
  char line[LINE_SIZE];
  int fid[3], save_fid[3];

  printf("info:  pty dev name = %s\n", name);

  save_fid[1] = dup2(1,1);
  fid[1] = dup2( fileno(stdout), 1);
  if (fid[1] == -1 ) printf("Can't dup stdout\n");
  save_fid[2] = dup2(2,2);
  fid[2] = dup2( fileno(stderr), 2);
  if (fid[2] == -1 ) printf("Can't dup stderr\n");

  const char *prompt = "tIocSh> ";

  while (1) {
    fputs(prompt, stdout);
    fflush(stdout);
    /* telnet close not detected ??? tbd */
    if (fgets(line, LINE_SIZE, stdin) == NULL) {
      dup2(save_fid[1],1);
      dup2(save_fid[2],2);
      return;
    }
    if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
    if (!strncmp( line, "bye",3)) {
      printf( "%s", "Will end session\n");
      dup2(save_fid[1],1);
      dup2(save_fid[2],2);
      return;
     }
     iocshCmd(line);
   }
}

#define SHELL_ENTRY telnet_pseudoIocsh

/*
 *  Telnet daemon configuration
 * 0 or NULL for most fields in this struct indicate default values to RTEMS.
 */
rtems_telnetd_config_table rtems_telnetd_config = {
  .command = SHELL_ENTRY,
  .arg = NULL,
  .priority = 0,
  .stack_size = 0,
  .client_maximum = 0,
  .login_check = NULL,
  .keep_stdio = false
};

#endif


/*
 * RTEMS Startup task
 */
void *
POSIX_Init ( void *argument __attribute__((unused)))
{
    int                	result;
    char               	*argv[3]         = { NULL, NULL, NULL };
    rtems_status_code  	sc;
    struct timespec  	now;
    char timeBuff[100];

    initConsole ();

    /*
     * Use BSP-supplied time of day if available otherwise supply default time.
     * It is very likely that other time synchronization facilities in EPICS
     * will soon override this value.
     */
    /* check for RTC ... unfortunately seems to be missing with libbsd and qemu ?
    if (checkRealtime() >= 0) {
      setRealTimeToRTEMS();
    } else
    */
    {
      // set time to 14.4.2014
      now.tv_sec = 1397460606;
      now.tv_nsec = 0;
      sc = clock_settime(CLOCK_REALTIME, &now);
      if (sc < 0)
        printf ("***** Can't set time: %s\n", rtems_status_text (sc));
    }
    sc = clock_gettime( CLOCK_REALTIME, &now);
    if ( sc < 0) {
      printf ("***** Can't get time: %s\n", rtems_status_text (sc));
    } else {
      strftime(timeBuff, sizeof timeBuff, "%D %T", gmtime(&now.tv_sec));
      printf("time set to : %s.%09ld UTC\n", timeBuff, now.tv_nsec);
    }

    /*
     * Explain why we're here
     */
    logReset();

    /* TBD ...
     * Architecture-specific hooks
     */
#ifdef RTEMS_LEGACY_STACK
    if (epicsRtemsInitPreSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0)
        delayedPanic("epicsRtemsInitPreSetBootConfigFromNVRAM");
    if (rtems_bsdnet_config.bootp == NULL) {
        extern void setBootConfigFromNVRAM(void);
        setBootConfigFromNVRAM();
    }
    if (epicsRtemsInitPostSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0)
        delayedPanic("epicsRtemsInitPostSetBootConfigFromNVRAM");
#endif
    /*
     * Override RTEMS Posix configuration, it gets started with posix prio 2
     */
    epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityIocsh);

    /*
     * Create a reasonable environment
     */
    
    putenv ("TERM=xterm");
    putenv ("IOCSH_HISTSIZE=20");

    /*
     * Display some OS information
     */
    printf("\n***** RTEMS Version: %s *****\n",
        rtems_get_version_string());

#ifndef RTEMS_LEGACY_STACK
#if defined(QEMU_FIXUPS) && defined(__i386__)
    // glorious hack to stub out useless EEPROM check
    // which takes sooooo longggg w/ QEMU
    // Writes a 'ret' instruction to immediatly return to the caller
    extern void _bsd_e1000_validate_nvm_checksum(void);
    *(char*)&_bsd_e1000_validate_nvm_checksum = 0xc3;
#endif
    /*
     * Start network (libbsd)
     *
     * start qemu like this
     * qemu-system-i386 -m 64 -no-reboot -serial stdio -display none \
     * -net nic,model=e1000 -net user,restrict=yes \
     * -append "--video=off --console=/dev/com1" -kernel libComTestHarness
     */
    printf("\n***** Initializing network (libbsd, dhcpcd) *****\n");
    rtems_bsd_setlogpriority("debug");
    on_exit(default_network_on_exit, NULL);

    /* Let other tasks run to complete background work */
    default_network_set_self_prio(RTEMS_MAXIMUM_PRIORITY - 1U);

    sc = rtems_bsd_initialize();
    assert(sc == RTEMS_SUCCESSFUL);

    /* Let the callout timer allocate its resources */
    sc = rtems_task_wake_after(2);
    assert(sc == RTEMS_SUCCESSFUL);

    printf("\n***** ifconfig lo0 *****\n");
    rtems_bsd_ifconfig_lo0();

    printf("\n***** add dhcpcd hook *****\n");
    dhcpDone = epicsEventMustCreate(epicsEventEmpty);
    rtems_dhcpcd_add_hook(&dhcpcd_hook);

    printf("\n***** Start default network dhcpcd *****\n");
    // if MY_BOOTP???
    default_network_dhcpcd();

    /* this seems to be hard coded in the BSP -> Sebastian Huber ? */
    printf("\n--Info (hpj)-- bsd task prio IRQS: %d  -----\n", rtems_bsd_get_task_priority("IRQS"));
    printf("\n--Info (hpj)-- bsd task prio TIME: %d  -----\n", rtems_bsd_get_task_priority("TIME"));


    // wait for dhcp done ... should be if SYNCDHCP is used
    epicsEventWaitStatus stat;
    printf("\n ---- Waiting for DHCP ...\n");
    stat = epicsEventWaitWithTimeout(dhcpDone, 600); 
    if (stat == epicsEventOK)
    	epicsEventDestroy(dhcpDone);
    else if (stat == epicsEventWaitTimeout)
        printf("\n ---- DHCP timed out!\n");
    else
	printf("\n ---- dhcpDone Event Unknown state %d\n", stat);

    const char* ifconfg_args[] = {
        "ifconfig", NULL
    };
    const char* netstat_args[] = {
        "netstat", "-rn", NULL
    };

    printf("-------------- IFCONFIG -----------------\n");
    rtems_bsd_command_ifconfig(1, (char**) ifconfg_args);
    printf("-------------- NETSTAT ------------------\n");
    rtems_bsd_command_netstat(2, (char**) netstat_args);

    /* until now there is no NTP support in libbsd -> Sebastian Huber ... */
    printf("\n***** Until now no NTP support in RTEMS 5 with rtems-libbsd *****\n");
    printf("\n***** Ask ntp server once... *****\n");
    if (rtemsInit_NTP_server_ip[0]=='\0') {
      printf ("***** No NTP server ...\n");
    } else if (epicsNtpGetTime(rtemsInit_NTP_server_ip, &now) < 0) {
      printf ("***** Can't get time from ntp ...\n");
    } else {
      if (clock_settime(CLOCK_REALTIME, &now) < 0){
        printf ("***** Can't set time: %s\n", rtems_status_text (sc));
      } else {
        strftime(timeBuff, sizeof timeBuff, "%D %T", gmtime(&now.tv_sec));
        printf("time from ntp : %s.%09ld UTC\n", timeBuff, now.tv_nsec);
      }
    }
#else // Legacy stack, old network initialization
    char *cp;
    if ((cp = getenv("EPICS_TS_NTP_INET")) != NULL)
        rtems_bsdnet_config.ntp_server[0] = cp;

    int rtems_bsdnet_ntpserver_count = 1;
    struct in_addr rtems_bsdnet_ntpserver[rtems_bsdnet_ntpserver_count];
    memcpy(rtems_bsdnet_ntpserver, rtems_bsdnet_config.ntp_server, sizeof(struct in_addr));

    if (rtems_bsdnet_config.network_task_priority == 0)
    {
        unsigned int p;
        if (epicsThreadHighestPriorityLevelBelow(epicsThreadPriorityScanLow, &p)
                                            == epicsThreadBooleanStatusSuccess)
        {
            rtems_bsdnet_config.network_task_priority = p;
        }
    }
    printf("\n***** Initializing network (Legacy Stack)  *****\n");
    rtems_bsdnet_initialize_network();
    printf("\n***** Network Status  *****\n");
    rtems_netstat(3);
    rtems_bsdnet_synchronize_ntp (0, 0);
#endif // not RTEMS_LEGACY_STACK

    printf("\n***** Setting up file system *****\n");
    initialize_remote_filesystem(argv, initialize_local_filesystem(argv));
    fixup_hosts();

    /*
     * More environment: iocsh prompt and hostname
     */
    {
        char hostname[1024];
        gethostname(hostname, 1023);
        char *cp = mustMalloc(strlen(hostname)+3, "iocsh prompt");
        sprintf(cp, "%s> ", hostname);
        epicsEnvSet ("IOCSH_PS1", cp);
        epicsEnvSet("IOC_NAME", hostname);
    }

    if (getenv("") == NULL) {
        const char *tzp = envGetConfigParamPtr(&EPICS_TZ);
        if (tzp == NULL) {
            printf("Warning -- no timezone information available -- times will be displayed as GMT.\n");
        }

    }
    tzset();
    printf(" check for time registered , C++ initialization ...\n");
    //osdTimeRegister();
    /*/Volumes/Epics/myExample/bin/RTEMS-xilinx_zynq_a9_qemu
     * Run the EPICS startup script
     */
#if __RTEMS_MAJOR__>4
    // if telnetd is requested ...
   // printf(" Will try to start telnetd with prio %d ...\n", rtems_telnetd_config.priority);
   // result = rtems_telnetd_initialize();
   // printf (" telnetd initialized with result %d\n", result);
#endif

    printf ("***** Preparing EPICS application *****\n");
    iocshRegisterRTEMS ();
    set_directory (argv[1]);
    epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]);
    atexit(exitHandler);
    errlogFlush();
    printf ("***** Starting EPICS application *****\n");


#ifndef RTEMS_LEGACY_STACK
    // switch OS to async logging
    rtems_bsd_set_vprintf_handler(bsd_log_to_erl);
#endif

#if 0
// Start an rtems shell before main, for debugging RTEMS system issues
    rtems_shell_init("SHLL", RTEMS_MINIMUM_STACK_SIZE * 4,
                     100, "/dev/console",
                     false, true,
                     NULL);
#endif

    result = main ((sizeof argv / sizeof argv[0]) - 1, argv);
    printf ("***** IOC application terminating *****\n");
    epicsThreadSleep(1.0);
    epicsExit(result);

#if defined(__rtems__)
    delayedPanic("will reset rtems ... end of POSIX_Init");
#endif
    exit(0);
}

#if defined(QEMU_FIXUPS)
/* Override some hooks (weak symbols)
 * if BSP defaults aren't configured for running tests.
 */


/* Ensure that stdio goes to serial (so it can be captured) */
#if defined(__i386__) && !USE_COM1_AS_CONSOLE
#include <uart.h>
#if __RTEMS_MAJOR__ > 4
#include <libchip/serial.h>
#endif

extern int BSPPrintkPort;
void bsp_predriver_hook(void)
{
#if __RTEMS_MAJOR__ > 4
    Console_Port_Minor = BSP_CONSOLE_PORT_COM1;
#else
    BSPConsolePort = BSP_CONSOLE_PORT_COM1;

#endif
    BSPPrintkPort = BSP_CONSOLE_PORT_COM1;
}
#endif

/* reboot immediately when done. */
#if defined(__i386__) && BSP_PRESS_KEY_FOR_RESET
void bsp_cleanup(void)
{
    void bsp_reset();
    bsp_reset();
}
#endif

#endif /* QEMU_FIXUPS */

int cexpdebug __attribute__((weak));
