Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

dfmgr.c File Reference

#include "postgres.h"
#include <sys/stat.h>
#include "dynloader.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "utils/dynamic_loader.h"
#include "utils/hsearch.h"
Include dependency graph for dfmgr.c:

Go to the source code of this file.

Data Structures

struct  rendezvousHashEntry
struct  df_files

Defines

#define SAME_INODE(A, B)   ((A).st_ino == (B).inode && (A).st_dev == (B).device)

Typedefs

typedef void(* PG_init_t )(void)
typedef void(* PG_fini_t )(void)
typedef struct df_files DynamicFileList

Functions

static void * internal_load_library (const char *libname)
static void incompatible_module_error (const char *libname, const Pg_magic_struct *module_magic_data)
static void internal_unload_library (const char *libname)
static bool file_exists (const char *name)
static char * expand_dynamic_library_name (const char *name)
static void check_restricted_library_name (const char *name)
static char * substitute_libpath_macro (const char *name)
static char * find_in_dynamic_libpath (const char *basename)
PGFunction load_external_function (char *filename, char *funcname, bool signalNotFound, void **filehandle)
void load_file (const char *filename, bool restricted)
PGFunction lookup_external_function (void *filehandle, char *funcname)
void ** find_rendezvous_variable (const char *varName)

Variables

static DynamicFileListfile_list = NULL
static DynamicFileListfile_tail = NULL
char * Dynamic_library_path
static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA

Define Documentation

#define SAME_INODE (   A,
  B 
)    ((A).st_ino == (B).inode && (A).st_dev == (B).device)

Definition at line 67 of file dfmgr.c.

Referenced by internal_load_library(), and internal_unload_library().


Typedef Documentation

typedef struct df_files DynamicFileList
typedef void(* PG_fini_t)(void)

Definition at line 32 of file dfmgr.c.

typedef void(* PG_init_t)(void)

Definition at line 31 of file dfmgr.c.


Function Documentation

static void check_restricted_library_name ( const char *  name  )  [static]

Definition at line 539 of file dfmgr.c.

References ereport, errcode(), errmsg(), ERROR, first_dir_separator(), and NULL.

Referenced by load_file().

{
    if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
        first_dir_separator(name + 16) != NULL)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("access to library \"%s\" is not allowed",
                        name)));
}

static char * expand_dynamic_library_name ( const char *  name  )  [static]

Definition at line 482 of file dfmgr.c.

References AssertArg, file_exists(), find_in_dynamic_libpath(), first_dir_separator(), palloc(), pfree(), pstrdup(), and substitute_libpath_macro().

Referenced by load_external_function(), and load_file().

{
    bool        have_slash;
    char       *new;
    char       *full;

    AssertArg(name);

    have_slash = (first_dir_separator(name) != NULL);

    if (!have_slash)
    {
        full = find_in_dynamic_libpath(name);
        if (full)
            return full;
    }
    else
    {
        full = substitute_libpath_macro(name);
        if (file_exists(full))
            return full;
        pfree(full);
    }

    new = palloc(strlen(name) + strlen(DLSUFFIX) + 1);
    strcpy(new, name);
    strcat(new, DLSUFFIX);

    if (!have_slash)
    {
        full = find_in_dynamic_libpath(new);
        pfree(new);
        if (full)
            return full;
    }
    else
    {
        full = substitute_libpath_macro(new);
        pfree(new);
        if (file_exists(full))
            return full;
        pfree(full);
    }

    /*
     * If we can't find the file, just return the string as-is. The ensuing
     * load attempt will fail and report a suitable message.
     */
    return pstrdup(name);
}

static bool file_exists ( const char *  name  )  [static]

Definition at line 450 of file dfmgr.c.

References AssertArg, ereport, errcode_for_file_access(), errmsg(), ERROR, and NULL.

Referenced by expand_dynamic_library_name(), find_in_dynamic_libpath(), isolation_start_test(), psql_start_test(), and results_differ().

