Logo Search packages:      
Sourcecode: nagircbot version File versions  Download package

anna.cpp

/* (C) 2006-2011 by folkert@vanheusden.com GPLv2 applies */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <pwd.h>
#include <sys/types.h>
#include <regex.h>

#include "utils.h"
#include "pl.h"
extern "C" {
#include "error.h"
#include "ssl.h"
#include "log.h"
}

#define S_DISCONNECTED        1
#define S_CONNECTED           2
#define S_JOINING_CHANNEL     3
#define S_ON_CHANNEL          4
#define S_DISCONNECTING       127

#define L_HOST                1
#define L_FILE                2

#define ST_HARD               1
#define ST_SOFT               0

int check_interval = 60;      /* check for new events every 1 minute */
int default_sleep = 2;        /* initial sleep time between connect failures */
int max_sleep_time = 300;
double sleep_multiply_factor = 2.0;
int minimum_time_for_successfull_login = 25; // one needs to be on-channel for at least 5 seconds to be considered a successfull login
int join_timeout = 5;   // it should take no longer then 5 seconds to join a channel, otherwhise: abort connection and retry
int max_n_join_tries = 2;     // try 2 times to get on a channel
char *server = "localhost:6667";    /* default irc server */
char *channel = "#nagircbot"; /* default channel to connect to */
char *nick_prefix = "";   /* prefix text for all messages sent to channel */
char *keyword = NULL;   /* keyword for the channel to connect to */
char *nick = "nagircbot";
char *user = "nagircbot";
char *password = NULL;
int one_line = 1;
char *username = "Nagios IRC Bot " VERSION ", (C) www.vanheusden.com";  /* complete username */
int verbose = 255;            /* default is log everything */
char *statuslog = "/usr/local/nagios/var/status.log";
int statuslog_version = 2;
int statuslog_location = L_FILE;
char use_colors = 0;
char topic_summary = 0;
char hard_only = 1;
int max_time_last_host_update = 300, max_time_oldest_host_update = 3600, max_time_last_host_check = 300, max_time_oldest_host_check = 3 * 86400, max_time_last_service_check = 20 * 60, max_time_oldest_service_check = 3 * 86400, max_time_oldest_next_service_check = 20 * 60;
int irc_server_keep_alive = 60;     /* send an irc-command at least every 300 seconds (currently 'TIME') */
int announce_global_status_interval = 0;
time_t last_announce_global_status_interval = 0;
char critical_only = 0;
regex_t *filters = NULL;
int n_filters = 0;
int use_ssl       = 0;


server_t server_conn;

char *state_str[4] = { " OK ", "WARN", "CRIT", " ?? " };
char *color_str[4];
struct stats *prev = NULL;
int n_prev = 0;
char topic[4096] = { 0 };
time_t last_irc_io = 0;
char *pidfile = NULL;

int send_irc(server_t server_conn, char *format, ...)
{
      int rc;
      char buffer[4096];
      va_list ap;

      va_start(ap, format);
      vsnprintf(buffer, sizeof(buffer) - 3, format, ap);
      va_end(ap);

      if (verbose > 1) dolog("OUT: `%s'", buffer);

      strcat(buffer, "\r\n"); /* yes this is safe: check the vsnprintf command */

      rc = IRCWRITE(server_conn, buffer, strlen(buffer));
      if (rc == -1)
      {
            dolog("error sending: -1");
            return -1;
      }
      else if (rc == 0)
      {
            dolog("connection closed");
            return -1;
      }

      time(&last_irc_io);

      return 0;
}

int irc_set_nick(server_t server_conn, char *nick)
{
      if (send_irc(server_conn, "NICK %s", nick) == -1)
            return -1;

      return 0;
}

int irc_login(server_t server_conn, char *user, char *username, char *server, char *password)
{
      if (password != NULL && send_irc(server_conn, "PASS %s", password) == -1)
            return -1;

      if (irc_set_nick(server_conn, nick) == -1)
            return -1;

      /* FIXME: localhost must be, ehr, local host */
      if (send_irc(server_conn, "USER %s \"localhost\" \"%s\" :%s", user, server, username) == -1)
            return -1;

      return 0;
}

int irc_join_channel(server_t server_conn, char *channel, char *keyword)
{
      if (keyword != NULL)
            return send_irc(server_conn, "JOIN %s %s", channel, keyword);

      return send_irc(server_conn, "JOIN %s", channel);
}

int irc_topic(server_t server_conn, char *channel, char *topic)
{
      return send_irc(server_conn, "TOPIC %s :%s", channel, topic);
}

