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