Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ksh93/bltins/mkservice.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1982-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* David Korn <[email protected]> *
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
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
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
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
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
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
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
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
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
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
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
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
407
int b_mkservice(int argc, char** argv, Shbltin_t *context)
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(context);
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 = context;
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
461
int b_eloop(int argc, char** argv, Shbltin_t *context)
462
{
463
register long timeout = -1;
464
NOT_USED(argc);
465
NOT_USED(context);
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
496