int check_ping(server_t server_conn, char *data)
{
      if (strncmp(data, "PING ", 5) == 0)
      {
            char *colon = strchr(data, ':');
            char *cr = strchr(data, '\r');
            char *lf = strchr(data, '\n');

            if (cr>lf && cr != NULL)
                  cr[1] = 0x00;
            else if (lf != NULL)
                  lf[1] = 0x00;
            if (colon)
            {
                  if (send_irc(server_conn, "PONG %s", colon + 1) == -1)
                        return -1;
            }
            else if (verbose > 1)
            {
                  dolog("Malformed PING request (%s)", data);
            }
      }

      return 0;
}

int irc_privmsg(server_t server_conn, char *channel, char *msg)
{
      return send_irc(server_conn, "PRIVMSG %s :%s", channel, msg);
}

int irc_time(server_t server_conn)
{
      return send_irc(server_conn, "TIME");
}

void load_statuslog(char *statuslog, char is_file, char is_20_format, int prev_n_elements_in_stats_array, struct stats **pstats, int *n_stats)
{
      char reload = 0;

      for(;;)
      {
            int fd;

            /* open file or connection to nagios status socket */
            if (is_file == 1)     /* file */
                  fd = open64(statuslog, O_RDONLY);
            else
                  fd = connect_to(statuslog);
            if (fd == -1)
            {
                  dolog("Failed to open nagios status-log at %s\n", statuslog);
                  sleep(1);
                  continue;
            }

            /* file/socket open, load data */
            if (is_20_format)
                  parse_2_0_statuslog(fd, pstats, n_stats);
            else
                  parse_1_0_statuslog(fd, pstats, n_stats);

            close(fd);

            if (*n_stats == prev_n_elements_in_stats_array)
            {
                  break;
            }

            dolog("Number of elements in status-log is different (%d) from previous run (%d), retrying", *n_stats, prev_n_elements_in_stats_array);
            reload = 1;
            sleep(1);

            /* remember the current number of elements for this load */
            prev_n_elements_in_stats_array = *n_stats;
            /* and free up - it'll be reloaded */
            free_stats_array(*pstats, *n_stats);
      }

      if (reload)
      {
            dolog("Size of status.log is stable (%d elements), continuing", *n_stats);
      }
}

int emit_status(server_t server_conn, char *channel, struct stats *what)
{
      int loop;
      char buffer[4096];
      char *prefix = "", *suffix = "";
      struct tm *ptm = localtime(&what -> last_state_change);

      if (use_colors)
      {
            prefix = color_str[what -> current_state];
            suffix = "\03";
      }

      if (n_filters || one_line)
      {
            snprintf(buffer, sizeof(buffer), "%s%s%04d/%02d/%02d %02d:%02d %s %s %s %s%s",
                        prefix,
                        nick_prefix,
                        ptm -> tm_year + 1900, ptm -> tm_mon + 1, ptm -> tm_mday, ptm -> tm_hour, ptm -> tm_min,
                        state_str[what -> current_state],
                        what -> host_name?what -> host_name:"",
                        what -> service_description?what -> service_description:"",
                        what -> plugin_output?what -> plugin_output:"",
                        suffix);
      }

      /* regexp matches? then do not emit */
      for(loop=0; loop<n_filters; loop++)
      {
            int rc = regexec(&filters[loop], buffer, 0, NULL, 0);

            if (rc == 0)
            {
#ifdef _DEBUG
                  printf("Line '%s' filtered by regexp\n", buffer);
#endif
                  return 0; /* assume we're still on channel */
            }
            else if (rc != REG_NOMATCH)
            {
                  char buffer[4096];

                  regerror(rc, &filters[loop], buffer, sizeof(buffer));

                  dolog("Executing of regexp failed: %s", buffer);
            }
      }

      if (one_line)
      {
            if (irc_privmsg(server_conn, channel, buffer) == -1)
                  return -1;
      }
      else
      {
            snprintf(buffer, sizeof(buffer), "%s%04d/%02d/%02d %02d:%02d",
                        prefix,
                        ptm -> tm_year + 1900, ptm -> tm_mon + 1, ptm -> tm_mday, ptm -> tm_hour, ptm -> tm_min);

            if (irc_privmsg(server_conn, channel, buffer) == -1) return -1;

            if (irc_privmsg(server_conn, channel, state_str[what -> current_state]) == -1) return -1;

            if (irc_privmsg(server_conn, channel, (char *)(what -> host_name?what -> host_name:"")) == -1) return -1;

            if (irc_privmsg(server_conn, channel, (char *)(what -> service_description?what -> service_description:"")) == -1) return -1;

            snprintf(buffer, sizeof(buffer), "%s%s", (char *)(what -> plugin_output?what -> plugin_output:""), suffix);
            if (irc_privmsg(server_conn, channel, buffer) == -1) return -1;
      }

      return 0;
}

