blob: 569affb82a823097efd1f0863285cac72deaac97 [file] [log] [blame]
/*
* Copyright (C) Tildeslash Ltd. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU Affero General Public License in all respects
* for all of the code used other than OpenSSL.
*/
#include "config.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include "protocol.h"
#undef READ_SIZE
#define READ_SIZE 1024
/* Private prototypes */
static int check_apache_stat(Socket_T s);
/**
* Check an Apache server to monitor its status.
* Do this using the server-status report from mod_status, which
* will only be available if the server is responding to
* some extent.
*
* Currently based on the Scoreboard response, which is available
* even with ExtendedStatus Off on Apache config file.
*
* @file
*/
int check_apache_status(Socket_T socket) {
char host[STRLEN];
const char *request= "/server-status?auto";
ASSERT(socket);
if(socket_print(socket, "GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Accept: */*\r\n"
"User-Agent: %s/%s\r\n"
"Connection: close\r\n\r\n",
request, Util_getHTTPHostHeader(socket, host, STRLEN),
prog, VERSION) < 0) {
socket_setError(socket, "HTTP: error sending data -- %s\n", STRERROR);
return FALSE;
}
return check_apache_stat(socket);
}
/* ----------------------------------------------------------------- Private */
/**
* Extract the Scoreboard line from the mod_status response.
* Count the active apache child processes, and those which are
* in other states. If each percentage exceeds the corresponding
* limit, then return FALSE.
* @param s A socket
* @param limit The maximum percentage of logging processes
* @return TRUE if logging is OK otherwise FALSE
*/
static int check_apache_stat(Socket_T socket) {
int scored = 0;
int errors = 0;
char line[READ_SIZE];
char search_string[READ_SIZE + 1];
int loglimit= 0;
int closelimit= 0;
int dnslimit= 0;
int keepalivelimit= 0;
int replylimit= 0;
int requestlimit= 0;
int startlimit= 0;
int waitlimit= 0;
int gracefullimit= 0;
int cleanuplimit= 0;
int no_logging = 0;
int no_close = 0;
int no_dns = 0;
int no_keepalive = 0;
int no_reply = 0;
int no_request = 0;
int no_start = 0;
int no_wait = 0;
int no_graceful = 0;
int no_cleanup = 0;
int active_servers = 0;
char *p;
Port_T myPort= (Port_T)socket_get_Port(socket);
ASSERT(myPort);
loglimit= myPort->ApacheStatus.loglimit;
closelimit= myPort->ApacheStatus.closelimit;
dnslimit= myPort->ApacheStatus.dnslimit;
keepalivelimit= myPort->ApacheStatus.keepalivelimit;
replylimit= myPort->ApacheStatus.replylimit;
requestlimit= myPort->ApacheStatus.requestlimit;
startlimit= myPort->ApacheStatus.startlimit;
waitlimit= myPort->ApacheStatus.waitlimit;
gracefullimit= myPort->ApacheStatus.gracefullimit;
cleanuplimit= myPort->ApacheStatus.cleanuplimit;
while(NULL != socket_readln(socket, line, READ_SIZE)) {
if(Str_startsWith(line, "Scoreboard")) {
if(1 != sscanf(line, "%*s%*[: ]%1024s", search_string)) {
Str_chomp(line);
socket_setError(socket, "APACHE-STATUS error: parsing Apache status response '%s'\n",
line);
return FALSE;
} else {
scored = 1;
}
}
}
DEBUG("Scoreboard: %s\n", search_string);
/* Check that some scoreboard line was found, if not return an error */
if(!scored){
socket_setError(socket, "APACHE-STATUS error: no scoreboard line returned by Apache\n");
return FALSE;
}
/* Total each of the status messages in the scoreboard */
for(p = search_string ; *p ; p++){
active_servers++;
switch(*p){
case 'S':
no_start++;
break;
case 'R':
no_request++;
break;
case 'W':
no_reply++;
break;
case 'K':
no_keepalive++;
break;
case 'D':
no_dns++;
break;
case 'C':
no_close++;
break;
case 'L':
no_logging++;
break;
case 'G':
no_graceful++;
break;
case 'I':
no_cleanup++;
break;
case '_':
no_wait++;
break;
case '.':
active_servers--;
break;
}
}
if(active_servers <= 0){
socket_setError(socket, "APACHE-STATUS warning: No idle server or threads found\n");
/* This is not really an error, only a very bussy server */
return TRUE;
}
/*
* Conditions are only tested if the limit parameter is greater than zero.
*/
if(loglimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.loglimitOP,
(100 * no_logging / active_servers), loglimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are logging\n", loglimit);
errors++;
}
}
if(startlimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.startlimitOP,
(100 * no_start / active_servers), startlimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are starting\n", startlimit);
errors++;
}
}
if(requestlimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.requestlimitOP,
(100 * no_request / active_servers), requestlimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are reading requests\n",
requestlimit);
errors++;
}
}
if(replylimit > 0 ){
if(Util_evalQExpression(myPort->ApacheStatus.replylimitOP,
(100 * no_reply / active_servers), replylimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are sending a reply\n", replylimit);
errors++;
}
}
if(keepalivelimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.keepalivelimitOP,
(100 * no_keepalive / active_servers), keepalivelimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are in keepalive\n", keepalivelimit);
errors++;
}
}
if(dnslimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.dnslimitOP,
(100 * no_dns / active_servers), dnslimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are waiting for DNS\n", dnslimit);
errors++;
}
}
if(closelimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.closelimitOP,
(100 * no_close / active_servers), closelimit)){
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are closing connections\n",
closelimit);
errors++;
}
}
if(gracefullimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.gracefullimitOP,
(100 * no_graceful / active_servers), gracefullimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are finishing gracefully\n",
gracefullimit);
errors++;
}
}
if(cleanuplimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.cleanuplimitOP,
(100 * no_cleanup / active_servers), cleanuplimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are in idle cleanup\n",
cleanuplimit);
errors++;
}
}
if(waitlimit > 0){
if(Util_evalQExpression(myPort->ApacheStatus.waitlimitOP,
(100 * no_wait / active_servers), waitlimit)) {
socket_setError(socket, "APACHE-STATUS error:"
" %i percent of Apache processes are waiting for a connection\n",
waitlimit);
errors++;
}
}
return (errors==0);
}