Cm : A multitask communication package.
Version v7r9
Christian ARNAULT
Pierre MASSART
LAL - Orsay, France. arnault@lal.in2p3.fr
July 9, 1999





Presentation.

The Cm package is an attempt to make simple and system independant the task-to-task communication problem. It covers the communication between tasks that operate on different operating systems, different architectures by hiding every detail of the use of TCPIP on which it is based.

These characteristics are quite important when one considers the domain of control-command in a real time environment, and in a more general meaning when one deals with distributed applications ans informations, the cooperative behaviour of applications permits to obtain a quite modular and structured architecture in the system design.



The internal protocol managed by CmMessage objects.

In order to maintain the global consistency of messages a framing format made of a prefix and a suffix is managed transparently for each CmMessage data. This protocol permits in particular to understand the global enveloppe of the CmMessage data, for detection of contiguous messages or for guaranteeing the completion of the data transfer for each CmMessage.

This mechanism makes the detection possible on message basis instead of on physical frame basis. Thus, a level of handlers is provided for CmMessages based on message types and specialized handlers are automatically triggered at each individual CmMessage reception.



CmMessage building and sending.

A CmMessage aimed at being sent is not associated to an existing connection (and to the CmConnect object). Instead, the CmMessage will be built (accumulating data in it) and then sent to some destination, or possibly several destinations (each of those having a dedicated CmConnect object transparently managed).

A CmMessage object must first be created using the CmMessageNew function that prepares it to act as an extensible structure for typed values such as numeric values, texts, arrays etc...

Several specialized functions are provided by Cm in order to construct the CmMessage structure dynamically:

The last two functions install arrays of values within the CmMessage structure either by copying it directly into the CmMessage data or by rather installing a description for it, avoiding the physical duplication of the array data. In this case, the array data is actually accessed only when the CmMessage is transfered. Arrays elements may be of any one of the simple types managed as single items and are converted the same way as simple items.

In both cases, the array is available at reception side the same way.

The conversion mechanism (using the Cvt converter object) takes care of byte ordering and word alignment according to the receiving machine. Control informations are installed along the CmMessage structure for guaranteeing the data integrity and to control the semantic of the data items while retrieving data from a received CmMessage.

Each CmMessage object may be given a type through the CmMessageSetType function. The type is provided as a free character string, and is meant to be used at the reception side to trigger dedicated handlers.

The function CmMessageSend is then used to send the CmMessage object to an application (specified by its name). This function takes care of the global message formatting, creates (or reuse) a CmConnect object and send the CmMessage object without any acknowledge manipulation.

The CmMessageSend operation completes in two phases: it first posts the message and then waits for its tranfer until it is finished. The CmMessageWait function is used internally for the second phase.

It is possible to control manually the two phases by using the CmMessagePost function which does not wait for the entire transmisson of the message. The use of this function must be done quite carefully since, for instance the CmMessage object is set in a special state (Sending) while packets are sent. While it is in this state, the CmMessage object cannot be used (Cm takes care of the internal protections) and for instance, trying to Put any item in a CmMessage while it is in this state has no effect on its internal state.

The CmMessagePost function receives as an argument a termination handler (which has the same syntax as the handlers declared for receiving messages) that will be called when the last packet of the CmMessage has been successfully sent, or if the connection is lost.

Each CmMessage object is controled internally by a finite set of possible states that are checked upon at each CmMessage operation. Actions are then either performed (yielding possibly a state transition) or ignored if the corresponding transition is forbidden. The following diagram shows the set of possible states and transitions permitted to a CmMessage object:


           +--------+
           |  Dead  |
           +--------+
             |   ^
         New |   |
             |   | Delete
             v   |
           +--------+
 +-------->|  New   |<--------+ 
 |         +--------+         | 
 |           |                | 
 |    PutXxx |   +----<---+   | 
 ^           |   |        |   | 
 |           v   v        |   | 
 | Reset +------------+   |   | 
 |---<---|  NotEmpty  |   ^   | 
 |       +------------+   |   | 
 |           |   |        |   ^ 
 |      Send |   | PutXxx |   | 
 ^           |   +---->---+   | 
 |           v                | 
 | Reset  +----------+        | 
 +---<----|  Closed  |        | 
          +----------+        | 
             |   ^  |         | 
             |   |  | PutXxx  | 
        Send |   |  +---->----+ 
             |   |
             v   |
          +----------+         
          | Sending  |         
          +----------+         
             |   ^             
        Send |   |             
             +---+             
            