void calc_statistics(struct stats *stats, int n_stats, char list_all_problems, char always_notify, char also_acknowledged, char hide_ok, char *buffer, int buffer_size)
{
      int n_critical=0, n_warning=0, n_ok=0, n_up=0, n_down=0, n_unreachable=0, n_pending=0;

      calc_stats_stats(stats, n_stats, list_all_problems, always_notify, also_acknowledged, hide_ok, &n_critical, &n_warning, &n_ok, &n_up, &n_down, &n_unreachable, &n_pending);
      snprintf(buffer, buffer_size, "Critical: %d, warning: %d, ok: %d, up: %d, down: %d, unreachable: %d, pending: %d",
                  n_critical, n_warning, n_ok, n_up, n_down, n_unreachable, n_pending);
}

int check_nagios_status(server_t server_conn)
{
      struct stats *cur = NULL;
      int n_cur = 0;
      int loop;
      int any_shown = 0;
#ifdef _DEBUG
      time_t now = time(NULL);
      printf("Checking @ %s", ctime(&now));
#endif

      load_statuslog(statuslog, statuslog_location == L_FILE?1:0, statuslog_version == 2?1:0, n_prev, &cur, &n_cur);

      if (topic_summary)
      {
            char buffer[4096];

            calc_statistics(cur, n_cur, 0, 0, 0, 1, buffer, sizeof(buffer));

            if (strcmp(topic, buffer) != 0)
            {
                  strcpy(topic, buffer);

                  if (irc_topic(server_conn, channel, buffer) == -1)
                        return -1;
            }
      }

      if (announce_global_status_interval > 0)
      {
            if ((now - last_announce_global_status_interval) >= announce_global_status_interval)
            {
                  char buffer[4096];

                  last_announce_global_status_interval = now;

                  calc_statistics(cur, n_cur, 0, 0, 0, 1, buffer, sizeof(buffer));

                  if (irc_privmsg(server_conn, channel, buffer) == -1)
                        return -1;
            }
      }

      for(loop=0; loop<n_cur; loop++)
      {
            char show = 0;
            int prev_index = -1;

            if (prev)
                  prev_index = find_index_by_host_and_service(prev, n_prev, cur[loop].host_name, cur[loop].service_description);

            if (prev_index == -1)
            {
                  if (prev == NULL)
                  {
                        if (should_i_show_entry(cur, n_cur, loop, 0, 0, 0, 1))
                        {
                              show = 1;
                        }
                  }
                  else
                  {
                        show = 1;
                  }
            }
            else
            {
                  if (hard_only)
                  {
                        if (prev[prev_index].state_type == ST_HARD && cur[loop].state_type == ST_HARD)
                        {
                              if (prev[prev_index].current_state != cur[loop].current_state)
                              {
                                    show = 1;
                              }
                        }
                        else if (prev[prev_index].state_type == ST_SOFT && cur[loop].state_type == ST_HARD)
                        {
                              if (cur[loop].current_state != 0)
                              {
                                    show = 1;
                              }
                        }
                  }
                  else
                  {
                        if (prev[prev_index].current_state != cur[loop].current_state)
                        {
                              show = 1;
                        }
                  }
            }

            /* also 'unknown' is emitted when 'critical_only' is selected */
            if (show && cur[loop].current_state < 2 && critical_only)
            {
                  show = 0;
            }

            if (show)
            {
#ifdef _DEBUG
                  if (prev != NULL && prev_index != -1)
                  {
                        printf("%s %s\n", cur[loop].host_name, cur[loop].service_description);
                        printf("cur[current_state]: %d, prev[current_state]: %d\n", cur[loop].current_state, prev[prev_index].current_state);
                        printf("cur[state_type]: %d, prev[state_type]: %d\n", cur[loop].state_type, prev[prev_index].state_type);
                  }
#endif

#ifdef _DEBUG
                  if (any_shown == 0)
                  {
                        any_shown = 1;
                        dolog("emitting results");
                  }
#endif
                  if (emit_status(server_conn, channel, &cur[loop]) == -1)
                        return -1;
            }
      }

      free_stats_array(prev, n_prev);
      prev = cur;
      n_prev = n_cur;

      return 0;
}

