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