Fig.1 State diagram for the CmMessage objects.

Some explanation will help understanding this diagram:

The following example shows the steps of a CmMessage object building and how it is sent to applications with a type:


/* File example6.c */

 #include <stdio.h>
 #include <CmMessage.h>

 void build_an_image (char** pixels, int* size)
 {
   static char image[100];
    
   *pixels = image;
   *size = sizeof(image);
 }

 main()
 {
   CmMessage message;
   int images = 3;
   int image;
   char* pixels;
   int imageSize;

   if (!CmMessageOpenServer ("Toto"))
   {
     fprintf (stderr, "Declaration error.\n");
     return (0);
   }

   message = CmMessageNew ();

   CmMessagePutText (message, "Hello you");
   CmMessagePutText (message, "I send you");
   CmMessagePutInt  (message, images);
   CmMessagePutText (message, "images : ");
   for (image = 0; image < images; image++)
   {
     /* something to build an image... */
     build_an_image (&pixels, &imageSize);
     CmMessagePutBytes (message, pixels, imageSize);
   }

   CmMessageSetType (message, "Image");

   CmMessageSend (message, "Client1");
   CmMessageSend (message, "Client2");
   CmMessageSend (message, "Client3");
 }
                
Fig.2 Building and sending a CmMessage object.


Receiving a CmMessage.

CmMessage objects are received by type-dedicated handlers managed by the basic Cm engine.

The detection and assembly of individual message frames are performed internally and handlers are triggered when each complete message is available with an automatic detection of message types.

Handlers are installed using the CmMessageInstallHandler when they are associated with one particular type or using the CmMessageInstallDefaultHandler for handling unforeseen message type occurence.

The CmMessage data items are retrieved from it using the following functions:

One should notice that retrieving arrays is done the same way whether they have been produced as embedded or external arrays. In both situations, data are received within the message frame.

The CmMessageGetType permits in addition to get the actual type of the received message (this feature is likely to be usefull in a default handler).

It is also possible to enquire the CmMessage object about the type of the next available item while retrieving the data, by using the CmMessageGetItemType function. The result of this function may be:

The first value, CmMessageItemTail means that the message's tail is met and no further data item is available. It is then useless (though harmless) to keep on getting more data items.

On the other hand, trying to retrieve a data item with the wrong type yields an error message, and results in skipping the current item. Therefore, in case the type of each data item is not statically known in the handler's context, the use of CmMessageGetItemType is strongly encouraged.

An example of an application receiving messages from the one in the previous example. One uses here a dedicated handler that takes care selectively of the messages typed "Image":


 /* File example7.c */

 #include <stdio.h>
 #include <CmMessage.h<

 CmMessageStatus my_image_handler (CmMessage message, char* sender,
                                   char* serverName);

 void show_an_image (char* pixels, int size)
 {
 }

 main()
 {
   if (!CmMessageOpenServer ("Client1"))
   {
     fprintf (stderr, "Declaration error.\n");
     return (0);
   }

   CmMessageInstallHandler (my_image_handler, "Image");

   CmMessageWait ();
 }

 /* (to be continued) ... */
                