int resend_nagios_status(server_t server_conn, char *to_who)
{
      int something_sent = 0;

      if (n_prev == 0)
            return irc_privmsg(server_conn, to_who, "not available yet");

      for(int loop=0; loop<n_prev; loop++)
      {
            if (should_i_show_entry(prev, n_prev, loop, 0, 0, 0, 1))
            {
                  something_sent = 1;

                  if (emit_status(server_conn, to_who, &prev[loop]) == -1)
                        return -1;
            }
      }

      if (!something_sent)
            return irc_privmsg(server_conn, to_who, "all fine");

      return 0;
}

int send_help(server_t server_conn, char *to_who)
{
      if (irc_privmsg(server_conn, to_who, "'resend' lets the bot resend the current nagios status in a private message") == -1)
            return -1;

      if (irc_privmsg(server_conn, to_who, "'statistics' sends you the statistics") == -1)
            return -1;

      if (irc_privmsg(server_conn, to_who, "'check' verifies if Nagios is still checking hosts/services") == -1)
            return -1;

      return 0;
}

int reload_statuslog(void)
{
      int fd_sl;
      struct stats *cur = NULL;
      int n_cur = 0;

      if (verbose > 1) dolog("reload_statuslog started");

      if (statuslog_location == L_FILE)     /* file */
            fd_sl = open64(statuslog, O_RDONLY);
      else
            fd_sl = connect_to(statuslog);

      if (fd_sl != -1)
      {
            if (statuslog_version == 2)
                  parse_2_0_statuslog(fd_sl, &cur, &n_cur);
            else
                  parse_1_0_statuslog(fd_sl, &cur, &n_cur);

            close(fd_sl);

            free_stats_array(prev, n_prev);
            prev = cur;
            n_prev = n_cur;

            return 0;
      }

      dolog("reload failed: %s", strerror(errno));

      return -1;
}

char * check_nagios()
{
      char *message = NULL;

      (void)check_max_age_last_check(prev, n_prev, max_time_last_host_update, max_time_oldest_host_update, max_time_last_host_check, max_time_oldest_host_check, max_time_last_service_check, max_time_oldest_service_check, max_time_oldest_next_service_check, &message);

      return message;
}

