Vulcan - OSRI Gateway

From Jim Starkey on the Firebird Development List 29th February 2004

Not one to shrug off a challenge, I wrote a quickie OSRI gateway provider to demonstrate the mechanism. I tested it against the Firebird 1.5 RC7 installed on my notebook. The actual Gateway class would use/used about six lines for make providers for Firebird 1.0, Interbase V6, and probably earlier. Here are representive pieces of code. I'm sure mail will screw up the formatting. The code itself is (or will shortly be) available in the Vulcan tree under src/gateway.

First the provider prolog. The Gateway constructor takes the name of the target library. The ExternalLibrary class will tack on ".so" or ".dll" if necessary and appropriate. The subsystems vector indentifies the provider objects to Dispatch in the Y-valve. The function "getSubsystems" is called by Dispatch to get the vector.

static Gateway provider ("fbclient");
static Subsystem* subsystems [] = { &provider, NULL };
extern "C" Subsystem **getSubsystems() { return subsystems; };

Here is the attachDatabase method, which is typical of all entrypoints. The magic is in the ENTRY macro, described below. The macro takes the name of the function and the functions signature, does any necessary symbol lookups, and if it finds the entrypoint casts the address to a function of that signature. The function actually follows the ENTRY macro. If the entrypoint isn't found, control falls through to entrypointUnavailable.

ISC_STATUS Gateway::attachDatabase(ISC_STATUS* userStatus,
     SSHORT file_length, const TEXT* fileName,
     DbHandle *dbHandle,
     SSHORT dpb_length, UCHAR* dpb,
     const char *providerName)
  {
  ENTRY (isc_attach_database, (ISC_STATUS*, int, const char*, void*, int, const UCHAR*))
  (userStatus, file_length, fileName, dbHandle, dpb_length, dpb);
  return entrypointUnavailable (userStatus);
  }

Here is the definition of the macro. I generally discourage macros, but this one saves writing the function name 5 times among other benefits.

#define ENTRY(fn,sig) \
if (!entrypoints[fn]) entrypoints[fn] =
externalLibrary->getEntryPoint (#fn); \
if (entrypoints[fn]) return ((ISC_STATUS (__stdcall *)sig)
entrypoints[fn])

The final piece of the puzzle is the configuration file. Here is a special "vulcan.conf" I wrote for the gateway test directory. Perhaps a production environment would want to map all databases to the QLI help database, but hey, this is just a proof of concept:

<database *>
  filename $(root)/help/help.fdb
  provider gateway
</database>

<provider gateway>
  library $(root)/bin/gateway
</provider>

include $(root)/client.conf

And, yes, it does work.