Fig.3


 /* ... File example7.c (continued) */

 CmMessageStatus my_image_handler (CmMessage message, char* sender,
                                   char* serverName)
 {
   char* text;
   int images;
   int image;
   char* pixels;
   int imageSize;

   text = CmMessageGetText  (message);
   text = CmMessageGetText  (message);
   images = CmMessageGetInt (message);
   text = CmMessageGetText  (message);

   for (image = 0; image < images; image++)
   {
     pixels = CmMessageGetBytes (message, &imageSize);
     /* something to use this image... */
     show_an_image (pixels, imageSize);
   }

   return (CmMessageok);
 }
                
Fig.1 Receiving CmMessage objects with a dedicated handler.


 /* File example8.c */

 #include <stdio.h>
 #include <CmMessage.h>

 CmMessageStatus my_image_handler (CmMessage message, char* sender,
                                   char* serverName);

 void show_an_image (char* pixels, int size)
 {
 }

 main()
 {
   if (!CmMessageOpenServer ("Client1"))
   {
     fprintf (stderr, "Declaration error.\n");
     return (0);
   }

   CmMessageInstallHandler (my_image_handler, "Image");

   for (;;)
   {
     CmMessageCheck ();

     /*
        Here come actions to be executed repeatedly 
        with no explicit wait for messages.
         The CmMessageCheck will only detects them and
        activate the appropriate handlers.
        ...
     */
   }
 }

 /* (to be continued) ... */
                
Fig.1


 /* ... File example8.c (continued) */

 CmMessageStatus my_image_handler (CmMessage message, char* sender,
                                   char* serverName)
 {
   char* text;
   int images;
   int image;
   char* pixels;
   int imageSize;

   text = CmMessageGetText  (message);
   text = CmMessageGetText  (message);
   images = CmMessageGetInt (message);
   text = CmMessageGetText  (message);

   for (image = 0; image < images; image++)
   {
     pixels = CmMessageGetBytes (message, &imageSize);
     /* something to use this image... */
     show_an_image (pixels, imageSize);
   }

   return (CmMessageOk);
 }
                
Fig.1 Non blocking detection of CmMessage objects.

Transaction management in Cm.

Cm is mainly designed to be operated asynchronously, and in an event driven non blocking mode. This in particular means that protocols installed between applications should not be based on a blocking wait for a dedicated answer.

However logic of the applications often require that a specific answer is expected after having send a request. Cm provides an optional transaction based mechanism to implement such a logic.





Description

A typical scenario showing how to use the Cm transaction could be:

  1. A transaction is first opened, creating a unique identifier for it, using the CmOpenTransaction function.
  2. This identifier is installed (by the user) in the message containing the request (thus the protocol is increased to include the transaction id).
  3. The request is sent and the requestor enters a conventional wait loop (typically using the CmMessageWait function)
  4. The application receiving the request is expected to answer is a finite amount of time. When the answer is sent back, it now includes the transaction id. Notice that the complete answer may include several messages, only the very last one has to include the returned transaction id.
  5. The request sender eventually receives the answer, and among it the message containing the transaction id. It then terminates the transaction using the CmTerminateTransaction function (from within the massage handler). (Notice that in this case the handler will return CmMessageOk and that the traditional CmMessageBreak should not be used any longer).
  6. The main wait loop is then stopped and the requestor can now close the transaction (using the CmCloseTransaction function). This last operation releases the allocated transaction id.



An example

An example of how to implement this scenario includes three code sections:

  1. The request sending, the wait for the anser and the transaction cleanup,

    
    int id;
    CmMessage request;
    
    id = CmOpenTransaction ("my request", NULL);
    
    ... ( now building the request )
    
    CmMessagePutInt (request, id);
    CmMessageSend (request, "server");
    
    while (!CmIsTransactionTerminated (id))
      {
        CmMessageWait ();
      }
    
    CmCloseTransaction (id);
                    
    Fig.1 Sending a request with a transaction id
  2. The handler in the receiver application used to manage the request, compose and send the answer,

    
    CmMessageStatus request_handler (CmMessage message, ...)
    {
      int id;
      CmMessage answer;
    
      ... ( getting the request contents )
    
      id = CmMessageGetInt (message);
    
      ... ( working with the request )
      ... ( and building the answer )
    
      CmMessagePutInt (answer, id);
      CmMessageSend (answer, sender);
      ...
    
      return (CmMessageOk);
    }
                    
    Fig.1 Building up an answer to a request with a transaction id
  3. The handler in the requestor used to receive the request.

    
    CmMessageStatus answer_handler (CmMessage message, ...)
    {
      int id;
    
      ... ( getting the answer contents )
    
      id = CmMessageGetInt (message);
    
      CmTerminateTransaction (id);
      ...
      return (CmMessageOk);
    }
                    
    Fig.1

