From: Nikolai Kondrashov Date: Mon, 25 Apr 2016 15:58:53 +0000 (+0300) Subject: Dlopen the actual linked libpython X-Git-Tag: release_3_0_13~11^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e6c8c3e2c5905410ca80040f4112f0e1225ca009;p=thirdparty%2Ffreeradius-server.git Dlopen the actual linked libpython In rlm_python, if dl_iterate_phdr(3) is available, dlopen libpython shared library at the actual path it was linked with on loading, instead of with just its linker name (version-less SONAME). This removes the need to have the linker name symlink (e.g. "libpython2.7.so") in library directory, which is normally installed only with the development packages. I.e. this removes the requirement of having python-devel/libpython-dev installed, when loading rlm_python. --- diff --git a/src/modules/rlm_python/config.h.in b/src/modules/rlm_python/config.h.in new file mode 100644 index 00000000000..64b950d7289 --- /dev/null +++ b/src/modules/rlm_python/config.h.in @@ -0,0 +1,22 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `dl_iterate_phdr' function. */ +#undef HAVE_DL_ITERATE_PHDR + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION diff --git a/src/modules/rlm_python/configure b/src/modules/rlm_python/configure index 49b97d3c719..6abaa0a0dde 100755 --- a/src/modules/rlm_python/configure +++ b/src/modules/rlm_python/configure @@ -1471,6 +1471,73 @@ fi as_fn_set_status $ac_retval } # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. @@ -3500,6 +3567,18 @@ fi fi fi fi + + for ac_func in dl_iterate_phdr +do : + ac_fn_c_check_func "$LINENO" "dl_iterate_phdr" "ac_cv_func_dl_iterate_phdr" +if test "x$ac_cv_func_dl_iterate_phdr" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DL_ITERATE_PHDR 1 +_ACEOF + +fi +done + else targetname= echo \*\*\* module rlm_python is disabled. @@ -3517,6 +3596,8 @@ $as_echo "$as_me: WARNING: FAILURE: rlm_python requires: $fail." >&2;}; fi fi +ac_config_headers="$ac_config_headers config.h" + @@ -3616,43 +3697,7 @@ test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' -# Transform confdefs.h into DEFS. -# Protect against shell expansion while executing Makefile rules. -# Protect against Makefile macro expansion. -# -# If the first sed substitution is executed (which looks for macros that -# take arguments), then branch to the quote section. Otherwise, -# look for a macro that doesn't take arguments. -ac_script=' -:mline -/\\$/{ - N - s,\\\n,, - b mline -} -t clear -:clear -s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g -t quote -s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g -t quote -b any -:quote -s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g -s/\[/\\&/g -s/\]/\\&/g -s/\$/$$/g -H -:any -${ - g - s/^\n// - s/\n/ /g - p -} -' -DEFS=`sed -n "$ac_script" confdefs.h` - +DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= @@ -4086,11 +4131,15 @@ case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" +config_headers="$ac_config_headers" _ACEOF @@ -4111,10 +4160,15 @@ Usage: $0 [OPTION]... [TAG]... --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE Configuration files: $config_files +Configuration headers: +$config_headers + Report bugs to the package provider." _ACEOF @@ -4175,7 +4229,18 @@ do esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; - --he | --h | --help | --hel | -h ) + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) @@ -4231,6 +4296,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 for ac_config_target in $ac_config_targets do case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; @@ -4244,6 +4310,7 @@ done # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree @@ -4431,8 +4498,116 @@ fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. -eval set X " :F $CONFIG_FILES " +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do @@ -4640,7 +4815,30 @@ which seems to be undefined. Please make sure it is defined" >&2;} esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; esac diff --git a/src/modules/rlm_python/configure.ac b/src/modules/rlm_python/configure.ac index 831a33aad02..00589f94a97 100644 --- a/src/modules/rlm_python/configure.ac +++ b/src/modules/rlm_python/configure.ac @@ -126,6 +126,8 @@ if test x$with_[]modname != xno; then fi fi fi + + AC_CHECK_FUNCS([dl_iterate_phdr]) else targetname= echo \*\*\* module modname is disabled. @@ -141,6 +143,7 @@ if test x"$fail" != x""; then fi fi +AC_CONFIG_HEADER(config.h) AC_SUBST(mod_ldflags) AC_SUBST(mod_cflags) AC_SUBST(targetname) diff --git a/src/modules/rlm_python/rlm_python.c b/src/modules/rlm_python/rlm_python.c index 381e68681b5..c52cec52c76 100644 --- a/src/modules/rlm_python/rlm_python.c +++ b/src/modules/rlm_python/rlm_python.c @@ -29,12 +29,19 @@ RCSID("$Id$") #define LOG_PREFIX "rlm_python - " +#include "config.h" #include #include #include #include #include +#ifdef HAVE_DL_ITERATE_PHDR +#include +#endif + +#define LIBPYTHON_LINKER_NAME \ + "libpython" STRINGIFY(PY_MAJOR_VERSION) "." STRINGIFY(PY_MINOR_VERSION) ".so" static uint32_t python_instances = 0; static void *python_dlhandle; @@ -768,6 +775,68 @@ static void python_parse_config(CONF_SECTION *cs, int lvl, PyObject *dict) DEBUG("%*s}", indent_section, " "); } +#ifdef HAVE_DL_ITERATE_PHDR +static int dlopen_libpython_cb(struct dl_phdr_info *info, + UNUSED size_t size, void *data) +{ + const char *pattern = "/" LIBPYTHON_LINKER_NAME; + char **ppath = (char **)data; + + if (strstr(info->dlpi_name, pattern) != NULL) { + if (*ppath != NULL) { + talloc_free(*ppath); + *ppath = NULL; + return EEXIST; + } else { + *ppath = talloc_strdup(NULL, info->dlpi_name); + if (*ppath == NULL) { + return errno; + } + } + } + return 0; +} + +/* Dlopen the already linked libpython */ +static void *dlopen_libpython(int flags) +{ + char *path = NULL; + int rc; + void *handle; + + /* Find the linked libpython path */ + rc = dl_iterate_phdr(dlopen_libpython_cb, &path); + if (rc != 0) { + WARN("Failed searching for libpython " + "among linked libraries: %s", strerror(rc)); + return NULL; + } else if (path == NULL) { + WARN("Libpython is not found among linked libraries"); + return NULL; + } + + /* Dlopen the found library */ + handle = dlopen(path, flags); + if (handle == NULL) { + WARN("Failed loading %s: %s", path, dlerror()); + } + talloc_free(path); + return handle; +} +#else /* ! HAVE_DL_ITERATE_PHDR */ +/* Dlopen libpython by its linker name (bare soname) */ +static void *dlopen_libpython(int flags) +{ + const char *name = LIBPYTHON_LINKER_NAME; + void *handle; + handle = dlopen(name, flags); + if (handle == NULL) { + WARN("Failed loading %s: %s", name, dlerror()); + } + return handle; +} +#endif /* ! HAVE_DL_ITERATE_PHDR */ + /** Initialises a separate python interpreter for this module instance * */ @@ -781,9 +850,8 @@ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) if (python_instances == 0) { INFO("Python version: %s", Py_GetVersion()); - python_dlhandle = dlopen("libpython" STRINGIFY(PY_MAJOR_VERSION) "." STRINGIFY(PY_MINOR_VERSION) ".so", - RTLD_NOW | RTLD_GLOBAL); - if (!python_dlhandle) WARN("Failed loading libpython symbols into global symbol table: %s", dlerror()); + python_dlhandle = dlopen_libpython(RTLD_NOW | RTLD_GLOBAL); + if (!python_dlhandle) WARN("Failed loading libpython symbols into global symbol table"); #if PY_VERSION_HEX > 0x03050000 {