xref: /onnv-gate/usr/src/lib/libshell/common/bltins/mkservice.c (revision 12068:08a39a083754)
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