ARPDAS_QNX6 1.0
cis.cc
Go to the documentation of this file.
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 */
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines