1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2008 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 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