Thanks for the input and the clever alternative of forking a subshell, dotsourcing any shellscripts in said subshell that modify the environment, and then serializing over the %ENV hash and eval'ing it in the parent process. This works for most normal environment variables, but not the ones that these Sybase libraries need. In particular, the Sybase libraries need custom LD_LIBRARY_PATH environment settings. LD_LIBRARY_PATH is interpreted by the OS's ld.so dynamic linker, but only at process initialization. Setting LD_LIBRARY_PATH from within a process will not affect the library search path of the process itself, only for subsequent childs the process launches.
To expound on the problem, see the following transcript (env -i clears the environment):
$ env -i PERL5LIB=.local/lib/perl5 perl -MSybase::Simple
Can't load '.local/lib/perl5/x86_64-linux-thread-multi/auto/Sybase/CTl
+ib/CTlib.so' for module Sybase::CTlib: libsybct_r64.so: cannot open s
+hared object file: No such file or directory at /usr/lib64/perl5/Dyna
+Loader.pm line 193.
at .local/lib/perl5/Sybase/Simple.pm line 19.
Compilation failed in require at .local/lib/perl5/Sybase/Simple.pm lin
+e 19.
BEGIN failed--compilation aborted at .local/lib/perl5/Sybase/Simple.pm
+ line 19.
Compilation failed in require.
BEGIN failed--compilation aborted.
$
The PERL5LIB environment variable is the only one that is set in the empty environment that launches the perl binary (to find the Sybase::Simple module). And indeed, in an empty environment, the XS .so blob that implements the actual database access layer in Sybase::CTlib (on which Sybase::Simple depends) refers to libraries that can't be resolved (and this is how these Sybase libraries work by design):
$ env -i ldd .local/lib/perl5/x86_64-linux-thread-multi/auto/Sybase/CT
+lib/CTlib.so
linux-vdso.so.1 (0x00007ffcebdce000)
libsybct_r64.so => not found
libsybcs_r64.so => not found
libsybblk_r64.so => not found
libperl.so.5.32 => /lib64/libperl.so.5.32 (0x00007f4970c00000)
libc.so.6 => /lib64/libc.so.6 (0x00007f4970800000)
libm.so.6 => /lib64/libm.so.6 (0x00007f4970b25000)
libcrypt.so.2 => /lib64/libcrypt.so.2 (0x00007f4970fc3000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4971032000)
$
The code I arrived at from your linked comment to 'steal' the environment from a subshell (with the added requirement of merging with any existing %ENV values), is:
#!/usr/bin/perl
BEGIN {
my $shellcode = <<__KOEKOEK__;
. sybsdk/SYBASE.sh
perl -MData::Dumper -e 'print Dumper [\%ENV]'
__KOEKOEK__
%ENV = (%ENV => @{eval qx{/bin/sh -c "$shellcode"}});
};
print $ENV{LD_LIBRARY_PATH};
require Sybase::Simple;
But this still fails (even though the environment is modified correctly (the first line is the content of $ENV{LD_LIBRARY_PATH}):
$ env -i PERL5LIB=.local/lib/perl5 perl ./do_shell_source.pl
~/sybsdk/DataAccess64/ODBC/lib:~/sybsdk/DataAccess/ODBC/lib:~/sybsdk/O
+CS-20_0/lib:~/sybsdk/OCS-20_0/lib3p64:~/sybsdk/OCS-20_0/lib3p:
Can't load '.local/lib/perl5/x86_64-linux-thread-multi/auto/Sybase/CTl
+ib/CTlib.so' for module Sybase::CTlib: libsybct_r64.so: cannot open s
+hared object file: No such file or directory at /usr/lib64/perl5/Dyna
+Loader.pm line 193.
at .local/lib/perl5/Sybase/Simple.pm line 19.
Compilation failed in require at .local/lib/perl5/Sybase/Simple.pm lin
+e 19.
BEGIN failed--compilation aborted at .local/lib/perl5/Sybase/Simple.pm
+ line 19.
Compilation failed in require at ./do_shell_source.pl line 14.
$
If we modify the script to just dump the %ENV hash, we see that all environment variables were indeed set correctly in the parent process from our detour in the qx{} subshell:
#!/usr/bin/perl
BEGIN {
my $shellcode = <<__KOEKOEK__;
. sybsdk/SYBASE.sh
perl -MData::Dumper -e 'print Dumper [\%ENV]'
__KOEKOEK__
%ENV = (%ENV => @{eval qx{/bin/sh -c "$shellcode"}});
};
use DDP;
p %ENV;
$ env -i PERL5LIB=.local/lib/perl5 perl ./do_shell_source.pl
{
_ "/usr/bin/perl",
INCLUDE "~/sybsdk/OCS-20_0/include:",
LD_LIBRARY_PATH "~/sybsdk/DataAccess64/ODBC/lib:~/sybsdk/DataAcc
+ess/ODBC/lib:~/sybsdk/OCS-20_0/lib:~/sybsdk/OCS-20_0/lib3p64:~/sybsdk
+/OCS-20_0/lib3p:",
LIB "~/sybsdk/OCS-20_0/lib:",
PATH "~/sybsdk/tools/bin:~/sybsdk/OCS-20_0/bin:/usr/l
+ocal/bin:/usr/bin",
PERL5LIB ".local/lib/perl5",
PWD "/tmp",
SAP_JRE8 "~/sybsdk/shared/SAPJRE-8_1_062_64BIT",
SAP_JRE8_64 "~/sybsdk/shared/SAPJRE-8_1_062_64BIT",
SHLVL 0,
SYBASE "~/sybsdk",
SYBASE_OCS "OCS-20_0",
SYBROOT "~/sybsdk"
}
$
And this corresponds to what's inside the (Sybase provided) shell script file for their proprietary libraries that is meant to be sourced in the shell's environment:
$ cat sybsdk/SYBASE.sh
#
# SAP Product Environment variables
#
SAP_JRE8="~/sybsdk/shared/SAPJRE-8_1_062_64BIT"
export SAP_JRE8
SAP_JRE8_64="~/sybsdk/shared/SAPJRE-8_1_062_64BIT"
export SAP_JRE8_64
SYBASE_OCS="OCS-20_0"
export SYBASE_OCS
INCLUDE="~/sybsdk/OCS-20_0/include":$INCLUDE
export INCLUDE
LIB="~/sybsdk/OCS-20_0/lib":$LIB
export LIB
PATH="~/sybsdk/OCS-20_0/bin":$PATH
export PATH
#
# Replace lib, lib3p, and lib3p64 with devlib, devlib3p, and devlib3p6
+4 when debugging
#
LD_LIBRARY_PATH="~/sybsdk/OCS-20_0/lib:~/sybsdk/OCS-20_0/lib3p64:~/syb
+sdk/OCS-20_0/lib3p":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
SYBASE="~/sybsdk"
export SYBASE
LD_LIBRARY_PATH="~/sybsdk/DataAccess/ODBC/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
LD_LIBRARY_PATH="~/sybsdk/DataAccess64/ODBC/lib":$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
SYBROOT="~/sybsdk"
export SYBROOT
PATH="~/sybsdk/tools/bin":$PATH
export PATH
$
So, coming back around again: a re-exec is still required in this case (due to the semantics and interaction of LD_LIBRARY_PATH and the system's dynamic linker). Moreover, because a Perl script file is first compiled before it is executed, any statements that fire at compile time (like use) need to be deferred to runtime (using require) when LD_LIBRARY_PATH potentially needs adjustments. Otherwise, the use Sybase::Simple; statements gets triggered before the re-exec through a shell that first sets LD_LIBRARY_PATH, causing compilation to abort due to DynaLoader getting an error from the OS's ld.so when trying to resolve libraries a module's XS .so file is linked to. |