The site I administer has some CGI scripts that run scripts of the form:
#!/usr/bin/env bash
perl my-script.pl
my-script.pl uses DBD::mysql.
use DBD::mysql;
My scripts use many CPAN modules and I do not want to pollute the "system" Perl (5.16) installed by the Linux distro. Our security policy requires that httpd run as user "apache" and that apache not have a home directory on our server, so my solution has been to install Perl with perlbrew under a different home dir I have access to. Then the Apache config file for the virtual host sets some env vars to access it.
SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib # this may not be needed
This works well-enough for loading most modules. For example, apache can run:
perl -mDateTime -e 'print $DateTime::VERSION' # prints "1.52"
but if apache attempts:
perl -mDBD::mysql -e 'print $DBD::mysql::VERSION'
it barfs:
Can't locate loadable object for module DBD::mysql in @INC (@INC contains: /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2/x86_64-linux /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2) at -e line 0.
Compilation failed in require.
BEGIN failed--compilation aborted.
The error message "Can't locate ..." is misleading. I confirmed that DBD::mysql is available from the 3rd path in @INC:
$ find ~user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 -name mysql -ls
16540213 4 drwxr-x--- 2 user1 user1 4096 Apr 21 12:51 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql
16540211 4 drwxr-xr-x 2 user1 user1 4096 Apr 21 11:26 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux/DBD/mysql
Furthermore, user1 can load DBD::mysql with no problem:
perl -mDBD::mysql -e 'print $DBD::mysql::VERSION' # prints 4.050
Therefore, I suspect that the above error message should have read "Can't load libmysqlclient.so ..."
libmysqlclient.so is located in /usr/lib64/mysql/
ls -l /usr/lib64/mysql/
total 3076
lrwxrwxrwx 1 root root 17 Apr 16 11:59 libmysqlclient_r.so -> libmysqlclient.so
lrwxrwxrwx 1 root root 20 Apr 16 11:59 libmysqlclient.so -> libmysqlclient.so.18
lrwxrwxrwx 1 root root 24 Apr 16 11:57 libmysqlclient.so.18 -> libmysqlclient.so.18.0.0
-rwxr-xr-x 1 root root 3135664 Aug 18 2019 libmysqlclient.so.18.0.0
-rwxr-xr-x 1 root root 6758 Aug 18 2019 mysql_config
drwxr-xr-x. 2 root root 4096 Apr 16 11:57 plugin
If user1 runs perl -V, the Linker and Dynamic Linking sections show the following:
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib /lib64 /usr/lib64 /usr/local/lib64
libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.17.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.17'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
If I run this same perl as apache, it will produce the same result:
sudo -u apache bash
PATH=~user1/perl5/perlbrew/perls/perl-5.30.2/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin
perl -V
...
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib /lib64 /usr/lib64 /usr/local/lib64
libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.17.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.17'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
How come user1 perl can load DBD::mysql but apache can't even though both are running the same Perl with the same @INC paths and their dynamic library loading paths look identical? Does anyone know what else can I do to get to the bottom of this?
For starters, you should never do
SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib
If you use .../perl-5.30.2/bin/perl
, it will know to look in .../perl-5.30.2/lib
, and that's the only perl
that should look in that directory.
Ideally, you wouldn't do the following either:
SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
The shebang of the script should point to the perl
it's meant to use (the one with which it was tested and known to work).
In other words, use the following in the bash
script:
./my-script.pl
And use the following shebang in my-script.pl
:
#!/export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin/perl
What you are currently doing isn't terrible, but could bite you if you try to upgrade something.
Finally, perl
can't find the module because of permission issues. Assuming the apache
user isn't a member of the user1
group, you showed that apache
user can't access lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql
(and it might not be able access other pertinent files either).
Fix:
chmod go+X \
/export \
/export/home \
/export/home/user1\
/export/home/user1/perl5 \
/export/home/user1/perl5/perlbrew \
/export/home/user1/perl5/perlbrew/perls
chmod -R go+rX /export/home/user1/perl5/perlbrew/perls/perl-5.30.2