{
    struct stat st;

    AssertArg(name != NULL);

    if (stat(name, &st) == 0)
        return S_ISDIR(st.st_mode) ? false : true;
    else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not access file \"%s\": %m", name)));

    return false;
}

static char * find_in_dynamic_libpath ( const char *  basename  )  [static]

Definition at line 591 of file dfmgr.c.

References AssertArg, AssertState, canonicalize_path(), DEBUG3, Dynamic_library_path, elog, ereport, errcode(), errmsg(), ERROR, file_exists(), first_dir_separator(), first_path_var_separator(), is_absolute_path, NULL, palloc(), pfree(), strlcpy(), and substitute_libpath_macro().

Referenced by expand_dynamic_library_name().

{
    const char *p;
    size_t      baselen;

    AssertArg(basename != NULL);
    AssertArg(first_dir_separator(basename) == NULL);
    AssertState(Dynamic_library_path != NULL);

    p = Dynamic_library_path;
    if (strlen(p) == 0)
        return NULL;

    baselen = strlen(basename);

    for (;;)
    {
        size_t      len;
        char       *piece;
        char       *mangled;
        char       *full;

        piece = first_path_var_separator(p);
        if (piece == p)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_NAME),
                     errmsg("zero-length component in parameter \"dynamic_library_path\"")));

        if (piece == NULL)
            len = strlen(p);
        else
            len = piece - p;

        piece = palloc(len + 1);
        strlcpy(piece, p, len + 1);

        mangled = substitute_libpath_macro(piece);
        pfree(piece);

        canonicalize_path(mangled);

        /* only absolute paths */
        if (!is_absolute_path(mangled))
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_NAME),
                     errmsg("component in parameter \"dynamic_library_path\" is not an absolute path")));

        full = palloc(strlen(mangled) + 1 + baselen + 1);
        sprintf(full, "%s/%s", mangled, basename);
        pfree(mangled);

        elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);

        if (file_exists(full))
            return full;

        pfree(full);

        if (p[len] == '\0')
            break;
        else
            p += len + 1;
    }

    return NULL;
}

void** find_rendezvous_variable ( const char *  varName  ) 

Definition at line 675 of file dfmgr.c.

References HASHCTL::entrysize, hash_create(), HASH_ELEM, HASH_ENTER, hash_search(), HASHCTL::keysize, MemSet, NULL, and rendezvousHashEntry::varValue.

Referenced by _PG_init().

{
    static HTAB *rendezvousHash = NULL;

    rendezvousHashEntry *hentry;
    bool        found;

    /* Create a hashtable if we haven't already done so in this process */
    if (rendezvousHash == NULL)
    {
        HASHCTL     ctl;

        MemSet(&ctl, 0, sizeof(ctl));
        ctl.keysize = NAMEDATALEN;
        ctl.entrysize = sizeof(rendezvousHashEntry);
        rendezvousHash = hash_create("Rendezvous variable hash",
                                     16,
                                     &ctl,
                                     HASH_ELEM);
    }

    /* Find or create the hashtable entry for this varName */
    hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
                                                 varName,
                                                 HASH_ENTER,
                                                 &found);

    /* Initialize to NULL if first time */
    if (!found)
        hentry->varValue = NULL;

    return &hentry->varValue;
}

static void incompatible_module_error ( const char *  libname,
const Pg_magic_struct module_magic_data 
) [static]

Definition at line 301 of file dfmgr.c.

References _, appendStringInfo(), appendStringInfoChar(), StringInfoData::data, ereport, errdetail(), errdetail_internal(), errmsg(), ERROR, Pg_magic_struct::float4byval, Pg_magic_struct::float8byval, Pg_magic_struct::funcmaxargs, Pg_magic_struct::indexmaxkeys, initStringInfo(), StringInfoData::len, Pg_magic_struct::namedatalen, and Pg_magic_struct::version.

