Getting Perl, MySQL and Apache to all work together on Mac OS/X

So, I've been running this blog for a few years now; over time I've been adding bits and pieces of dynamic functionality - not the least of which is a comment section. A little while ago, a reader pointed out that the comment section doesn't allow for formatted source code, which is something I wanted to fix. Before I did so, though, I wanted to set up a local test bed on my computer that I could verify my fixes against. The comments section (and other dynamic functionality) of my miniature content-management system is built on Perl and MySql, so to create a local instance, all I ought to need is Apache, Perl & MySql. As it turns out, Mac OS/X comes with Apache, MySql, and Perl all pre-installed... so it should be a simple matter to create a "dev" environment copy of my blog and get them all working together, right? Well, as it turned out, not quite. It was easy enough to use mysqldump to get a copy of my simple database schema and replicate that on my local MySQL instance. I had Perl also, but didn't have DBI or the DBD::MySQL module that Perl requires to access a MySQL database. Both were easy enough to download, install and test; I was able to verify that I could run a Perl script and retrieve and insert data into my local MySQL instance. My next step was to grab all of my CGI scripts and copy them into /Library/WebServer/CGI-Executabes. When I tried to run it, though, I got a blank page and this error in /var/log/apache2/error_log:

[Fri Jul 24 10:49:11.776642 2015] [cgi:error] [pid 1797] [client ::1:50793] AH01215: install_driver(mysql) failed: Can't locate DBD/ in @INC 
(you may need to install the DBD::mysql module) 
(@INC contains: /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 
/Library/Perl/Updates/5.18.2 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level 
/System/Library/Perl/Extras/5.18 .) at (eval 5) line 3.
[Fri Jul 24 10:49:11.776802 2015] [cgi:error] [pid 1797] [client ::1:50793] AH01215: Perhaps the DBD::mysql perl module hasn't been fully installed,
[Fri Jul 24 10:49:11.783131 2015] [cgi:error] [pid 1797] [client ::1:50793] AH01215: or perhaps the capitalisation of 'mysql' isn't right.
[Fri Jul 24 10:49:11.783192 2015] [cgi:error] [pid 1797] [client ::1:50793] AH01215: Available drivers: DBM, ExampleP, File, GetInfo, Gofer, Proxy, SQLite, Sponge.
[Fri Jul 24 10:49:11.783227 2015] [cgi:error] [pid 1797] [client ::1:50793] AH01215:  at /Library/WebServer/CGI-Executables/showarticle.cgi line 76.

However, MySQL DBD was installed and worked just fine:

use DBI();

my $dbh = DBI->connect("DBI:mysql:blog", "user", "password");
my $sth = $dbh->prepare("SELECT count(*) FROM comment");
my $count = $sth->execute();
print $count . "\n";

Apparently, the Perl I use has a different search directory than the one that the web server uses:

$ perl -e 'print "@INC" . "\n"'
/opt/local/lib/perl5/site_perl/5.16.3/darwin-thread-multi-2level /opt/local/lib/perl5/site_perl/5.16.3 
/opt/local/lib/perl5/vendor_perl/5.16.3/darwin-thread-multi-2level /opt/local/lib/perl5/vendor_perl/5.16.3 
/opt/local/lib/perl5/5.16.3/darwin-thread-multi-2level /opt/local/lib/perl5/5.16.3 /opt/local/lib/perl5/site_perl /opt/local/lib/perl5/vendor_perl .

My first thought, because I'm lazy, is to just take my working DBD::MySQL installation and force it into one of the directories listed in the @INC path that Apache is reporting. I can download and build the DBD::MySQL distribution (that's how I got it installed in the first place); so you'd think I could just say, "Install it here, instead of there." You would think... however, after an hour of examining the install script, I couldn't find a way to force it to install itself into, say /Library/Perl/5.18/darwin-thread-multi-2level.

Just so you know, the really brute force approach of copying the files themselves doesn't work (although you had probably already guessed that...)

So, I figure one of two things is happening — either my login is overwriting some default Perl include search path, or, inexplicably, I have two Perl instances installed and my ordinary login was loading one Perl instance and Apache was loading the other. So... how to figure out where Apache was finding its Perl search path? For one thing, mod_perl isn't installed; I'm running perl as scripts. I considered that a perfectly reasonable tradeoff since I'm just testing. Given that, I should be able to assume the identity of the web server user and find his Perl instance by which perl. I can become root and verify that his Perl is the same as mine. httpd doesn't run as root, though, it runs as:

