Back: libltdl Memory Management
Forward: libltdl Dependent Libraries
 
FastBack: libltdl Dependent Libraries
Up: Using libltdl
FastForward: Portable Library Design
Top: Autoconf, Automake, and Libtool
Contents: Table of Contents
Index: Index
About: About this document

18.2.3 Module Loader


This section contains a fairly minimal libltdl based dynamic module loader that you can use as a base for your own code. It implements the same API as the simple module loader in 17.4 A Simple GNU/Linux Module Loader, and because of the way libltdl is written is able to load modules written for that loader, too. The only part of this code which is arguably more complex than the equivalent from the previous example loader, is that lt_dlinit and lt_dlexit must be called in the appropriate places. In contrast, The module search path initialisation is much simplified thanks to another relative improvement in the libltdl API:

Function: int lt_dlsetsearchpath (const char *path)
This function takes a colon separated list of directories, which will be the first directories libltdl will search when trying to locate a dynamic module.

Another new API function is used to actually load the module:

Function: lt_dlhandle lt_dlopenext (const char *filename)
This function is used in precisely the same way as lt_dlopen. However, if the search for the named module by exact match against filename fails, it will try again with a `.la' extension, and then the native shared library extension (`.sl' on HP-UX, for example).

The advantage of using lt_dlopenext to load dynamic modules is that it will work equally well when loading modules not compiled with Libtool. Also, by passing the module name parameter with no extension, this function allows module coders to manage without Libtool.

 
#include <stdio.h>
#include <stdlib.h>
#ifndef EXIT_FAILURE
#  define EXIT_FAILURE        1
#  define EXIT_SUCCESS        0
#endif

#include <limits.h>
#ifndef PATH_MAX
#  define PATH_MAX 255
#endif

#include <string.h>
#include <ltdl.h>

#ifndef MODULE_PATH_ENV
#  define MODULE_PATH_ENV        "MODULE_PATH"
#endif

typedef int entrypoint (const char *argument);

/* Save and return a copy of the dlerror() error  message,
   since the next API call may overwrite the original. */
static char *dlerrordup (char *errormsg);

int
main (int argc, const char *argv[])
{
  char *errormsg = NULL;
  lt_dlhandle module = NULL;
  entrypoint *run = NULL;
  int errors = 0;

  if (argc != 3)
    {
      fprintf (stderr, "USAGE: main MODULENAME ARGUMENT\n");
      exit (EXIT_FAILURE);
    }

  /* Initialise libltdl. */
  errors = lt_dlinit ();

  /* Set the module search path. */
  if (!errors)
    {
      const char *path = getenv (MODULE_PATH_ENV);

      if (path != NULL)
        errors = lt_dlsetsearchpath (path);
    }
  
  /* Load the module. */
  if (!errors)
    module = lt_dlopenext (argv[1]);

  /* Find the entry point. */
  if (module)
    {
      run = (entrypoint *) lt_dlsym (module, "run");

      /* In principle, run might legitimately be NULL, so
         I don't use run == NULL as an error indicator
         in general. */
      errormsg = dlerrordup (errormsg);
      if (errormsg != NULL)
        {
          errors = lt_dlclose (module);
          module = NULL;
        }
    }
  else
    errors = 1;

  /* Call the entry point function. */
  if (!errors)
    {
      int result = (*run) (argv[2]);
      if (result < 0)
        errormsg = strdup ("module entry point execution failed");
      else
        printf ("\t=> %d\n", result);
    }

  /* Unload the module, now that we are done with it. */
  if (!errors)
    errors = lt_dlclose (module);

  if (errors)
    {
      /* Diagnose the encountered error. */
      errormsg = dlerrordup (errormsg);

      if (!errormsg)
        {
          fprintf (stderr, "%s: dlerror() failed.\n", argv[0]);
          return EXIT_FAILURE;
        }
    }

  /* Finished with ltdl now. */
  if (!errors)
    if (lt_dlexit () != 0)
      errormsg = dlerrordup (errormsg);

  if (errormsg)
    {
      fprintf (stderr, "%s: %s.\n", argv[0], errormsg);
      free (errormsg);
      exit (EXIT_FAILURE);
    }

  return EXIT_SUCCESS;
}

/* Be careful to save a copy of the error message,
   since the  next API call may overwrite the original. */
static char *
dlerrordup (char *errormsg)
{
  char *error = (char *) lt_dlerror ();
  if (error && !errormsg)
    errormsg = strdup (error);
  return errormsg;
}

This file must be compiled with libtool, so that the dependent libraries (`libdl.so' on my GNU/Linux machine) are handled correctly, and so that the dlpreopen support is compiled in correctly (see section 18.4 dlpreopen Loading):

 
$ libtool --mode=link gcc -g -o ltdl-loader -dlopen self \
-rpath /tmp/lib ltdl-loader.c -lltdl
gcc -g -o ltdl-loader -Wl,--rpath,/tmp/lib ltdl-loader.c -lltdl -ldl

By using both of lt_dlopenext and lt_dlsetsearchpath, this module loader will make a valiant attempt at loading anything you pass to it -- including the module I wrote for the simple GNU/Linux module loader earlier (see section 17.5 A Simple GNU/Linux Dynamic Module). Here, you can see the new ltdl-loader loading and using the `simple-module' module from 17.5 A Simple GNU/Linux Dynamic Module:

 
$ ltdl-loader simple-module World
Hello, World!
        => 0



This document was generated by Gary V. Vaughan on February, 8 2006 using texi2html