/*
 * 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);

}
