Logo Search packages:      
Sourcecode: redhat-cluster-suite version File versions  Download package

clurmtabd.c

Go to the documentation of this file.
/*
  Copyright Red Hat, Inc. 2002-2003

  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, 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
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; see the file COPYING.  If not, write to the
  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
  MA 02139, USA.
*/
/** @file
 * Keeps /var/lib/nfs/rmtab in sync across the cluster.
 *
 * Author: Lon H. Hohberger <lhh at redhat.com>
 *
 * Synchronizes entries in a mount point with /var/lib/nfs/rmtab.
 */

#define CM_NFS_DIR ".clumanager"
 
#include <stdio.h>
#include <rmtab.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <syslog.h>
#include <clulog.h>
#include <unistd.h>
#include <limits.h>
#include <regex.h>

#define POLLINT_DEFAULT 2

/* FIXME DAEMON_STR is equivalent to the one in quorumd.c: CLURMTABD_DAEMON */
/* Would be nice to have stuff like this defined in one place */
#define DAEMON_STR      "clurmtabd"
#define LOGLEVEL_STR    DAEMON_STR "%logLevel"
#define POLLINT_STR     DAEMON_STR "%pollInterval"

/*
 * Globals
 */

static int exiting = 0;
static int poll_interval = POLLINT_DEFAULT;

/*
 * Function Prototypes
 */
static int rmtab_modified(void);
static int rmtab_copy_bypath(rmtab_node ** dest, rmtab_node ** src,
                       const char *path);
static int rmtab_get_update(rmtab_node ** rmtab, rmtab_node ** pruned_rmtab,
                      rmtab_node ** diff, char *path);

/* Signal Handlers */
static void sh_sync(int sig);
static void sh_exit(int sig);
static void sh_reconfigure(int sig);
static inline void register_sighandlers(void);

/* Configuration */
#if 0
static inline int __get_int_param(char *str, int *val, int dflt);
#endif
static int get_rmtabd_loglevel(int *level);
static int get_rmtabd_pollinterval(int *interval);
static void rmtabd_reconfigure(void);

/* Initialization */
static int rmtabd_config_init(void);
int main(int argc, char **argv);


/**
 * stat _PATH_RMTAB and see if it's changed.
 *
 * @returns       1 if it's been modified; 0 if not.
 */
static int
00091 rmtab_modified(void)
{
      /* Preserved data */
      static struct stat prev_stat;
      static int __prev_stat = 0;

      struct stat curr_stat;
      int rv = 1;

      /* Initialize */
      if (!__prev_stat) {
            memset(&prev_stat, 0, sizeof (prev_stat));
            stat(_PATH_RMTAB, &prev_stat);
            __prev_stat = 1;
            return 1;
      }

      memset(&curr_stat, 0, sizeof (curr_stat));
      while (stat(_PATH_RMTAB, &curr_stat) == -1) {
            if (errno != ENOENT) {
                  clulog(LOG_ERR, "#15: %s: stat: %s\n", __FUNCTION__,
                         strerror(errno));
                  return -1;
            }

            /* Create the file. */
            clulog(LOG_WARNING, "#62: " _PATH_RMTAB
                   " does not exist - creating");
            close(open(_PATH_RMTAB, O_CREAT | O_SYNC, 0600));
      }

      if ((rv = memcmp(&prev_stat.st_mtime, &curr_stat.st_mtime,
                   sizeof (curr_stat.st_mtime)))) {
            clulog(LOG_DEBUG, "Detected modified " _PATH_RMTAB "\n");
            memcpy(&prev_stat, &curr_stat, sizeof (prev_stat));
      }

      return !!rv;
}


/**
 * Insert (copy) entries in **src with the same rn_path as *path to
 * destination list **dest.
 *
 * @param dest          Destination list pointer.
 * @param src           Source list pointer.
 * @param path          Path to prune (only copy entries in the specified
 *                path).
 * Returns -1 if rmtab_insert fails; 0 on success
 */
static int
00143 rmtab_copy_bypath(rmtab_node ** dest, rmtab_node ** src, const char *path)
{
      rmtab_node *curr, *last = NULL;

      for (curr = *src; curr; curr = curr->rn_next)
            if (!strcmp(path, curr->rn_path))
                  if ((last = rmtab_insert(dest, last,
                                    curr->rn_hostname,
                                    curr->rn_path,
                                    curr->rn_count)) == NULL)
                        return -1;
      return 0;
}


