1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1982-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
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 */
27da2e3ebdSchin
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 ;
67da2e3ebdSchin
68da2e3ebdSchin
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 ;
87da2e3ebdSchin
88da2e3ebdSchin
89da2e3ebdSchin #include "defs.h"
90da2e3ebdSchin
91da2e3ebdSchin #include <cmd.h>
92da2e3ebdSchin #include <error.h>
93da2e3ebdSchin #include <nval.h>
94da2e3ebdSchin #include <sys/socket.h>
95da2e3ebdSchin #include <netinet/in.h>
96da2e3ebdSchin
97da2e3ebdSchin #define ACCEPT 0
98da2e3ebdSchin #define ACTION 1
99da2e3ebdSchin #define CLOSE 2
100da2e3ebdSchin
101da2e3ebdSchin #ifndef O_SERVICE
102da2e3ebdSchin # define O_SERVICE O_NOCTTY
103da2e3ebdSchin #endif
104da2e3ebdSchin
105da2e3ebdSchin static const char* disctab[] =
106da2e3ebdSchin {
107da2e3ebdSchin "accept",
108da2e3ebdSchin "action",
109da2e3ebdSchin "close",
110da2e3ebdSchin 0
111da2e3ebdSchin };
112da2e3ebdSchin
113da2e3ebdSchin typedef struct Service_s Service_t;
114da2e3ebdSchin
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 };
127da2e3ebdSchin
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);
135da2e3ebdSchin
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 }
154da2e3ebdSchin
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 }
182da2e3ebdSchin
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 }
210da2e3ebdSchin
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;
215da2e3ebdSchin
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 }
262da2e3ebdSchin
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 }
272da2e3ebdSchin
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 }
281da2e3ebdSchin
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;
286da2e3ebdSchin
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];
295da2e3ebdSchin
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 }
309da2e3ebdSchin
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;
314da2e3ebdSchin
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];
323da2e3ebdSchin
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 }
332da2e3ebdSchin
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;
336da2e3ebdSchin
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 }
345da2e3ebdSchin
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;
353da2e3ebdSchin
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 }
374da2e3ebdSchin
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 }
397da2e3ebdSchin
398da2e3ebdSchin static const Namdisc_t servdisc =
399da2e3ebdSchin {
400da2e3ebdSchin sizeof(Service_t),
401da2e3ebdSchin putval,
402da2e3ebdSchin 0,
403da2e3ebdSchin 0,
404da2e3ebdSchin setdisc
405da2e3ebdSchin };
406da2e3ebdSchin
b_mkservice(int argc,char ** argv,void * extra)407da2e3ebdSchin int b_mkservice(int argc, char** argv, void* extra)
408da2e3ebdSchin {
409da2e3ebdSchin register char* var;
410da2e3ebdSchin register char* path;
411da2e3ebdSchin register Namval_t* np;
412da2e3ebdSchin register Service_t* sp;
413da2e3ebdSchin register int fd;
414da2e3ebdSchin
415da2e3ebdSchin NOT_USED(argc);
416da2e3ebdSchin NOT_USED(extra);
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;
441da2e3ebdSchin sp->context = extra;
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 }
460da2e3ebdSchin
b_eloop(int argc,char ** argv,void * extra)461da2e3ebdSchin int b_eloop(int argc, char** argv, void* extra)
462da2e3ebdSchin {
463da2e3ebdSchin register long timeout = -1;
464da2e3ebdSchin NOT_USED(argc);
465da2e3ebdSchin NOT_USED(extra);
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 }
495