1 # Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved
2 # Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
3 # Copyright (C) 2010 Andras Becsi (abecsi@inf.u-szeged.hu), University of Szeged
4 # Copyright (C) 2011 Research In Motion Limited. All rights reserved.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 # its contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 # Module to share code to start and stop the Apache daemon.
38 use File::Spec::Functions;
46 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
49 @EXPORT = qw(&getHTTPDPath
51 &getHTTPDConfigPathForTestDirectory
52 &getDefaultConfigForTestDirectory
55 &setShouldWaitForUserInterrupt
63 $tmpDir = convertMsysPath($tmpDir) if isMsys();
64 my $httpdLockPrefix = "WebKitHttpd.lock.";
66 my $exclusiveLockFile = File::Spec->catfile($tmpDir, "WebKit.lock");
67 my $httpdPidDir = File::Spec->catfile($tmpDir, "WebKit");
68 my $httpdPidFile = File::Spec->catfile($httpdPidDir, "httpd.pid");
70 my $waitForUserInterrupt = 0;
74 $SIG{'INT'} = 'handleInterrupt';
75 $SIG{'TERM'} = 'handleInterrupt';
80 if (isDebianBased()) {
81 $httpdPath = "/usr/sbin/apache2";
83 $httpdPath = 'c:\program files\apache software foundation\apache2.2\bin\httpd.exe';
85 $httpdPath = "/usr/sbin/httpd";
92 my @command = (getHTTPDPath(), "-v");
93 return system(@command) == 0;
96 sub getDefaultConfigForTestDirectory
98 my ($testDirectory) = @_;
99 die "No test directory has been specified." unless ($testDirectory);
101 my $httpdConfig = getHTTPDConfigPathForTestDirectory($testDirectory);
102 my $documentRoot = "$testDirectory/http/tests";
103 my $jsTestResourcesDirectory = $testDirectory . "/fast/js/resources";
104 my $mediaResourcesDirectory = $testDirectory . "/media";
105 my $typesConfig = "$testDirectory/http/conf/mime.types";
106 my $httpdLockFile = File::Spec->catfile($httpdPidDir, "httpd.lock");
107 my $httpdScoreBoardFile = File::Spec->catfile($httpdPidDir, "httpd.scoreboard");
110 "-f", "$httpdConfig",
111 "-C", "DocumentRoot \"$documentRoot\"",
112 # Setup a link to where the js test templates are stored, use -c so that mod_alias will already be loaded.
113 "-c", "Alias /js-test-resources \"$jsTestResourcesDirectory\"",
114 "-c", "Alias /media-resources \"$mediaResourcesDirectory\"",
115 "-c", "TypesConfig \"$typesConfig\"",
116 "-c", "PidFile \"$httpdPidFile\"",
117 "-c", "ScoreBoardFile \"$httpdScoreBoardFile\"",
121 # Apache wouldn't run CGIs with permissions==700 otherwise
122 "-c", "User \"#$<\"",
123 "-c", "LockFile \"$httpdLockFile\""
126 # FIXME: Enable this on Windows once <rdar://problem/5345985> is fixed
127 # The version of Apache we use with Cygwin does not support SSL
128 my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem";
129 push(@httpdArgs, "-c", "SSLCertificateFile \"$sslCertificate\"") unless isCygwin();
135 sub getHTTPDConfigPathForTestDirectory
137 my ($testDirectory) = @_;
138 die "No test directory has been specified." unless ($testDirectory);
141 my $httpdPath = getHTTPDPath();
142 my $httpdConfDirectory = "$testDirectory/http/conf/";
145 my $libPHP4DllPath = "/usr/lib/apache/libphp4.dll";
146 # FIXME: run-webkit-tests should not modify the user's system, especially not in this method!
147 unless (-x $libPHP4DllPath) {
148 copy("$httpdConfDirectory/libphp4.dll", $libPHP4DllPath);
149 chmod(0755, $libPHP4DllPath);
151 $httpdConfig = "cygwin-httpd.conf"; # This is an apache 1.3 config.
153 $httpdConfig = "apache2-msys-httpd.conf";
154 } elsif (isDebianBased()) {
155 $httpdConfig = "apache2-debian-httpd.conf";
156 } elsif (isFedoraBased()) {
157 $httpdConfig = "fedora-httpd.conf"; # This is an apache2 config, despite the name.
159 # All other ports use apache2, so just use our default apache2 config.
160 $httpdConfig = "apache2-httpd.conf";
162 return "$httpdConfDirectory/$httpdConfig";
168 die "No HTTPD configuration has been specified" unless (@args);
169 mkdir($httpdPidDir, 0755);
170 die "No write permissions to $httpdPidDir" unless (-w $httpdPidDir);
172 if (-f $httpdPidFile) {
173 open (PIDFILE, $httpdPidFile);
174 my $oldPid = <PIDFILE>;
177 if (0 != kill 0, $oldPid) {
178 print "\nhttpd is already running: pid $oldPid, killing...\n";
179 if (!killHTTPD($oldPid)) {
181 die "Timed out waiting for httpd to quit";
184 unlink $httpdPidFile;
187 my $httpdPath = getHTTPDPath();
189 open2(">&1", \*HTTPDIN, $httpdPath, @args);
192 while (!-f $httpdPidFile && $retryCount) {
199 die "Timed out waiting for httpd to start";
202 $httpdPid = <PIDFILE> if open(PIDFILE, $httpdPidFile);
203 chomp $httpdPid if $httpdPid;
206 waitpid($httpdPid, 0) if ($waitForUserInterrupt && $httpdPid);
214 my $succeeded = killHTTPD($httpdPid);
216 unless ($succeeded) {
217 print STDERR "Timed out waiting for httpd to terminate!\n" unless $succeeded;
227 return 1 unless $pid;
232 while (kill(0, $pid) && $retryCount) {
236 return $retryCount != 0;
239 sub setShouldWaitForUserInterrupt
241 $waitForUserInterrupt = 1;
246 # On Cygwin, when we receive a signal Apache is still running, so we need
247 # to kill it. On other platforms (at least Mac OS X), Apache will have
248 # already been killed, and trying to kill it again will cause us to hang.
249 # All we need to do in this case is clean up our own files.
263 unlink $exclusiveLockFile;
264 unlink $myLockFile if $myLockFile;
267 sub extractLockNumber
270 return -1 unless $lockFile;
271 return substr($lockFile, length($httpdLockPrefix));
276 opendir(TMPDIR, $tmpDir) or die "Could not open " . $tmpDir . ".";
277 my @lockFiles = grep {m/^$httpdLockPrefix\d+$/} readdir(TMPDIR);
278 @lockFiles = sort { extractLockNumber($a) <=> extractLockNumber($b) } @lockFiles;
283 sub getNextAvailableLockNumber
285 my @lockFiles = getLockFiles();
286 return 0 unless @lockFiles;
287 return extractLockNumber($lockFiles[-1]) + 1;
290 sub getLockNumberForCurrentRunning
292 my @lockFiles = getLockFiles();
293 return 0 unless @lockFiles;
294 return extractLockNumber($lockFiles[0]);
299 $waitBeginTime = time;
300 scheduleHttpTesting();
301 # If we are the only one waiting for Apache just run the tests without any further checking
302 if (scalar getLockFiles() > 1) {
303 my $currentLockFile = File::Spec->catfile($tmpDir, "$httpdLockPrefix" . getLockNumberForCurrentRunning());
304 my $currentLockPid = <SCHEDULER_LOCK> if (-f $currentLockFile && open(SCHEDULER_LOCK, "<$currentLockFile"));
305 # Wait until we are allowed to run the http tests
306 while ($currentLockPid && $currentLockPid != $$) {
307 $currentLockFile = File::Spec->catfile($tmpDir, "$httpdLockPrefix" . getLockNumberForCurrentRunning());
308 if ($currentLockFile eq $myLockFile) {
309 $currentLockPid = <SCHEDULER_LOCK> if open(SCHEDULER_LOCK, "<$currentLockFile");
310 if ($currentLockPid != $$) {
311 print STDERR "\nPID mismatch.\n";
322 sub scheduleHttpTesting
324 # We need an exclusive lock file to avoid deadlocks and starvation and ensure that the scheduler lock numbers are sequential.
325 # The scheduler locks are used to schedule the running test sessions in first come first served order.
326 while (!(open(SEQUENTIAL_GUARD_LOCK, ">$exclusiveLockFile") && flock(SEQUENTIAL_GUARD_LOCK, LOCK_EX|LOCK_NB))) {}
327 $myLockFile = File::Spec->catfile($tmpDir, "$httpdLockPrefix" . getNextAvailableLockNumber());
328 open(SCHEDULER_LOCK, ">$myLockFile");
329 print SCHEDULER_LOCK "$$";
330 print SEQUENTIAL_GUARD_LOCK "$$";
331 close(SCHEDULER_LOCK);
332 close(SEQUENTIAL_GUARD_LOCK);
333 unlink $exclusiveLockFile;
339 if ($waitBeginTime && $waitEndTime) {
340 $waitTime = $waitEndTime - $waitBeginTime;
348 return unless isMsys();
350 $path = `cmd.exe //c echo $path`;