FloydATC has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to get a Perl section in Apache 2.4 to work, the idea is to pull virtualhost definitions from a MySQL table during httpd startup. Here's the relevant configuration file under "/etc/httpd/conf.d/":

<Perl> use DBI; use Data::Dumper; my $dbtype = "mysql"; my $dbhost = "XXXXXXXX.XXX.XX"; my $dbname = "XXXXXXXX"; my $dbuser = "XXXXXXXX"; my $dbpass = "XXXXXXXX"; my %users = (); my %groups = (); while ( my $user = getpwent() ) { $users{$user} = 1; } while ( my $group = getgrent() ) { $groups{$group} = 1; } my $self = Apache2::ServerUtil->server; my $flags = { PrintError => 1 }; my $dbh = DBI->connect("dbi:$dbtype:$dbname:$dbhost", $dbuser, $dbpass +, $flags); if (defined $dbh) { # Load configuration from MySQL table my $sth = $dbh->prepare(" SELECT * FROM virtualhosts ORDER BY ServerName "); $sth->execute; while (my $record = $sth->fetchrow_hashref) { my $url = $record->{'ServerName'}; my $docroot = $record->{'DocumentRoot'}; my $cacheroot = "/var/www/mason/$url"; my $uid = $record->{'Username'}; my $gid = $record->{'Groupname'}; # Verify DocumentRoot exists unless ( -d $docroot ) { warn "Skipped $record->{'ServerName'}: DocumentRoot '$docroot' i +nvalid\n"; next; } # Verify Username exists unless ( $users{$uid} ) { warn "Skipped $record->{'ServerName'}: Username '$uid' invalid\n +"; next; } # Verify Groupname exists unless ( $groups{$gid} ) { warn "Skipped $record->{'ServerName'}: Groupname '$gid' invalid\ +n"; next; } push @{$VirtualHost{'*:80'}}, { ServerName => $url, ServerAdmin => $record->{'ServerAdmin'}, DocumentRoot => $docroot, SuexecUserGroup => "$uid $gid", }; } open(my $fh, '>', '/var/log/httpd/debug.log'); print $fh Dumper(\%VirtualHost); close $fh; } else { die "MySQL error: ".$DBI::errstr; } </Perl>

You'll notice that I'm dumping the generated hash to a debug logfile, here's what it ends up looking like (actual names redacted):

$VAR1 = { '*:80' => [ { 'ServerName' => 'vsite1.XXX.XX', 'SuexecUserGroup' => 'XXXXXXXX XXXXXXXX', 'ServerAdmin' => 'XXXXXXXX@XXX.XX', 'DocumentRoot' => '/home/XXXXXXXX/public_html' }, { 'ServerName' => 'vsite2.XXX.XX', 'SuexecUserGroup' => 'XXXXXXXX XXXXXXXX', 'ServerAdmin' => 'XXXXXXXX@XXX.XX', 'DocumentRoot' => '/home/XXXXXXXX/public_html' }, { 'ServerName' => 'vsite3.XXX.XX', 'SuexecUserGroup' => 'XXXXXXXX XXXXXXXX', 'ServerAdmin' => 'XXXXXXXX@XXX.XX', 'DocumentRoot' => '/home/XXXXXXXX/public_html' } ] };

Now, here's what Apache seems to think of the configuration. This looks almost like I had expected, except each entry shows up twice. I don't understand why. I know it's not because the VirtualHosts are defined somewhere else, because if I make a simple change to the Perl section (like adding something to $url) the changed name still shows up twice. Maybe the whole section is executed twice, I don't know:

$ httpd -S [Mon Dec 01 18:11:12.970705 2014] [so:warn] [pid 19208] AH01574: modul +e apreq_module is already loaded, skipping [Mon Dec 01 18:11:12.971179 2014] [so:warn] [pid 19208] AH01574: modul +e perl_module is already loaded, skipping VirtualHost configuration: *:80 is a NameVirtualHost default server vsite1.XXX.XX (mod_perl:1) port 80 namevhost vsite1.XXX.XX (mod_perl:1) port 80 namevhost vsite1.XXX.XX (mod_perl:1) port 80 namevhost vsite2.XXX.XX (mod_perl:7) port 80 namevhost vsite2.XXX.XX (mod_perl:7) port 80 namevhost vsite3.XXX.XX (mod_perl:13) port 80 namevhost vsite3.XXX.XX (mod_perl:13) ServerRoot: "/etc/httpd" Main DocumentRoot: "/var/www/html" Main ErrorLog: "/etc/httpd/logs/error_log" Mutex default: dir="/run/httpd/" mechanism=default Mutex mpm-accept: using_defaults Mutex authdigest-opaque: using_defaults Mutex proxy-balancer-shm: using_defaults Mutex rewrite-map: using_defaults Mutex authdigest-client: using_defaults Mutex proxy: using_defaults Mutex authn-socache: using_defaults PidFile: "/run/httpd/httpd.pid" Define: DUMP_VHOSTS Define: DUMP_RUN_CFG Define: MODPERL2 User: name="apache" id=48 Group: name="apache" id=48

But that's not the real problem. The real problem is that no matter what I do with the virtualhosts, Apache keeps serving "/var/www/html" to everyone. If I define the VirtualHosts manually (outside the Perl section) they work as intended, but the Perl magic simply does absolutely nothing except confuse me.

This is all taking place on a test server running Centos 7. I have an old Centos 5 server running a similar setup based on mod_perl 1 and it has served wonderfully for several years so I know the idea isn't completely retarded.

Besides, it looks so very easy when explained here: http://modperlbook.org/html/4-4-1-Constructing-lt-Perl-gt-Sections.html

Does anyone have a clue why it isn't working?

This is perl 5, version 16, subversion 3 (v5.16.3) built for x86_64-li +nux-thread-multi Linux XXXX.XXX.XX 3.10.0-123.9.3.el7.x86_64 #1 SMP Thu Nov 6 15:06:03 +UTC 2014 x86_64 x86_64 x86_64 GNU/Linux httpd-2.4.6-18.el7.centos.x86_64 mod_perl-2.0.8-10.20140624svn1602105.el7.x86_64

Solved

...and it only took two days of beating my head against the wall.

It turns out that for some reason, getpwent() and getgrent() return user and group names as expected when I execute httpd -S but when actually reloading/restarting the web server they return undef. This causes my script to skip those hosts, a fact that did not become obvious until I enabled PerlSwitches -w (which is commented out in perl.conf by default).

As soon as I disabled the username/groupname checks by commenting out the 'next;' statements, the virtualhosts behaved as expected.

Now all I have to do is figure out why getpwent() and getgrent() don't work properly under mod_perl 2.

-- FloydATC

Time flies when you don't know what you're doing

Replies are listed 'Best First'.
Re: Configuring Apache VirtualHosts using mod_perl 2
by Anonymous Monk on Dec 01, 2014 at 18:47 UTC

    Maybe you can try  Apache2::PerlSections->store($filename); in a seperate perl section?

      Maybe you can try Apache2::PerlSections->store($filename); in a seperate perl section?

      Hmmm... Provided I also add "$Apache2::PerlSections::Save = 1;" in the first section, I get pretty much what I had expected:

      $VirtualHost{'*:80'} = [ { 'ServerName' => 'vsite1.XXX.XX', 'SuexecUserGroup' => 'XXXXXXXX XXXXXXXX', 'ServerAdmin' => 'XXXXXXXX@XXX.XX', 'DocumentRoot' => '/home/XXXXXXXX/public_ht +ml' }, { 'ServerName' => 'vsite2.XXX.XX', 'SuexecUserGroup' => 'XXXXXXXX XXXXXXXX', 'ServerAdmin' => 'XXXXXXXX@XXX.XX', 'DocumentRoot' => '/home/XXXXXXXX/public_ht +ml' }, { 'ServerName' => 'vsite2.XXX.XX', 'SuexecUserGroup' => 'XXXXXXXX XXXXXXXX', 'ServerAdmin' => 'XXXXXXXX@XXX.XX', 'DocumentRoot' => '/home/XXXXXXXX/public_ht +ml' } ]; 1; __END__

      It's as if it's all there except there's a tiny piece of the puzzle missing. A module or library which needs to be loaded, or perhaps some magic incantation that I overlooked? The documentation points out that any misspelled keyword will be silently ignored, that's not very reassuring...

      -- FloydATC

      Time flies when you don't know what you're doing

Re: [SOLVED] Configuring Apache VirtualHosts using mod_perl 2
by Anonymous Monk on Dec 02, 2014 at 12:59 UTC
    The behavior of those two functions at startup certainly sounds "buggy" to me. Let's not call this problem truly "solved" until we establish that it's proper for it to be behaving that way.

      I agree in principle, although it's not really related to configuring VirtualHosts so it would probably deserve to be discussed in a node of its own.

      ...By someone considerably more skilled than myself :-/

      I worked around the problem by scanning the passwd/group files manually. Not at all portable ofcourse, but that's no particular concern of mine.

      my %users = (); my %groups = (); my $pfname = '/etc/passwd'; open(my $pfh, $pfname) || warn "Error reading $pfname: $!"; while (my $line = <$pfh>) { my ($name) = split(/:/, $line); $users{$name} = 1; } close $pfh; my $gfname = '/etc/group'; open(my $gfh, $gfname) || warn "Error reading $gfname: $!"; while (my $line = <$gfh>) { my ($name) = split(/:/, $line); $groups{$name} = 1; } close $gfh;
      -- FloydATC

      Time flies when you don't know what you're doing

        When getpwent returns undef, what is output of sub Fudge?