Referenced by internal_load_library().

{
    StringInfoData details;

    /*
     * If the version doesn't match, just report that, because the rest of the
     * block might not even have the fields we expect.
     */
    if (magic_data.version != module_magic_data->version)
        ereport(ERROR,
                (errmsg("incompatible library \"%s\": version mismatch",
                        libname),
              errdetail("Server is version %d.%d, library is version %d.%d.",
                        magic_data.version / 100,
                        magic_data.version % 100,
                        module_magic_data->version / 100,
                        module_magic_data->version % 100)));

    /*
     * Otherwise, spell out which fields don't agree.
     *
     * XXX this code has to be adjusted any time the set of fields in a magic
     * block change!
     */
    initStringInfo(&details);

    if (module_magic_data->funcmaxargs != magic_data.funcmaxargs)
    {
        if (details.len)
            appendStringInfoChar(&details, '\n');
        appendStringInfo(&details,
                         _("Server has FUNC_MAX_ARGS = %d, library has %d."),
                         magic_data.funcmaxargs,
                         module_magic_data->funcmaxargs);
    }
    if (module_magic_data->indexmaxkeys != magic_data.indexmaxkeys)
    {
        if (details.len)
            appendStringInfoChar(&details, '\n');
        appendStringInfo(&details,
                         _("Server has INDEX_MAX_KEYS = %d, library has %d."),
                         magic_data.indexmaxkeys,
                         module_magic_data->indexmaxkeys);
    }
    if (module_magic_data->namedatalen != magic_data.namedatalen)
    {
        if (details.len)
            appendStringInfoChar(&details, '\n');
        appendStringInfo(&details,
                         _("Server has NAMEDATALEN = %d, library has %d."),
                         magic_data.namedatalen,
                         module_magic_data->namedatalen);
    }
    if (module_magic_data->float4byval != magic_data.float4byval)
    {
        if (details.len)
            appendStringInfoChar(&details, '\n');
        appendStringInfo(&details,
                       _("Server has FLOAT4PASSBYVAL = %s, library has %s."),
                         magic_data.float4byval ? "true" : "false",
                         module_magic_data->float4byval ? "true" : "false");
    }
    if (module_magic_data->float8byval != magic_data.float8byval)
    {
        if (details.len)
            appendStringInfoChar(&details, '\n');
        appendStringInfo(&details,
                       _("Server has FLOAT8PASSBYVAL = %s, library has %s."),
                         magic_data.float8byval ? "true" : "false",
                         module_magic_data->float8byval ? "true" : "false");
    }

    if (details.len == 0)
        appendStringInfo(&details,
              _("Magic block has unexpected length or padding difference."));

    ereport(ERROR,
            (errmsg("incompatible library \"%s\": magic block mismatch",
                    libname),
             errdetail_internal("%s", details.data)));
}

static void * internal_load_library ( const char *  libname  )  [static]

Definition at line 179 of file dfmgr.c.

References df_files::device, ereport, errcode(), errcode_for_file_access(), errhint(), errmsg(), ERROR, df_files::filename, free, df_files::handle, incompatible_module_error(), df_files::inode, Pg_magic_struct::len, malloc, memcmp(), MemSet, df_files::next, NULL, pg_dlclose, pg_dlerror, pg_dlopen, pg_dlsym, PG_MAGIC_FUNCTION_NAME_STRING, and SAME_INODE.

Referenced by load_external_function(), and load_file().

