Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 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 (at) 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 
    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, 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 
    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