Logo Search packages:      
Sourcecode: ksh version File versions  Download package

mkservice.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1982-2007 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                  David Korn <dgk@research.att.com>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * mkservice varname pathname
 * eloop [-t timeout]
 * Written by David Korn
 * AT&T Labs
 */

static const char mkservice_usage[] =
"[-?\n@(#)$Id: mkservice (AT&T Research) 2001-06-13 $\n]"
USAGE_LICENSE
"[+NAME? mkservice - create a shell server ]"
"[+DESCRIPTION?\bmkservice\b creates a tcp or udp server that is "
      "implemented by shell functions.]"
"[+?The \aservice_path\a must be of the form \b/dev/tcp/localhost/\b\aportno\a "
      "or \b/dev/udp/localhost/\b\aportno\a depending on whether the "
      "\btcp\b or \budp\b protocol is used.  \aportno\a is the port "
      "number that the service will use.]"
"[+?The shell variable \avarname\a is associated with the service.  This "
      "variable can have subvariables that keeps the state of all "
      "active connections.  The functions \avarname\a\b.accept\b, "
      "\avarname\a\b.action\b and \avarname\a\b.close\b implement the "
      "service as follows:]{"
      "[+accept?This function is invoked when a client tries to connect "
            "to the service.  It is called with an argument which "
            "is the file descriptor number associated with the "
            "accepted connection.  If the function returns a non-zero "
            "value, this connection will be closed.]"
      "[+action?This function is invoked when there is data waiting "
            "to be read from one of the active connections.  It is "
            "called with the file descriptor number that has data "
            "to be read.  If the function returns a non-zero "
            "value, this connection will be closed.]" 
      "[+close?This function is invoked when the connection is closed.]"
      "}"
"[+?If \avarname\a is unset, then all active connection, and the service "
      "itself will be closed.]"
""
"\n"
"\nvarname service_path\n"
"\n"
"[+EXIT STATUS?]{"
        "[+0?Success.]"
        "[+>0?An error occurred.]"
"}"
"[+SEE ALSO?\beloop\b(1)]"
;


static const char eloop_usage[] =
"[-?\n@(#)$Id: eloop (AT&T Research) 2001-06-13 $\n]"
USAGE_LICENSE
"[+NAME? eloop - process event loop]"
"[+DESCRIPTION?\beloop\b causes the shell to block waiting for events "
      "to process.  By default, \beloop\b does not return.]"
"[t]#[timeout?\atimeout\a is the number of milliseconds to wait "
      "without receiving any events to process.]"
"\n"
"\n\n"
"\n"
"[+EXIT STATUS?If no timeout is specified, \beloop\b will not return "
      "unless interrupted.  Otherwise]{"
        "[+0?The specified timeout interval occurred.]"
        "[+>0?An error occurred.]"
"}"
"[+SEE ALSO?\bmkservice\b(1)]"
;


#include    "defs.h"

#include    <cmd.h>
#include    <error.h>
#include    <nval.h>
#include    <sys/socket.h>
#include    <netinet/in.h>

#define ACCEPT    0
#define ACTION    1
#define CLOSE     2

#ifndef O_SERVICE
#   define O_SERVICE    O_NOCTTY
#endif

static const char*      disctab[] =
{
      "accept",
      "action",
      "close",
      0
};

typedef struct Service_s Service_t;

struct Service_s
{
      Namfun_t    fun;
      short       fd;
      int         refcount;
      int         (*acceptf)(Service_t*,int);
      int         (*actionf)(Service_t*,int,int);
      int         (*errorf)(Service_t*,int,const char*, ...);
      void        *context;
      Namval_t*   node;
      Namval_t*   disc[elementsof(disctab)-1];
};

static short            *file_list;
static Sfio_t           **poll_list;
static Service_t  **service_list;
static int        npoll;
static int        nready;
static int        ready;
static int        (*covered_fdnotify)(int, int);

static int fdclose(Service_t *sp, register int fd)
{
      register int i;
      service_list[fd] = 0;
      if(sp->fd==fd)
            sp->fd = -1;
      for(i=0; i < npoll; i++)
      {
            if(file_list[i]==fd)
            {
                  file_list[i] = file_list[npoll--];
                  if(sp->actionf)
                        (*sp->actionf)(sp, fd, 1);
                  return(1);
            }
      }
      return(0);
}