int do_nagircbot(server_t server_conn, char *channel)
{
      time_t last_check = 0;

      for(;;)
      {
            fd_set rfds;
            struct timeval tv;
            time_t now = time(NULL);

            if ((now - last_check) >= check_interval)
            {
                  if (check_nagios_status(server_conn) == -1)
                        return -1;

                  last_check = now;
            }

            if (now >= (last_irc_io + irc_server_keep_alive))
            {
                  if (irc_time(server_conn) == -1)
                        return -1;
            }

            FD_ZERO(&rfds);
            FD_SET(server_conn.fd, &rfds);

            tv.tv_sec = max(check_interval - (now - last_check), 0);
            tv.tv_usec = 0;

            if (irc_server_keep_alive > 0)
            {
                  tv.tv_sec = min(max(0, (last_irc_io + irc_server_keep_alive) - now), tv.tv_sec);
            }

            if (announce_global_status_interval > 0)
            {
                  tv.tv_sec = min(max(0, (last_announce_global_status_interval + announce_global_status_interval) - now), tv.tv_sec);
            }

            if (select(server_conn.fd + 1, &rfds, NULL, NULL, &tv) == -1)
            {
                  if (errno == EAGAIN || errno == EINTR)
                        continue;

                  error_exit("select() failed %s\n", strerror(errno));
            }

            if (FD_ISSET(server_conn.fd, &rfds))
            {
                  char recv_buffer[4096];
                  int rc = IRCREAD(server_conn, recv_buffer, sizeof(recv_buffer) - 1);

                  if (rc == 0)
                  {
                        dolog("connection closed by IRC server");
                        return -1;
                  }
                  else if (rc == -1)
                  {
                        if (errno != EAGAIN && errno != EINTR)
                        {
                              dolog("error transmitting data to IRC server: %s", strerror(errno));
                              return -1;
                        }
                  }
                  else
                  {
                        char *next_response = recv_buffer;
                        char *response = NULL;
                        char *cmd = NULL;
                        char *crlf = NULL;

                        recv_buffer[rc] = 0x00;

                        while ((response = next_response)
                               && (crlf = strchr( response, '\r')))
                        {
                              if (*(crlf + 1) != '\n')
                              {
                                    dolog( "Malformed server response: `%s' at `%s'.", recv_buffer, response );
                                    return -1;
                              }

                              *crlf = '\0';
                              if (( crlf + 2 ) - recv_buffer < rc)
                                    next_response = crlf + 2;
                              else
                                    next_response = NULL;

                              if (verbose > 1) dolog("IN: `%s'", response);

                              if (check_ping(server_conn, response) == -1)
                                    return -1;

                              cmd = strchr(response, ' ');
                              if (cmd)
                              {
                                    while(*cmd == ' ') cmd++;

                                    if (strncmp(cmd, "KICK ", 5) == 0)
                                    {
                                          char *dummy = strchr(cmd, ' ');
                                          if (dummy)
                                          {
                                                while(*dummy == ' ') dummy++;
                                                dummy = strchr(dummy, ' ');
                                          }
                                          if (dummy)
                                          {
                                                while(*dummy == ' ') dummy++;

                                                if (strncmp(dummy, nick, strlen(nick)) == 0)
                                                {
                                                      dolog("nagircbot got kicked (%s)", response);
                                                      return -1;
                                                }
                                                else if (verbose > 1)
                                                {
                                                      dolog("user %s got kicked from channel", dummy);
                                                }
                                          }
                                    }
                                    else if (strncmp(cmd, "PRIVMSG ", 8) == 0)
                                    {
                                          /* :flok!~flok@rammstein.amc.nl PRIVMSG nagircbot :test */

                                          char *to_me = &cmd[8];
                                          while(*to_me == ' ') to_me++;

                                          /* message to this bot? */
                                          if (strncmp(to_me, nick, strlen(nick)) == 0)
                                          {
                                                /* yes */
                                                char *msg = strchr(cmd, ':');
                                                char *from_who = response + 1;
                                                char *dummy = strchr(from_who, '!');

                                                if (msg != NULL && dummy != NULL)
                                                {
                                                      msg++;
                                                      *dummy = 0x00;

                                                      if (strcasecmp(msg, "help") == 0)
                                                      {
                                                            int rc = 0;

                                                            dolog("help requested by %s", from_who);

                                                            rc |= irc_privmsg(server_conn, from_who, "resend - resend the last known problems");
                                                            rc |= irc_privmsg(server_conn, from_who, "statistics - returns the number of criticals/warnings/etc.");
                                                            rc |= irc_privmsg(server_conn, from_who, "reload - completely forced reload the nagios status");
                                                            rc |= irc_privmsg(server_conn, from_who, "check - check if nagios is still running");

                                                            if (rc == -1)
                                                                  return -1;
                                                      }
                                                      else if (strcmp(msg, "resend") == 0)
                                                      {
                                                            dolog("resend requested by %s", from_who);

                                                            if (resend_nagios_status(server_conn, from_who) == -1)
                                                                  return -1;
                                                      }
                                                      else if (strcmp(msg, "check") == 0)
                                                      {
                                                            char *message;
                                                            int rc;

                                                            dolog("nagios check requested by %s", from_who);

                                                            message = check_nagios();
                                                            if (message)
                                                            {
                                                                  char buffer[4096];
                                                                  snprintf(buffer, sizeof(buffer), "Nagios has stopped running! -> %s", message);
                                                                  rc = irc_privmsg(server_conn, from_who, buffer);
                                                                  free(message);
                                                            }
                                                            else
                                                                  rc = irc_privmsg(server_conn, from_who, "Nagios is still running");

                                                            if (rc == -1)
                                                                  return -1;
                                                      }
                                                      else if (strcmp(msg, "statistics") == 0)
                                                      {
                                                            char buffer[4096];

                                                            dolog("statistics requested by %s", from_who);

                                                            calc_statistics(prev, n_prev, 0, 0, 0, 1, buffer, sizeof(buffer));

                                                            if (irc_privmsg(server_conn, from_who, buffer) == -1)
                                                                  return -1;
                                                      }
                                                      else if (strcmp(msg, "reload") == 0)
                                                      {
                                                            dolog("reload requested by %s", from_who);

                                                            if (reload_statuslog() == -1)
                                                            {
                                                                  if (irc_privmsg(server_conn, from_who, "cannot access status.log") == -1)
                                                                        return -1;
                                                            }
                                                            else
                                                            {
                                                                  if (irc_privmsg(server_conn, from_who, "nagios status reloaded") == -1)
                                                                        return -1;
                                                            }
                                                      }
                                                      else
                                                      {
                                                            char buffer[4096];

                                                            dolog("giberish sent by %s", from_who);

                                                            snprintf(buffer, sizeof(buffer), "'%s' is not understood - send 'help' for help", msg);
                                                            if (irc_privmsg(server_conn, from_who, buffer) == -1)
                                                                  return -1;

                                                            if (send_help(server_conn, from_who) == -1)
                                                                  return -1;
                                                      }
                                                }
                                          }

                                    }
                                    else if (strncmp(cmd, "QUIT ", 5) == 0)
                                    {
                                          char *quit_nick = &response[1];
                                          char *exclamation_mark = strchr(quit_nick, '!');
                                          if (exclamation_mark) *exclamation_mark = 0x00;

                                          if (strcmp(nick, quit_nick) == 0)
                                          {
                                                dolog("Got QUITed (%s)", cmd);
                                                return -1;
                                          }
                                          else if (verbose > 1)
                                          {
                                                dolog("user %s quit", quit_nick);
                                          }
                                    }

                              }
                        }
                  }
            }
      }

      dolog("NagIRCBot dropped of IRC server");

      return -1;
}

