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