# ps -fe | grep http
0    77     1   0  8:42AM ??         0:00.58 /usr/sbin/httpd -D FOREGROUND
70   315    77   0  8:42AM ??         0:00.02 /usr/sbin/httpd -D FOREGROUND
70  3277    77   0 11:09AM ??         0:00.00 /usr/sbin/httpd -D FOREGROUND
70  3278    77   0 11:10AM ??         0:00.01 /usr/sbin/httpd -D FOREGROUND
70  3279    77   0 11:10AM ??         0:00.00 /usr/sbin/httpd -D FOREGROUND
70  3280    77   0 11:10AM ??         0:00.01 /usr/sbin/httpd -D FOREGROUND
70  3281    77   0 11:10AM ??         0:00.01 /usr/sbin/httpd -D FOREGROUND
70  3282    77   0 11:10AM ??         0:00.00 /usr/sbin/httpd -D FOREGROUND
70  3283    77   0 11:10AM ??         0:00.00 /usr/sbin/httpd -D FOREGROUND

# grep ":70:" /etc/passwd 
_www:*:70:70:World Wide Web Server:/Library/WebServer:/usr/bin/false

Who, of course, you can't log in as: shell is /usr/bin/false. So what is his search path, and how do I change it?

I can "cheat" by running this CGI script:

print "Content-type: text/html; charset=iso-8859-1\n\n";
print "<html>";
print "<body>";
print "$ENV{'PATH'}";
print "</body>";
print "</html>";

This informs me that _www has the simplest path: /usr/bin:/bin:/usr/sbin:/sbin. And, sure enough, there's another perl instance install under /usr/bin that my ordinary user never sees, because /opt/local/bin is further up in his search path than /usr/bin.

At this point, I can do one of two things - I can reinstall DBI::Mysql under /usr/bin/perl (i.e. 5.18) or change the default path to find /opt/local/bin/perl (5.16). Changing the default path is a little disconcerting; I don't know what else might rely on this path configuration. However, by just temporarily changing my current path to put /usr/bin first and reinstalling DBI & DBD::MySQL:

$ export PATH=/usr/bin:$PATH
$ perl Makefile.PL
$ make
$ sudo make install

I can get DBD::mysql to load... almost. Now it fails with:

[Wed Aug 19 13:01:02.582506 2015] [cgi:error] [pid 3281] [client ::1:52522] AH01215: install_driver(mysql) failed: Can't load 
'/Library/Perl/5.18/darwin-thread-multi-2level/auto/DBD/mysql/mysql.bundle' for 
module DBD::mysql: dlopen(/Library/Perl/5.18/darwin-thread-multi-2level/auto/DBD/mysql/mysql.bundle, 1): Library not loaded: libmysqlclient.18.dylib

Because the mysqlclient libraries are not in _www's `DYLD_LIBRARY_PATH. The simplest fix for this is to create a symlink to it:

$ sudo ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/libmysqlclient.18.dylib

And voila! I have a working copy of my blog's CMS with which I can test out new functionality, like code-formatted comments — which, if you look below, you can now leave at the bottom of this post!

After doing a bit of research, I believe that the reason I have two Perl installations is because I used MacPorts to install some third-party software that itself was dependent of Perl. It appears that I'm not the only person to have been surprised by mismatched Perl installations.

Add a comment:

Completely off-topic or spam comments will be removed at the discretion of the moderator.

You may preserve formatting (e.g. a code sample) by indenting with four spaces preceding the formatted line(s)

Name: Name is required
Email (will not be displayed publicly):
Comment is required
My Book

I'm the author of the book "Implementing SSL/TLS Using Cryptography and PKI". Like the title says, this is a from-the-ground-up examination of the SSL protocol that provides security, integrity and privacy to most application-level internet protocols, most notably HTTP. I include the source code to a complete working SSL implementation, including the most popular cryptographic algorithms (DES, 3DES, RC4, AES, RSA, DSA, Diffie-Hellman, HMAC, MD5, SHA-1, SHA-256, and ECC), and show how they all fit together to provide transport-layer security.

My Picture

Joshua Davies

Past Posts