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