2021年10月12日 14:01
Covers techniques to restart mod_perl enabled Apache, SUID scripts, monitoring, and other maintenance chores, as well as some specific setups.
All of these techniques require that you know the server process id (PID). The easiest way to find the PID is to look it up in thehttpd.pid
file. It's easy to discover where to look, by looking in thehttpd.conf
file. Open the file and locate the entryPidFile
. Here is the line from one of my ownhttpd.conf
files:
PidFile /usr/local/var/httpd_perl/run/httpd.pid
As you see, with my configuration the file is /usr/local/var/httpd_perl/run/httpd.pid
.
Another way is to use theps
andgrep
utilities. Assuming that the binary is called httpd_perl, we would do:
% ps auxc | grep httpd_perl
or maybe:
% ps -ef | grep httpd_perl
This will produce a list of all thehttpd_perl
(parent and children) processes. You are looking for the parent process. If you run your server as root, you will easily locate it since it belongs to root. If you run the server as some other user (when youdon't have root access
, the processes will belong to that user unless defined differently inhttpd.conf
. It's still easy to find which is the parent--usually it's the process with the smallest PID.
You will see severalhttpd
processes running on your system, but you should never need to send signals to any of them except the parent, whose pid is in thePidFile
. There are three signals that you can send to the parent:SIGTERM
,SIGHUP
, andSIGUSR1
.
Some folks prefer to specify signals using numerical values, rather than using symbols. If you are looking for these, check out yourkill(1)
man page. My page points to/usr/include/linux/signal.h
, the relevant entries are:
#define SIGHUP 1 /* hangup, generated when terminal disconnects */
#define SIGKILL 9 /* last resort */
#define SIGTERM 15 /* software termination signal */
#define SIGUSR1 30 /* user defined signal 1 */
Note that to send these signals from the command line theSIG
prefix must be omitted and under some operating systems they will need to be preceded by a minus sign, e.g.kill -15
orkill -TERM
followed by the PID.
We will concentrate here on the implications of sendingTERM
,HUP
, andUSR1
signals (as arguments tokill(1)
) to a mod_perl enabled server. Seehttp://www.apache.org/docs/stopping.html
for documentation on the implications of sending these signals to a plain Apache server.
Sending theTERM
signal to the parent causes it to immediately attempt to kill off all its children. Any requests in progress are terminated, and no further requests are served. This process may take quite a few seconds to complete. To stop a child, the parent sends it aSIGHUP
signal. If that fails it sends another. If that fails it sends theSIGTERM
signal, and as a last resort it sends theSIGKILL
signal. For each failed attempt to kill a child it makes an entry in theerror_log
.
When all the child processes were terminated, the parent itself exits and any open log files are closed. This is when all the accumulatedEND
blocks, apart from the ones located in scripts running underApache::Registry
orApache::PerlRun
handlers. In the latter case,END
blocks are executed after each request is served.
Sending theHUP
signal to the parent causes it to kill off its children as if theTERM
signal had been sent, i.e. any requests in progress are terminated; but the parent does not exit. Instead, the parent re-reads its configuration files, spawns a new set of child processes and continues to serve requests. It is almost equivalent to stopping and then restarting the server.
If the configuration files contain errors when restart is signaled, the parent will exit, so it is important to check the configuration files for errors before issuing a restart. How to perform the check will be covered shortly;
Sometimes using this approach to restart mod_perl enabled Apache may cause the processes memory incremental growth after each restart. This happens when Perl code loaded in memory is not completely torn down, leading to a memory leak.
The USR1
signal causes the parent process to advise the children to exit after serving their current requests, or to exit immediately if they're not serving a request. The parent re-reads its configuration files and re-opens its log files. As each child dies off the parent replaces it with a child from the new generation (the new children use the new configuration) and it begins serving new requests immediately.
The only difference betweenUSR1
andHUP
is thatUSR1
allows the children to complete any current requests prior to killing them off and there is no interruption in the services compared to the killing withHUP
signal, where it might take a few seconds for a restart to get completed and there is no real service at this time.
By default, if a server is restarted (usingkill -USR1 `cat logs/httpd.pid`
or with theHUP
signal), Perl scripts and modules are not reloaded. To reloadPerlRequires
,PerlModules
, otheruse()
'd modules and flush theApache::Registry
cache, use this directive inhttpd.conf
:
PerlFreshRestart On
Make sure you read Evil things might happen when using PerlFreshRestart
.
We've already mentioned that restart or termination can sometimes take quite a long time, (e.g. tens of seconds), for a mod_perl server. The reason for that is a call to theperl_destruct()
Perl API function during the child exit phase. This will cause proper execution of END blocks found during server startup and will invoke theDESTROY
method on global objects which are still alive.
It is also possible that this operation may take a long time to finish, causing a long delay during a restart. Sometimes this will be followed by a series of messages appearing in the servererror_log
file, warning that certain child processes did not exit as expected. This happens when after a few attempts advising the child process to quit, the child is still in the middle ofperl_destruct()
, and a lethalKILL
signal is sent, aborting any operation the child has happened to execute and brutally killing it.
If your code does not contain anyEND
blocks orDESTROY
methods which need to be run during child server shutdown, or may have these, but it's insignificant to execute them, this destruction can be avoided by setting thePERL_DESTRUCT_LEVEL
environment variable to-1
. For example add this setting to thehttpd.conf
file:
PerlSetEnv PERL_DESTRUCT_LEVEL -1
What constitutes a significant cleanup? Any change of state outside of the current process that would not be handled by the operating system itself. So committing database transactions and removing the lock on some resource are significant operations, but closing an ordinary file isn't.
The Apache distribution comes with a script to control the server. It's calledapachectl
and it is installed into the same location as the httpd executable. We will assume for the sake of our examples that it's in/usr/local/sbin/httpd_perl/apachectl
:
To start httpd_perl:
% /usr/local/sbin/httpd_perl/apachectl start
To stop httpd_perl:
% /usr/local/sbin/httpd_perl/apachectl stop
To restart httpd_perl (if it is running, send SIGHUP
; if it is not already running just start it):
% /usr/local/sbin/httpd_perl/apachectl restart
Do a graceful restart by sending a SIGUSR1
, or start if not running:
% /usr/local/sbin/httpd_perl/apachectl graceful
To do a configuration test:
% /usr/local/sbin/httpd_perl/apachectl configtest
Replacehttpd_perl
withhttpd_docs
in the above calls to control thehttpd_docs
server.
There are other options forapachectl
, use thehelp
option to see them all.
It's important to remember thatapachectl
uses the PID file, which is specified by thePIDFILE
directive inhttpd.conf
. If you delete the PID file by hand while the server is running,apachectl
will be unable to stop or restart the server.
You have prepared a new version of code, uploaded it into a production server, restarted it and it doesn't work. What could be worse than that? You also cannot go back, because you have overwritten the good working code.
It's quite easy to prevent it, just don't overwrite the previous working files!
Personally I do all updates on the live server with the following sequence. Assume that the server root directory is/home/httpd/perl/rel
. When I'm about to update the files I create a new directory/home/httpd/perl/beta
, copy the old files from/home/httpd/perl/rel
and update it with the new files. Then I do some last sanity checks (check file permissions are [read+executable], and runperl -c
on the new modules to make sure there no errors in them). When I think I'm ready I do:
% cd /home/httpd/perl
% mv rel old && mv beta rel && stop && sleep 3 && restart && err
Let me explain what this does.
Firstly, note that I put all the commands on one line, separated by&&
, and only then press theEnter
key. As I am working remotely, this ensures that if I suddenly lose my connection (sadly this happens sometimes) I won't leave the server down if only thestop
command squeezed in.&&
also ensures that if any command fails, the rest won't be executed. I am using aliases (which I have already defined) to make the typing easier:
% alias | grep apachectl
graceful /usr/local/apache/bin/apachectl graceful
rehup /usr/local/apache/sbin/apachectl restart
restart /usr/local/apache/bin/apachectl restart
start /usr/local/apache/bin/apachectl start
stop /usr/local/apache/bin/apachectl stop
% alias err
tail -f /usr/local/apache/logs/error_log
Taking the line apart piece by piece:
mv rel old &&
back up working directory to old
mv beta rel &&
put the new one in its place
stop &&
stop the server
sleep 3 &&
give it a few seconds to shut down (it might take even longer)
restart &&
restart
the server
err
view of the tail of the error_log
file in order to see that everything is OK
apachectl
generates the status messages a little too early (e.g. when you issueapachectl stop
it says the server has been stopped, while in fact it's still running) so don't rely on it, rely on theerror_log
file instead.
Also notice that I userestart
and not juststart
. I do this because of Apache's potentially long stopping times (it depends on what you do with it of course!). If you usestart
and Apache hasn't yet released the port it's listening to, the start would fail anderror_log
would tell you that the port is in use, e.g.:
Address already in use: make_sock: could not bind to port 8080
But if you use restart
, it will wait for the server to quit and then will cleanly restart it.
Now what happens if the new modules are broken? First of all, I see immediately an indication of the problems reported in theerror_log
file, which Itail -f
immediately after arestart
command. If there's a problem, I just put everything back as it was before:
% mv rel bad && mv old rel && stop && sleep 3 && restart && err
Usually everything will be fine, and I have had only about 10 seconds of downtime, which is pretty good!
What happens if you really must take down the server or disable the scripts? This situation might happen when you need to do some maintenance work on your database server. If you have to take your database down then any scripts that use it will fail.
If you do nothing, the user will see either the greyAn Error has happened
message or perhaps a customized error message if you have added code to trap and customize the errors. SeeRedirecting Errors to the Client instead of to the error_log
for the latter case.
A much friendlier approach is to confess to your users that you are doing some maintenance work and plead for patience, promising (keep the promise!) that the service will become fully functional in X minutes. There are a few ways to do this:
The first doesn't require messing with the server. It works when you have to disable a script running under Apache::Registry
and relies on the fact that it checks whether the file was modified before using the cached version. Obviously it won't work under other handlers because these serve the compiled version of the code and don't check to see if there was a change in the code on the disk.
So if you want to disable an Apache::Registry
script, prepare a little script like this:
/home/http/perl/maintenance.pl
#!/usr/bin/perl -Tw
use strict;
use CGI;
my $q = new CGI;
print $q->header, $q->p(
"Sorry, the service is temporarily down for maintenance.
It will be back in ten to fifteen minutes.
Please, bear with us.
Thank you!");
So if you now have to disable a script for example /home/http/perl/chat.pl
, just do this:
% mv /home/http/perl/chat.pl /home/http/perl/chat.pl.orig
% ln -s /home/http/perl/maintenance.pl /home/http/perl/chat.pl
Of course you server configuration should allow symbolic links for this trick to work. Make sure you have the directive
Options FollowSymLinks
in the<Location>
or<Directory>
section of yourhttpd.conf
.
When you're done, it's easy to restore the previous setup. Just do this:
% mv /home/http/perl/chat.pl.orig /home/http/perl/chat.pl
wichi overwrites the symbolic link.
Now make sure that the script will have the current timestamp:
% touch /home/httpd/perl/chat.pl
Apache will automatically detect the change and will use the moved script instead.
The second approach is to change the server configuration and configure a whole directory to be handled by a My::Maintenance
handler (which you must write). For example if you write something like this:
My/Maintenace.pm
----------------
use strict;
use Apache::Constants qw(:common);
sub handler {
my $r = shift;
print $r->send_http_header("text/plain");
print qq{
We apologize, but this service is temporarily stopped for
maintenance. It will be back in ten to fifteen minutes.
Please, bear with us. Thank you!
};
return OK;
}
1;
and put it in a directory that is in the server's@INC
, to disable all the scripts in Location/perl
you would replace:
<Location /perl>
SetHandler perl-script
PerlHandler My::Handler
[snip]
</Location>
with
<Location /perl>
SetHandler perl-script
PerlHandler My::Maintenance
[snip]
</Location>
Now restart the server. Your users will be happy to go and read http://slashdot.org
for ten minutes, knowing that you are working on a much better version of the service.
If you need to disable a location handled by some module, the second approach would work just as well.
If you want to allow a few people in your team to start and stop the server you will have to give them the root password, which is not a good thing to do. The less people know the password, the less problems are likely to be encountered. But there is an easy solution for this problem available on UNIX platforms. It's called a setuid executable.
The setuid executable has a setuid permissions bit set. This sets the process's effective user ID to that of the file upon execution. You perform this setting with the following command:
% chmod u+s filename
You probably have used setuid executables before without even knowing about it. For example when you change your password you execute thepasswd
utility, which among other things modifies the/etc/passwd file
. In order to change this file you need root permissions, thepasswd
utility has the setuid bit set, therefore when you execute this utility, its effective ID is the same of the root user ID.
You should avoid using setuid executables as a general practice. The less setuid executables you have the less likely that someone will find a way to break into your system, by exploiting some bug you didn't know about.
When the executable is setuid to root, you have to make sure that it doesn't have the group and world read and write permissions. If we take a look at the passwd
utility we will see:
% ls -l /usr/bin/passwd
-r-s--x--x 1 root root 12244 Feb 8 00:20 /usr/bin/passwd
You achieve this with the following command:
% chmod 4511 filename
The first digit (4) stands for setuid bit, the second digit (5) is a compound of read (4) and executable (1) permissions for the user, and the third and the fourth digits are setting the executable permissions for the group and the world.
In our case, we want to allow setuid access only to a specific group of users, who all belong to the same group. For the sake of our example we will use the group namedapache
. It's important that users who aren't root or who don't belong to theapache
group will not be able to execute this script. Therefore we perform the following commands:
% chgrp apache apachectl
% chmod 4510 apachectl
The execution order is important. If you swap the command execution order you will lose the setuid bit.
Now if we look at the file we see:
% ls -l apachectl
-r-s--x--- 1 root apache 32 May 13 21:52 apachectl
Now we are all set... Almost...
When you start Apache, Apache and Perl modules are being loaded, code can be executed. Since all this happens with root effective ID, any code executed as if the root user was doing that. You should be very careful because while you didn't gave anyone the root password, all the users in the apache group have an indirect root access. Which means that if Apache loads some module or executes some code that is writable by some of these users, users can plant code that will allow them to gain a shell access to root account and become a real root.
Of course if you don't trust your team you shouldn't use this solution in first place. You can try to check that all the files Apache loads aren't writable by anyone but root, but there are too many of them, especially in the mod_perl case, where many Perl modules are loaded at the server startup.
By the way, don't let all this setuid stuff to confuse you -- when the parent process is loaded, the children processes are spawned as non-root processes. This section has presented a way to allow non-root users to start the server as root user, the rest is exactly the same as if you were executing the script as root in first place.
Now if you are still with us, here is an example of the setuid Apache startup script.
Note the line markedWORKAROUND
, which fixes an obscure error when starting mod_perl enabled Apache by setting the real UID to the effective UID. Without this workaround, a mismatch between the real and the effective UID causes Perl to croak on the-e
switch.
Note that you must be using a version of Perl that recognizes and emulates the suid bits in order for this to work. This script will do different things depending on whether it is namedstart_httpd
,stop_httpd
orrestart_httpd
. You can use symbolic links for this purpose.
suid_apache_ctl
---------------
#!/usr/bin/perl -T
# These constants will need to be adjusted.
# これらの定数は調整される必要あり
$PID_FILE = '/home/www/logs/httpd.pid';
$HTTPD = '/home/www/httpd -d /home/www';
# These prevent taint warnings while running suid
# これらは suid 実行中に汚染警告を防止する
$ENV{PATH} = '/bin:/usr/bin';
$ENV{IFS} = '';
# This sets the real to the effective ID, and prevents
# an obscure error when starting apache/mod_perl
# これは実効 ID にリアルをセットして, apache/mod_perl のスタート時に
# 曖昧なエラーを防止する
$< = $>; # WORKAROUND
$( = $) = 0; # set the group to root too (グループも root に)
# Do different things depending on our name
# 名前により違うことをする
($name) = $0 =~ m|([^/]+)$|;
if ($name eq 'start_httpd') {
system $HTTPD and die "Unable to start HTTP";
print "HTTP started.\n";
extit 0;
}
# extract the process id and confirm that it is numeric
# プロセス ID を抽出しそれが数字であることを確認
$pid = `cat $PID_FILE`;
$pid =~ /(\d+)/ or die "PID $pid not numeric";
$pid = $1;
if ($name eq 'stop_httpd') {
kill 'TERM',$pid or die "Unable to signal HTTP";
print "HTTP stopped.\n";
exit 0;
}
if ($name eq 'restart_httpd') {
kill 'HUP',$pid or die "Unable to signal HTTP";
print "HTTP restarted.\n";
exit 0;
}
die "Script must be named start_httpd, stop_httpd, or restart_httpd.\n";
When you run your own development box, it's okay to start the webserver by hand when you need to. On a production system it is possible that the machine the server is running on will have to be rebooted. When the reboot is completed, who is going to remember to start the server? It's easy to forget this task, and what happens if you aren't around when the machine is rebooted?
After the server installation is complete, it's important not to forget that you need to put a script to perform the server startup and shutdown into the standard system location, for example/etc/rc.d
under RedHat Linux, or/etc/init.d/apache
under Debian Slink Linux.
This is the directory which contains scripts to start and stop all the other daemons. The directory and file names vary from one Operating System (OS) to another, and even between different distributions of the same OS.
Generally the simplest solution is to copy the apachectl script to your startup directory or create a symbolic link from the startup directory to the apachectl script. You will find apachectl in the same directory as the httpd executable after Apache installation. If you have more than one Apache server you will need a separate script for each one, and of course you will have to rename them so that they can co-exist in the same directories.
For example on a RedHat Linux machine with two servers, I have the following setup:
/etc/rc.d/init.d/httpd_docs
/etc/rc.d/init.d/httpd_perl
/etc/rc.d/rc3.d/S91httpd_docs -> ../init.d/httpd_docs
/etc/rc.d/rc3.d/S91httpd_perl -> ../init.d/httpd_perl
/etc/rc.d/rc6.d/K16httpd_docs -> ../init.d/httpd_docs
/etc/rc.d/rc6.d/K16httpd_perl -> ../init.d/httpd_perl
The scripts themselves reside in the /etc/rc.d/init.d
directory. There are symbolic links to these scripts in other directories. The names are the same as the script names but they have numerical prefixes, which are used for executing the scripts in a particular order: the lower numbers are executed earlier.
When the system starts (level 3) we want the Apache to be started when almost all of the services are running already, therefore I've usedS91
. For example if the mod_perl enabled Apache issues aconnect_on_init()
the SQL server should be started before Apache.
When the system shuts down (level 6), Apache should be stopped as one of the first processes, therefore I've used K16
. Again if the server does some cleanup processing during the shutdown event and requires third party services to be running (e.g. SQL server) it should be stopped before these services.
Notice that it's normal for more than one symbolic link to have the same sequence number.
Under RedHat Linux and similar systems, when a machine is booted and its runlevel set to 3 (multiuser + network), Linux goes into/etc/rc.d/rc3.d/
and executes the scripts the symbolic links point to with thestart
argument. When it seesS91httpd_perl
, it executes:
/etc/rc.d/init.d/httpd_perl start
When the machine is shut down, the scripts are executed through links from the/etc/rc.d/rc6.d/
directory. This time the scripts are called with thestop
argument, like this:
/etc/rc.d/init.d/httpd_perl stop
Most systems have GUI utilities to automate the creation of symbolic links. For example RedHat Linux includes thecontrol-panel
utility, which amongst other things includes theRunLevel Manager
. (which can be invoked directly as eitherntsysv(8)
ortksysv(8)
). This will help you to create the proper symbolic links. Of course before you use it, you should putapachectl
or similar scripts into theinit.d
or equivalent directory. Or you can have a symbolic link to some other location instead.
The simplest approach is to use thechkconfig(8)
utility which adds and removes the services for you. The following example shows how to add anhttpd_perl
startup script to the system.
First move or copy the file into the directory /etc/rc.d/init.d
:
% mv httpd_perl /etc/rc.d/init.d
Now open the script in your favorite editor and add the following lines after the main header of the script:
# Comments to support chkconfig on RedHat Linux
# (RedHat Linux で chkconfig をサポートするためのコメント)
# chkconfig: 2345 91 16
# description: mod_perl enabled Apache Server
# (説明: mod_perl が有効化された Apache Server)
So now the beginning of the script looks like:
#!/bin/sh
#
# Apache control script designed to allow an easy command line
# interface to controlling Apache. Written by Marc Slemko,
# 1997/08/23
# (Apache のコントロールを簡単なコマンドラインインターフェイスで
# できるように設計された Apache コントロールスクリプト. Marc Slemko により書かれた,
# 1997/08/23)
# Comments to support chkconfig on RedHat Linux
# chkconfig: 2345 91 16
# description: mod_perl enabled Apache Server
#
# The exit codes retuned are:
# ...
Adjust the line:
# chkconfig: 2345 91 16
to your needs. The above setting says to says that the script should be started in levels 2, 3, 4, and 5, that its start priority should be 91, and that its stop priority should be 16.
Now all you have to do is to ask chkconfig
to configure the startup scripts. Before we do that let's look at what we have:
% find /etc/rc.d | grep httpd_perl
/etc/rc.d/init.d/httpd_perl
Which means that we only have the startup script itself. Now we execute:
% chkconfig --add httpd_perl
and see waht has changed:
% find /etc/rc.d | grep httpd_perl
/etc/rc.d/init.d/httpd_perl
/etc/rc.d/rc0.d/K16httpd_perl
/etc/rc.d/rc1.d/K16httpd_perl
/etc/rc.d/rc2.d/S91httpd_perl
/etc/rc.d/rc3.d/S91httpd_perl
/etc/rc.d/rc4.d/S91httpd_perl
/etc/rc.d/rc5.d/S91httpd_perl
/etc/rc.d/rc6.d/K16httpd_perl
As you can see chkconfig
created all the symbolic links for us, using the startup and shutdown priorities as specified in the line:
# chkconfig: 234 91 16
If for some reason you want to remove the service from the startup scripts, all you have to do is to tell chkconfig
to remove the links:
% chkconfig --del httpd_perl
Now if we look at the files under the directory /etc/rc.d/
we see again only the script itself.
% find /etc/rc.d | grep httpd_perl
/etc/rc.d/init.d/httpd_perl
Of course you may keep the startup script in any other directory as long as you can link to it. For example if you want to keep this file with all the Apache binaries in /usr/local/apache/bin
, all you have to do is to provide a symbolic link to this file:
% ln -s /usr/local/apache/bin/apachectl /etc/rc.d/init.d/httpd_perl
and then:
% chkconfig --add httpd_perl
Note that in case of using symlinks the link name in /etc/rc.d/init.d
is what matters and not the name of the script the link points to.
With mod_perl many things can happen to your server. It is possible that the server might die when you are not around. As with any other critical service you need to run some kind of watchdog.
One simple solution is to use a slightly modifiedapachectl
script, which I've namedapache.watchdog
. Call it from thecrontab
every 30 minutes -- or even every minute -- to make sure the server is up all the time.
The crontab
entry for 30 minutes intervals:
0,30 * * * * /path/to/the/apache.watchdog >/dev/null 2>&1
The script:
#!/bin/sh
# this script is a watchdog to see wheter the server is online
# It tries to restart the server, and if it's
# down it sends an email alert to admin
# このスクリプトはサーバがオンラインかどうかを見るための watchdog
# サーバのリスタートを試みて, それがダウンしているなら
# admin に email アラートを送信する
# admin's email
EMAIL=webmaster@example.com
# the path to your PID file
PIDFILE=/usr/local/var/httpd_perl/run/httpd.pid
# the path to your httpd binary, including options if necessary
HTTPD=/usr/local/sbin/httpd_perl/httpd_perl
# check for pidfile
if [ -f $PIDFILE ] ; then
PID=`cat $PIDFILE`
if kill -0 $PID; then
STATUS="httpd (pid $PID) running"
RUNNING=1
else
STATUS="httpd (pid $PID?) not running"
RUNNING=0
fi
else
STATUS="httpd (no pid file) not running"
RUNNING=0
fi
if [ $RUNNING -eq 0 ]; then
echo "$0 $ARG: httpd not running, trying to start"
if $HTTPD ; then
echo "$0 $ARG: http started"
mail $EMAIL -s "$0 $ARG: httpd started" > /dev/null 2>&1
else
echo "$0 $ARG: httpd could not be started"
mail $EMAIL -s \
"$0 $ARG: httpd could not be started" > /dev/null 2>&1
fi
fi
Another approach, probably even more practical, is to use the cool LWP
Perl package to test the server by trying to fetch some document (script) served by the server. Why is it more practical? Because while the server can be up as a process, it can be stuck and not working. Failing to get the document will trigger restart, and "probably" the problem will go away.
Like before we set a cronjob to call this script every few minutes to fetch some very light script. The best thing of course is to call it every minute. Why so often? If your server starts to spin and trash your disk space with multiple error messages filling theerror_log
, in five minutes you might run out of free disk space which might bring your system to its knees. Chances are that no other child will be able to serve requests, since the system will be too busy writing to theerror_log
file. Think big--if you are running a heavy service (which is very fast since you are running under mod_perl) adding one more request every minute will not be felt by the server at all.
So we end up with a crontab
entry like this:
* * * * * /path/to/the/watchdog.pl >/dev/null 2>&1
And the watchdog itself:
#!/usr/bin/perl -wT
# untaint
# 非汚染
$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
use strict;
use diagnostics;
use URI::URL;
use LWP::MediaTypes
my $VERSION = '0.01';
use vars qw($ua $proxy);
$proxy = '';
require LWP::UserAgent;
use HTTP::Status;
###### Config #########
my $test_script_url = 'http://www.example.com:81/perl/test.pl';
my $monitor_email = 'root@localhost';
my $restart_command = '/usr/local/sbin/httpd_perl/apachectl restart';
my $mail_program = '/usr/lib/sendmail -t -n';
######################
$ua = new LWP::UserAgent;
$ua->agent("$0/watchdog " . $ua->agent);
# Uncomment the proxy if you access a machine from behind a firewall
# あなたがファイアウォールの背後からマシンにアクセスする場合はこのプロクシをアンコメント
# $proxy = "http://www-proxy.com";
$ua->proxy('http', $proxy) if $proxy;
# If it returns '1' it means we are alive
# '1' をリターンした場合は私たちが生存していることを意味する
exit 1 if checkurl($test_script_url);
# Houston, we have a problem.
# The server seems to be down, try to restart it.
# ヒューストン, 問題が発生した.
# サーバがダウンしているようだ, リスタートを試みる.
my $status = system $restart_command;
my $message = ($status == 0)
? "Server was down and successfully restarted!"
: "Server is donw. Can't restart.";
my $subject = ($status == 0)
? "Attention! Webserver restarted"
: "Attention! Webserver is down. can't restart";
# email the monitoring person
# モニタリング担当者に email
my $to = $monitor_email;
my $from = $monitor_email;
send_mail($from,$to,$subject,$message);
# input: URL to check
# output: 1 for success, 0 for failure
#######################
sub checkurl{
my ($url) = @_;
# Fetch document
# ドキュメントの取得
my $res = $ua->request(HTTP::Request->new(GET => $url));
# Check the result status
# 結果ステータスのチェック
return 1 if is_success($res->code);
# failed
# 失敗した
return 0;
} # end of sub checkurl
# サブルーチン checkurl の終わり
# send email about the problem
# 問題についての email を送信
#######################
sub send_mail{
my ($from,$to,$subject,$messagebody) = @_;
open MAIL, "|$mail_program"
or die "Can't open a pipe to a $mail_program :$!\n";
print MAIL