1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                  David Korn <dgk@research.att.com>                   *
18*4887Schin *                                                                      *
19*4887Schin ***********************************************************************/
20*4887Schin #pragma prototyped
21*4887Schin /*
22*4887Schin  * mkservice varname pathname
23*4887Schin  * eloop [-t timeout]
24*4887Schin  * Written by David Korn
25*4887Schin  * AT&T Labs
26*4887Schin  */
27*4887Schin 
28*4887Schin static const char mkservice_usage[] =
29*4887Schin "[-?\n@(#)$Id: mkservice (AT&T Research) 2001-06-13 $\n]"
30*4887Schin USAGE_LICENSE
31*4887Schin "[+NAME? mkservice - create a shell server ]"
32*4887Schin "[+DESCRIPTION?\bmkservice\b creates a tcp or udp server that is "
33*4887Schin 	"implemented by shell functions.]"
34*4887Schin "[+?The \aservice_path\a must be of the form \b/dev/tcp/localhost/\b\aportno\a "
35*4887Schin 	"or \b/dev/udp/localhost/\b\aportno\a depending on whether the "
36*4887Schin 	"\btcp\b or \budp\b protocol is used.  \aportno\a is the port "
37*4887Schin 	"number that the service will use.]"
38*4887Schin "[+?The shell variable \avarname\a is associated with the service.  This "
39*4887Schin 	"variable can have subvariables that keeps the state of all "
40*4887Schin 	"active connections.  The functions \avarname\a\b.accept\b, "
41*4887Schin 	"\avarname\a\b.action\b and \avarname\a\b.close\b implement the "
42*4887Schin 	"service as follows:]{"
43*4887Schin 	"[+accept?This function is invoked when a client tries to connect "
44*4887Schin 		"to the service.  It is called with an argument which "
45*4887Schin 		"is the file descriptor number associated with the "
46*4887Schin 		"accepted connection.  If the function returns a non-zero "
47*4887Schin 		"value, this connection will be closed.]"
48*4887Schin 	"[+action?This function is invoked when there is data waiting "
49*4887Schin 		"to be read from one of the active connections.  It is "
50*4887Schin 		"called with the file descriptor number that has data "
51*4887Schin 		"to be read.  If the function returns a non-zero "
52*4887Schin 		"value, this connection will be closed.]"
53*4887Schin 	"[+close?This function is invoked when the connection is closed.]"
54*4887Schin 	"}"
55*4887Schin "[+?If \avarname\a is unset, then all active connection, and the service "
56*4887Schin 	"itself will be closed.]"
57*4887Schin ""
58*4887Schin "\n"
59*4887Schin "\nvarname service_path\n"
60*4887Schin "\n"
61*4887Schin "[+EXIT STATUS?]{"
62*4887Schin         "[+0?Success.]"
63*4887Schin         "[+>0?An error occurred.]"
64*4887Schin "}"
65*4887Schin "[+SEE ALSO?\beloop\b(1)]"
66*4887Schin ;
67*4887Schin 
68*4887Schin 
69*4887Schin static const char eloop_usage[] =
70*4887Schin "[-?\n@(#)$Id: eloop (AT&T Research) 2001-06-13 $\n]"
71*4887Schin USAGE_LICENSE
72*4887Schin "[+NAME? eloop - process event loop]"
73*4887Schin "[+DESCRIPTION?\beloop\b causes the shell to block waiting for events "
74*4887Schin 	"to process.  By default, \beloop\b does not return.]"
75*4887Schin "[t]#[timeout?\atimeout\a is the number of milliseconds to wait "
76*4887Schin 	"without receiving any events to process.]"
77*4887Schin "\n"
78*4887Schin "\n\n"
79*4887Schin "\n"
80*4887Schin "[+EXIT STATUS?If no timeout is specified, \beloop\b will not return "
81*4887Schin 	"unless interrupted.  Otherwise]{"
82*4887Schin         "[+0?The specified timeout interval occurred.]"
83*4887Schin         "[+>0?An error occurred.]"
84*4887Schin "}"
85*4887Schin "[+SEE ALSO?\bmkservice\b(1)]"
86*4887Schin ;
87*4887Schin 
88*4887Schin 
89*4887Schin #include	"defs.h"
90*4887Schin 
91*4887Schin #include	<cmd.h>
92*4887Schin #include	<error.h>
93*4887Schin #include	<nval.h>
94*4887Schin #include	<sys/socket.h>
95*4887Schin #include 	<netinet/in.h>
96*4887Schin 
97*4887Schin #define ACCEPT	0
98*4887Schin #define ACTION	1
99*4887Schin #define CLOSE	2
100*4887Schin 
101*4887Schin #ifndef O_SERVICE
102*4887Schin #   define O_SERVICE	O_NOCTTY
103*4887Schin #endif
104*4887Schin 
105*4887Schin static const char*	disctab[] =
106*4887Schin {
107*4887Schin 	"accept",
108*4887Schin 	"action",
109*4887Schin 	"close",
110*4887Schin 	0
111*4887Schin };
112*4887Schin 
113*4887Schin typedef struct Service_s Service_t;
114*4887Schin 
115*4887Schin struct Service_s
116*4887Schin {
117*4887Schin 	Namfun_t	fun;
118*4887Schin 	short		fd;
119*4887Schin 	int		refcount;
120*4887Schin 	int		(*acceptf)(Service_t*,int);
121*4887Schin 	int		(*actionf)(Service_t*,int,int);
122*4887Schin 	int		(*errorf)(Service_t*,int,const char*, ...);
123*4887Schin 	void		*context;
124*4887Schin 	Namval_t*	node;
125*4887Schin 	Namval_t*	disc[elementsof(disctab)-1];
126*4887Schin };
127*4887Schin 
128*4887Schin static short		*file_list;
129*4887Schin static Sfio_t		**poll_list;
130*4887Schin static Service_t	**service_list;
131*4887Schin static int		npoll;
132*4887Schin static int		nready;
133*4887Schin static int		ready;
134*4887Schin static int		(*covered_fdnotify)(int, int);
135*4887Schin 
136*4887Schin static int fdclose(Service_t *sp, register int fd)
137*4887Schin {
138*4887Schin 	register int i;
139*4887Schin 	service_list[fd] = 0;
140*4887Schin 	if(sp->fd==fd)
141*4887Schin 		sp->fd = -1;
142*4887Schin 	for(i=0; i < npoll; i++)
143*4887Schin 	{
144*4887Schin 		if(file_list[i]==fd)
145*4887Schin 		{
146*4887Schin 			file_list[i] = file_list[npoll--];
147*4887Schin 			if(sp->actionf)
148*4887Schin 				(*sp->actionf)(sp, fd, 1);
149*4887Schin 			return(1);
150*4887Schin 		}
151*4887Schin 	}
152*4887Schin 	return(0);
153*4887Schin }
154*4887Schin 
155*4887Schin static int fdnotify(int fd1, int fd2)
156*4887Schin {
157*4887Schin 	Service_t *sp;
158*4887Schin 	if (covered_fdnotify)
159*4887Schin 		(*covered_fdnotify)(fd1, fd2);
160*4887Schin 	if(fd2!=SH_FDCLOSE)
161*4887Schin 	{
162*4887Schin 		register int i;
163*4887Schin 		service_list[fd2] = service_list[fd1];
164*4887Schin 		service_list[fd1] = 0;
165*4887Schin 		for(i=0; i < npoll; i++)
166*4887Schin 		{
167*4887Schin 			if(file_list[i]==fd1)
168*4887Schin 			{
169*4887Schin 				file_list[i] = fd2;
170*4887Schin 				return(0);
171*4887Schin 			}
172*4887Schin 		}
173*4887Schin 	}
174*4887Schin 	else if(sp = service_list[fd1])
175*4887Schin 	{
176*4887Schin 		fdclose(sp,fd1);
177*4887Schin 		if(--sp->refcount==0)
178*4887Schin 			nv_unset(sp->node);
179*4887Schin 	}
180*4887Schin 	return(0);
181*4887Schin }
182*4887Schin 
183*4887Schin static void process_stream(Sfio_t* iop)
184*4887Schin {
185*4887Schin 	int r=0, fd = sffileno(iop);
186*4887Schin 	Service_t * sp = service_list[fd];
187*4887Schin 	if(fd==sp->fd)	/* connection socket */
188*4887Schin 	{
189*4887Schin 		struct sockaddr addr;
190*4887Schin 		socklen_t addrlen = sizeof(addr);
191*4887Schin 		fd = accept(fd, &addr, &addrlen);
192*4887Schin 		service_list[fd] = sp;
193*4887Schin 		sp->refcount++;
194*4887Schin 		file_list[npoll++] = fd;
195*4887Schin 		if(fd>=0)
196*4887Schin 		{
197*4887Schin 			if(sp->acceptf)
198*4887Schin 				r = (*sp->acceptf)(sp,fd);
199*4887Schin 		}
200*4887Schin 	}
201*4887Schin 	else if(sp->actionf)
202*4887Schin 	{
203*4887Schin 		service_list[fd] = 0;
204*4887Schin 		r = (*sp->actionf)(sp, fd, 0);
205*4887Schin 		service_list[fd] = sp;
206*4887Schin 		if(r<0)
207*4887Schin 			close(fd);
208*4887Schin 	}
209*4887Schin }
210*4887Schin 
211*4887Schin static int waitnotify(int fd, long timeout, int rw)
212*4887Schin {
213*4887Schin 	Sfio_t *special=0, **pstream;
214*4887Schin 	register int	i;
215*4887Schin 
216*4887Schin 	if (fd >= 0)
217*4887Schin 		special = sh_fd2sfio(fd);
218*4887Schin 	while(1)
219*4887Schin 	{
220*4887Schin 		pstream = poll_list;
221*4887Schin 		while(ready < nready)
222*4887Schin 			process_stream(pstream[ready++]);
223*4887Schin 		if(special)
224*4887Schin 			*pstream++ = special;
225*4887Schin 		for(i=0; i < npoll; i++)
226*4887Schin 		{
227*4887Schin 			if(service_list[file_list[i]])
228*4887Schin 				*pstream++ = sh_fd2sfio(file_list[i]);
229*4887Schin 		}
230*4887Schin #if 1
231*4887Schin 		for(i=0; i < pstream-poll_list; i++)
232*4887Schin 			sfset(poll_list[i],SF_WRITE,0);
233*4887Schin #endif
234*4887Schin 		nready = ready = 0;
235*4887Schin 		errno = 0;
236*4887Schin #ifdef DEBUG
237*4887Schin 		sfprintf(sfstderr,"before poll npoll=%d",pstream-poll_list);
238*4887Schin 		for(i=0; i < pstream-poll_list; i++)
239*4887Schin 			sfprintf(sfstderr," %d",sffileno(poll_list[i]));
240*4887Schin 		sfputc(sfstderr,'\n');
241*4887Schin #endif
242*4887Schin 		nready  = sfpoll(poll_list,pstream-poll_list,timeout);
243*4887Schin #ifdef DEBUG
244*4887Schin 		sfprintf(sfstderr,"after poll nready=%d",nready);
245*4887Schin 		for(i=0; i < nready; i++)
246*4887Schin 			sfprintf(sfstderr," %d",sffileno(poll_list[i]));
247*4887Schin 		sfputc(sfstderr,'\n');
248*4887Schin #endif
249*4887Schin #if 1
250*4887Schin 		for(i=0; i < pstream-poll_list; i++)
251*4887Schin 			sfset(poll_list[i],SF_WRITE,1);
252*4887Schin #endif
253*4887Schin 		if(nready<=0)
254*4887Schin 			return(errno? -1: 0);
255*4887Schin 		if(special && poll_list[0]==special)
256*4887Schin 		{
257*4887Schin 			ready = 1;
258*4887Schin 			return(fd);
259*4887Schin 		}
260*4887Schin 	}
261*4887Schin }
262*4887Schin 
263*4887Schin static int service_init(void)
264*4887Schin {
265*4887Schin 	file_list =  newof(NULL,short,n,0);
266*4887Schin 	poll_list =  newof(NULL,Sfio_t*,n,0);
267*4887Schin 	service_list =  newof(NULL,Service_t*,n,0);
268*4887Schin 	covered_fdnotify = sh_fdnotify(fdnotify);
269*4887Schin 	sh_waitnotify(waitnotify);
270*4887Schin 	return(1);
271*4887Schin }
272*4887Schin 
273*4887Schin void service_add(Service_t *sp)
274*4887Schin {
275*4887Schin 	static int init;
276*4887Schin 	if (!init)
277*4887Schin 		init = service_init();
278*4887Schin 	service_list[sp->fd] = sp;
279*4887Schin 	file_list[npoll++] = sp->fd;
280*4887Schin }
281*4887Schin 
282*4887Schin static int Accept(register Service_t *sp, int accept_fd)
283*4887Schin {
284*4887Schin 	register Namval_t*	nq = sp->disc[ACCEPT];
285*4887Schin 	int			fd;
286*4887Schin 
287*4887Schin 	fd = fcntl(accept_fd, F_DUPFD, 10);
288*4887Schin 	if (fd >= 0)
289*4887Schin 	{
290*4887Schin 		close(accept_fd);
291*4887Schin 		if (nq)
292*4887Schin 		{
293*4887Schin 			char*	av[3];
294*4887Schin 			char	buff[20];
295*4887Schin 
296*4887Schin 			av[1] = buff;
297*4887Schin 			av[2] = 0;
298*4887Schin 			sfsprintf(buff, sizeof(buff), "%d", fd);
299*4887Schin 			if (sh_fun(nq, sp->node, av))
300*4887Schin 			{
301*4887Schin 				close(fd);
302*4887Schin 				return -1;
303*4887Schin 			}
304*4887Schin 		}
305*4887Schin 	}
306*4887Schin 	sfsync(NiL);
307*4887Schin 	return fd;
308*4887Schin }
309*4887Schin 
310*4887Schin static int Action(Service_t *sp, int fd, int close)
311*4887Schin {
312*4887Schin 	register Namval_t*	nq;
313*4887Schin 	int			r=0;
314*4887Schin 
315*4887Schin 	if(close)
316*4887Schin 		nq = sp->disc[CLOSE];
317*4887Schin 	else
318*4887Schin 		nq = sp->disc[ACTION];
319*4887Schin 	if (nq)
320*4887Schin 	{
321*4887Schin 		char*	av[3];
322*4887Schin 		char	buff[20];
323*4887Schin 
324*4887Schin 		av[1] = buff;
325*4887Schin 		av[2] = 0;
326*4887Schin 		sfsprintf(buff, sizeof(buff), "%d", fd);
327*4887Schin 		r=sh_fun(nq, sp->node, av);
328*4887Schin 	}
329*4887Schin 	sfsync(NiL);
330*4887Schin 	return r > 0 ? -1 : 1;
331*4887Schin }
332*4887Schin 
333*4887Schin static int Error(Service_t *sp, int level, const char* arg, ...)
334*4887Schin {
335*4887Schin 	va_list			ap;
336*4887Schin 
337*4887Schin 	va_start(ap, arg);
338*4887Schin 	if(sp->node)
339*4887Schin 		nv_unset(sp->node);
340*4887Schin 	free((void*)sp);
341*4887Schin         errorv(NiL, ERROR_exit(1), ap);
342*4887Schin         va_end(ap);
343*4887Schin 	return 0;
344*4887Schin }
345*4887Schin 
346*4887Schin static char* setdisc(Namval_t* np, const char* event, Namval_t* action, Namfun_t* fp)
347*4887Schin {
348*4887Schin 	register Service_t*	sp = (Service_t*)fp;
349*4887Schin 	register const char*	cp;
350*4887Schin 	register int		i;
351*4887Schin 	register int		n = strlen(event) - 1;
352*4887Schin 	register Namval_t*	nq;
353*4887Schin 
354*4887Schin 	for (i = 0; cp = disctab[i]; i++)
355*4887Schin 	{
356*4887Schin 		if (memcmp(event, cp, n))
357*4887Schin 			continue;
358*4887Schin 		if (action == np)
359*4887Schin 			action = sp->disc[i];
360*4887Schin 		else
361*4887Schin 		{
362*4887Schin 			if (nq = sp->disc[i])
363*4887Schin 				free((void*)nq);
364*4887Schin 			if (action)
365*4887Schin 				sp->disc[i] = action;
366*4887Schin 			else
367*4887Schin 				sp->disc[i] = 0;
368*4887Schin 		}
369*4887Schin 		return action ? (char*)action : "";
370*4887Schin 	}
371*4887Schin 	/* try the next level */
372*4887Schin 	return nv_setdisc(np, event, action, fp);
373*4887Schin }
374*4887Schin 
375*4887Schin static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp)
376*4887Schin {
377*4887Schin 	register Service_t* sp = (Service_t*)fp;
378*4887Schin 	if (!val)
379*4887Schin 		fp = nv_stack(np, NiL);
380*4887Schin 	nv_putv(np, val, flag, fp);
381*4887Schin 	if (!val)
382*4887Schin 	{
383*4887Schin 		register int i;
384*4887Schin 		for(i=0; i< sh.lim.open_max; i++)
385*4887Schin 		{
386*4887Schin 			if(service_list[i]==sp)
387*4887Schin 			{
388*4887Schin 				close(i);
389*4887Schin 				if(--sp->refcount<=0)
390*4887Schin 					break;
391*4887Schin 			}
392*4887Schin 		}
393*4887Schin 		free((void*)fp);
394*4887Schin 		return;
395*4887Schin 	}
396*4887Schin }
397*4887Schin 
398*4887Schin static const Namdisc_t servdisc =
399*4887Schin {
400*4887Schin 	sizeof(Service_t),
401*4887Schin 	putval,
402*4887Schin 	0,
403*4887Schin 	0,
404*4887Schin 	setdisc
405*4887Schin };
406*4887Schin 
407*4887Schin int	b_mkservice(int argc, char** argv, void* extra)
408*4887Schin {
409*4887Schin 	register char*		var;
410*4887Schin 	register char*		path;
411*4887Schin 	register Namval_t*	np;
412*4887Schin 	register Service_t*	sp;
413*4887Schin 	register int		fd;
414*4887Schin 
415*4887Schin 	NOT_USED(argc);
416*4887Schin 	NOT_USED(extra);
417*4887Schin 	for (;;)
418*4887Schin 	{
419*4887Schin 		switch (optget(argv, mkservice_usage))
420*4887Schin 		{
421*4887Schin 		case 0:
422*4887Schin 			break;
423*4887Schin 		case ':':
424*4887Schin 			error(2, opt_info.arg);
425*4887Schin 			continue;
426*4887Schin 		case '?':
427*4887Schin 			error(ERROR_usage(2), opt_info.arg);
428*4887Schin 			continue;
429*4887Schin 		}
430*4887Schin 		break;
431*4887Schin 	}
432*4887Schin 	argv += opt_info.index;
433*4887Schin 	if (error_info.errors || !(var = *argv++) || !(path = *argv++) || *argv)
434*4887Schin 		error(ERROR_usage(2), optusage(NiL));
435*4887Schin 	if (!(sp = newof(0, Service_t, 1, 0)))
436*4887Schin 		error(ERROR_exit(1), "out of space");
437*4887Schin 	sp->acceptf = Accept;
438*4887Schin 	sp->actionf = Action;
439*4887Schin 	sp->errorf = Error;
440*4887Schin 	sp->refcount = 1;
441*4887Schin 	sp->context = extra;
442*4887Schin 	sp->node = 0;
443*4887Schin 	sp->fun.disc = &servdisc;
444*4887Schin 	if((fd = sh_open(path, O_SERVICE|O_RDWR))<=0)
445*4887Schin 	{
446*4887Schin 		free((void*)sp);
447*4887Schin 		error(ERROR_exit(1), "%s: cannot start service", path);
448*4887Schin 	}
449*4887Schin 	if((sp->fd = fcntl(fd, F_DUPFD, 10))>=10)
450*4887Schin 		close(fd);
451*4887Schin 	else
452*4887Schin 		sp->fd = fd;
453*4887Schin 	np = nv_open(var,sh.var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
454*4887Schin 	sp->node = np;
455*4887Schin 	nv_putval(np, path, 0);
456*4887Schin 	nv_stack(np, (Namfun_t*)sp);
457*4887Schin 	service_add(sp);
458*4887Schin 	return(0);
459*4887Schin }
460*4887Schin 
461*4887Schin int	b_eloop(int argc, char** argv, void* extra)
462*4887Schin {
463*4887Schin 	register long	timeout = -1;
464*4887Schin 	NOT_USED(argc);
465*4887Schin 	NOT_USED(extra);
466*4887Schin 	for (;;)
467*4887Schin 	{
468*4887Schin 		switch (optget(argv, eloop_usage))
469*4887Schin 		{
470*4887Schin 		case 0:
471*4887Schin 			break;
472*4887Schin 		case 't':
473*4887Schin 			timeout = opt_info.num;
474*4887Schin 			continue;
475*4887Schin 		case ':':
476*4887Schin 			error(2, opt_info.arg);
477*4887Schin 			continue;
478*4887Schin 		case '?':
479*4887Schin 			error(ERROR_usage(2), opt_info.arg);
480*4887Schin 			continue;
481*4887Schin 		}
482*4887Schin 		break;
483*4887Schin 	}
484*4887Schin 	argv += opt_info.index;
485*4887Schin 	if (error_info.errors  || *argv)
486*4887Schin 		error(ERROR_usage(2), optusage(NiL));
487*4887Schin 	while(1)
488*4887Schin 	{
489*4887Schin 		if(waitnotify(-1, timeout, 0)==0)
490*4887Schin 			break;
491*4887Schin 		sfprintf(sfstderr,"interrupted\n");
492*4887Schin 	}
493*4887Schin 	return(errno != 0);
494*4887Schin }
495