14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1982-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * David Korn <dgk@research.att.com> *
184887Schin * *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin /*
224887Schin * mkservice varname pathname
234887Schin * eloop [-t timeout]
244887Schin * Written by David Korn
254887Schin * AT&T Labs
264887Schin */
274887Schin
284887Schin static const char mkservice_usage[] =
294887Schin "[-?\n@(#)$Id: mkservice (AT&T Research) 2001-06-13 $\n]"
304887Schin USAGE_LICENSE
314887Schin "[+NAME? mkservice - create a shell server ]"
324887Schin "[+DESCRIPTION?\bmkservice\b creates a tcp or udp server that is "
334887Schin "implemented by shell functions.]"
344887Schin "[+?The \aservice_path\a must be of the form \b/dev/tcp/localhost/\b\aportno\a "
354887Schin "or \b/dev/udp/localhost/\b\aportno\a depending on whether the "
364887Schin "\btcp\b or \budp\b protocol is used. \aportno\a is the port "
374887Schin "number that the service will use.]"
384887Schin "[+?The shell variable \avarname\a is associated with the service. This "
394887Schin "variable can have subvariables that keeps the state of all "
404887Schin "active connections. The functions \avarname\a\b.accept\b, "
414887Schin "\avarname\a\b.action\b and \avarname\a\b.close\b implement the "
424887Schin "service as follows:]{"
434887Schin "[+accept?This function is invoked when a client tries to connect "
444887Schin "to the service. It is called with an argument which "
454887Schin "is the file descriptor number associated with the "
464887Schin "accepted connection. If the function returns a non-zero "
474887Schin "value, this connection will be closed.]"
484887Schin "[+action?This function is invoked when there is data waiting "
494887Schin "to be read from one of the active connections. It is "
504887Schin "called with the file descriptor number that has data "
514887Schin "to be read. If the function returns a non-zero "
524887Schin "value, this connection will be closed.]"
534887Schin "[+close?This function is invoked when the connection is closed.]"
544887Schin "}"
554887Schin "[+?If \avarname\a is unset, then all active connection, and the service "
564887Schin "itself will be closed.]"
574887Schin ""
584887Schin "\n"
594887Schin "\nvarname service_path\n"
604887Schin "\n"
614887Schin "[+EXIT STATUS?]{"
624887Schin "[+0?Success.]"
634887Schin "[+>0?An error occurred.]"
644887Schin "}"
654887Schin "[+SEE ALSO?\beloop\b(1)]"
664887Schin ;
674887Schin
684887Schin
694887Schin static const char eloop_usage[] =
704887Schin "[-?\n@(#)$Id: eloop (AT&T Research) 2001-06-13 $\n]"
714887Schin USAGE_LICENSE
724887Schin "[+NAME? eloop - process event loop]"
734887Schin "[+DESCRIPTION?\beloop\b causes the shell to block waiting for events "
744887Schin "to process. By default, \beloop\b does not return.]"
754887Schin "[t]#[timeout?\atimeout\a is the number of milliseconds to wait "
764887Schin "without receiving any events to process.]"
774887Schin "\n"
784887Schin "\n\n"
794887Schin "\n"
804887Schin "[+EXIT STATUS?If no timeout is specified, \beloop\b will not return "
814887Schin "unless interrupted. Otherwise]{"
824887Schin "[+0?The specified timeout interval occurred.]"
834887Schin "[+>0?An error occurred.]"
844887Schin "}"
854887Schin "[+SEE ALSO?\bmkservice\b(1)]"
864887Schin ;
874887Schin
884887Schin
894887Schin #include "defs.h"
904887Schin
914887Schin #include <cmd.h>
924887Schin #include <error.h>
934887Schin #include <nval.h>
944887Schin #include <sys/socket.h>
954887Schin #include <netinet/in.h>
964887Schin
974887Schin #define ACCEPT 0
984887Schin #define ACTION 1
994887Schin #define CLOSE 2
1004887Schin
1014887Schin #ifndef O_SERVICE
1024887Schin # define O_SERVICE O_NOCTTY
1034887Schin #endif
1044887Schin
1054887Schin static const char* disctab[] =
1064887Schin {
1074887Schin "accept",
1084887Schin "action",
1094887Schin "close",
1104887Schin 0
1114887Schin };
1124887Schin
1134887Schin typedef struct Service_s Service_t;
1144887Schin
1154887Schin struct Service_s
1164887Schin {
1174887Schin Namfun_t fun;
1184887Schin short fd;
1194887Schin int refcount;
1204887Schin int (*acceptf)(Service_t*,int);
1214887Schin int (*actionf)(Service_t*,int,int);
1224887Schin int (*errorf)(Service_t*,int,const char*, ...);
1234887Schin void *context;
1244887Schin Namval_t* node;
1254887Schin Namval_t* disc[elementsof(disctab)-1];
1264887Schin };
1274887Schin
1284887Schin static short *file_list;
1294887Schin static Sfio_t **poll_list;
1304887Schin static Service_t **service_list;
1314887Schin static int npoll;
1324887Schin static int nready;
1334887Schin static int ready;
1344887Schin static int (*covered_fdnotify)(int, int);
1354887Schin
fdclose(Service_t * sp,register int fd)1364887Schin static int fdclose(Service_t *sp, register int fd)
1374887Schin {
1384887Schin register int i;
1394887Schin service_list[fd] = 0;
1404887Schin if(sp->fd==fd)
1414887Schin sp->fd = -1;
1424887Schin for(i=0; i < npoll; i++)
1434887Schin {
1444887Schin if(file_list[i]==fd)
1454887Schin {
1464887Schin file_list[i] = file_list[npoll--];
1474887Schin if(sp->actionf)
1484887Schin (*sp->actionf)(sp, fd, 1);
1494887Schin return(1);
1504887Schin }
1514887Schin }
1524887Schin return(0);
1534887Schin }
1544887Schin
fdnotify(int fd1,int fd2)1554887Schin static int fdnotify(int fd1, int fd2)
1564887Schin {
1574887Schin Service_t *sp;
1584887Schin if (covered_fdnotify)
1594887Schin (*covered_fdnotify)(fd1, fd2);
1604887Schin if(fd2!=SH_FDCLOSE)
1614887Schin {
1624887Schin register int i;
1634887Schin service_list[fd2] = service_list[fd1];
1644887Schin service_list[fd1] = 0;
1654887Schin for(i=0; i < npoll; i++)
1664887Schin {
1674887Schin if(file_list[i]==fd1)
1684887Schin {
1694887Schin file_list[i] = fd2;
1704887Schin return(0);
1714887Schin }
1724887Schin }
1734887Schin }
1744887Schin else if(sp = service_list[fd1])
1754887Schin {
1764887Schin fdclose(sp,fd1);
1774887Schin if(--sp->refcount==0)
1784887Schin nv_unset(sp->node);
1794887Schin }
1804887Schin return(0);
1814887Schin }
1824887Schin
process_stream(Sfio_t * iop)1834887Schin static void process_stream(Sfio_t* iop)
1844887Schin {
1854887Schin int r=0, fd = sffileno(iop);
1864887Schin Service_t * sp = service_list[fd];
1874887Schin if(fd==sp->fd) /* connection socket */
1884887Schin {
1894887Schin struct sockaddr addr;
1904887Schin socklen_t addrlen = sizeof(addr);
1914887Schin fd = accept(fd, &addr, &addrlen);
1924887Schin service_list[fd] = sp;
1934887Schin sp->refcount++;
1944887Schin file_list[npoll++] = fd;
1954887Schin if(fd>=0)
1964887Schin {
1974887Schin if(sp->acceptf)
1984887Schin r = (*sp->acceptf)(sp,fd);
1994887Schin }
2004887Schin }
2014887Schin else if(sp->actionf)
2024887Schin {
2034887Schin service_list[fd] = 0;
2044887Schin r = (*sp->actionf)(sp, fd, 0);
2054887Schin service_list[fd] = sp;
2064887Schin if(r<0)
2074887Schin close(fd);
2084887Schin }
2094887Schin }
2104887Schin
waitnotify(int fd,long timeout,int rw)2114887Schin static int waitnotify(int fd, long timeout, int rw)
2124887Schin {
2134887Schin Sfio_t *special=0, **pstream;
2144887Schin register int i;
2154887Schin
2164887Schin if (fd >= 0)
2174887Schin special = sh_fd2sfio(fd);
2184887Schin while(1)
2194887Schin {
2204887Schin pstream = poll_list;
2214887Schin while(ready < nready)
2224887Schin process_stream(pstream[ready++]);
2234887Schin if(special)
2244887Schin *pstream++ = special;
2254887Schin for(i=0; i < npoll; i++)
2264887Schin {
2274887Schin if(service_list[file_list[i]])
2284887Schin *pstream++ = sh_fd2sfio(file_list[i]);
2294887Schin }
2304887Schin #if 1
2314887Schin for(i=0; i < pstream-poll_list; i++)
2324887Schin sfset(poll_list[i],SF_WRITE,0);
2334887Schin #endif
2344887Schin nready = ready = 0;
2354887Schin errno = 0;
2364887Schin #ifdef DEBUG
2374887Schin sfprintf(sfstderr,"before poll npoll=%d",pstream-poll_list);
2384887Schin for(i=0; i < pstream-poll_list; i++)
2394887Schin sfprintf(sfstderr," %d",sffileno(poll_list[i]));
2404887Schin sfputc(sfstderr,'\n');
2414887Schin #endif
2424887Schin nready = sfpoll(poll_list,pstream-poll_list,timeout);
2434887Schin #ifdef DEBUG
2444887Schin sfprintf(sfstderr,"after poll nready=%d",nready);
2454887Schin for(i=0; i < nready; i++)
2464887Schin sfprintf(sfstderr," %d",sffileno(poll_list[i]));
2474887Schin sfputc(sfstderr,'\n');
2484887Schin #endif
2494887Schin #if 1
2504887Schin for(i=0; i < pstream-poll_list; i++)
2514887Schin sfset(poll_list[i],SF_WRITE,1);
2524887Schin #endif
2534887Schin if(nready<=0)
2544887Schin return(errno? -1: 0);
2554887Schin if(special && poll_list[0]==special)
2564887Schin {
2574887Schin ready = 1;
2584887Schin return(fd);
2594887Schin }
2604887Schin }
2614887Schin }
2624887Schin
service_init(void)2634887Schin static int service_init(void)
2644887Schin {
2654887Schin file_list = newof(NULL,short,n,0);
2664887Schin poll_list = newof(NULL,Sfio_t*,n,0);
2674887Schin service_list = newof(NULL,Service_t*,n,0);
2684887Schin covered_fdnotify = sh_fdnotify(fdnotify);
2694887Schin sh_waitnotify(waitnotify);
2704887Schin return(1);
2714887Schin }
2724887Schin
service_add(Service_t * sp)2734887Schin void service_add(Service_t *sp)
2744887Schin {
2754887Schin static int init;
2764887Schin if (!init)
2774887Schin init = service_init();
2784887Schin service_list[sp->fd] = sp;
2794887Schin file_list[npoll++] = sp->fd;
2804887Schin }
2814887Schin
Accept(register Service_t * sp,int accept_fd)2824887Schin static int Accept(register Service_t *sp, int accept_fd)
2834887Schin {
2844887Schin register Namval_t* nq = sp->disc[ACCEPT];
2854887Schin int fd;
2864887Schin
2874887Schin fd = fcntl(accept_fd, F_DUPFD, 10);
2884887Schin if (fd >= 0)
2894887Schin {
2904887Schin close(accept_fd);
2914887Schin if (nq)
2924887Schin {
2934887Schin char* av[3];
2944887Schin char buff[20];
2954887Schin
2964887Schin av[1] = buff;
2974887Schin av[2] = 0;
2984887Schin sfsprintf(buff, sizeof(buff), "%d", fd);
2994887Schin if (sh_fun(nq, sp->node, av))
3004887Schin {
3014887Schin close(fd);
3024887Schin return -1;
3034887Schin }
3044887Schin }
3054887Schin }
3064887Schin sfsync(NiL);
3074887Schin return fd;
3084887Schin }
3094887Schin
Action(Service_t * sp,int fd,int close)3104887Schin static int Action(Service_t *sp, int fd, int close)
3114887Schin {
3124887Schin register Namval_t* nq;
3134887Schin int r=0;
3144887Schin
3154887Schin if(close)
3164887Schin nq = sp->disc[CLOSE];
3174887Schin else
3184887Schin nq = sp->disc[ACTION];
3194887Schin if (nq)
3204887Schin {
3214887Schin char* av[3];
3224887Schin char buff[20];
3234887Schin
3244887Schin av[1] = buff;
3254887Schin av[2] = 0;
3264887Schin sfsprintf(buff, sizeof(buff), "%d", fd);
3274887Schin r=sh_fun(nq, sp->node, av);
3284887Schin }
3294887Schin sfsync(NiL);
3304887Schin return r > 0 ? -1 : 1;
3314887Schin }
3324887Schin
Error(Service_t * sp,int level,const char * arg,...)3334887Schin static int Error(Service_t *sp, int level, const char* arg, ...)
3344887Schin {
3354887Schin va_list ap;
3364887Schin
3374887Schin va_start(ap, arg);
3384887Schin if(sp->node)
3394887Schin nv_unset(sp->node);
3404887Schin free((void*)sp);
3414887Schin errorv(NiL, ERROR_exit(1), ap);
3424887Schin va_end(ap);
3434887Schin return 0;
3444887Schin }
3454887Schin
setdisc(Namval_t * np,const char * event,Namval_t * action,Namfun_t * fp)3464887Schin static char* setdisc(Namval_t* np, const char* event, Namval_t* action, Namfun_t* fp)
3474887Schin {
3484887Schin register Service_t* sp = (Service_t*)fp;
3494887Schin register const char* cp;
3504887Schin register int i;
3514887Schin register int n = strlen(event) - 1;
3524887Schin register Namval_t* nq;
3534887Schin
3544887Schin for (i = 0; cp = disctab[i]; i++)
3554887Schin {
3564887Schin if (memcmp(event, cp, n))
3574887Schin continue;
3584887Schin if (action == np)
3594887Schin action = sp->disc[i];
3604887Schin else
3614887Schin {
3624887Schin if (nq = sp->disc[i])
3634887Schin free((void*)nq);
3644887Schin if (action)
3654887Schin sp->disc[i] = action;
3664887Schin else
3674887Schin sp->disc[i] = 0;
3684887Schin }
3694887Schin return action ? (char*)action : "";
3704887Schin }
3714887Schin /* try the next level */
3724887Schin return nv_setdisc(np, event, action, fp);
3734887Schin }
3744887Schin
putval(Namval_t * np,const char * val,int flag,Namfun_t * fp)3754887Schin static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp)
3764887Schin {
3774887Schin register Service_t* sp = (Service_t*)fp;
3784887Schin if (!val)
3794887Schin fp = nv_stack(np, NiL);
3804887Schin nv_putv(np, val, flag, fp);
3814887Schin if (!val)
3824887Schin {
3834887Schin register int i;
3844887Schin for(i=0; i< sh.lim.open_max; i++)
3854887Schin {
3864887Schin if(service_list[i]==sp)
3874887Schin {
3884887Schin close(i);
3894887Schin if(--sp->refcount<=0)
3904887Schin break;
3914887Schin }
3924887Schin }
3934887Schin free((void*)fp);
3944887Schin return;
3954887Schin }
3964887Schin }
3974887Schin
3984887Schin static const Namdisc_t servdisc =
3994887Schin {
4004887Schin sizeof(Service_t),
4014887Schin putval,
4024887Schin 0,
4034887Schin 0,
4044887Schin setdisc
4054887Schin };
4064887Schin
b_mkservice(int argc,char ** argv,void * extra)4074887Schin int b_mkservice(int argc, char** argv, void* extra)
4084887Schin {
4094887Schin register char* var;
4104887Schin register char* path;
4114887Schin register Namval_t* np;
4124887Schin register Service_t* sp;
4134887Schin register int fd;
4144887Schin
4154887Schin NOT_USED(argc);
4164887Schin NOT_USED(extra);
4174887Schin for (;;)
4184887Schin {
4194887Schin switch (optget(argv, mkservice_usage))
4204887Schin {
4214887Schin case 0:
4224887Schin break;
4234887Schin case ':':
4244887Schin error(2, opt_info.arg);
4254887Schin continue;
4264887Schin case '?':
4274887Schin error(ERROR_usage(2), opt_info.arg);
4284887Schin continue;
4294887Schin }
4304887Schin break;
4314887Schin }
4324887Schin argv += opt_info.index;
4334887Schin if (error_info.errors || !(var = *argv++) || !(path = *argv++) || *argv)
4344887Schin error(ERROR_usage(2), optusage(NiL));
4354887Schin if (!(sp = newof(0, Service_t, 1, 0)))
4364887Schin error(ERROR_exit(1), "out of space");
4374887Schin sp->acceptf = Accept;
4384887Schin sp->actionf = Action;
4394887Schin sp->errorf = Error;
4404887Schin sp->refcount = 1;
4414887Schin sp->context = extra;
4424887Schin sp->node = 0;
4434887Schin sp->fun.disc = &servdisc;
4444887Schin if((fd = sh_open(path, O_SERVICE|O_RDWR))<=0)
4454887Schin {
4464887Schin free((void*)sp);
4474887Schin error(ERROR_exit(1), "%s: cannot start service", path);
4484887Schin }
4494887Schin if((sp->fd = fcntl(fd, F_DUPFD, 10))>=10)
4504887Schin close(fd);
4514887Schin else
4524887Schin sp->fd = fd;
4534887Schin np = nv_open(var,sh.var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
4544887Schin sp->node = np;
4554887Schin nv_putval(np, path, 0);
4564887Schin nv_stack(np, (Namfun_t*)sp);
4574887Schin service_add(sp);
4584887Schin return(0);
4594887Schin }
4604887Schin
b_eloop(int argc,char ** argv,void * extra)4614887Schin int b_eloop(int argc, char** argv, void* extra)
4624887Schin {
4634887Schin register long timeout = -1;
4644887Schin NOT_USED(argc);
4654887Schin NOT_USED(extra);
4664887Schin for (;;)
4674887Schin {
4684887Schin switch (optget(argv, eloop_usage))
4694887Schin {
4704887Schin case 0:
4714887Schin break;
4724887Schin case 't':
4734887Schin timeout = opt_info.num;
4744887Schin continue;
4754887Schin case ':':
4764887Schin error(2, opt_info.arg);
4774887Schin continue;
4784887Schin case '?':
4794887Schin error(ERROR_usage(2), opt_info.arg);
4804887Schin continue;
4814887Schin }
4824887Schin break;
4834887Schin }
4844887Schin argv += opt_info.index;
4854887Schin if (error_info.errors || *argv)
4864887Schin error(ERROR_usage(2), optusage(NiL));
4874887Schin while(1)
4884887Schin {
4894887Schin if(waitnotify(-1, timeout, 0)==0)
4904887Schin break;
4914887Schin sfprintf(sfstderr,"interrupted\n");
4924887Schin }
4934887Schin return(errno != 0);
4944887Schin }
495