{
    DynamicFileList *file_scanner;
    PGModuleMagicFunction magic_func;
    char       *load_error;
    struct stat stat_buf;
    PG_init_t   PG_init;

    /*
     * Scan the list of loaded FILES to see if the file has been loaded.
     */
    for (file_scanner = file_list;
         file_scanner != NULL &&
         strcmp(libname, file_scanner->filename) != 0;
         file_scanner = file_scanner->next)
        ;

    if (file_scanner == NULL)
    {
        /*
         * Check for same files - different paths (ie, symlink or link)
         */
        if (stat(libname, &stat_buf) == -1)
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not access file \"%s\": %m",
                            libname)));

        for (file_scanner = file_list;
             file_scanner != NULL &&
             !SAME_INODE(stat_buf, *file_scanner);
             file_scanner = file_scanner->next)
            ;
    }

    if (file_scanner == NULL)
    {
        /*
         * File not loaded yet.
         */
        file_scanner = (DynamicFileList *)
            malloc(sizeof(DynamicFileList) + strlen(libname));
        if (file_scanner == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));

        MemSet(file_scanner, 0, sizeof(DynamicFileList));
        strcpy(file_scanner->filename, libname);
        file_scanner->device = stat_buf.st_dev;
#ifndef WIN32
        file_scanner->inode = stat_buf.st_ino;
#endif
        file_scanner->next = NULL;

        file_scanner->handle = pg_dlopen(file_scanner->filename);
        if (file_scanner->handle == NULL)
        {
            load_error = (char *) pg_dlerror();
            free((char *) file_scanner);
            /* errcode_for_file_access might not be appropriate here? */
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not load library \"%s\": %s",
                            libname, load_error)));
        }

        /* Check the magic function to determine compatibility */
        magic_func = (PGModuleMagicFunction)
            pg_dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
        if (magic_func)
        {
            const Pg_magic_struct *magic_data_ptr = (*magic_func) ();

            if (magic_data_ptr->len != magic_data.len ||
                memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)
            {
                /* copy data block before unlinking library */
                Pg_magic_struct module_magic_data = *magic_data_ptr;

                /* try to unlink library */
                pg_dlclose(file_scanner->handle);
                free((char *) file_scanner);

                /* issue suitable complaint */
                incompatible_module_error(libname, &module_magic_data);
            }
        }
        else
        {
            /* try to unlink library */
            pg_dlclose(file_scanner->handle);
            free((char *) file_scanner);
            /* complain */
            ereport(ERROR,
                  (errmsg("incompatible library \"%s\": missing magic block",
                          libname),
                   errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
        }

        /*
         * If the library has a _PG_init() function, call it.
         */
        PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
        if (PG_init)
            (*PG_init) ();

        /* OK to link it into list */
        if (file_list == NULL)
            file_list = file_scanner;
        else
            file_tail->next = file_scanner;
        file_tail = file_scanner;
    }

    return file_scanner->handle;
}

static void internal_unload_library ( const char *  libname  )  [static]

Definition at line 396 of file dfmgr.c.

References clear_external_function_hash(), ereport, errcode_for_file_access(), errmsg(), ERROR, df_files::filename, free, df_files::handle, df_files::next, pg_dlclose, pg_dlsym, and SAME_INODE.

Referenced by load_file().

{
#ifdef NOT_USED
    DynamicFileList *file_scanner,
               *prv,
               *nxt;
    struct stat stat_buf;
    PG_fini_t   PG_fini;

    /*
     * We need to do stat() in order to determine whether this is the same
     * file as a previously loaded file; it's also handy so as to give a good
     * error message if bogus file name given.
     */
    if (stat(libname, &stat_buf) == -1)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not access file \"%s\": %m", libname)));

    /*
     * We have to zap all entries in the list that match on either filename or
     * inode, else internal_load_library() will still think it's present.
     */
    prv = NULL;
    for (file_scanner = file_list; file_scanner != NULL; file_scanner = nxt)
    {
        nxt = file_scanner->next;
        if (strcmp(libname, file_scanner->filename) == 0 ||
            SAME_INODE(stat_buf, *file_scanner))
        {
            if (prv)
                prv->next = nxt;
            else
                file_list = nxt;

            /*
             * If the library has a _PG_fini() function, call it.
             */
            PG_fini = (PG_fini_t) pg_dlsym(file_scanner->handle, "_PG_fini");
            if (PG_fini)
                (*PG_fini) ();

            clear_external_function_hash(file_scanner->handle);
            pg_dlclose(file_scanner->handle);
            free((char *) file_scanner);
            /* prv does not change */
        }
        else
            prv = file_scanner;
    }
