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

main.c

/******************************************************************************
*******************************************************************************
**
**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
**
**  This copyrighted material is made available to anyone wishing to use,
**  modify, copy, or redistribute it subject to the terms and conditions
**  of the GNU General Public License v.2.
**
*******************************************************************************
******************************************************************************/

#include <inttypes.h>
#include <unistd.h>
#include <signal.h>
#include "copyright.cf"
#include "cnxman-socket.h"
#include "cman_tool.h"

#define OPTION_STRING         ("m:n:v:e:2p:c:r:i:N:t:o:XVwqh?d")
#define OP_JOIN               1
#define OP_LEAVE        2
#define OP_EXPECTED           3
#define OP_VOTES        4
#define OP_KILL               5
#define OP_VERSION            6
#define OP_WAIT               7
#define OP_STATUS       8
#define OP_NODES        9
#define OP_SERVICES           10


static void print_usage(void)
{
      printf("Usage:\n");
      printf("\n");
      printf("%s <join|leave|kill|expected|votes|version|wait|status|nodes|services> [options]\n",
             prog_name);
      printf("\n");
      printf("Options:\n");
      printf("  -h               Print this help, then exit\n");
      printf("  -V               Print program version information, then exit\n");
      printf("  -d               Enable debug output\n");
      printf("\n");

      printf("join\n");
      printf("  -m <addr>      * Multicast address to use (combines with -i)\n");
      printf("  -i <ifname>    * Interfaces for above multicast addresses\n");
      printf("  -v <votes>       Number of votes this node has (default 1)\n");
      printf("  -e <votes>       Number of expected votes for the cluster (no default)\n");
      printf("  -c <clustername> Name of the cluster to join\n");
      printf("  -2               This is a two node cluster (-e must be 1)\n");
      printf("  -p <port>        UDP port number for cman communications (default %d)\n", DEFAULT_PORT);
      printf("  -n <nodename>  * The name of this node (defaults to hostname)\n");
      printf("  -N <id>          Node id (defaults to automatic)\n");
      printf("  -X               Do not use cluster.conf values from CCS\n");
      printf("  -w               Wait until node has joined a cluster\n");
      printf("  -q               Wait until the cluster is quorate\n");
      printf("  -t               Maximum time (in seconds) to wait\n");
      printf("  options with marked * can be specified multiple times for multi-path systems\n");

      printf("\n");
      printf("wait               Wait until the node is a member of a cluster\n");
      printf("  -q               Wait until the cluster is quorate\n");
      printf("  -t               Maximum time (in seconds) to wait\n");
      printf("\n");

      printf("leave\n");
      printf("  -w               If cluster is in transition, wait and keep trying\n");
      printf("  -t               Maximum time (in seconds) to wait\n");
      printf("  remove           Tell other nodes to ajust quorum downwards if necessary\n");
      printf("  force            Leave even if cluster subsystems are active\n");

      printf("\n");
      printf("kill\n");
      printf("  -n <nodename>    The name of the node to kill (can specify multiple times)\n");

      printf("\n");
      printf("expected\n");
      printf("  -e <votes>       New number of expected votes for the cluster\n");

      printf("\n");
      printf("votes\n");
      printf("  -v <votes>       New number of votes for this node\n");

      printf("\n");
      printf("status             Show local record of cluster status\n");
      printf("\n");
      printf("nodes              Show local record of cluster nodes\n");
      printf("\n");
      printf("services           Show local record of cluster services\n");

      printf("\n");
      printf("version\n");
      printf("  -r <config>      A new config version to set on all members\n");
      printf("\n");
}

static void sigalarm_handler(int sig)
{
      fprintf(stderr, "Timed-out waiting for cluster\n");
      exit(2);
}

static void show_file(char *name)
{
      FILE *file;
      char line[256];

      file = fopen(name, "r");
      if (!file)
            die("can't open %s, cman not running", name);

      while (fgets(line, 256, file))
            printf("%s", line);

      fclose(file);
}

static void show_status(void)
{
      show_file("/proc/cluster/status");
}

static void show_nodes(void)
{
      show_file("/proc/cluster/nodes");
}

static void show_services(void)
{
      show_file("/proc/cluster/services");
}

static int open_cluster_socket()
{
      int cluster_sock;

      cluster_sock = socket(AF_CLUSTER, SOCK_DGRAM, CLPROTO_MASTER);
      if (cluster_sock == -1)
            die("can't open cluster socket, cman kernel module probably not loaded");

      return cluster_sock;
}

char *cman_error(int err)
{
      char *die_error;

      switch (errno) {
      case ENOTCONN:
            die_error = "Cluster software not started";
            break;
      case ENOENT:
            die_error = "Node is not yet a cluster member";
            break;
      case EBUSY:
            die_error = "Cluster is in transition, try later or use -w";
            break;
      default:
            die_error = strerror(errno);
            break;
      }
      return die_error;
}