static int fdnotify(int fd1, int fd2)
{
      Service_t *sp;
      if (covered_fdnotify)
            (*covered_fdnotify)(fd1, fd2);
      if(fd2!=SH_FDCLOSE)
      {
            register int i;
            service_list[fd2] = service_list[fd1];
            service_list[fd1] = 0;
            for(i=0; i < npoll; i++)
            {
                  if(file_list[i]==fd1)
                  {
                        file_list[i] = fd2;
                        return(0);
                  }
            }
      }
      else if(sp = service_list[fd1])
      {
            fdclose(sp,fd1);
            if(--sp->refcount==0)
                  nv_unset(sp->node);
      }
      return(0);
}

static void process_stream(Sfio_t* iop)
{
      int r=0, fd = sffileno(iop);
      Service_t * sp = service_list[fd];
      if(fd==sp->fd)    /* connection socket */
      {
            struct sockaddr addr;
            socklen_t addrlen = sizeof(addr);
            fd = accept(fd, &addr, &addrlen);
            service_list[fd] = sp;
            sp->refcount++;
            file_list[npoll++] = fd;
            if(fd>=0)
            {
                  if(sp->acceptf)
                        r = (*sp->acceptf)(sp,fd);
            }
      }
      else if(sp->actionf)
      {
            service_list[fd] = 0;
            r = (*sp->actionf)(sp, fd, 0);
            service_list[fd] = sp;
            if(r<0)
                  close(fd);
      }
}
                        
static int waitnotify(int fd, long timeout, int rw)
{
      Sfio_t *special=0, **pstream;
      register int      i;

      if (fd >= 0)
            special = sh_fd2sfio(fd);
      while(1)
      {
            pstream = poll_list;
            while(ready < nready)
                  process_stream(pstream[ready++]);
            if(special)
                  *pstream++ = special;
            for(i=0; i < npoll; i++)
            {
                  if(service_list[file_list[i]])
                        *pstream++ = sh_fd2sfio(file_list[i]);
            }
#if 1
            for(i=0; i < pstream-poll_list; i++)
                  sfset(poll_list[i],SF_WRITE,0);
#endif
            nready = ready = 0;
            errno = 0;
#ifdef DEBUG
            sfprintf(sfstderr,"before poll npoll=%d",pstream-poll_list);
            for(i=0; i < pstream-poll_list; i++)
                  sfprintf(sfstderr," %d",sffileno(poll_list[i]));
            sfputc(sfstderr,'\n');
#endif
            nready  = sfpoll(poll_list,pstream-poll_list,timeout);
#ifdef DEBUG
            sfprintf(sfstderr,"after poll nready=%d",nready);
            for(i=0; i < nready; i++)
                  sfprintf(sfstderr," %d",sffileno(poll_list[i]));
            sfputc(sfstderr,'\n');
#endif
#if 1
            for(i=0; i < pstream-poll_list; i++)
                  sfset(poll_list[i],SF_WRITE,1);
#endif
            if(nready<=0)
                  return(errno? -1: 0);
            if(special && poll_list[0]==special)
            {
                  ready = 1;
                  return(fd);
            }
      }
}

static int service_init(void)
{
      file_list =  newof(NULL,short,n,0);
      poll_list =  newof(NULL,Sfio_t*,n,0);
      service_list =  newof(NULL,Service_t*,n,0);
      covered_fdnotify = sh_fdnotify(fdnotify);
      sh_waitnotify(waitnotify);
      return(1);
}

void service_add(Service_t *sp)
{
      static int init;
      if (!init)
            init = service_init();
      service_list[sp->fd] = sp;
      file_list[npoll++] = sp->fd;
}

static int Accept(register Service_t *sp, int accept_fd)
{
      register Namval_t*      nq = sp->disc[ACCEPT];
      int               fd;

      fd = fcntl(accept_fd, F_DUPFD, 10);
      if (fd >= 0)
      {
            close(accept_fd);
            if (nq)
            {
                  char* av[3];
                  char  buff[20];

                  av[1] = buff;
                  av[2] = 0;
                  sfsprintf(buff, sizeof(buff), "%d", fd);
                  if (sh_fun(nq, sp->node, av))
                  {
                        close(fd);
                        return -1;
                  }
            }
      }
      sfsync(NiL);
      return fd;
}

static int Action(Service_t *sp, int fd, int close)
{
      register Namval_t*      nq;
      int               r=0;

      if(close)
            nq = sp->disc[CLOSE];
      else
            nq = sp->disc[ACTION];
      if (nq)
      {
            char* av[3];
            char  buff[20];

            av[1] = buff;
            av[2] = 0;
            sfsprintf(buff, sizeof(buff), "%d", fd);
            r=sh_fun(nq, sp->node, av);
      }
      sfsync(NiL);
      return r > 0 ? -1 : 1;
}

static int Error(Service_t *sp, int level, const char* arg, ...)
{
      va_list                 ap;

      va_start(ap, arg);
      if(sp->node)
            nv_unset(sp->node);
      free((void*)sp);
        errorv(NiL, ERROR_exit(1), ap);
        va_end(ap);
      return 0;
}

static char* setdisc(Namval_t* np, const char* event, Namval_t* action, Namfun_t* fp)
{
      register Service_t*     sp = (Service_t*)fp;
      register const char*    cp;
      register int            i;
      register int            n = strlen(event) - 1;
      register Namval_t*      nq;

      for (i = 0; cp = disctab[i]; i++)
      {
            if (memcmp(event, cp, n))
                  continue;
            if (action == np)
                  action = sp->disc[i];
            else
            {
                  if (nq = sp->disc[i])
                        free((void*)nq);
                  if (action)
                        sp->disc[i] = action;
                  else
                        sp->disc[i] = 0;
            }
            return action ? (char*)action : "";
      }
      /* try the next level */
      return nv_setdisc(np, event, action, fp);
}

static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp)
{
      register Service_t* sp = (Service_t*)fp;
      if (!val)
            fp = nv_stack(np, NiL);
      nv_putv(np, val, flag, fp);
      if (!val)
      {
            register int i;
            for(i=0; i< sh.lim.open_max; i++)
            {
                  if(service_list[i]==sp)
                  {
                        close(i);
                        if(--sp->refcount<=0)
                              break;
                  }
            }
            free((void*)fp);
            return;
      }
}

static const Namdisc_t servdisc =
{
      sizeof(Service_t),
      putval,
      0,
      0,
      setdisc
};

int   b_mkservice(int argc, char** argv, void* extra)
{
      register char*          var;
      register char*          path;
      register Namval_t*      np;
      register Service_t*     sp;
      register int            fd;

      NOT_USED(argc);
      NOT_USED(extra);
      for (;;)
      {
            switch (optget(argv, mkservice_usage))
            {
            case 0:
                  break;
            case ':':
                  error(2, opt_info.arg);
                  continue;
            case '?':
                  error(ERROR_usage(2), opt_info.arg);
                  continue;
            }
            break;
      }
      argv += opt_info.index;
      if (error_info.errors || !(var = *argv++) || !(path = *argv++) || *argv)
            error(ERROR_usage(2), optusage(NiL));
      if (!(sp = newof(0, Service_t, 1, 0)))
            error(ERROR_exit(1), "out of space");
      sp->acceptf = Accept;
      sp->actionf = Action;
      sp->errorf = Error;
      sp->refcount = 1;
      sp->context = extra;
      sp->node = 0;
      sp->fun.disc = &servdisc;
      if((fd = sh_open(path, O_SERVICE|O_RDWR))<=0)
      {
            free((void*)sp);
            error(ERROR_exit(1), "%s: cannot start service", path);
      }
      if((sp->fd = fcntl(fd, F_DUPFD, 10))>=10)
            close(fd);
      else
            sp->fd = fd;
      np = nv_open(var,sh.var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
      sp->node = np;
      nv_putval(np, path, 0); 
      nv_stack(np, (Namfun_t*)sp);
      service_add(sp);
      return(0);
}

int   b_eloop(int argc, char** argv, void* extra)
{
      register long     timeout = -1;
      NOT_USED(argc);
      NOT_USED(extra);
      for (;;)
      {
            switch (optget(argv, eloop_usage))
            {
            case 0:
                  break;
            case 't':
                  timeout = opt_info.num;
                  continue;
            case ':':
                  error(2, opt_info.arg);
                  continue;
            case '?':
                  error(ERROR_usage(2), opt_info.arg);
                  continue;
            }
            break;
      }
      argv += opt_info.index;
      if (error_info.errors  || *argv)
            error(ERROR_usage(2), optusage(NiL));
      while(1)
      {
            if(waitnotify(-1, timeout, 0)==0)
                  break;
            sfprintf(sfstderr,"interrupted\n");
      }
      return(errno != 0);
}

Generated by  Doxygen 1.6.0   Back to index