Autoconf, Automake, and Libtool: libltdl Module Loader

| ### 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