#endif   /* NOT_USED */
}

PGFunction load_external_function ( char *  filename,
char *  funcname,
bool  signalNotFound,
void **  filehandle 
)

Definition at line 102 of file dfmgr.c.

References ereport, errcode(), errmsg(), ERROR, expand_dynamic_library_name(), internal_load_library(), NULL, pfree(), and pg_dlsym.

Referenced by fmgr_c_validator(), and fmgr_info_C_lang().

{
    char       *fullname;
    void       *lib_handle;
    PGFunction  retval;

    /* Expand the possibly-abbreviated filename to an exact path name */
    fullname = expand_dynamic_library_name(filename);

    /* Load the shared library, unless we already did */
    lib_handle = internal_load_library(fullname);

    /* Return handle if caller wants it */
    if (filehandle)
        *filehandle = lib_handle;

    /* Look up the function within the library */
    retval = (PGFunction) pg_dlsym(lib_handle, funcname);

    if (retval == NULL && signalNotFound)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("could not find function \"%s\" in file \"%s\"",
                        funcname, fullname)));

    pfree(fullname);
    return retval;
}

void load_file ( const char *  filename,
bool  restricted 
)

Definition at line 141 of file dfmgr.c.

References check_restricted_library_name(), expand_dynamic_library_name(), internal_load_library(), internal_unload_library(), and pfree().

Referenced by load_libraries(), standard_ProcessUtility(), and WalReceiverMain().

{
    char       *fullname;

    /* Apply security restriction if requested */
    if (restricted)
        check_restricted_library_name(filename);

    /* Expand the possibly-abbreviated filename to an exact path name */
    fullname = expand_dynamic_library_name(filename);

    /* Unload the library if currently loaded */
    internal_unload_library(fullname);

    /* Load the shared library */
    (void) internal_load_library(fullname);

    pfree(fullname);
}

PGFunction lookup_external_function ( void *  filehandle,
char *  funcname 
)

Definition at line 166 of file dfmgr.c.

References pg_dlsym.

Referenced by fetch_finfo_record().

{
    return (PGFunction) pg_dlsym(filehandle, funcname);
}

static char * substitute_libpath_macro ( const char *  name  )  [static]

Definition at line 554 of file dfmgr.c.

References AssertArg, ereport, errcode(), errmsg(), ERROR, first_dir_separator(), NULL, palloc(), pkglib_path, and pstrdup().

Referenced by expand_dynamic_library_name(), and find_in_dynamic_libpath().

{
    const char *sep_ptr;
    char       *ret;

    AssertArg(name != NULL);

    /* Currently, we only recognize $libdir at the start of the string */
    if (name[0] != '$')
        return pstrdup(name);

    if ((sep_ptr = first_dir_separator(name)) == NULL)
        sep_ptr = name + strlen(name);

    if (strlen("$libdir") != sep_ptr - name ||
        strncmp(name, "$libdir", strlen("$libdir")) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_NAME),
                 errmsg("invalid macro name in dynamic library path: %s",
                        name)));

    ret = palloc(strlen(pkglib_path) + strlen(sep_ptr) + 1);

    strcpy(ret, pkglib_path);
    strcat(ret, sep_ptr);

    return ret;
}


Variable Documentation

Definition at line 72 of file dfmgr.c.

Referenced by find_in_dynamic_libpath().

DynamicFileList* file_list = NULL [static]

Definition at line 62 of file dfmgr.c.

DynamicFileList* file_tail = NULL [static]

Definition at line 63 of file dfmgr.c.

const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA [static]

Definition at line 85 of file dfmgr.c.