Go to the previous, next section.

# ILU Customization

## Introduction

ILU includes a number of internal interfaces that allow various functionality of the ILU kernel library to be replaced by user functionality.

- In a single-threaded application, the entire "main loop" can be replaced; in a threaded application, features of the thread system can be described to the ILU kernel so that the kernel will use them.
- New messaging protocols and inter-address-space transport mechanisms can be registered for use by an application.
- Object incarnation routines can be registered for `kernel servers', so that true instances can be created on reference, rather than before reference.
- URL parsing routines can be registered for various URL schemes, so that application-specific object URLs can be used.
- New identity schemes can be defined and registered with the kernel to support application-specific authorization and accounting schemes.
- Handler routines can be registered to deal with ``malloc()`` failures in application-specific ways.
- Handler routines can be registered to perform application-specific actions when errors are signalled.
- If debugging is enabled at configuration time, specific sets of debugging messages can be selected at run time, and an application routine can be defined to intercept and handle debugging messages.

Most of these interfaces are defined in two ILU source files, ``ILUSRC/runtime/kernel/iluxport.h'` and ``ILUSRC/runtime/kernel/iluntrnl.h'`. We will not attempt to duplicate that documentation here, to avoid the inevitable errors when documentation is provided in two different forms; rather, this section of the manual will provide sketches of the interfaces, and refer the reader to the appropriate header files. For any discrepancies noted between the material here, and the material in the header files, the header files should be assumed to the `truth'.

## Event Loops and Threads