void sighandler(int pid)
{
      if (pid == SIGTERM)
      {
            if (pidfile)
                  unlink(pidfile);

            exit(1);
      }
}

void version(void)
{
      printf("nagircbot, v" VERSION " (C) 2006-2011 by folkert@vanheusden.com\n");
}

void usage(void)
{
      version();

      printf("\n");

      printf("-f     path to status.log ('status_file' parameter in nagios.cfg)\n");
      printf("-F     host:port for retrieving status.log\n");
      printf("-x     status.log is in nagios 1.0 format\n");
      printf("-X     status.log is in nagios 2.0/3.0 format\n");
      printf("-s     IRC server to connect to (host:port)\n");
      printf("-c     channel to connect to (#channel - do not forget to escape the '#' in your shell)\n");
      printf("-k     keyword for the channel to connect to (default: no keyword)\n");
      printf("-C     use colors\n");
      printf("-n     nick\n");
      printf("-u     username (for logging into the irc server)\n");
      printf("-U     name (as seen by other users)\n");
      printf("-p     password (for logging into the irc server)\n");
      printf("-N     prefix for all in-channel messages, e.g. for nick highlight\n");
      printf("-m     display all information on separate lines\n");
      printf("-t     show a summary in the topic-line\n");
      printf("-i     check interval (in seconds - default 60)\n");
      printf("-I     how often to announce the global status in the channel (in seconds, 0 for off)\n");
      printf("       do not set it smaller then the -i value\n");
      printf("-d     do not fork into the background\n");
      printf("-T x   checks to see if nagios is still running. comma-seperated list\n");
      printf("       (without spaces!) with the following elements:\n");
      printf("       max_time_last_host_update, max_time_oldest_host_update,\n");
      printf("       max_time_last_host_check, max_time_oldest_host_check,\n");
      printf("       max_time_last_service_check, max_time_oldest_service_check,\n");
      printf("       max_time_oldest_next_service_check\n");
      printf("       send 'check' in a private message to invoke the check\n");
      printf("-z user   user to run as\n");
      printf("-H     show only state type 'HARD' (default)\n");
      printf("-S     show also state type 'SOFT'\n");
      printf("-R     only announce CRITICAL/UNKNOWN errors on the channel\n");
      printf("-A x   filter (omit) lines that match with the given regular expression\n");
      printf("-P x   write pid to file x\n");
      printf("-e     Use encryption (SSL) (default: no)\n");
}

void add_filter(char *string)
{
      filters = (regex_t *)realloc(filters, sizeof(regex_t) * (n_filters + 1));
      if (!filters)
            error_exit("add_filter: out of memory");

      if (regcomp(&filters[n_filters], string, REG_EXTENDED))
            error_exit("add_filter: failed to compile regexp '%s'", string);

      n_filters++;
}

