14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1982-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.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 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 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 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 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 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 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 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 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 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 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 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 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 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