Every ILU address space uses either real threads, or some sort of event dispatching loop to simulate threads. ILU is thread-safe internally, and by default will continue to check its usage for even when event dispatching is used. Since many different thread systems and event dispatching loops exist, ILU provides interfaces to allow the user to describe the particular one that they're using to the ILU kernel. See section section Threads and Event Loops for more information on these topics. See ``ILUSRC/runtime/kernel/iluxport.h'` for documentation of the interfaces.

## RPC Protocols and Data Transport Mechanisms

The ILU remote procedure call mechanism operates in layers.

- The application, at the highest layer, calls down into language-specific, interface-specific stub code, passing language-specific, method-specific values;
- that stub code calls down into an interface-independent language-specific runtime layer, passing method-independent, but language-specific values;
- that LSR calls down into language-independent, interface-independent ILU `kernel' code, passing simple C values;
- the kernel calls into the code for a specific RPC protocol, such as ONC RPC, passing C values;
- the protocol code calls down into transport-mechanism code, passing `messages' as opaque byte sequences;
- the transport code, which may consist of several layers, may perform various transformations on the messages, and finally conveys them to the peer transport layers.

ILU includes registration mechanisms to allow applications to add to the kinds of RPC protocols and data transport mechanisms that can be used.

### RPC Protocols

Each ``ilu_Protocol`` object reflects a particular mapping of the abstract ILU RPC protocol onto a specific externally-defined RPC protocol. (See section Protocols and Transports for a discussion of the abstract ILU protocol, and how it is mapped to the ONC RPC protocol, and to the XNS Courier protocol.)

New RPC message protocols can be added to the ILU kernel by writing a new ``ilu_Protocol`` object, and calling the ILU kernel function ``ilu_RegisterProtocol()``. register it. The structure and requirements of an ``ilu_Protocol`` object are defined in the file ``ILUSRC/runtime/kernel/iluntrnl.h'`; the methods of the protocol are considered to be `inside' the ILU kernel, and must therefore conform to all ILU locking and error conventions. The locking conventions are discussed in ``ILUSRC/runtime/kernel/iluxport.h'`; the error conventions are documented in @br ``ILUSRC/runtime/kernel/iluerror.h'`, and pre-defined errors are documented in @br ``ILUSRC/runtime/kernel/iluerrs.h'`.

Various examples of ILU protocols are available for study:

- the ONC RPC mapping (``ILUSRC/runtime/kernel/sunrpc.c'`
- the XNS Courier protocol (``ILUSRC/runtime/kernel/courier.c'`)
- the OMG Internet Inter-Orb Protocol (``ILUSRC/runtime/kernel/iiop.c'`)

### Transport Filters

In general, ILU protocols form `messages' consisting of sequences of bytes, which are then passed to the ILU transport layer to be conveyed to another address space. The transport layer itself is composed of one or more ILU transport filters, each of which handles the message in turn. These filters are either communication filters, such as the filters which actually convey messages via TCP/IP or UDP/IP, or transformation filters, which alter the message and pass it to another transport filter, such as the ONC RPC record-marking filter, or the secure transport filter.

Each transport filter is either reliable or unreliable. All transformation filters are reliable; communication filters may or may not be reliable. A communication filter is reliable if it guarantees that any messages handed to it for transport will be reliably delivered to the other end of the communication connection. This in turn means that the communication mechanism used by the transport will take care of timeouts, retries, etc., internally, so that the ILU application need not worry about these itself. Unreliable communication filters are those which may require ILU participation in timeout and resending of messages to achieve reliable delivery.

Each filter is also either boundaried or non-boundaried. Boundaried filters are those which can comprehend and preserve message boundaries. Non-boundaried filters simply deal in chunks of bytes and have no way to recognize or preserve message boundaries. Various protocols and filters may have requirements as to whether the next filter below it in the communication stack is boundaried or non-boundaried.

New transport filters may be registered with the ILU kernel by calling the kernel function ``ilu_RegisterTransport()``, described in ``ILU/runtime/kernel/iluxport.h'`, with the name of a new transport filter and the address of a routine which returns an instance of the new transport object type. Implementing a new transport object type actually consists of implementing several related object types, including ``ilu_TransportCreator``, ``ilu_TransportClass``, and ``ilu_Mooring``. These object types are defined in the file ``ILUSRC/runtime/kernel/iluntrnl.h'`.

As with protocols, the methods of the transport filter are considered to be `inside' the ILU kernel, and must therefore conform to all ILU locking and error conventions. The locking conventions are discussed in ``ILUSRC/runtime/kernel/iluxport.h'`; the error conventions are documented in ``ILUSRC/runtime/kernel/iluerror.h'`, and pre-defined errors are documented in ``ILUSRC/runtime/kernel/iluerrs.h'`.

Examples of transformation filters may be found in ``ILUSRC/runtime/kernel/sunrpcrm.c'`, which is a boundaried filter implementing ONC RPC's TCP/IP record marking scheme, and ``ILUSRC/runtime/kernel/security.c'`, which is a non-boundaried filter implementing message integrity, sender authentication, and message privacy. Examples of communication filters may be found in ``ILUSRC/runtime/kernel/newtcp.c'`, which is a non-boundaried reliable filter implementing data communication via TCP/IP, ``ILUSRC/runtime/kernel/udp.c'`, which is a non-boundaried unreliable filter implementing data communication via UDP/IP, and ``ILUSRC/runtime/kernel/inmem.c'`, which is a boundaried reliable filter implementing intra-address-space communication via memory buffers.

## Object Incarnation Procedures

ILU true objects live in kernel servers, a kernel data structure that handles communication and other aspects of the object implementation. When an object reference is received from another address space, the kernel server is responsible for mapping this reference to an actual object. Normally, the kernel server simply consults an internal hash table for an object corresponding to a specified `instance handle'; however, an application may register an application-specific callback function to be used instead. This allows on-the-fly creation of objects, which is often vital when handling many objects. Actual in-memory representations of the objects can be garbage-collected, then dynamically re-incarnated when needed by a client.

The application registers this functionality by creating an implementation of an ``ilu_ObjectTable`` object, and passing that implementation as a parameter to ``ilu_CreateTrueServer`` when creating the kernel server. Typically, ``ilu_CreateTrueServer`` is called directly only by a language-specific runtime; the actual application would work with object tables via whatever mechanism is exported by the language-specific runtime. Check the documentation for your particular language runtime for more information.

## Object URLs

It is sometimes useful to define application-specific URL syntax which carries some of the data implicitly. For instance, you might want to define a URL scheme for the OMG CORBA `IIOP` something like

`iiop_1_0://<hostname>.<port>/<object-key>`

with an implicit object type of `IDL://omg.org/CORBA.Object:1.0`. An application can register a parser for an application-specific URL scheme with the ILU kernel by calling the function ``ilu_RegisterSBHParser``.

## Identity Types

As discussed in @xref{Security and ILU}, application-specific identity types can be registered with the ILU kernel for use with various authorization and accounting schemes, and to support various forms of security in wire protocols and transports. An application does this by creating a new value of type `ilu_IdentityType`, as specified in ``ILUSRC/runtime/kernel/iluxport.h'`, and calling the kernel function `ilu_RegisterIdentityType`. After this is done, values of the new identity types may be used. The major use for these identity types is to work together with new application-specific RPC protocols and message transports (described above), to implement various security and access policies for distributed systems.

Note that the mere act of registering a new identity type with the ILU kernel will not cause values of that identity type to be automatically transmitted in ILU calls. This will only happen if an appropriately designed transport or protocol, which knows to do this, is also used. For experimental purposes, we provide a switch will will cause the various flavors of `Sun RPC` implemented for ILU to automatically pass one specific identity type. To enable this, set the environment variable `ILU_SUNRPC_PREFERRED_IDENTITY` to the name of the identity type to be passed automatically before running any of your ILU programs.

By default, the `Sun RPC` protocols will automatically pass the UNIX identification of the caller (the user id, group id, host IP address, and list of groups to which the caller belongs). The identity type for this information is called `"SunRPCAuthUnixIdentity"`. To prevent its being passed automatically, set the environment variable `ILU_NO_SUNRPC_UNIX_AUTH` to any value before running your ILU programs.

## Malloc Failure Recovery

ILU uses a number of internal interfaces to allocate and free memory, such as ``ilu_malloc()``, ``ilu_free()``, and ``ilu_realloc()``. These functions wrap calls to the standard ``malloc()``, etc., in wrappers that allow for better error handling. They are documented in

``ILUSRC/runtime/kernel/iluxport.h'`.

Applications can register callback functions to handle `malloc` failures, in two ways. The kernel function ``ilu_AddFreer()`` allows registration of routines which can be called to free up memory, to allow a `malloc` call to succeed. The kernel functions ``ilu_SetMemFaultAction()`` and ``ilu_SetMemFaultConsumer()`` allow applications to determine what action should be taken if a `malloc` failure occurs.

## Error Reporting

ILU includes a comprehensive error-signalling system in the kernel library, which is documented in ``ILUSRC/runtime/kernel/iluerror.h'`. In addition, the kernel library contains many calls to ``_ilu_Assert``, which check that various kernel invariants are maintained. When a runtime assertion fails, the kernel may either call an application-specified failure handler, set by a call to ``ilu_SetAssertionFailureConsumer()``, or take one of three default actions, chosen by a call on ``ilu_SetAssertionFailureAction()``. The three default actions are (1) to generate an illegal instruction trap, and thus coredump; (2) to exit with some error code; and (3) to enter an endless loop, calling ``sleep()`` repeatedly. The third action is the default action; the intent is to stop the program with all invalid data intact on the stack, and network connections intact, so that a debugger may attach to the `live' process.

## Debugging Interfaces

The ILU kernel contains a large number of debugging print statements, which document various things going on inside the kernel. The specific things printed may be controlled by calling either ``ilu_SetDebugLevel()`` or ``ilu_SetDebugLevelViaString()``. The specific bits which can be specified to ``ilu_SetDebugLevel()``, or names which can be specified to ``ilu_SetDebugLevelViaString()``, are documented ``ILUSRC/runtime/kernel/iludebug.h'`.

All debugging messages are displayed via calls to the kernel function ``ilu_DebugPrintf()``. Normally, this routine simply calls `vfprintf (stderr, ...)` to actually output the messages. However, this can be changed to call some application-specific message output system by calling ``ilu_SetDebugMessageHandler()``, documented in ``ILUSRC/runtime/kernel/iluxport.h'`. Two special values are defined for, and accepted by, ``ilu_SetDebugMessageHandler()``; the value ``ILU_DEFAULT_DEBUG_MESSAGE_HANDLER`` causes the debug system to revert to the original output handler; the value ``ILU_NIL_DEBUG_MESSAGE_HANDLER`` causes the debug system to simply discard any debugging messages.

Debugging output can be directed to a file, by calling ``ilu_SendDebugOutputToFile()`` with a filename as an argument. The file will be created, and debugging messages will be written to it.

Go to the previous, next section.