/**
 * Update the rmtab from /var/lib/nfs/rmtab.  We need to maintain two separate
 * lists (rmtab and pruned_rmtab).  This is because we don't want to sync
 * non-cluster exports.  Non-cluster exports will not show up in pruned_rmtab,
 * however, when we receive an update from our peer, we'd lose non-cluster
 * entries if we didn't preserve them when we merge in changes from our peer.
 *
 * The current (full) rmtab is passed in as **rmtab.  The current cluster-only
 * (pruned) rmtab is passed in as **pruned_rmtab.  The differences between
 * the current and new cluster-only rmtabs are passed out in **diff, and the
 * new versions (if any) of the full rmtab and pruned rmtab are moved into
 *
 * @return        -1 on error, 0 on success, 1 if no differences exist.
 */
static int
00173 rmtab_get_update(rmtab_node ** rmtab, rmtab_node ** pruned_rmtab,
             rmtab_node ** diff, char *path)
{
      int rv = -1;
      rmtab_node *old_rmtab = NULL, *old_pruned = NULL;

      if (!rmtab_modified())
            return 1;

      /* Save the current full list */
      rmtab_move(&old_rmtab, rmtab);

      if (rmtab_read(rmtab, _PATH_RMTAB) == -1) {
            clulog(LOG_ERR, "#16: Failed to reread rmtab: %s\n",
                   strerror(errno));

            /* Don't kill the list if we fail to reread. */
            rmtab_move(rmtab, &old_rmtab);
            goto out;
      }

      /* Save the current cluster-specific list */
      rmtab_move(&old_pruned, pruned_rmtab);

      if (rmtab_copy_bypath(pruned_rmtab, rmtab, path) == -1) {
            clulog(LOG_ERR, "#17: Failed to prune rmtab: %s\n",
                   strerror(errno));

            /* 
             * Since we couldn't build a new list, restore the old
             * one.  Otherwise, next time, we'd send a weird diff to
             * our peer with all entries as "added".
             */
            rmtab_move(pruned_rmtab, &old_pruned);
            goto out;
      }

      if (!diff) {
            rv = 1;
            goto out;
      }

      /* find the differences */
      if (rmtab_diff(old_pruned, *pruned_rmtab, diff)) {
            clulog(LOG_ERR, "Failed to diff rmtab: %s\n", strerror(errno));
            goto out;
      }

      if (!*diff) {
            /* No differences */
            rv = 1;
            goto out;
      }

      rv = 0;
      out:
      /* stick a finger in the memory dike */
      rmtab_kill(&old_rmtab); /* these will be NOPs if NULL */
      rmtab_kill(&old_pruned);

      return rv;
}




/* **************** *
 * SIGNAL HANDLERS!
 * **************** */

/**
 * INT, USR1, USR2 handler.
 *
 * What these signals actually do is interrupt the select(2) we enter when we
 * call sleep().  Effectively, this causes sleep() to short out, and makes 
 * us drop down into rmtab_get_update() - causing us to re-check and sync
 * changes if there are any.  The service script, svclib_nfs, sends us a
 * TERM whenever it receives the request to stop a service (which happens
 * when the service manager relocates as well), thus, when a service is
 * disabled or relocates to the other node, clurmtabd syncs immediately its
 * current state to the other node, preventing a timing window between
 * "service relocate" and "rmtabd update" during which a client could
 * receive ESTALE.
 */
static void
00258 sh_sync(int sig)
{
      clulog(LOG_DEBUG, "Signal %d received; syncing ASAP\n", sig);
}


/**
 * QUIT, TERM
 *
 * In this case, we go down ASAP.  But first, we sync.  These will, like the
 * above, short-out msg_accept_timeout() and drop down for that one last
 * sync.
 */
static void
00272 sh_exit(int sig)       
{
      clulog(LOG_DEBUG, "Signal %d received; exiting\n", sig);
      exiting = 1;
}


/**
 * HUP
 *
 * Traditional behavior.  Reconfigure on SIGHUP.
 */
static void
00285 sh_reconfigure(int sig)
{
      clulog(LOG_DEBUG, "Re-reading the cluster database\n");
      rmtabd_reconfigure();
}


/**
 * Set up signal handlers.
 */