static void leave(commandline_t *comline)
{
      int cluster_sock;
      int result;
      int flags = CLUSTER_LEAVEFLAG_DOWN;

      cluster_sock = open_cluster_socket();

      /* "cman_tool leave remove" adjusts quorum downward */

      if (comline->remove)
            flags |= CLUSTER_LEAVEFLAG_REMOVED;
      if (comline->force)
            flags |= CLUSTER_LEAVEFLAG_FORCE;

      /* If the join count is != 1 then there are other things using
         the cluster and we need to be forced */

      if ((result = ioctl(cluster_sock, SIOCCLUSTER_GET_JOINCOUNT, 0)) != 0) {
            if (result < 0)
                  die("error getting join count: %s", cman_error(errno));

            if (!comline->force) {
                  die("Can't leave cluster while there are %d active subsystems\n", result);
            }
      }

      /* Unlikely this will be needed, but no point in leaving it out */
      if (comline->wait_opt && comline->timeout) {
            signal(SIGALRM, sigalarm_handler);
            alarm(comline->timeout);
      }

      do {
            result = ioctl(cluster_sock, SIOCCLUSTER_LEAVE_CLUSTER, flags);
            if (result < 0 && errno == EBUSY && comline->wait_opt)
                  sleep(1);

      } while (result < 0 && errno == EBUSY && comline->wait_opt);

      if (result) {
            die("Error leaving cluster: %s", cman_error(errno));
      }

      close(cluster_sock);
}

static void set_expected(commandline_t *comline)
{
      int cluster_sock;
      int result;

      cluster_sock = open_cluster_socket();

      if ((result = ioctl(cluster_sock, SIOCCLUSTER_SETEXPECTED_VOTES,
                      comline->expected_votes)))
            die("can't set expected votes: %s", cman_error(errno));

      close(cluster_sock);
}

static void set_votes(commandline_t *comline)
{
      int cluster_sock;
      int result;

      cluster_sock = open_cluster_socket();

      if ((result = ioctl(cluster_sock, SIOCCLUSTER_SET_VOTES,
                      comline->votes)))
            die("can't set votes: %s", cman_error(errno));

      close(cluster_sock);
}

static void version(commandline_t *comline)
{
      struct cl_version ver;
      int cluster_sock;
      int result;

      cluster_sock = open_cluster_socket();

      if ((result = ioctl(cluster_sock, SIOCCLUSTER_GET_VERSION, &ver)))
            die("can't get version: %s", cman_error(errno));

      if (!comline->config_version) {
            printf("%d.%d.%d config %d\n", ver.major, ver.minor, ver.patch,
                   ver.config);
            goto out;
      }

      ver.config = comline->config_version;

      if ((result = ioctl(cluster_sock, SIOCCLUSTER_SET_VERSION, &ver)))
            die("can't set version: %s", cman_error(errno));
 out:
      close(cluster_sock);
}

static int wait_for_sock(int sock)
{
      int recvbuf[256]; /* Plenty big enough for an OOB message */
      int ret;

      ret = recv(sock, recvbuf, sizeof(recvbuf), MSG_OOB);
      if (ret < 0)
      {
            return errno;
      }

      /* EOF also means we are no longer connected to the cluster */
      if (ret == 0)
            return ENOTCONN;

      return 0;
}

static int cluster_wait(commandline_t *comline)
{
    int cluster_sock;
    int ret = 0;

    cluster_sock = socket(AF_CLUSTER, SOCK_DGRAM, CLPROTO_CLIENT);
    if (cluster_sock == -1)
          die("can't open cluster socket, cman kernel module probably not loaded");

    if (comline->wait_quorate_opt) {
          while (ioctl(cluster_sock, SIOCCLUSTER_ISQUORATE, 0) <= 0) {
                if ((ret = wait_for_sock(cluster_sock)))
                      goto end_wait;
          }
    }
    else {
          while (ioctl(cluster_sock, SIOCCLUSTER_GETMEMBERS, 0) < 0) {
                if ((ret = wait_for_sock(cluster_sock)))
                      goto end_wait;
          }
    }

 end_wait:
    close(cluster_sock);

    return ret;
}

static void kill_node(commandline_t *comline)
{
      int cluster_sock;
      int i;
      struct cl_cluster_node node;

      if (!comline->num_nodenames) {
          die("No node name specified\n");
      }

      cluster_sock = open_cluster_socket();

      for (i=0; i<comline->num_nodenames; i++) {

          /* Resolve node name into a number */
          node.node_id = 0;
          strcpy(node.name, comline->nodenames[i]);
          if (ioctl(cluster_sock, SIOCCLUSTER_GETNODE, &node)) {
            fprintf(stderr, "Can't kill node %s : %s\n", node.name, strerror(errno));
            continue;
          }


          if (ioctl(cluster_sock, SIOCCLUSTER_KILLNODE, node.node_id))
            perror("kill node failed");
      }

      close(cluster_sock);
}


static int get_int_arg(char argopt, char *arg)
{
      char *tmp;
      int val;

      val = strtol(arg, &tmp, 10);
      if (tmp == arg || tmp != arg + strlen(arg))
            die("argument to %c (%s) is not an integer", argopt, arg);

      if (val < 0)
            die("argument to %c cannot be negative", argopt);

      return val;
}


static void decode_arguments(int argc, char *argv[], commandline_t *comline)
{
      int cont = TRUE;
      int optchar, i;

      while (cont) {
            optchar = getopt(argc, argv, OPTION_STRING);

            switch (optchar) {

            case 'i':
                  i = comline->num_interfaces;
                  if (i >= MAX_INTERFACES)
                        die("maximum of %d interfaces allowed",
                            MAX_INTERFACES);
                  comline->interfaces[i] = strdup(optarg);
                  if (!comline->interfaces[i])
                        die("no memory");
                  comline->num_interfaces++;
                  break;

            case 'o':
                  comline->override_nodename = strdup(optarg);
                  break;

            case 'm':
                    i = comline->num_multicasts;
                  if (i >= MAX_INTERFACES)
                          die("maximum of %d multicast addresses allowed",
                            MAX_INTERFACES);
                  if (strlen(optarg) > MAX_MCAST_NAME_LEN)
                        die("maximum multicast name length is %d",
                            MAX_MCAST_NAME_LEN);
                  comline->multicast_names[i] = strdup(optarg);
                  comline->num_multicasts++;
                  break;

            case 'n':
                    i = comline->num_nodenames;
                  if (i >= MAX_INTERFACES)
                          die("maximum of %d node names allowed",
                            MAX_INTERFACES);
                  if (strlen(optarg) > MAX_NODE_NAME_LEN)
                        die("maximum node name length is %d",
                            MAX_NODE_NAME_LEN);
                  comline->nodenames[i] = strdup(optarg);
                  comline->num_nodenames++;
                  break;

            case 'r':
                  comline->config_version = get_int_arg(optchar, optarg);
                  comline->config_version_opt = TRUE;
                  break;

            case 'v':
                  comline->votes = get_int_arg(optchar, optarg);
                  comline->votes_opt = TRUE;
                  break;

            case 'e':
                  comline->expected_votes = get_int_arg(optchar, optarg);
                  comline->expected_votes_opt = TRUE;
                  break;

            case '2':
                  comline->two_node = TRUE;
                  break;

            case 'p':
                  comline->port = get_int_arg(optchar, optarg);
                  comline->port_opt = TRUE;
                  break;

            case 'N':
                  comline->nodeid = get_int_arg(optchar, optarg);
                  comline->nodeid_opt = TRUE;
                  break;

            case 'c':
                  if (strlen(optarg) >= MAX_CLUSTER_NAME_LEN)
                        die("maximum cluster name length is %d",
                            MAX_CLUSTER_NAME_LEN);
                  strcpy(comline->clustername, optarg);
                  comline->clustername_opt = TRUE;
                  break;

            case 'X':
                  comline->no_ccs = TRUE;
                  break;

            case 'V':
                  printf("cman_tool %s (built %s %s)\n",
                        CMAN_RELEASE_NAME, __DATE__, __TIME__);
                  printf("%s\n", REDHAT_COPYRIGHT);
                  exit(EXIT_SUCCESS);
                  break;

            case 'h':
                  print_usage();
                  exit(EXIT_SUCCESS);
                  break;

            case ':':
            case '?':
                  fprintf(stderr, "Please use '-h' for usage.\n");
                  exit(EXIT_FAILURE);
                  break;

            case 'd':
                    comline->verbose++;
                  break;

            case 'w':
                  comline->wait_opt = TRUE;
                  break;

            case 'q':
                  comline->wait_quorate_opt = TRUE;
                  break;

            case 't':
                  comline->timeout = get_int_arg(optchar, optarg);
                  break;

            case EOF:
                  cont = FALSE;
                  break;

            default:
                  die("unknown option: %c", optchar);
                  break;
            };
      }

      while (optind < argc) {
            if (strcmp(argv[optind], "join") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_JOIN;
            } else if (strcmp(argv[optind], "leave") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_LEAVE;
            } else if (strcmp(argv[optind], "expected") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_EXPECTED;
            } else if (strcmp(argv[optind], "votes") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_VOTES;
            } else if (strcmp(argv[optind], "kill") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_KILL;
            } else if (strcmp(argv[optind], "version") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_VERSION;
            } else if (strcmp(argv[optind], "wait") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_WAIT;
            } else if (strcmp(argv[optind], "status") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_STATUS;
            } else if (strcmp(argv[optind], "nodes") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_NODES;
            } else if (strcmp(argv[optind], "services") == 0) {
                  if (comline->operation)
                        die("can't specify two operations");
                  comline->operation = OP_SERVICES;
            } else if (strcmp(argv[optind], "remove") == 0) {
                  comline->remove = TRUE;
            } else if (strcmp(argv[optind], "force") == 0) {
                  comline->force = TRUE;
            } else
                  die("unknown option %s", argv[optind]);

            optind++;
      }

      if (!comline->operation)
            die("no operation specified");
}

static void check_arguments(commandline_t *comline)
{
      int error;

      if (!comline->expected_votes)
              die("expected votes not set");

      if (!comline->clustername[0])
            die("cluster name not set");

      if (!comline->votes_opt)
            comline->votes = DEFAULT_VOTES;

      if (!comline->port)
            comline->port = DEFAULT_PORT;

      if (comline->two_node && comline->expected_votes != 1)
            die("expected_votes value (%d) invalid in two node mode",
                comline->expected_votes);

      if (!comline->nodenames[0]) {
            struct utsname utsname;
            error = uname(&utsname);
            if (error)
                  die("cannot get node name, uname failed");

            comline->nodenames[0] = strdup(utsname.nodename);
            comline->num_nodenames++;
      }

      if (!comline->num_interfaces) {
              comline->interfaces[0] = strdup("eth0");
            if (!comline->interfaces[0])
                  die("no memory");
      }

      if (comline->num_multicasts != comline->num_interfaces) {
              die("Number of multicast addresses (%d) must match number of "
                "interfaces (%d)", comline->num_multicasts,
                comline->num_interfaces);
      }

      if (comline->num_nodenames && comline->num_multicasts &&
          comline->num_nodenames != comline->num_multicasts) {
              die("Number of node names (%d) must match number of multicast "
                "addresses (%d)", comline->num_nodenames,
                comline->num_multicasts);
      }

      if (comline->port <= 0 || comline->port > 65535)
            die("Port must be a number between 1 and 65535");

      /* This message looks like it contradicts the condition but
         a nodeid of zero simply means "assign one for me" and is a
         perfectly reasonable override */
      if (comline->nodeid < 0 || comline->nodeid > 4096)
              die("Node id must be between 1 and 4096");

      /* Cluster name needs to include trailing NUL */
      if (strlen(comline->clustername) > MAX_CLUSTER_NAME_LEN-1) {
              die("Cluster name must be < %d characters long",
                MAX_CLUSTER_NAME_LEN);
      }

      if (comline->timeout && !comline->wait_opt && !comline->wait_quorate_opt)
            die("timeout is only appropriate with wait");
}

int main(int argc, char *argv[])
{
      commandline_t comline;
      int ret;

      prog_name = argv[0];

      memset(&comline, 0, sizeof(commandline_t));

      decode_arguments(argc, argv, &comline);

      switch (comline.operation) {
      case OP_JOIN:
            if (!comline.no_ccs)
                  get_ccs_join_info(&comline);
            check_arguments(&comline);

            if (comline.timeout) {
                  signal(SIGALRM, sigalarm_handler);
                  alarm(comline.timeout);
            }

            join(&comline);
            if (comline.wait_opt || comline.wait_quorate_opt) {
                  do {
                        ret = cluster_wait(&comline);
                        if (ret == ENOTCONN)
                              join(&comline);

                  } while (ret == ENOTCONN);
            }
            break;

      case OP_LEAVE:
            leave(&comline);
            break;

      case OP_EXPECTED:
            set_expected(&comline);
            break;

      case OP_VOTES:
            set_votes(&comline);
            break;

      case OP_KILL:
            kill_node(&comline);
            break;

      case OP_VERSION:
            version(&comline);
            break;

      case OP_WAIT:
            if (comline.timeout) {
                  signal(SIGALRM, sigalarm_handler);
                  alarm(comline.timeout);
            }
            cluster_wait(&comline);
            break;

      case OP_STATUS:
            show_status();
            break;

      case OP_NODES:
            show_nodes();
            break;

      case OP_SERVICES:
            show_services();
            break;

      /* FIXME: support CLU_SET_NODENAME? */
      }

      exit(EXIT_SUCCESS);
}

char *prog_name;

Generated by  Doxygen 1.6.0   Back to index