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