static inline void
00296 register_sighandlers(void)
{
      sigset_t set;
      struct sigaction act;

      sigemptyset(&set);
      sigaddset(&set, SIGINT);
      sigaddset(&set, SIGUSR1);
      sigaddset(&set, SIGUSR2);

      sigaddset(&set, SIGHUP);

      sigaddset(&set, SIGTERM);
      sigaddset(&set, SIGQUIT);

      sigaddset(&set, SIGILL);
      sigaddset(&set, SIGIO);
      sigaddset(&set, SIGSEGV);
      sigaddset(&set, SIGBUS);

      sigprocmask(SIG_UNBLOCK, &set, NULL);

      memset(&act, 0, sizeof (act));
      sigemptyset(&act.sa_mask);

      /* In some cases, just continue */
      act.sa_handler = sh_sync;

      sigaction(SIGINT, &act, NULL);
      sigaction(SIGUSR1, &act, NULL);
      sigaction(SIGUSR2, &act, NULL);

      /* Ok, reconfigure here */
      act.sa_handler = sh_reconfigure;
      sigaction(SIGHUP, &act, NULL);

      /* Exit signals */
      act.sa_handler = sh_exit;
      sigaction(SIGTERM, &act, NULL);
      sigaction(SIGQUIT, &act, NULL);
}

/* ******************************* *
 * Configuration Utility Functions
 * ******************************* */

/**
 * Retrieve an integer parameter from the config file.
 *
 * @param str           config token
 * @param val           return value
 * @param dflt          Default integer value.
 * @return 0                                                      s
 */
#if 0
static inline int
__get_int_param(char *str, int *val, int dflt)
{
      char *value;
      int ret;

      ret = CFG_Get(str, NULL, &value);

      switch (ret) {
      case CFG_DEFAULT:
            *val = dflt;
            break;
      case CFG_OK:
            *val = atoi(value);
            break;
      default:
            clulog(LOG_ERR, "#19: Cannot get \"%s\" from database; "
                   "CFG_Get() failed, err=%d\n", ret);
            return 0;
      }

      return 0;
}
#endif


/**
 * Gets the loglevel of rmtabd
 */
static int
00381 get_rmtabd_loglevel(int *level)
{
#if 0
      return __get_int_param(LOGLEVEL_STR, level, LOG_DEFAULT);
#endif
      return LOG_INFO;
}


/**
 * Retrieves the polling interval, in seconds, of _RMTAB_PATH from the cluster
 * configuration database.
 */
static int
00395 get_rmtabd_pollinterval(int *interval)
{
#if 0
      return __get_int_param(POLLINT_STR, interval, POLLINT_DEFAULT);
#endif
      return POLLINT_DEFAULT;
}


/**
 * This is called at init and by sh_reconfigure and sets up daemon-specific
 * configuration params.
 */
static void
00409 rmtabd_reconfigure(void)
{
      int level, old_level, old_interval;

      /* loglevel */
      old_level = clu_get_loglevel();
      get_rmtabd_loglevel(&level);

      if (old_level != level) {
            if (clu_set_loglevel(level) == -1)
                  clulog(LOG_ERR, "#20: Failed set log level\n");
            else
                  clulog(LOG_DEBUG, "Log level is now %d\n", level);
      }

      /* rmtabd polling interval (tw33k4bl3) */
      old_interval = poll_interval;
      get_rmtabd_pollinterval(&poll_interval);

      /* bounds-check */
      if (poll_interval < 1)
            poll_interval = 1;
      else if (poll_interval > 10)
            poll_interval = 10;

      if (old_interval != poll_interval) {
            clulog_and_print(LOG_DEBUG,
                         "Polling interval is now %d seconds\n",
                         poll_interval);
      }
}


/**
 * Set up local parameters & signal handlers.
 */
static int
00446 rmtabd_config_init(void)
{
      /* Yes, it does this twice */
#if 0
      if (CFG_ReadFile(CLU_CONFIG_FILE) != CFG_OK)
            return -1;
#endif

      rmtabd_reconfigure();
      register_sighandlers();
      return 0;
}


/**
 * Initializes and synchronizes /var/lib/nfs/rmtab with
 * [path]/.clumanager/rmtab.
 *
 * @param path          Path to mount point we're monitoring.
 * @param rmtab         Will contain full rmtab upon exit.
 * @param pruned_rmtab  Will contain rmtab entries we care about on exit.
 */
int
00469 rmtab_init(char *path, rmtab_node **rmtab, rmtab_node **pruned_rmtab)
{
      char buf[PATH_MAX];

      snprintf(buf, sizeof(buf), "%s/%s", path, CM_NFS_DIR);

      if ((mkdir(buf, 0700) == -1) && (errno != EEXIST)) {
            clulog_and_print(LOG_ERR, "#21: Couldn't read/create %s: %s\n",
                         buf, strerror(errno));
            return -1;
      }

      snprintf(buf, sizeof(buf), "%s/%s/rmtab", path, CM_NFS_DIR);

      if (rmtab_read(rmtab, buf) == -1) {
            clulog_and_print(LOG_ERR, "#22: Failed to read %s: %s\n", buf,
                         strerror(errno));
            return -1;
      }

      /*
       * Read into the same pointer -> inserting each node will
       * cause the nodes with the greater count to be kept.
       */
      if (rmtab_read(rmtab, _PATH_RMTAB) == -1) {
            clulog_and_print(LOG_ERR, "#23: Failed to read %s: %s\n",
                         _PATH_RMTAB, strerror(errno));
            return -1;
      }

      /*
       * Prune by our path
       */
      if (rmtab_copy_bypath(pruned_rmtab, rmtab, path) == -1) {
            clulog_and_print(LOG_ERR, "#24: Failed to prune rmtab: %s\n",
                         strerror(errno));
            return -1;
      }

      /*
       * XXX could lose a mount if rpc.mountd writes a file before
       * we rewrite the file.
       */
      if (rmtab_write_atomic(*rmtab, _PATH_RMTAB) == -1) {
            clulog_and_print(LOG_ERR, "#25: Failed to write %s: %s\n",
                         _PATH_RMTAB, strerror(errno));
            return -1;
      }
      /*
       * Write new contents.
       */
      if (rmtab_write_atomic(*pruned_rmtab, buf) == -1) {
            clulog_and_print(LOG_ERR, "#26: Failed to write %s: %s\n", buf,
                         strerror(errno));
            return -1;
      }
      
      return 0;
}


/**
 * Fork off into the background and store our pid file in
 * [path]/.clumanager/pid
 *
 * @param path          Mount point we're monitoring.
 * @return        -1 on failure, 0 on success.
 */
static int
00538 daemonize(char *path)
{
      FILE *fp=NULL;
      char filename[PATH_MAX];

      if (daemon(0,0) == -1)
            return -1;

      memset(filename,0,PATH_MAX);
      snprintf(filename, sizeof(filename), "%s/%s/pid", path, CM_NFS_DIR);

      fp = fopen(filename, "w");
      if (fp == NULL) {
            clulog(LOG_WARNING, "#63: Couldn't write PID!\n");
      }

      fprintf(fp, "%d", getpid());
      fclose(fp);

      return 0;
}


/**
 * main
 *
 * Main.  Main.  Main.  Main.  Main.  Main.  Main.  Main.  Main.  Main.  
 */
int
main(int argc, char **argv)
{
      char path[PATH_MAX];
      char rmtab_priv[PATH_MAX];
      
      rmtab_node *rmtab = NULL, *pruned_rmtab = NULL, *diff = NULL;
      
      if (argc < 2) {
            fprintf(stderr, "usage: clurmtabd <mount-point>\n");
            return -1;
      }

      /* Set up configuration parameters */
      if (rmtabd_config_init() == -1) {
            clulog_and_print(LOG_ERR,
                           "#27: Couldn't initialize - exiting\n");
            return -1;
      }

        /* Set up our internal variables */
      snprintf(path, sizeof(path), "%s", argv[1]);
      snprintf(rmtab_priv, sizeof(rmtab_priv), "%s/%s/rmtab", path,
             CM_NFS_DIR);

      /*
       * Synchronize the rmtab files
       *
       * We do this before we call daemonize() to ensure that when
       * the service script calls exportfs, /var/lib/nfs/rmtab has
       * all the necessary entries.
       */
      if (rmtab_init(path, &rmtab, &pruned_rmtab) == -1) {
            clulog_and_print(LOG_WARNING,
                         "#64: Could not validate %s\n", path);
            clulog_and_print(LOG_WARNING,
                         "#65: NFS Failover of %s will malfunction\n",
                         path);
            return -1;
      }

      /* Jump off into the background */
      if (daemonize(path) == -1) {
            clulog_and_print(LOG_ERR, "#28: daemonize: %s\n",
                         strerror(errno));
            return -1;
      }

      /* Main loop */
      while (!exiting) {

            /* Snooze a bit */
            sleep(poll_interval);

            /* Check for updates */
            if (rmtab_get_update(&rmtab, &pruned_rmtab, &diff, path)
                == 0) {
                  /* Handle updates */
                  rmtab_merge(&pruned_rmtab, diff);
                  rmtab_kill(&diff);
                  if (rmtab_write_atomic(pruned_rmtab, rmtab_priv) == -1)
                        clulog(LOG_ERR,
                               "#29: rmtab_write_atomic: %s\n",
                               strerror(errno));
            }
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index