summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/bltins/mkservice.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/bltins/mkservice.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/bltins/mkservice.c')
-rw-r--r--src/cmd/ksh93/bltins/mkservice.c494
1 files changed, 494 insertions, 0 deletions
diff --git a/src/cmd/ksh93/bltins/mkservice.c b/src/cmd/ksh93/bltins/mkservice.c
new file mode 100644
index 0000000..0cbba25
--- /dev/null
+++ b/src/cmd/ksh93/bltins/mkservice.c
@@ -0,0 +1,494 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* 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, Shbltin_t *context)
+{
+ register char* var;
+ register char* path;
+ register Namval_t* np;
+ register Service_t* sp;
+ register int fd;
+
+ NOT_USED(argc);
+ NOT_USED(context);
+ 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 = context;
+ 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, Shbltin_t *context)
+{
+ register long timeout = -1;
+ NOT_USED(argc);
+ NOT_USED(context);
+ 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);
+}