int main(int argc, char *argv[])
{
      int state = S_DISCONNECTED;
      int fd = -1;
      int sleep_time = default_sleep;
      int c;
      int do_fork = 1;
      time_t time_join_channel_started = (time_t)0;
      time_t time_tcp_connected = (time_t)0;
      int join_tries = 0;
      char *runas = NULL;

      color_str[0] = mystrdup("_3,1 ");
      color_str[1] = mystrdup("_8,1 ");
      color_str[2] = mystrdup("_4,1 ");
      color_str[3] = mystrdup("_11,1 ");
      color_str[0][0] = color_str[1][0] = color_str[2][0] = color_str[3][0] = 3;

      while((c = getopt(argc, argv, "N:A:eRP:xXF:f:i:hHSs:c:k:Ctn:u:U:p:T:mvdVz:I:")) != -1)
      {
            switch(c)
            {
                  case 'A':
                        add_filter(optarg);
                        break;

                  case 'R':
                        critical_only = 1;
                        break;

                  case 'e':
                        use_ssl = 1;
                        break;

                  case 'P':
                        pidfile = optarg;
                        break;

                  case 'I':
                        announce_global_status_interval = atoi(optarg);
                        break;

                  case 'z':
                        runas = optarg;
                        break;

                  case 'T':
                        {
                              char *dummy = optarg;

                              max_time_last_host_update = atoi(dummy);
                              dummy = strchr(dummy, ',') + 1;

                              max_time_oldest_host_update = atoi(dummy);
                              dummy = strchr(dummy, ',') + 1;

                              max_time_last_host_check = atoi(dummy);
                              dummy = strchr(dummy, ',') + 1;

                              max_time_oldest_host_check = atoi(dummy);
                              dummy = strchr(dummy, ',') + 1;

                              max_time_last_service_check = atoi(dummy);
                              dummy = strchr(dummy, ',') + 1;

                              max_time_oldest_service_check = atoi(dummy);
                              dummy = strchr(dummy, ',') + 1;

                              max_time_oldest_next_service_check = atoi(dummy);
                              break;
                        }

                  case 'H':
                        hard_only = 1;
                        break;

                  case 'S':
                        hard_only = 0;
                        break;

                  case 'd':
                        do_fork = 0;
                        break;

                  case 't':
                        topic_summary = 1;
                        break;

                  case 'C':
                        use_colors = 1;
                        break;

                  case 'm':
                        one_line = 0;
                        break;

                  case 'N':
                        nick_prefix = mystrdup(optarg);
                        break;

                  case 'p':
                        password = mystrdup(optarg);
                        break;

                  case 'U':
                        username = mystrdup(optarg);
                        break;

                  case 'n':
                        nick = mystrdup(optarg);
                        break;

                  case 'u':
                        user = mystrdup(optarg);
                        break;

                  case 's':
                        server = mystrdup(optarg);
                        break;

                  case 'c':
                        channel = mystrdup(optarg);
                        break;

                  case 'k':
                        keyword = mystrdup(optarg);
                        break;

                  case 'x':
                        statuslog_version = 1;
                        break;

                  case 'X':
                        statuslog_version = 2;
                        break;

                  case 'f':
                        statuslog = optarg;
                        statuslog_location = L_FILE;
                        break;

                  case 'F':
                        statuslog = optarg;
                        statuslog_location = L_HOST;
                        break;

                  case 'i':
                        check_interval = atoi(optarg);
                        break;

                  case 'v':
                        verbose = 1;
                        break;

                  case 'V':
                        version();
                        return 0;

                  case 'h':
                        usage();
                        return 0;

                  default:
                        usage();
                        return 1;
            }
      }

      if (do_fork)
      {
            if (daemon(0, 0) == -1)
            {
                  error_exit("Failed to become daemon process!");
            }
      }

      if (pidfile)
            write_pidfile(pidfile);

      if (runas)
      {
            struct passwd *p = getpwnam(runas);
            if (p)
            {
                  if (setgid(p -> pw_gid) == -1)
                        error_exit("setgid(%d) failed", p -> pw_gid);

                  if (setuid(p -> pw_uid) == -1)
                        error_exit("setuid(%d) failed", p -> pw_uid);
            }
            else
            {
                  error_exit("User '%s' not found.", runas);
            }
      }

      signal(SIGPIPE, SIG_IGN);
      signal(SIGTERM, sighandler);
      server_conn.addr = server;
      for(;;)
      {
            if (state == S_DISCONNECTED)
            {
                  dolog("Connecting to %s", server);

                  fd = connect_to(server);
                  if (fd == -1)
                  {
                        if (verbose > 1)
                              dolog("Cannot connect to %s: %m, will sleep %d seconds", server, sleep_time);

                        sleep(sleep_time);
                        if (sleep_time < max_sleep_time)
                              sleep_time = (int)((double)sleep_time * 1.5);
                        else
                              sleep_time = max_sleep_time;
                  }
                  else
                  {
                        server_conn.fd = fd;
                        if( use_ssl ) {
                              dolog("Doing SSL handshake");
                              if( !ssl_handshake( &server_conn ) ){
                                    dolog("SSL handshake failed!");
                                    return 1;
                              }
                        }
                        state = S_CONNECTED;
                        join_tries = 0;
                        time_tcp_connected = time(NULL);
                  }

                  if (verbose == 1)
                        dolog("Connected to IRC server %s", server);
            }

            if (state == S_CONNECTED)
            {

                  dolog("Logging in...");

                  if (irc_login(server_conn, user, username, server, password) == -1)
                        state = S_DISCONNECTING;

                  if (irc_join_channel(server_conn, channel, keyword) == -1)
                        state = S_DISCONNECTING;

                  state = S_JOINING_CHANNEL;

                  time_join_channel_started = time(NULL);
            }

            if (state == S_JOINING_CHANNEL)
            {
                  fd_set rfds;
                  struct timeval tv;

                  dolog("Joining channel...");

                  FD_ZERO(&rfds);
                  FD_SET(server_conn.fd, &rfds);

                  tv.tv_sec = 1;
                  tv.tv_usec = 0;

                  if (select(server_conn.fd + 1, &rfds, NULL, NULL, &tv) == -1)
                  {
                        if (errno != EAGAIN && errno != EINTR)
                              error_exit("select() failed");
                  }

                  if (FD_ISSET(server_conn.fd, &rfds))
                  {
                        char recv_buffer[4096];
                        int rc = IRCREAD(server_conn, recv_buffer, sizeof(recv_buffer) - 1);

                        if (rc == 0)
                              state = S_DISCONNECTING;
                        else if (rc == -1)
                        {
                              if (errno != EAGAIN && errno != EINTR)
                              {
                                    dolog("failed joining channel: %s", strerror(errno));
                                    state = S_DISCONNECTING;
                              }
                        }
                        else
                        {
                              char *cmd, *pnt = recv_buffer, *end_marker;

                              recv_buffer[rc] = 0x00;

                              if (check_ping(server_conn, recv_buffer) == -1)
                                    return -1;

                              do
                              {
                                    end_marker = strchr(pnt, '\n');
                                    if (end_marker) *end_marker = 0x00;
                                    if (!end_marker) end_marker = strchr(pnt, '\r');
                                    if (end_marker) *end_marker = 0x00;
#ifdef _DEBUG
                                    //                                  printf("[%s]", pnt);
#endif

                                    cmd = strchr(pnt, ' ');
                                    if (cmd)
                                    {
                                          while(*cmd == ' ') cmd++;

                                          if (strncmp(cmd, "JOIN ", 5) == 0)
                                          {
                                                if (verbose > 1)
                                                      dolog("on channel");
                                                state = S_ON_CHANNEL;
                                          }
                                          else if (strncmp(cmd, "KICK ", 5) == 0)
                                          {
                                                char *dummy = strchr(cmd, ' ');
                                                if (dummy)
                                                {
                                                      while(*dummy == ' ') dummy++;
                                                      dummy = strchr(dummy, ' ');
                                                }
                                                if (dummy)
                                                {
                                                      while(*dummy == ' ') dummy++;

                                                      if (strncmp(dummy, nick, strlen(nick)) == 0)
                                                            state = S_CONNECTED;
                                                }
                                          }
                                    }

                                    pnt = end_marker + 1;
                              }
                              while(end_marker);
                        }
                  }
            }

            if (state == S_JOINING_CHANNEL && (time(NULL) - time_join_channel_started) > join_timeout)
            {
                  if (++join_tries == max_n_join_tries)
                  {
                        state = S_DISCONNECTING;
                        dolog("joining the channel %s too to long (%ds, timeout set to %ds), aborting and retrying ", channel, time(NULL) - time_join_channel_started, join_timeout);
                  }
                  else
                  {
                        state = S_CONNECTED;
                        dolog("failed joining channel %s, retrying (%d/%d)", channel, join_tries, max_n_join_tries);
                  }
            }

            if (state == S_ON_CHANNEL)
            {
                  dolog("On channel, starting nagtail...");
                  sleep_time = default_sleep;

                  (void)do_nagircbot(server_conn, channel);

                  state = S_DISCONNECTING;
            }

            if (state == S_DISCONNECTING)
            {
                  int took = time(NULL) - time_tcp_connected;

                  dolog("Disconnected");

                  close(fd);
                  fd = -1;
                  state = S_DISCONNECTED;

                  dolog("Session took %d seconds", took);

                  if (took < minimum_time_for_successfull_login)
                  {
                        dolog("Will sleep %d seconds before retrying", sleep_time);

                        sleep(sleep_time);

                        if (sleep_time < max_sleep_time)
                              sleep_time = (int)((double)sleep_time * sleep_multiply_factor);
                        else
                              sleep_time = max_sleep_time;
                  }
            }
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index