Newer
Older
mailpiler / src / stats.c
/*
 * stats.c, SJ
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netdb.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <locale.h>
#include <syslog.h>
#include <getopt.h>
#include <piler.h>

#define SMTP_TIMEOUT 5

extern char *optarg;
extern int optind;

struct stats {
   uint64 rcvd;
   uint64 size;
   uint64 ssize;

   uint64 sphx;
   uint64 ram_bytes;
   uint64 disk_bytes;

   uint64 error_emails;
   long last_email;
   float smtp_response_time;
};


int query_counters(struct session_data *sdata, struct stats *stats){
   int rc=ERR;
   struct sql sql;

   if(prepare_sql_statement(sdata, &sql, "select rcvd, size, stored_size from counter") == ERR) return rc;

   p_bind_init(&sql);

   if(p_exec_stmt(sdata, &sql) == OK){
      p_bind_init(&sql);

      sql.sql[sql.pos] = (char *)&(stats->rcvd); sql.type[sql.pos] = TYPE_LONGLONG; sql.len[sql.pos] = sizeof(uint64); sql.pos++;
      sql.sql[sql.pos] = (char *)&(stats->size); sql.type[sql.pos] = TYPE_LONGLONG; sql.len[sql.pos] = sizeof(uint64); sql.pos++;
      sql.sql[sql.pos] = (char *)&(stats->ssize); sql.type[sql.pos] = TYPE_LONGLONG; sql.len[sql.pos] = sizeof(uint64); sql.pos++;

      p_store_results(&sql);

      if(p_fetch_results(&sql) == OK) rc = OK;

      p_free_results(&sql);
   }

   close_prepared_statement(&sql);

   return rc;
}


int get_last_email_archived_timestamp(struct session_data *sdata, struct stats *stats){
   int rc=ERR;
   unsigned long arrived=86400;
   struct sql sql;

   // By default we haven't archived a mail a day ago, and this value should represent an error
   time(&(sdata->now));
   arrived = sdata->now - 86400;

   if(prepare_sql_statement(sdata, &sql, "select arrived from metadata order by id desc limit 1") == ERR) return rc;

   p_bind_init(&sql);

   if(p_exec_stmt(sdata, &sql) == OK){
      p_bind_init(&sql);

      sql.sql[sql.pos] = (char *)&arrived; sql.type[sql.pos] = TYPE_LONG; sql.len[sql.pos] = sizeof(arrived); sql.pos++;

      p_store_results(&sql);

      if(p_fetch_results(&sql) == OK) rc = OK;

      p_free_results(&sql);
   }

   close_prepared_statement(&sql);

   stats->last_email = sdata->now - arrived;

   return rc;
}


void sphinx_queries(struct session_data *sdata, struct stats *stats){
   MYSQL_RES *result;
   MYSQL_ROW row;

   p_query(sdata, "SHOW STATUS LIKE 'queries'");

   result = mysql_store_result(&(sdata->mysql));
   if(result){
      row = mysql_fetch_row(result);

      if(row){
         if(mysql_num_fields(result) == 2){
            stats->sphx = strtoull(row[1], NULL, 10);
         }
      }

      mysql_free_result(result);
   }

   p_query(sdata, "SHOW INDEX main1 STATUS");

   result = mysql_store_result(&(sdata->mysql));
   if(result){
      while((row = mysql_fetch_row(result))){
         if(strcmp((char*)row[0], "ram_bytes") == 0) stats->ram_bytes = strtoull(row[1], NULL, 10);
         if(strcmp((char*)row[0], "disk_bytes") == 0) stats->disk_bytes = strtoull(row[1], NULL, 10);
      }

      mysql_free_result(result);
   }
}


void count_error_emails(struct stats *stats){
   DIR *dir;
   struct dirent *de;
   struct stat st;
   char buf[SMALLBUFSIZE];

   dir = opendir(ERROR_DIR);
   if(dir){
      while((de = readdir(dir))){
         if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;

         snprintf(buf, sizeof(buf)-1, "%s/%s", ERROR_DIR, de->d_name);

         if(stat(buf, &st) == 0 && S_ISREG(st.st_mode)){
            stats->error_emails++;
         }
      }

      closedir(dir);
   }
}


void check_smtp_status(struct stats *stats, struct config *cfg){
   int sd, rc;
   char port_string[8];
   char buf[SMALLBUFSIZE];
   struct addrinfo hints, *res;
   struct timezone tz;
   struct timeval tv1, tv2;

   // Set this to a very high number, 1 hour in ms
   stats->smtp_response_time = 3600000;

   memset(buf, 0, sizeof(buf));

   snprintf(port_string, sizeof(port_string)-1, "%d", cfg->listen_port);

   memset(&hints, 0, sizeof(hints));
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;

   if((rc = getaddrinfo(cfg->listen_addr, port_string, &hints, &res)) != 0){
      fprintf(stderr, "getaddrinfo for '%s': %s\n", cfg->listen_addr, gai_strerror(rc));
      return;
   }

   if((sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){
      fprintf(stderr, "cannot create socket\n");
      goto ENDE_CHECK_SMTP_STATUS;
   }

   gettimeofday(&tv1, &tz);

   if(connect(sd, res->ai_addr, res->ai_addrlen) == -1){
      fprintf(stderr, "connect()\n");
      goto ENDE_CHECK_SMTP_STATUS;
   }

   recvtimeout(sd, buf, sizeof(buf)-1, SMTP_TIMEOUT);

   close(sd);

   gettimeofday(&tv2, &tz);

   if(strncmp(buf, "220 ", 4) == 0){
      stats->smtp_response_time = tvdiff(tv2, tv1) / 1000.0; // response time in ms
   }

ENDE_CHECK_SMTP_STATUS:
   freeaddrinfo(res);
}


void print_json_results(struct stats *stats){
   printf("{\n");
   printf("\t\"rcvd\": %llu,\n", stats->rcvd);
   printf("\t\"size\": %llu,\n", stats->size);
   printf("\t\"ssize\": %llu,\n", stats->ssize);
   printf("\t\"sphx\": %llu,\n", stats->sphx);
   printf("\t\"ram_bytes\": %llu,\n", stats->ram_bytes);
   printf("\t\"disk_bytes\": %llu,\n", stats->disk_bytes);
   printf("\t\"error_emails\": %llu,\n", stats->error_emails);
   printf("\t\"last_email\": %ld,\n", stats->last_email);
   printf("\t\"smtp_response\": %.2f\n", stats->smtp_response_time);

   printf("}\n");
}


int main(int argc, char **argv){
   struct session_data sdata;
   struct stats stats;
   struct config cfg;
   char *configfile=CONFIG_FILE;

   memset(&stats, 0, sizeof(stats));

   srand(getpid());

   (void) openlog("pilerstat", LOG_PID, LOG_MAIL);

   cfg = read_config(configfile);

   if(open_database(&sdata, &cfg) == ERR) return 0;

   query_counters(&sdata, &stats);
   get_last_email_archived_timestamp(&sdata, &stats);

   close_database(&sdata);


   cfg.mysqlsocket[0] = '\0';
   snprintf(cfg.mysqlhost, MAXVAL-2, "127.0.0.1");
   cfg.mysqlport = 9306;

   if(open_database(&sdata, &cfg) == ERR) return 0;

   sphinx_queries(&sdata, &stats);

   close_database(&sdata);

   count_error_emails(&stats);

   check_smtp_status(&stats, &cfg);

   print_json_results(&stats);

   return 0;
}