ARPDAS_QNX6 1.0
|
00001 /* cis.c Defines functions used by Command Interpreter Server */ 00002 #include <errno.h> 00003 #include <stdio.h> 00004 #include <stdlib.h> 00005 #include <stddef.h> 00006 #include <stdlib.h> 00007 #include <unistd.h> 00008 #include <stdarg.h> 00009 #include <string.h> 00010 #include <ctype.h> 00011 #include "cis.h" 00012 #include "tm.h" 00013 #include "nortlib.h" 00014 #include "nl_assert.h" 00015 00016 static int io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb); 00017 static int io_write(resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb); 00018 static int io_notify(resmgr_context_t *ctp, io_notify_t *msg, RESMGR_OCB_T *ocb); 00019 static int io_close_ocb(resmgr_context_t *ctp, void *rsvd, RESMGR_OCB_T *ocb); 00020 static command_out_t *new_command(void); 00021 static command_out_t *free_command( command_out_t *cmd ); 00022 00023 static resmgr_connect_funcs_t connect_funcs; 00024 static resmgr_io_funcs_t rd_io_funcs, wr_io_funcs; 00025 static IOFUNC_ATTR_T wr_attr; 00026 static int wr_id; 00027 static resmgr_attr_t resmgr_attr; 00028 static dispatch_t *dpp; 00029 static IOFUNC_ATTR_T *rd_attrs; 00030 00031 // These will be generated by cmdgen and included in a separate module. 00032 // char ci_version[] = ""; 00033 // void cis_initialize(void) {} 00034 // void cis_terminate(void) {} 00035 // void cis_interfaces(void) {} 00036 // int cmd_batch( char *cmd, int test ) { 00037 // cmd = cmd; 00038 // test = test; 00039 // } 00040 00041 static struct ocb *ocb_calloc (resmgr_context_t *ctp, IOFUNC_ATTR_T *device) { 00042 ocb_t *ocb = calloc( 1, sizeof(ocb_t) ); 00043 if ( ocb == 0 ) return 0; 00044 /* Initialize any other elements. Currently all zeros is good. */ 00045 /* Increment count on first command */ 00046 if ( device->first_cmd ) 00047 device->first_cmd->ref_count++; 00048 ocb->next_command = device->first_cmd; 00049 return ocb; 00050 } 00051 00052 static void ocb_free (struct ocb *ocb) { 00053 /* Be sure to remove this from the blocking list: 00054 Actually, there really is no way it should be on 00055 the blocking list. */ 00056 assert( ocb->rcvid == 0 ); 00057 assert( ocb->next_ocb == 0 ); 00058 if ( ocb->next_command ) { 00059 assert( ocb->next_command->ref_count > 0 ); 00060 ocb->next_command->ref_count--; 00061 } 00062 free( ocb ); 00063 } 00064 00065 static iofunc_funcs_t ocb_funcs = { /* our ocb allocating & freeing functions */ 00066 _IOFUNC_NFUNCS, 00067 ocb_calloc, 00068 ocb_free 00069 }; 00070 00071 /* the mount structure, we have only one so we statically declare it */ 00072 static iofunc_mount_t mountpoint = { 0, 0, 0, 0, &ocb_funcs }; 00073 00074 /* rdrs is a linked list of rdr IOFUNC_ATTR_Ts */ 00075 typedef struct rdrs_s { 00076 struct rdrs_s *next; 00077 int id; 00078 IOFUNC_ATTR_T *rdr; 00079 } rdrs_t; 00080 static rdrs_t *rdrs; 00081 00082 /* Called from code generated by cmdgen for each interface definition */ 00083 IOFUNC_ATTR_T *cis_setup_rdr( char *node ) { 00084 ioattr_t *rd_attr = new_memory(sizeof(ioattr_t)); 00085 char nodebase[80], *nodename; 00086 int id; 00087 rdrs_t *rs = new_memory(sizeof(rdrs_t)); 00088 00089 /* initialize attribute structure used by the device */ 00090 iofunc_attr_init(&(rd_attr->attr), S_IFNAM | 0444, 0, 0); 00091 IOFUNC_NOTIFY_INIT(rd_attr->notify); 00092 rd_attr->attr.nbytes = 0; 00093 rd_attr->attr.mount = &mountpoint; 00094 rd_attr->nodename = nl_strdup( node ); 00095 00096 /* Check Experiment variable for sanity: \w[\w.]* */ 00097 /* Build device name */ 00098 snprintf( nodebase, 80, "cmd/%s", node ); 00099 nodename = tm_dev_name( nodebase ); 00100 00101 rd_attr->first_cmd = rd_attr->last_cmd = new_command(); 00102 rd_attr->next = rd_attrs; 00103 rd_attrs = rd_attr; 00104 00105 /* attach our device name */ 00106 id = resmgr_attach(dpp, /* dispatch handle */ 00107 &resmgr_attr, /* resource manager attrs */ 00108 nodename, /* device name */ 00109 _FTYPE_ANY, /* open type */ 00110 0, /* flags */ 00111 &connect_funcs, /* connect routines */ 00112 &rd_io_funcs, /* I/O routines */ 00113 rd_attr); /* handle */ 00114 if(id == -1) { 00115 nl_error( 3, "Unable to attach name: '%s'", nodename ); 00116 } 00117 rs->rdr = rd_attr; 00118 rs->id = id; 00119 rs->next = rdrs; 00120 rdrs = rs; 00121 return rd_attr; 00122 } 00123 00124 static void cis_shutdown_rdr( rdrs_t **p ) { 00125 rdrs_t *cur_rdr = *p; 00126 IOFUNC_ATTR_T *attr; 00127 int rv; 00128 00129 assert( cur_rdr != NULL); 00130 attr = cur_rdr->rdr; 00131 nl_error( -2, "Shutting down reader %s", attr->nodename ); 00132 rv = resmgr_detach( dpp, cur_rdr->id, _RESMGR_DETACH_ALL ); 00133 if ( rv < 0 ) 00134 nl_error( 2, "Error %d from resmgr_detach(%s)", 00135 errno, attr->nodename ); 00136 nl_free_memory( attr->nodename ); 00137 nl_free_memory( attr ); 00138 *p = cur_rdr->next; 00139 nl_free_memory( cur_rdr ); 00140 } 00141 00142 static int quit_received = 0; 00143 00144 static int all_closed(void) { 00145 rdrs_t **cur_rdr_p = &rdrs; 00146 rdrs_t *cur_rdr; 00147 IOFUNC_ATTR_T *cur_attr; 00148 for (;;) { 00149 cur_rdr = *cur_rdr_p; 00150 if ( cur_rdr == NULL ) break; 00151 assert( cur_rdr->rdr != NULL ); 00152 cur_attr = cur_rdr->rdr; 00153 if ( cur_attr->attr.count == 0 ) 00154 cis_shutdown_rdr( cur_rdr_p ); 00155 else 00156 cur_rdr_p = &(cur_rdr->next); 00157 } 00158 return rdrs == NULL && wr_attr.attr.count == 0; 00159 } 00160 00161 static void process_quit(void) { 00162 rdrs_t *rr; 00163 int rv; 00164 nl_error( -2, "Processing Quit" ); 00165 for ( rr = rdrs; rr != NULL; rr = rr->next ) { 00166 cis_turf( rr->rdr, "" ); 00167 } 00168 cis_interfaces_close(); 00169 rv = resmgr_detach( dpp, wr_id, _RESMGR_DETACH_PATHNAME ); 00170 if ( rv < 0 ) 00171 nl_error( 2, "Error %d from resmgr_detach(wr_id)", errno ); 00172 all_closed(); 00173 quit_received = 1; 00174 } 00175 00176 void ci_server(void) { 00177 int use_threads = 0; 00178 char *server_name; 00179 00180 cis_initialize(); 00181 00182 /* initialize dispatch interface */ 00183 if((dpp = dispatch_create()) == NULL) { 00184 nl_error(3, "Unable to allocate dispatch handle."); 00185 } 00186 00187 /* initialize resource manager attributes. */ 00188 /* planning to share this struct between rd and wr */ 00189 memset(&resmgr_attr, 0, sizeof resmgr_attr); 00190 // resmgr_attr.nparts_max = 0; 00191 // resmgr_attr.msg_max_size = 0; 00192 00193 /* initialize functions for handling messages */ 00194 iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs, 00195 _RESMGR_IO_NFUNCS, &rd_io_funcs); 00196 rd_io_funcs.read = io_read; 00197 /* Will want to handle _IO_NOTIFY at least */ 00198 rd_io_funcs.notify = io_notify; 00199 rd_io_funcs.close_ocb = io_close_ocb; 00200 00201 iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs, 00202 _RESMGR_IO_NFUNCS, &wr_io_funcs); 00203 wr_io_funcs.write = io_write; 00204 00205 /* initialize attribute structure used by the device */ 00206 iofunc_attr_init(&wr_attr.attr, S_IFNAM | 0664, 0, 0); 00207 IOFUNC_NOTIFY_INIT(wr_attr.notify); 00208 wr_attr.attr.nbytes = 0; 00209 wr_attr.attr.mount = &mountpoint; 00210 wr_attr.nodename = nl_strdup("writer"); 00211 00212 server_name = nl_strdup( tm_dev_name( CMDSRVR_NAME ) ); 00213 /* Check Experiment variable for sanity: \w[\w.]* */ 00214 /* Build device name */ 00215 /* attach our device name */ 00216 wr_id = resmgr_attach(dpp, /* dispatch handle */ 00217 &resmgr_attr, /* resource manager attrs */ 00218 server_name, /* device name */ 00219 _FTYPE_ANY, /* open type */ 00220 0, /* flags */ 00221 &connect_funcs, /* connect routines */ 00222 &wr_io_funcs, /* I/O routines */ 00223 &wr_attr); /* handle */ 00224 if( wr_id == -1 ) 00225 nl_error( 3, "Unable to attach name"); 00226 00227 // Call the cmdgen-generated initialization routine 00228 cis_interfaces(); 00229 // if_foo = cis_setup_rdr( "foo" ); 00230 // if_bar = cis_setup_rdr( "bar" ); 00231 00232 if ( use_threads ) { 00233 thread_pool_attr_t pool_attr; 00234 thread_pool_t *tpp; 00235 00236 /* initialize thread pool attributes */ 00237 memset(&pool_attr, 0, sizeof pool_attr); 00238 pool_attr.handle = dpp; 00239 pool_attr.context_alloc = dispatch_context_alloc; 00240 pool_attr.block_func = dispatch_block; 00241 pool_attr.handler_func = dispatch_handler; 00242 pool_attr.context_free = dispatch_context_free; 00243 pool_attr.lo_water = 2; 00244 pool_attr.hi_water = 4; 00245 pool_attr.increment = 1; 00246 pool_attr.maximum = 50; /* allocate a thread pool handle */ 00247 if((tpp = thread_pool_create(&pool_attr, 00248 POOL_FLAG_EXIT_SELF)) == NULL) { 00249 nl_error(3, "Unable to initialize thread pool"); 00250 } /* start the threads, will not return */ 00251 thread_pool_start(tpp); 00252 } else { 00253 dispatch_context_t *ctp; 00254 ctp = dispatch_context_alloc(dpp); 00255 while ( 1 ) { 00256 if ((ctp = dispatch_block(ctp)) == NULL) { 00257 nl_error( 2, "block error\n" ); 00258 return; 00259 } 00260 // printf( " type = %d,%d attr.count = %d\n", 00261 // ctp->resmgr_context.msg->type, 00262 // ctp->resmgr_context.msg->connect.subtype, attr.count ); 00263 dispatch_handler(ctp); 00264 if ( quit_received && ctp->resmgr_context.rcvid == 0 00265 && all_closed() ) { 00266 break; 00267 } 00268 } 00269 } 00270 return; 00271 } 00272 00273 // This is where commands are recieved. We'll require that only one 00274 // command be received per write. That prevents doing something like 00275 // cat commandlist.txt > /dev/huarp/Exp/cmd/server 00276 // but there's no particular reason why we'd want to do that. In 00277 // actual use, commands come in one at a time. Also, it's easy enough 00278 // to do: 00279 // cat commandlist.txt | while read line; do 00280 // echo $line >/dev/huarp/Exp/cmd/server; done 00281 static int io_write( resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb ) { 00282 int status, msgsize; 00283 char buf[CMD_MAX_COMMAND_IN+1]; 00284 00285 status = iofunc_write_verify(ctp, msg, (iofunc_ocb_t *)ocb, NULL); 00286 if ( status != EOK ) 00287 return status; 00288 00289 if ((msg->i.xtype &_IO_XTYPE_MASK) != _IO_XTYPE_NONE ) 00290 return ENOSYS; 00291 00292 msgsize = msg->i.nbytes; 00293 if ( msgsize > CMD_MAX_COMMAND_IN ) 00294 return E2BIG; 00295 00296 _IO_SET_WRITE_NBYTES( ctp, msg->i.nbytes ); 00297 00298 resmgr_msgread( ctp, buf, msgsize, sizeof(msg->i) ); 00299 buf[msgsize] = '\0'; 00300 00301 // Parse leading options 00302 // No spaces, colons or right brackets allowed in mnemonics 00303 { char *mnemonic = "--"; 00304 int quiet = 0; 00305 int testing = 0; 00306 char *s = buf; 00307 00308 if ( *s == '[' ) { 00309 s++; 00310 if ( isgraph(*s) && *s != ':' && *s != ']' ) { 00311 mnemonic = s++; 00312 while ( isgraph(*s) && *s != ':' && *s != ']' ) 00313 s++; 00314 } 00315 if ( !isgraph(*s) ) { 00316 nl_error( 2, "Invalid mnemonic string" ); 00317 return EINVAL; 00318 } 00319 if ( *s == ':' ) { 00320 int end_of_opts = 0; 00321 char *ver; 00322 00323 *s++ = '\0'; // terminate the mnemonic 00324 // and then handle the options 00325 while (!end_of_opts) { 00326 switch (*s) { 00327 case 'T': testing = 1; s++; break; 00328 case 'Q': quiet = 1; s++; break; 00329 case 'X': process_quit(); return EOK; 00330 case 'V': // handle version command 00331 ver = ++s; 00332 while ( *s != ']' && *s != '\0' ) s++; 00333 if ( *s == '\0' ) { 00334 nl_error( 2, "Unterminated version string" ); 00335 return EINVAL; 00336 } 00337 *s = '\0'; 00338 if ( strcmp( ver, ci_version ) == 0 ) 00339 return EOK; 00340 nl_error( 2, "Command Versions don't match" ); 00341 return EINVAL; 00342 case ']': end_of_opts = 1; break; 00343 default: 00344 nl_error( 2, "Invalid option" ); 00345 return EINVAL; 00346 } 00347 } 00348 } 00349 // blank out trailing ']' in case it's the end of the mnemonic 00350 *s++ = '\0'; 00351 } 00352 { char *cmd = s; 00353 int len = 0; 00354 int rv; 00355 int clen; 00356 00357 // Now s points to a command we want to parse. 00358 // Make sure it's kosher 00359 while ( *s ) { 00360 if ( ! isprint(*s) && *s != '\n' ) { 00361 nl_error( 2, "Invalid character in command" ); 00362 return EINVAL; 00363 } 00364 len++; 00365 s++; 00366 } 00367 if ( len > 0 && cmd[len-1] == '\n' ) len--; 00368 nl_error( quiet ? -2 : 0, "%s: %*.*s", 00369 mnemonic, len, len, cmd ); 00370 cmd_init(); 00371 rv = cmd_batch( cmd, testing ); 00372 ocb->hdr.attr->attr.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME; 00373 switch ( CMDREP_TYPE(rv) ) { 00374 case 0: return EOK; 00375 case 1: 00376 if (testing) return EOK; 00377 process_quit(); 00378 return ENOENT; 00379 case 2: /* Report Syntax Error */ 00380 if ( nl_response ) { 00381 nl_error( 2, "%s: Syntax Error", mnemonic ); 00382 nl_error( 2, "%*.*s", len, len, cmd); 00383 nl_error( 2, "%*s", rv - CMDREP_SYNERR, "^"); 00384 } 00385 return EINVAL; 00386 default: return EIO; 00387 } 00388 } 00389 } 00390 } 00391 00392 static int io_notify(resmgr_context_t *ctp, io_notify_t *msg, RESMGR_OCB_T *ocb) { 00393 IOFUNC_ATTR_T *rd_attr = ocb->hdr.attr; 00394 int trig = 0; 00395 if ( ocb->next_command->cmdlen > ocb->hdr.offset || 00396 ocb->next_command->next ) 00397 trig |= _NOTIFY_COND_INPUT; 00398 return(iofunc_notify(ctp, msg, rd_attr->notify, trig, NULL, NULL )); 00399 } 00400 00401 static int io_close_ocb(resmgr_context_t *ctp, void *rsvd, RESMGR_OCB_T *ocb) { 00402 IOFUNC_ATTR_T *rd_attr = ocb->hdr.attr; 00403 iofunc_notify_remove(ctp, rd_attr->notify); 00404 return(iofunc_close_ocb_default(ctp, rsvd, (iofunc_ocb_t *)ocb)); 00405 } 00406 00407 static void read_reply( RESMGR_OCB_T *ocb ) { 00408 int nb = ocb->nbytes_requested; 00409 command_out_t *cmd = ocb->next_command; 00410 int cmdbytes = cmd->cmdlen - ocb->hdr.offset; 00411 int bytes_returned = nb > cmdbytes ? cmdbytes : nb; 00412 00413 assert(cmd->ref_count > 0); 00414 assert(cmdbytes >= 0); 00415 MsgReply( ocb->rcvid, bytes_returned, 00416 cmd->command + ocb->hdr.offset, bytes_returned ); 00417 ocb->rcvid = 0; 00418 if ( bytes_returned < cmdbytes ) { 00419 ocb->hdr.offset += bytes_returned; 00420 } else { 00421 IOFUNC_ATTR_T *handle = ocb->hdr.attr; 00422 ocb->hdr.offset = 0; 00423 cmd->ref_count--; 00424 ocb->next_command = cmd->next; 00425 ocb->next_command->ref_count++; 00426 if ( handle->first_cmd->ref_count == 0 && 00427 handle->first_cmd->next != 0 ) { 00428 handle->first_cmd = free_command( handle->first_cmd ); 00429 } 00430 } 00431 } 00432 00433 static int io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb) { 00434 int status, nonblock = 0; 00435 IOFUNC_ATTR_T *handle = ocb->hdr.attr; 00436 00437 if ((status = iofunc_read_verify( ctp, msg, 00438 (iofunc_ocb_t *)ocb, &nonblock)) != EOK) 00439 return (status); 00440 00441 if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) 00442 return (ENOSYS); 00443 00444 ocb->rcvid = ctp->rcvid; 00445 ocb->nbytes_requested = msg->i.nbytes; 00446 if ( ocb->next_command->cmdlen > ocb->hdr.offset || 00447 ocb->next_command->next ) { 00448 // we've got something to send now 00449 read_reply( ocb ); 00450 } else if (nonblock) { 00451 ocb->rcvid = 0; 00452 return EAGAIN; 00453 } else { 00454 // Nothing at the moment. 00455 ocb->next_ocb = handle->blocked; 00456 handle->blocked = ocb; 00457 } 00458 return _RESMGR_NOREPLY; 00459 } 00460 00461 static command_out_t *free_commands; 00462 00463 static command_out_t *new_command(void) { 00464 command_out_t *cmd; 00465 if ( free_commands ) { 00466 cmd = free_commands; 00467 free_commands = cmd->next; 00468 } else { 00469 cmd = new_memory(sizeof(command_out_t)); 00470 } 00471 cmd->next = NULL; 00472 cmd->ref_count = 0; 00473 cmd->command[0] = '\0'; 00474 cmd->cmdlen = 0; 00475 return cmd; 00476 } 00477 00478 // Returns the next command so it's easy to free the 00479 // first command in a list: 00480 // list = free_command( list ); 00481 static command_out_t *free_command( command_out_t *cmd ) { 00482 command_out_t *nxt; 00483 assert( cmd != NULL ); 00484 assert( cmd->ref_count == 0 ); 00485 nxt = cmd->next; 00486 cmd->next = free_commands; 00487 free_commands = cmd; 00488 return nxt; 00489 } 00490 00491 void cis_turf( IOFUNC_ATTR_T *handle, char *format, ... ) { 00492 va_list arglist; 00493 command_out_t *cmd; 00494 int nb; 00495 00496 assert( handle != NULL ); 00497 cmd = handle->last_cmd; 00498 va_start( arglist, format ); 00499 nb = vsnprintf( cmd->command, CMD_MAX_COMMAND_OUT, format, arglist ); 00500 va_end( arglist ); 00501 if ( nb >= CMD_MAX_COMMAND_OUT ) { 00502 nl_error( 2, "Output buffer overflow to node %s", handle->nodename ); 00503 cmd->command[0] = '\0'; 00504 } else { 00505 cmd->cmdlen = nb; 00506 cmd->next = handle->last_cmd = new_command(); 00507 // Now run the queue of blocked clients: 00508 while ( handle->blocked ) { 00509 IOFUNC_OCB_T *ocb = handle->blocked; 00510 handle->blocked = ocb->next_ocb; 00511 ocb->next_ocb = NULL; 00512 assert(ocb->hdr.offset == 0); 00513 read_reply(ocb); 00514 } 00515 // Then notify non-blocking clients: 00516 if (IOFUNC_NOTIFY_INPUT_CHECK(handle->notify, 1, 0)) 00517 iofunc_notify_trigger(handle->notify, 1, IOFUNC_NOTIFY_INPUT); 00518 } 00519 } 00520 00521 /* 00522 =Name ci_server(): Command Server main loop 00523 =Subject Command Server and Client 00524 00525 =Synopsis 00526 00527 #include "tm.h" 00528 void ci_server(void); 00529 00530 =Description 00531 00532 ci_server() does all the work for a command server. It does 00533 not return until =cmd_batch=() returns a CMDREP_QUIT or it receives 00534 a CMDINTERP_QUIT message.<P> 00535 00536 It registers the CMDINTERP_NAME with the operating system, then 00537 loops to Receive messages. For each received command, ci_server() 00538 calls =cmd_init=() and =cmd_batch=(). If needed, a hook could 00539 be added to Receive other messages. 00540 00541 =Returns 00542 Nothing. 00543 00544 =SeeAlso 00545 =Command Server and Client= functions. 00546 00547 =End 00548 00549 =Name cmd_batch(): Parse a command in batch mode 00550 =Subject Command Server and Client 00551 =Name cmd_init(): Initialize command parser 00552 =Subject Command Server and Client 00553 =Synopsis 00554 00555 #include "cmdalgo.h" 00556 int cmd_batch( char *cmd, int test ); 00557 void cmd_init( void ); 00558 void cis_initialize(void); 00559 void cis_terminate(void); 00560 void cis_interfaces(void); 00561 00562 =Description 00563 00564 These functions are all generated by CMDGEN. cmd_init() 00565 resets the parser to its start state. cmd_batch() provides an 00566 input string that the parser will act on. If test is non-zero, 00567 no actions associated with the command will be executed. 00568 cis_initialize() is called at the very beginning of ci_server(), 00569 and cis_terminate() is called at the very end. These are used 00570 by cmdgen and soldrv to handle proxy-based commands in QNX4. 00571 It remains to be seen if these will be used in QNX6. 00572 cis_interfaces() is called from ci_server() to initialize 00573 reader interfaces in QNX6. 00574 00575 =Returns 00576 00577 cmd_batch() returns the same error codes as =ci_sendcmd=(). 00578 00579 =SeeAlso 00580 00581 =Command Server and Client= functions. 00582 00583 =End 00584 00585 */