/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <unistd.h>
#include <libgen.h>

#include "daemon.h"
#include "opendd.h"
#include "globals.h"

#include "./include/config_option.h"
#include "./include/socket.h"
#include "./include/util.h"

void usage(const char *pgm) {
/* -----------------------------------------------------------------------------
 * Print usage and exit
 *
 * @param const char *, program name
 * -------------------------------------------------------------------------- */
  printf("usage : %s [-c <configfile>] [-v] [-h]\n", pgm);
  printf("        -c <configfile> : Define a config file path other than %s\n", CONFIG_FILE);
  printf("        -v : Verbose -> do not log to syslog, print to the stderr instead\n");
  printf("        -h : Print this and exit\n");
  exit(0);
}

/* MAIN PROGRAM */
int main(int argc, char **argv) {

  extern char *optarg;
  int opt, read_config = 0;
  char *config_file = NULL;
  char *current_dir = NULL;
  char *ip = NULL;
  int do_update = 0, force_update = 0;
  struct chain_string *hostnames = NULL;
  int day_lifetime = 0, second_lifetime = 0, tmp_val = 0;

  const char *ident = (const char *)basename(argv[0]);

  current_dir = get_currentdir();

#ifdef USE_SOCKET_SSL
  SSL_load_error_strings();
  ERR_load_BIO_strings();
  OpenSSL_add_all_algorithms();
#endif

  /* We check the runtime parameters */
  while((opt = getopt(argc, argv, "c:vh")) != -1) {
    switch (opt) {
      case 'c':
        if (optarg[0] != '/')
        /* This is a relative path file */
          asprintf(&config_file, "%s/%s", current_dir, optarg);
        else
        /* This is an absolute path file */
          config_file = strdup(optarg);
        if (config_file == NULL) {
          fprintf(stderr, "%s : %s\n", ident, strerror(errno));
          return -1;
        }
        break;
      case 'v':
        set_verbose_mode(1);
        break;
      case 'h':
        usage(ident);
        break;
    }
  }

  FREE(current_dir);

  if (config_file != NULL) {
    if (!check_config_file_mode(config_file))
    /* If the file is not only owner readable, exit */
      return -1;
    else if (parse_config_file(config_file)) {
      set_config_file(config_file);
      read_config = 1;
    }
    else if (errno != 0) {
      logmsg(LOG_ERR, "main() : cannot parse config file %s : %s", config_file, strerror(errno));
      FREE(config_file);
      return -1;
    }
  }
  else {
    if (!check_config_file_mode(CONFIG_FILE))
    /* If the file is not only owner readable, exit */
      return -1;
    else if (parse_config_file(CONFIG_FILE))
      read_config = 1;
    else if (errno != 0) {
      logmsg(LOG_ERR, "main() : cannot parse config file %s : %s", CONFIG_FILE, strerror(errno));
      return -1;
    }
  }

  if (!read_config) {
    logmsg(LOG_ERR, "main() : cannot find a config file in %s", CONFIG_FILE);
    return -2;
  }
  /* A config file has been read */

  if (!runasdaemon()) {

    if (use_syslog()) {
      /* Open syslog */
      openlog(ident, LOG_PID, syslog_facility());
      set_syslog_mode(1);
    }

    logmsg(LOG_INFO, "-- running OpenDD %s in normal mode", VERSION);

    /* Drop privileges (if possible) */
    if (drop_privileges() == -1)
      return -1;

    /* Now get the IP address */
    if (getifaceaddr(&ip)) {
    /* Do a dyndns update */
      logmsg(LOG_INFO, "main() : getting my ip address : %s", ip);
      
      /* We must get the hostnames to be updated */
      switch (getdyndnshostnames(ip, &hostnames, 0)) {
      case 0:
      case -1:
        logmsg(LOG_ERR, "main() : No hostname(s) to update");
        break;
      default:
      /* Some hostnames must be updated */
        tmp_val = dyndns(ip, hostnames);
        if (tmp_val) {
          if (tmp_val == EXIT_DYNDNS_FAILED) {
          /* DynDNS update failure */
            logmsg(LOG_ERR, "main() : a dyndns() update error occurred. "
                  "You should read the error message in mail report or syslog report");
          }
          
          logmsg(LOG_INFO, "main() : dyndns() exit normally");
          FREE(ip);
          drop_all_chain_string(hostnames);
          return 0;
        }
        drop_all_chain_string(hostnames);
        hostnames = NULL;
        FREE(ip);
        logmsg(LOG_ERR, "main() : dyndns() exit abnormally");
        
        break;
      }
    }
    else
      logmsg(LOG_ERR, "main() : cannot get IP address for update");
    if (ip != NULL)
      FREE(ip);
  }
  else {
    if (get_verbose_mode()) {
      logmsg(LOG_ERR, "main() : cannot run as daemon on verbose mode");  
    }
    else {

      /* Try to create the lock file */
      switch (open_lock_pidfile()) {
      case -1:
      /* lock error : another opendd process is probably already running */
        fprintf(stderr, "%s : an opendd daemon is already running\n", ident);
        return -1;
        break;
      case 0:
      /* any errors */
        return -1;
        break;
      }
      close_lock_pidfile();

      /* Run as daemon */
      if (!setdaemon()) {
      /* Parent processus must exit
       * => return to the shell
       */
        return 0;
      }

      /* Reopen syslog */
      openlog(ident, LOG_PID, syslog_facility());
      set_syslog_mode(1);

      /* Here, we are on the child process */
      logmsg(LOG_INFO, "-- running OpenDD %s as daemon", VERSION);

      /*
       * Try to recreate the lock file.
       * We have to recreate the lock, because locks are not inherited after
       * a fork() done in setdaemon()
       */
      switch (open_lock_pidfile()) {
      case -1:
      /* lock error : another opendd process is probably already running
       * We SHOULD not be here, as the test was successfully passed, above
       */
        logmsg(LOG_ERR, "main() : an opendd daemon is already running");
        return -1;
        break;
      case 0:
      /* any errors */
        return -1;
        break;
      }

      /* Write the pid number in the lock file */
      write_pid_lock_pidfile();

      /* Drop privileges (if possible) */
      if (drop_privileges() == -1)
        return -1;

      /* Here comes the infinite loop */
      for(;;) {
        if (get_my_ip() == NULL) {
        /* This is the first time */
          if (getifaceaddr(&ip)) {
            if (strcmp(ip, "0.0.0.0") == 0) {
            /* Ignore IP 0.0.0.0
             * Do nothing
             */
            }
            else
              do_update = 1;
          }
        }
        else {
          if (getifaceaddr(&ip)) {
            if (strcmp(ip, "0.0.0.0") == 0) {
            /* Ignore IP 0.0.0.0
             * Do nothing
             */
            }
            else if (strcmp(get_my_ip(), ip) != 0) {
            /* IP has changed */
              do_update = 1; 
            }
            else {
              if (day_lifetime >= get_domain_lifetime()) {
                set_force_update_status(1);

                logmsg(LOG_INFO, "main() : dyndns update must be done by force "
                      "after %d days with no change",
                      get_domain_lifetime());
              }
            }
          }
        }

        /* Get force update status
         * It can be update by SIGUSR1 signal or limit reached lifetime
         */
        force_update = get_force_update_status();
        if (force_update)
          do_update = 1;

        if ((do_update)
        && (is_service_online())) {
        /* An Update must be done */

          /* We must get the hostnames to be updated */
          switch (getdyndnshostnames(ip, &hostnames, force_update)) {
          case -1:
          /* Errors ... do nothing */
            FREE(ip);
            break;
          case 0:
          /* No need to update */
            set_my_ip(ip);  /* We copy the new IP address in cache */
            break;
          default:
          /* Some hostnames must be updated */
            if (force_update) {
              logmsg(LOG_INFO, "main() : dyndns update by force is processing ...");
            }

            tmp_val = dyndns(ip, hostnames);
            if (tmp_val) {
              if (tmp_val == EXIT_DYNDNS_FAILED) {
              /* DynDNS update failure */
                logmsg(LOG_ERR, "main() : a dyndns() update error occurred. "
                      "You should read the error message in mail report or syslog report");
                logmsg(LOG_ERR, "main() : OpenDD will stop processing updates "
                      "until you solved the problem.");
                logmsg(LOG_ERR, "main() : Send a SIGHUP signal to restore the update process.");

                /* Set the OpenDD service offline */
                set_service_online(0);
              }
              else {
                /* DynDNS successful
                 * so we must keep the valid IP address */
                set_my_ip(ip);  /* We copy the new IP address in cache */
              }
              logmsg(LOG_INFO, "main() : dyndns() exit normally");
            }
            else {
              if (ip != NULL)
                FREE(ip);
              logmsg(LOG_ERR, "main() : dyndns() exit abnormally");        
            }
            drop_all_chain_string(hostnames);
            hostnames = NULL;

            /* Reinitialize the lifetime */
            set_second_lifetime(0);

            update_proc_title();

            break;
          }
        }
        else if (ip != NULL)
          FREE(ip);

        do_update = 0;
        set_force_update_status(0);
        tmp_val = getpollfrequency();

        /* Sleep in second */
        my_sleep(tmp_val);

        /* Increment the domain lifetime in second */
        second_lifetime = get_second_lifetime();
        second_lifetime += tmp_val;
        set_second_lifetime(second_lifetime);
        /* And get the lifetime in day */
        day_lifetime = second_lifetime / (60 * 60 * 24);

        update_proc_title();

      } /* Loop */
    }
  }

  set_config_file(NULL);
  set_ssl_pathstore(NULL);

  if (get_syslog_mode())
    closelog();
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1