1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
22 * mkservice varname pathname
24 * Written by David Korn
28 static const char mkservice_usage
[] =
29 "[-?\n@(#)$Id: mkservice (AT&T Research) 2001-06-13 $\n]"
31 "[+NAME? mkservice - create a shell server ]"
32 "[+DESCRIPTION?\bmkservice\b creates a tcp or udp server that is "
33 "implemented by shell functions.]"
34 "[+?The \aservice_path\a must be of the form \b/dev/tcp/localhost/\b\aportno\a "
35 "or \b/dev/udp/localhost/\b\aportno\a depending on whether the "
36 "\btcp\b or \budp\b protocol is used. \aportno\a is the port "
37 "number that the service will use.]"
38 "[+?The shell variable \avarname\a is associated with the service. This "
39 "variable can have subvariables that keeps the state of all "
40 "active connections. The functions \avarname\a\b.accept\b, "
41 "\avarname\a\b.action\b and \avarname\a\b.close\b implement the "
42 "service as follows:]{"
43 "[+accept?This function is invoked when a client tries to connect "
44 "to the service. It is called with an argument which "
45 "is the file descriptor number associated with the "
46 "accepted connection. If the function returns a non-zero "
47 "value, this connection will be closed.]"
48 "[+action?This function is invoked when there is data waiting "
49 "to be read from one of the active connections. It is "
50 "called with the file descriptor number that has data "
51 "to be read. If the function returns a non-zero "
52 "value, this connection will be closed.]"
53 "[+close?This function is invoked when the connection is closed.]"
55 "[+?If \avarname\a is unset, then all active connection, and the service "
56 "itself will be closed.]"
59 "\nvarname service_path\n"
63 "[+>0?An error occurred.]"
65 "[+SEE ALSO?\beloop\b(1)]"
69 static const char eloop_usage
[] =
70 "[-?\n@(#)$Id: eloop (AT&T Research) 2001-06-13 $\n]"
72 "[+NAME? eloop - process event loop]"
73 "[+DESCRIPTION?\beloop\b causes the shell to block waiting for events "
74 "to process. By default, \beloop\b does not return.]"
75 "[t]#[timeout?\atimeout\a is the number of milliseconds to wait "
76 "without receiving any events to process.]"
80 "[+EXIT STATUS?If no timeout is specified, \beloop\b will not return "
81 "unless interrupted. Otherwise]{"
82 "[+0?The specified timeout interval occurred.]"
83 "[+>0?An error occurred.]"
85 "[+SEE ALSO?\bmkservice\b(1)]"
94 #include <sys/socket.h>
95 #include <netinet/in.h>
102 # define O_SERVICE O_NOCTTY
105 static const char* disctab
[] =
113 typedef struct Service_s Service_t
;
120 int (*acceptf
)(Service_t
*,int);
121 int (*actionf
)(Service_t
*,int,int);
122 int (*errorf
)(Service_t
*,int,const char*, ...);
125 Namval_t
* disc
[elementsof(disctab
)-1];
128 static short *file_list
;
129 static Sfio_t
**poll_list
;
130 static Service_t
**service_list
;
134 static int (*covered_fdnotify
)(int, int);
136 static int fdclose(Service_t
*sp
, register int fd
)
139 service_list
[fd
] = 0;
142 for(i
=0; i
< npoll
; i
++)
146 file_list
[i
] = file_list
[npoll
--];
148 (*sp
->actionf
)(sp
, fd
, 1);
155 static int fdnotify(int fd1
, int fd2
)
158 if (covered_fdnotify
)
159 (*covered_fdnotify
)(fd1
, fd2
);
163 service_list
[fd2
] = service_list
[fd1
];
164 service_list
[fd1
] = 0;
165 for(i
=0; i
< npoll
; i
++)
167 if(file_list
[i
]==fd1
)
174 else if(sp
= service_list
[fd1
])
177 if(--sp
->refcount
==0)
183 static void process_stream(Sfio_t
* iop
)
185 int r
=0, fd
= sffileno(iop
);
186 Service_t
* sp
= service_list
[fd
];
187 if(fd
==sp
->fd
) /* connection socket */
189 struct sockaddr addr
;
190 socklen_t addrlen
= sizeof(addr
);
191 fd
= accept(fd
, &addr
, &addrlen
);
192 service_list
[fd
] = sp
;
194 file_list
[npoll
++] = fd
;
198 r
= (*sp
->acceptf
)(sp
,fd
);
203 service_list
[fd
] = 0;
204 r
= (*sp
->actionf
)(sp
, fd
, 0);
205 service_list
[fd
] = sp
;
211 static int waitnotify(int fd
, long timeout
, int rw
)
213 Sfio_t
*special
=0, **pstream
;
217 special
= sh_fd2sfio(fd
);
221 while(ready
< nready
)
222 process_stream(pstream
[ready
++]);
224 *pstream
++ = special
;
225 for(i
=0; i
< npoll
; i
++)
227 if(service_list
[file_list
[i
]])
228 *pstream
++ = sh_fd2sfio(file_list
[i
]);
231 for(i
=0; i
< pstream
-poll_list
; i
++)
232 sfset(poll_list
[i
],SF_WRITE
,0);
237 sfprintf(sfstderr
,"before poll npoll=%d",pstream
-poll_list
);
238 for(i
=0; i
< pstream
-poll_list
; i
++)
239 sfprintf(sfstderr
," %d",sffileno(poll_list
[i
]));
240 sfputc(sfstderr
,'\n');
242 nready
= sfpoll(poll_list
,pstream
-poll_list
,timeout
);
244 sfprintf(sfstderr
,"after poll nready=%d",nready
);
245 for(i
=0; i
< nready
; i
++)
246 sfprintf(sfstderr
," %d",sffileno(poll_list
[i
]));
247 sfputc(sfstderr
,'\n');
250 for(i
=0; i
< pstream
-poll_list
; i
++)
251 sfset(poll_list
[i
],SF_WRITE
,1);
254 return(errno
? -1: 0);
255 if(special
&& poll_list
[0]==special
)
263 static int service_init(void)
265 file_list
= newof(NULL
,short,n
,0);
266 poll_list
= newof(NULL
,Sfio_t
*,n
,0);
267 service_list
= newof(NULL
,Service_t
*,n
,0);
268 covered_fdnotify
= sh_fdnotify(fdnotify
);
269 sh_waitnotify(waitnotify
);
273 void service_add(Service_t
*sp
)
277 init
= service_init();
278 service_list
[sp
->fd
] = sp
;
279 file_list
[npoll
++] = sp
->fd
;
282 static int Accept(register Service_t
*sp
, int accept_fd
)
284 register Namval_t
* nq
= sp
->disc
[ACCEPT
];
287 fd
= fcntl(accept_fd
, F_DUPFD
, 10);
298 sfsprintf(buff
, sizeof(buff
), "%d", fd
);
299 if (sh_fun(nq
, sp
->node
, av
))
310 static int Action(Service_t
*sp
, int fd
, int close
)
312 register Namval_t
* nq
;
316 nq
= sp
->disc
[CLOSE
];
318 nq
= sp
->disc
[ACTION
];
326 sfsprintf(buff
, sizeof(buff
), "%d", fd
);
327 r
=sh_fun(nq
, sp
->node
, av
);
330 return r
> 0 ? -1 : 1;
333 static int Error(Service_t
*sp
, int level
, const char* arg
, ...)
341 errorv(NiL
, ERROR_exit(1), ap
);
346 static char* setdisc(Namval_t
* np
, const char* event
, Namval_t
* action
, Namfun_t
* fp
)
348 register Service_t
* sp
= (Service_t
*)fp
;
349 register const char* cp
;
351 register int n
= strlen(event
) - 1;
352 register Namval_t
* nq
;
354 for (i
= 0; cp
= disctab
[i
]; i
++)
356 if (memcmp(event
, cp
, n
))
359 action
= sp
->disc
[i
];
362 if (nq
= sp
->disc
[i
])
365 sp
->disc
[i
] = action
;
369 return action
? (char*)action
: "";
371 /* try the next level */
372 return nv_setdisc(np
, event
, action
, fp
);
375 static void putval(Namval_t
* np
, const char* val
, int flag
, Namfun_t
* fp
)
377 register Service_t
* sp
= (Service_t
*)fp
;
379 fp
= nv_stack(np
, NiL
);
380 nv_putv(np
, val
, flag
, fp
);
384 for(i
=0; i
< sh
.lim
.open_max
; i
++)
386 if(service_list
[i
]==sp
)
389 if(--sp
->refcount
<=0)
398 static const Namdisc_t servdisc
=
407 int b_mkservice(int argc
, char** argv
, void* extra
)
411 register Namval_t
* np
;
412 register Service_t
* sp
;
419 switch (optget(argv
, mkservice_usage
))
424 error(2, opt_info
.arg
);
427 error(ERROR_usage(2), opt_info
.arg
);
432 argv
+= opt_info
.index
;
433 if (error_info
.errors
|| !(var
= *argv
++) || !(path
= *argv
++) || *argv
)
434 error(ERROR_usage(2), optusage(NiL
));
435 if (!(sp
= newof(0, Service_t
, 1, 0)))
436 error(ERROR_exit(1), "out of space");
437 sp
->acceptf
= Accept
;
438 sp
->actionf
= Action
;
443 sp
->fun
.disc
= &servdisc
;
444 if((fd
= sh_open(path
, O_SERVICE
|O_RDWR
))<=0)
447 error(ERROR_exit(1), "%s: cannot start service", path
);
449 if((sp
->fd
= fcntl(fd
, F_DUPFD
, 10))>=10)
453 np
= nv_open(var
,sh
.var_tree
,NV_ARRAY
|NV_VARNAME
|NV_NOASSIGN
);
455 nv_putval(np
, path
, 0);
456 nv_stack(np
, (Namfun_t
*)sp
);
461 int b_eloop(int argc
, char** argv
, void* extra
)
463 register long timeout
= -1;
468 switch (optget(argv
, eloop_usage
))
473 timeout
= opt_info
.num
;
476 error(2, opt_info
.arg
);
479 error(ERROR_usage(2), opt_info
.arg
);
484 argv
+= opt_info
.index
;
485 if (error_info
.errors
|| *argv
)
486 error(ERROR_usage(2), optusage(NiL
));
489 if(waitnotify(-1, timeout
, 0)==0)
491 sfprintf(sfstderr
,"interrupted\n");