One should notice the differences between this mechanism and the traditional use of a return (CmMessageBreak) used in the receiving handler:



Remote debugging facilities for transactions

Cm provides internal debugging facilities for transactions. Through sending a dedicated Cm messages, it is possible to



Programming interface

The following entry points are provided so as to manipulate transactions:

int CmOpenTransaction (const char* info, void* user_object) This function opens a new transaction, allocating a free identifier for it. A free informational character string can be provided as well as a reference to a user object. This reference can be retrieved at any time using the CmGetTransactionObject function.
void CmTerminateTransaction (int id) This function terminates an opened transaction. The main effect of terminating a transaction is to stop the current waiting loop.
void CmCloseTransaction (int id) This function definitively closes an existing transaction. The identifier will be released (and can be reused for another transaction).
int CmIsTransactionTerminated (int id) This function tests whether the referenced transaction is terminated.
void* CmGetTransactionObject (int id) This function retrieves the reference to an object associated with the specified transaction.
char* CmGetTransactionInfo (int id) This function retrieves the informational character string associated with the specified transaction.
CmTransactionStatus CmCloseTransaction (int id) Definitively close a transaction, releasing its identifier. No other operation are permitted on this transaction.

Possible values for the returned CmTransactionStatus are:

  • CmTransactionOk
  • CmTransactionNotFound
  • CmTransactionAlreadyClosed
