
/*
 *  example.c
 *
 *  This module contains the source code for the /dev/example device
 *  developed as part of the Neutrino "Writing a Resource Manager"
 *  section.
 *
 *  If using a shared target, please change EXAMPLE_NAME to something
 *  unique for you, so to avoid testing somebody else's code.
 *
 *
 *  This module contains all of the functions necessary.
 *
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#include <sys/resmgr.h>
#include <sys/select.h>
#include <sys/time.h>

/* default name for this device: /dev/example */

#define EXAMPLE_NAME "/dev/example"

void options (int argc, char *argv[]);

/*
 *  these prototypes are needed since we are using their names in main ()
*/

int io_open (resmgr_context_t *ctp, io_open_t  *msg, RESMGR_HANDLE_T *handle, void *extra);
int io_read (resmgr_context_t *ctp, io_read_t  *msg, RESMGR_OCB_T *ocb);

/*
 *  our connect and I/O functions
*/

resmgr_connect_funcs_t  connect_funcs;
resmgr_io_funcs_t       io_funcs;

/*
 *  our dispatch, resource manager and iofunc variables
*/

dispatch_t              *dpp;
dispatch_context_t      *ctp;
iofunc_attr_t           attr;

char    *progname = "example";
int     optv;                               // -v for verbose operation
int     fd[2];    /* pipe file descriptors */

int main (int argc, char *argv[])
{
    thread_pool_attr_t    pool_attr;
    thread_pool_t         *tpp;
    dispatch_t            *dpp;
    sigset_t set;
    int                   id;

    printf ("%s:  starting...\n", progname);

    options (argc, argv);

    if((dpp = dispatch_create()) == NULL) {
        fprintf( stderr,
           "%s: Unable to allocate dispatch handle.\n",
           argv[0] );
        return EXIT_FAILURE;
    }

    memset( &pool_attr, 0, sizeof pool_attr );
    pool_attr.handle = dpp;
    pool_attr.context_alloc = dispatch_context_alloc;
    pool_attr.block_func = dispatch_block;
    pool_attr.unblock_func = dispatch_unblock;
    pool_attr.handler_func = dispatch_handler;
    pool_attr.context_free = dispatch_context_free;
    pool_attr.lo_water = 2;
    pool_attr.hi_water = 4;
    pool_attr.increment = 1;
    pool_attr.maximum = 50;

    if((tpp = thread_pool_create( &pool_attr,
    		0)) == NULL ) {
        fprintf(stderr,
                "%s: Unable to initialize thread pool.\n",
                argv[0]);
        return EXIT_FAILURE;
    }

    iofunc_func_init( _RESMGR_CONNECT_NFUNCS,
                      &connect_funcs,
                      _RESMGR_IO_NFUNCS, &io_funcs );
    iofunc_attr_init( &attr, S_IFNAM | 0666, 0, 0 );

    /* over-ride the connect_funcs handler for open with our io_open,
     * and over-ride the io_funcs handlers for read and write with our
     * io_read and io_write handlers
     */
    connect_funcs.open = io_open;
    io_funcs.read = io_read;

    if((id = resmgr_attach( dpp, NULL,
    						"/dev/example",
                           _FTYPE_ANY, 0, &connect_funcs,
                            &io_funcs,
                            &attr )) == -1) {
        fprintf( stderr,
                 "%s: Unable to attach name.\n", argv[0] );
        return EXIT_FAILURE;
    }


    if (pipe(fd) == -1) {
        fprintf( stderr,
                 "failed to create unnamed pipe\n");
        return EXIT_FAILURE;
    }

    thread_pool_start( tpp );


    /* Mask out unnecessary signals */
    sigfillset (&set);
    sigdelset (&set, SIGINT);
    sigdelset (&set, SIGTERM);
    pthread_sigmask (SIG_BLOCK, &set, NULL);

    /* Wait for one of these signals */
    sigemptyset (&set);
    sigaddset (&set, SIGINT);
    sigaddset (&set, SIGQUIT);
    sigaddset (&set, SIGTERM);

    printf("resource manager started\n");

    /* Wait for a signal */
    while (1)
    {
        switch (SignalWaitinfo (&set, NULL))
        {
            case SIGTERM:
            case SIGQUIT:
            case SIGINT:
                printf("Termination Signal correctly received.\n");
                goto done;

            default:
                break;
        }
    }

done:
    return 0;
}

/*
 *  io_open
 *
 *  we are called here when the client does an open.
 *  It is up to us to establish a context (in this
 *  case NULL will do just fine), and return a status
 *  code.
*/

int
io_open (resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
    if (optv) {
        printf ("%s:  in io_open\n", progname);
    }

    return (iofunc_open_default (ctp, msg, handle, extra));
}

/*
 *  io_read
 *
 *  At this point, the client has called their library "read"
 *  function, and expects zero or more bytes. We will simply
 *  return there is nothing to read.
*/

int
io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb)
{
    int status;

    fd_set rfds;
    int ret = 0, maxfd, waitFd;
    struct timeval tv;
    
    if (optv) {
        printf ("%s:  in io_read\n", progname);
    }

    if ((status = iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK)
        return (status);
        
    // No special xtypes
    if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
        return(ENOSYS);
    }

    /* Set Timeout to wait: */
    tv.tv_sec = 0;
    tv.tv_usec = 500;

    waitFd = fd[0];
    FD_ZERO(&rfds);
    FD_SET(waitFd, &rfds);
    maxfd = waitFd + 1;

/*******************************************************************/
//#if 0  //Uncomment to allow main thread to catch SIGTERM
    ret = select(maxfd, &rfds, NULL, NULL, &tv);

    if (ret == -1) {
        printf("select failed.\n");
    }
    else if (!ret) {
        printf("select timeout.\n");
    }
    if (FD_ISSET(waitFd, &rfds)) {
        printf("select unblocked.\n");
    }
//#endif
/*******************************************************************/

    return (_RESMGR_NPARTS (0));
}


/*
 *  options
 *
 *  This routine handles the command line options.
 *  For our simple /dev/example, we support:
 *      -v      verbose operation
*/

void
options (int argc, char *argv[])
{
    int     opt;
   
    optv = 0;

    while ((opt = getopt (argc, argv, "v")) != -1) {
        switch (opt) {
        case 'v':
            optv++;
            break;
        }
    }
     
}