CmTransactionStatus CmTerminateTransaction (int id) Terminate an opened transaction. This operation generally results in stopping the current wait loop, marking the transaction as terminated(/i>. The transaction may then be either restarted or stopped.
CmTransactionStatus CmRestartTransaction (int id) Restart a transaction for a next occurence of the event associated with it.
CmTransactionState CmGetTransactionState (int id) Returns the current state of the referenced transaction. Possible values are:
  • CmTransactionClosed
  • CmTransactionPending
  • CmTransactionTerminated



Handling errors in Cm.

Two mechanisms permit a user to detect misfunctions while using Cm. Firstly, most (if not all!) Cm entry points return a value which can be either a requested object (such as CmMessageNew) or a status value (such as CmMessageSend or CmMessageWait). Whenever a failure in doing the required operation occurs, the return value would be NULL (for returned objects) or 0 (zero) (for status values). The user should therefore test upon these returned values in order to avoid continuing an illegal Cm sequence. However, internal states are maintained and checked upon for every Cm object in order to keep some safety level.

Secondly, internally detected errors that would not be directly translated into returned values often produce error messages that are sent by default onto stderr. A user defined printer operator, syntacticly similar to the standard vprintf function (use man vprintf in order to get the exact and complete syntax for it) may be declared to Cm with the CmMessageInstallPrinter function. Typical usages for such printer operators are for putting error messages into log-files or displaying them into graphical windows.



The NameServer.

The NameServer is a special application (built with Cm) aimed at managing associations between logical names chosen by the Cm applications and their physical addresses (Internet host addresses and port numbers).

Each such address must be unique on the network, therefore, one of roles of the NameServer is to allocate one dedicated port number per application running on a given machine.

The mechanism used to ensure the correct allocation consists in the selection of a value within a specified range for the following entities:

One sees that the set of this four values is enough to specify a complete Cm domain or environment able to manage a coherent set of Cm servers, and opaque to any other Cm domain as far as there is no overlap on the specified address ranges. Within each such domain the server names should be unique, while a given name may exist in two different Cm domains.

The domains that correspond to an individual set of configuration parameters are described within one dedicated text file named $CMROOT/mgr/CmDomains. However, no mechanism or tool is provided yet to ensure that the definitions are consistent and yield no overlap. The greatest care is thus required by system managers while defining the address ranges in this database. Nevertheless, the NameServer applications that manage each individual domain are automatically configured from this database, giving some security level.

The format for this file is described as follows:

An application (corresponding to one executable object) may declare several servers. The behaviour is strictly equivalent then as if the servers would be handled each by one application. The port numbers are allocated individually as well. Clients for both servers should never notice that they are physically installed in the same space.




Document structure.

This document may be read at different levels, whether one merely wants to use Cm or if expertise is required in order to understand the detailed internal mechanisms.

So for a first approach the specification section, the section describing the CmMessage class and the one describing how to build a Cm application could be sufficient.

Then the CmConnect class description introduces some more internal mechanisms and permits to understand the detailed behaviour of the CmMessage objects.

The section on package installation and reconstruction is needed for the system manager who will install Cm, in particular for understanding how to configure the environment.

Lastly, a section is reserved for implementation details and other technical bits. Experienced programmers who want to work on additional layers on top on Cm (for instance) will require it.

Examples are provided at many places in the document. They all correspond to a real C file in the package distribution (the file name is mentionned with each example) and they may be compiled and linked to exercize Cm.

All possible remarks and suggestions either on the package (such as bugs, installation or behaviour problems) or on this document itself are welcome and will be addressed to the author:

Christian Arnault

email: arnault@lal.in2p3.fr



The query operations for the NameServer.

A set of requests may be sent to the NameServer to get informations on the connections it is managing or to ask it to perform some operations.

Each of those requests has to be sent under the form of a CmMessage sent to the NameServer, with dedicated types for each kind of request. When needed request parameters are installed as the CmMessage contents. An answer is always returned to the caller, in the form of CmMessages with corresponding types, so that an application will declare dedicated handlers being in charge of understanding the result of the requests.

On another hand, the prebuilt application cm (shipped in the distribution kit) exploits this functionality and allows to interactively interrogate the NameServer using a shell command as follows:


Unix> cm 
                
Fig.1

The various requests understood by the NameServer can be summarized in the following table. Each request is described with the message type used and the data items provided. Answers are described with the message type and the data items returned.

Request Parameters Answer
NSGetAddress {text name;} NSAddress {text original-name; int port; text new-name;}
NSGetPort NSPort {text name; int port;}
NSGetNames {text reg-expr;} NSNames {text name; ...}
NSStop NSStopped
NSRestart NSStopped
NSGetConnects NSConnects {text name; text host; int port;}
NSGetPorts NSPorts {int port; ...}
NSSetPeriod {int period;} NSPeriod {int new-value;}
NSGetPeriod NSGetPeriod {int value;}

The NSGetAddress and NSGetPort are meant for internal use only. A dedicated handler is declared within Cm and thus should never be overriden whithout unexpected results.


Data persistency in the NameServer.

Each NameServer activation maintains within dedicated files the whole set of informations on connections it manages. This permits in particular to restart it after it stopped (either on explicit user's request or due to a crash!!) while rebuilding the complete knowledge of active connections.

The connections between applications are not killed by a NameServer interruption, only new connections are impossible (and of course requests to NameServer). Eventually, when the NameServer is restarted, reconnection by the applications is performed transparently.

Thie database is maintained in a directory named with the domain name and installed in a root directory specified in the domain configuration file.

Each connection is stored in a specific file named with connection's name.



Activating the NameServer.

The NameServer application must be activated with the entire set of values that defines one Cm domain. Domains available to a site where Cm is installed are all specified in a single textual file:

$CMROOT/mgr/CmDomains

which contains one individual line per domain with the following format:

The Cm distribution kit provides a tool (a Unix shell script located in $CMROOT/mgr/NameServer.start) that, in addition to performing some checks about the effectiveness of the current domain definitions (although these checks are also performed within the NameServer itself) would handle an automatic restart mechanism of the NameServer when it fails for strange reasons, guaranteeing a permanent life. This script is able to understand the various conditions that may occur for a termination of the NameServer, allowing to actually stop it when this is required by the manager, or when a non-recoverable error is detected (such as a bad environment definition or a failure to bind the TCPIP port).

It is important to understand that manually launching the NameServer executable (i.e. without using the dedicated script) is generally a non-safe operation since this may not be done within the proper directory, or on the appropriate machine. The result of doing this is therefore unspecified.

An example of a typical Unix session where the NameServer is first activated then stopped (by a request) and lastly restarted is shown as follows:


   >##   One first selects a domain from the set specified
   >##   in the $CMROOT/mgr/CmDomains file.

   > more $CMROOT/mgr/CmDomains
   LAL     as2.lal.in2p3.fr 22000 22001 999 $CMROOT/infos
   LALDev  as2.lal.in2p3.fr 30000 30001 999 /tmp/Cm
   > setenv CMDOMAIN LALDev
   >
   >##   Actual NameServer activation :   
   >
   > $CMROOT/mgr/NameServer.start
   >
   >##   Then some applications are activated, using this
   >##   Cm domain for a while.
   >##   ...
   >##   Then one decides to stop the NameServer.
   >
   > cm stop
   >
   >##   When the NameServer is reactivated, and as long as
   >##  the same domain is still selected, the database will be
   >##  restored, and still alive process will be re-connected to
   >##  the NameServer.
   >
   >##  Reactivating the NameServer is just a matter of
   >##  using the same shell script :
   >
   > $CMROOT/mgr/NameServer.start
   >
                
Fig.1 Controling the NameServer by the special NameServer.start script.

The special script could be installed in the system's frame for automatic daemon activation tools (rc.local or cron) but it also provides an automatic reactivation mechanism of the NameServer in case of unexpected crash.


cm send: The interactive Cm exerciser.

The cm send utility available in the Cm package provides means of building and sending any kind of message to any application.

The message description is built using arguments given to the cm send command as follows:

When a single value is given such as in:

-int 10

then a scalar value is installed in the message (using CmMessagePutInt here) and when a list of values is specified, such as in:

-int 10,11,12,16

then an array of values is installed in the message (using CmMessagePutArray here).

The following example shows how to send a request for comment to an application and receive the corresponding answer:


> cm send -to DbServerv4r3 -type CmRequestComment -handler CmComment
Message type CmComment received from DbServerv4r3
                
Fig.1 Using the Cm exerciser.



Linking Cm to XWindows.

Linking Cm to XWindows, Motif or other such style of interactive environment requires to manage the event-loop coherently in the two worlds (since both are event-driven). Special modules are provided by the Cm distribution kit to handle this question properly either in a context of pure Xt/Motif manipulation or of the OnX package.

These modules provide each a setup function CmXtSetup or CmOnxSetup that will integrate the handler activation scheme of Cm and the callbacks mechanism through a common use of the general wait function CmMessageWait.

The integration of the two environments is shown in the following example:


 /* File example10.c */

 #include <stdio.h>
 #include <Ci.h>
 #include <OKit.h>
 #include <OWidget.h>

 #include <CmMessage.h>

 Widget XtWidgetTop ();
 int OXtDispatchEvent (XEvent*);
 void CmSwitch ();

 /* A Cm handler */
 CmMessageStatus MyHandler (CmMessage message, 
                                 char* name, 
                                 char* serverName);

 /* An OnX callback */
 void SendMessage (char* to, char* text);

      
 /* (to be continued) ... */
                
Fig.1


 int main (int argc, char** argv)
 {
   if (!CmMessageOpenServer ("Toto")) return (0);

   /* Declares the Cm handler */
   CmMessageInstallDefaultHandler (MyHandler);

   /* Install Cm in the C interpretor */
   CiPathNew ("CMSRC");
   CiBindClass ("CmSwitch",     (CiRoutine) CmSwitch);

   /* Declares the OnX callback */
   CiDo ("void SendMessage (char* to, char* text);");
   CiFunctionBind ("SendMessage", (CiRoutine) SendMessage);

   /* OnX is initialized */
   OKitInitClass (argc, argv);

   /* Install the Onx dispatcher inside Cm */
   {      
     Display* display;
     Widget w;
 
     w = XtWidgetTop ();
     display = XtDisplay (w);
     CmOnxSetup (display, OXtDispatchEvent);
   }
 
   /*
     The CmMessageWait function is substituted to
     the usual OKitMainLoop function.
   */
 
   CmMessageWait ();
 
   OKitClearClass   ();
 }
 
 /* (to be continued) ... */
                
Fig.1


 /* ... File example10.c (continued) */

 CmMessageStatus MyHandler (CmMessage message, 
                            char* name, 
                            char* serverName)
 {
   char* text;
 
   text = CmMessageGetText (message);
   printf ("Received from %s : %s.\n", Name, text);

   return (CmMessageOk);
 }

 void SendMessage (char* to, char* text)
 {
   static CmMessage message = 0;
 
   if (!message) message = CmMessageNew ();

   CmMessageReset (message);
   CmMessagePutText (message, text);
   if (!CmMessageSend (message, to))
   {
     printf ("Error sending message to %s\n", to);
   }
 }
                
Fig.1 Integration of Cm and OnX.



The Cm package: use and installation.

The Cm package is distributed with the following components:

The general management of Cm (both for its building up and its usage) is handled through the methods environment. This in particular controls the way environment variables and make macros are generated.





Configuring Cm.

The system configuration for Cm is two-sided: on the one hand, some environment variables are used to access the package (sources, binaries or management tools) and on the other hand Cm domains are specified using a special environment variable. Let's first look at the configuration variables:

$CMROOT The root directory where Cm is installed. For instance, at LAL, the version v7r9 is installed in

Unix> /lal/Cm/v7r9

$CMROOT/src The directory where sources and header files are stored.
$CMROOT/mgr The directory where administration and reconstruction files (scripts and Makefile) are stored.
$CMROOT/$CMCONFIG The directory where package binaries are stored~: the various libraries and executables (NameServer and cm.exe). Usual values are:

  • $CMROOT/alpha
  • $CMROOT/HP-UX
  • $CMROOT/SunOs
  • $CMROOT/LynxOS

Then the current Cm domain is specified using the CMDOMAIN environment variable. The set of possible domains is described in the file CmDomains (installed in the management directory of Cm) and contains one line per domain with each having the format (items are separated by spaces):



Building a Cm application and using it.

The Cm application code should first get access to both the Cm functions definitions and to the Cm types. This is done in C language by including the Cm header file:


 #include <CmMessage.h> 
                
Fig.1 Using the Cm header file in an application.

The first operation installed in the application's code is to initialize one Cm personnal connection, giving the selected server's name. The important decision that must be taken at this level is whether it will be clonable or not. Making a server clonable means that its name cannot be hard-coded in any of it's client, since its name will be suffixed by a sequence number by the NameServer. Only non clonable servers can be referred to explicitely by clients. Clonable ones may only be answered on requests.

The functions:

permit one of the two possible initialisation modes of Cm servers. They all receive the server's name as their argument, and can be tested upon normal completion.

During the application's life, several servers may be activated or disactivated. Disactivation of a server is done using the function:

An old style of server activation was used in the previous versions of Cm (up to version v5rx). The corresponding entry points have been marked as 'obsolete'. The developpers are thus strongly encouraged to switch to the new style. The entry points concerned with this remark are:





Compiling and linking a Cm application.

Developping an application using the methods environment automatically provides all required connections and references to include search paths or library usage related with Cm.

This will be obtained if one specifies in the requirements file of the developped package:


use Cm v7r9

application MyApp MyApp.c
                
Fig.1 The use statement for Cm.


Running a Cm application.

The proper Cm context has to be correctly setup prior any application can be started. This consists in the definition of some environment variables (usually by executing the shell script $CMROOT/mgr/setup.csh) and launching the NameServer application (only if thhis is required and by executing the shell script $CMROOT/mgr/NameServer.start). One way to ensure the proper installation is done one could be to execute the following sequence of commands:


      Unix> source .../Cm/../mgr/setup.csh

    Check the NameServer :
      Unix> cm names
      CmNameServer
      cm_1

      (Minimal answer when NameServer is already active)

    or :
      Unix> cm names
      Cm> Impossible to reach the NameServer.

      (Answer when NameServer is unavailable)

    Start the NameServer (when needed) :
      Unix> $CMROOT/mgr/NameServer.start

    Run the application :
      Unix> toto.exe
                
Fig.1 Setting and checking the Cm context.



Specifications.

This section and all subsequent ones in this version of the document always take into account the most recent changes in the package.

Cm is meant to manage the task-to-task communications (sending or receiving messages) running on heterogeneous machines (with different architectures or operating systems) without limitations on the number of active connections (apart those induced by the operating systems).

The set of tasks (or applications) that may participate this network define a Cm domain managed by one special application - the NameServer - in charge of the physical addressing scheme, allowing several independant such domains to coexist.

An application with which a connection is requested is referenced by a name, that must be unique within one Cm domain and that doesn't need to mention anyhow the machine on which it runs, nor the transport characteristics (such as TCPIP parameters).

The central manager application NameServer is in charge of every mechanism for name registration, port number allocation and physical addressing operations transparently for the user applications.

Sending and receiving messages are managed asynchronously (without acknowledge management). This in particular implies that a special data framing protocol is added to the internal basic protocol (TCPIP) used for Cm.

The basic TCPIP protocol ensures the effective message transmission but not the arrival time nor the data packets organization (since successive message may be concatenated or split into pieces). It is therefore required to add a software layer on top of it in order to ensure the asynchronous data architecture. Cm provides this feature by the CmMessage package.

On the reception side, a callback-based mechanism is installed, so that user declared functions are triggered on message detection. This mechanism is combined with blocking (with transaction handling) or non-blocking capabilities in order to provide a rich integration scheme for interactive or real-time environments.





Porting the Cm package on a new system.

In order to be properly rebuilt and operated on a given system, one should ensure the availability of an ANSI C compiler with the socket interface environment supporting TCPIP.

The Cm package is distributed as a compressed Unix tar file comprising the sources, the manual and the various administration or reconstruction files.

Once untarred, one should have a look at the file ./Cm/v7r9/mgr/requirements. It contains definitions for various environment variables, that may need to be adjusted. Once this is done, the following sequence of action should be performed:


 Unix> cd <some root>
 Unix> ftp lalftp.in2p3.fr
 anonymous
 > cd pub/Cm
 > get methodsv3r2.tar.Z     ! if needed 
 > get CSetv2r5.tar.Z        ! if needed
 > get Cmv7r9.tar.Z
 > quit
 Unix> mkdir Cm
 Unix> cd Cm
 Unix> uncompress ../Cmv7r9.tar.Z
 Unix> tar xvf Cmv7r9.tar
 Unix> cd v7r9/mgr
 Unix> vi requirements
 ...
 Unix> vi CmDomains
 ...
 Unix> pck config
 Unix> source setup.csh
 Unix> [g]make
 Unix> setenv CMDOMAIN ...
 Unix> $CMROOT/mgr/NameServer.start &
 Unix> cm names
 ...
                
Fig.1



How to figure out the actual situation of a Cm context.

Often, one has to understand on a given machine or environment whether Cm is configured, whether the NameServer is running or not, and which domains are available or used.

The following points can be easily checked upon in order to figure out what is the current setup of Cm: