| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef _GNU_SOURCE |
| # define _GNU_SOURCE |
| #endif |
| |
| #include <crm_internal.h> |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #ifdef HAVE_GETOPT_H |
| # include <getopt.h> |
| #endif |
| |
| #include <crm/crm.h> |
| |
| |
| |
| |
| |
| |
| static char *crm_short_options = NULL; |
| static pcmk__cli_option_t *crm_long_options = NULL; |
| static const char *crm_app_description = NULL; |
| static const char *crm_app_usage = NULL; |
| |
| void |
| pcmk__cli_option_cleanup() |
| { |
| free(crm_short_options); |
| crm_short_options = NULL; |
| } |
| |
| static struct option * |
| create_long_opts(pcmk__cli_option_t *long_options) |
| { |
| struct option *long_opts = NULL; |
| |
| #ifdef HAVE_GETOPT_H |
| int index = 0, lpc = 0; |
| |
| |
| |
| |
| |
| |
| |
| |
| long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option)); |
| long_opts[index].name = "__dummmy__"; |
| long_opts[index].has_arg = 0; |
| long_opts[index].flag = 0; |
| long_opts[index].val = '_'; |
| index++; |
| |
| |
| |
| for (lpc = 0; long_options[lpc].name != NULL; lpc++) { |
| if (long_options[lpc].name[0] == '-') { |
| continue; |
| } |
| |
| long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option)); |
| |
| |
| long_opts[index].name = long_options[lpc].name; |
| long_opts[index].has_arg = long_options[lpc].has_arg; |
| long_opts[index].flag = long_options[lpc].flag; |
| long_opts[index].val = long_options[lpc].val; |
| index++; |
| } |
| |
| |
| long_opts = pcmk__realloc(long_opts, (index + 1) * sizeof(struct option)); |
| long_opts[index].name = NULL; |
| long_opts[index].has_arg = 0; |
| long_opts[index].flag = 0; |
| long_opts[index].val = 0; |
| #endif |
| |
| return long_opts; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| pcmk__set_cli_options(const char *short_options, const char *app_usage, |
| pcmk__cli_option_t *long_options, const char *app_desc) |
| { |
| if (short_options) { |
| crm_short_options = strdup(short_options); |
| |
| } else if (long_options) { |
| int lpc = 0; |
| int opt_string_len = 0; |
| char *local_short_options = NULL; |
| |
| for (lpc = 0; long_options[lpc].name != NULL; lpc++) { |
| if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) { |
| local_short_options = pcmk__realloc(local_short_options, |
| opt_string_len + 4); |
| local_short_options[opt_string_len++] = long_options[lpc].val; |
| |
| if (long_options[lpc].has_arg == optional_argument) { |
| local_short_options[opt_string_len++] = ':'; |
| } |
| if (long_options[lpc].has_arg >= required_argument) { |
| local_short_options[opt_string_len++] = ':'; |
| } |
| local_short_options[opt_string_len] = 0; |
| } |
| } |
| crm_short_options = local_short_options; |
| crm_trace("Generated short option string: '%s'", local_short_options); |
| } |
| |
| if (long_options) { |
| crm_long_options = long_options; |
| } |
| if (app_desc) { |
| crm_app_description = app_desc; |
| } |
| if (app_usage) { |
| crm_app_usage = app_usage; |
| } |
| } |
| |
| int |
| pcmk__next_cli_option(int argc, char **argv, int *index, const char **longname) |
| { |
| #ifdef HAVE_GETOPT_H |
| static struct option *long_opts = NULL; |
| |
| if (long_opts == NULL && crm_long_options) { |
| long_opts = create_long_opts(crm_long_options); |
| } |
| |
| *index = 0; |
| if (long_opts) { |
| int flag = getopt_long(argc, argv, crm_short_options, long_opts, index); |
| |
| switch (flag) { |
| case 0: |
| if (long_opts[*index].val) { |
| return long_opts[*index].val; |
| } else if (longname) { |
| *longname = long_opts[*index].name; |
| } else { |
| crm_notice("Unhandled option --%s", long_opts[*index].name); |
| return flag; |
| } |
| case -1: |
| break; |
| case ':': |
| crm_trace("Missing argument"); |
| pcmk__cli_help('?', CRM_EX_USAGE); |
| break; |
| case '?': |
| pcmk__cli_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE)); |
| break; |
| } |
| return flag; |
| } |
| #endif |
| |
| if (crm_short_options) { |
| return getopt(argc, argv, crm_short_options); |
| } |
| |
| return -1; |
| } |
| |
| void |
| pcmk__cli_help(char cmd, crm_exit_t exit_code) |
| { |
| int i = 0; |
| FILE *stream = (exit_code ? stderr : stdout); |
| |
| if (cmd == 'v' || cmd == '$') { |
| fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION); |
| fprintf(stream, "Written by Andrew Beekhof\n"); |
| goto out; |
| } |
| |
| if (cmd == '!') { |
| fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES); |
| goto out; |
| } |
| |
| fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description); |
| |
| if (crm_app_usage) { |
| fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage); |
| } |
| |
| if (crm_long_options) { |
| fprintf(stream, "Options:\n"); |
| for (i = 0; crm_long_options[i].name != NULL; i++) { |
| if (crm_long_options[i].flags & pcmk__option_hidden) { |
| |
| } else if (crm_long_options[i].flags & pcmk__option_paragraph) { |
| fprintf(stream, "%s\n\n", crm_long_options[i].desc); |
| |
| } else if (crm_long_options[i].flags & pcmk__option_example) { |
| fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc); |
| |
| } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) { |
| fprintf(stream, "%s\n", crm_long_options[i].desc); |
| |
| } else { |
| |
| if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) { |
| fprintf(stream, " -%c,", crm_long_options[i].val); |
| } else { |
| fputs(" ", stream); |
| } |
| fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name, |
| crm_long_options[i].has_arg == optional_argument ? "[=value]" : |
| crm_long_options[i].has_arg == required_argument ? "=value" : "", |
| crm_long_options[i].desc ? crm_long_options[i].desc : ""); |
| } |
| } |
| |
| } else if (crm_short_options) { |
| fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description); |
| for (i = 0; crm_short_options[i] != 0; i++) { |
| int has_arg = no_argument ; |
| |
| if (crm_short_options[i + 1] == ':') { |
| if (crm_short_options[i + 2] == ':') |
| has_arg = optional_argument ; |
| else |
| has_arg = required_argument ; |
| } |
| |
| fprintf(stream, " -%c %s\n", crm_short_options[i], |
| has_arg == optional_argument ? "[value]" : |
| has_arg == required_argument ? "{value}" : ""); |
| i += has_arg; |
| } |
| } |
| |
| fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT); |
| |
| out: |
| crm_exit(exit_code); |
| while(1); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const char * |
| pcmk__env_option(const char *option) |
| { |
| char env_name[NAME_MAX]; |
| const char *value = NULL; |
| |
| snprintf(env_name, NAME_MAX, "PCMK_%s", option); |
| value = getenv(env_name); |
| if (value != NULL) { |
| crm_trace("Found %s = %s", env_name, value); |
| return value; |
| } |
| |
| snprintf(env_name, NAME_MAX, "HA_%s", option); |
| value = getenv(env_name); |
| if (value != NULL) { |
| crm_trace("Found %s = %s", env_name, value); |
| return value; |
| } |
| |
| crm_trace("Nothing found for %s", option); |
| return NULL; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| pcmk__set_env_option(const char *option, const char *value) |
| { |
| char env_name[NAME_MAX]; |
| |
| snprintf(env_name, NAME_MAX, "PCMK_%s", option); |
| if (value) { |
| crm_trace("Setting %s to %s", env_name, value); |
| setenv(env_name, value, 1); |
| } else { |
| crm_trace("Unsetting %s", env_name); |
| unsetenv(env_name); |
| } |
| |
| snprintf(env_name, NAME_MAX, "HA_%s", option); |
| if (value) { |
| crm_trace("Setting %s to %s", env_name, value); |
| setenv(env_name, value, 1); |
| } else { |
| crm_trace("Unsetting %s", env_name); |
| unsetenv(env_name); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool |
| pcmk__env_option_enabled(const char *daemon, const char *option) |
| { |
| const char *value = pcmk__env_option(option); |
| |
| return (value != NULL) && (crm_is_true(value) || strstr(value, daemon)); |
| } |
| |
| |
| |
| |
| |
| |
| bool |
| pcmk__valid_interval_spec(const char *value) |
| { |
| (void) crm_parse_interval_spec(value); |
| return errno == 0; |
| } |
| |
| bool |
| pcmk__valid_boolean(const char *value) |
| { |
| int tmp; |
| |
| return crm_str_to_boolean(value, &tmp) == 1; |
| } |
| |
| bool |
| pcmk__valid_number(const char *value) |
| { |
| if (value == NULL) { |
| return false; |
| |
| } else if (pcmk_str_is_minus_infinity(value) || |
| pcmk_str_is_infinity(value)) { |
| return true; |
| } |
| |
| errno = 0; |
| crm_parse_ll(value, NULL); |
| return errno == 0; |
| } |
| |
| bool |
| pcmk__valid_positive_number(const char *value) |
| { |
| return pcmk_str_is_infinity(value) || |
| (crm_parse_ll(value, NULL) > 0); |
| } |
| |
| bool |
| pcmk__valid_quorum(const char *value) |
| { |
| return pcmk__strcase_any_of(value, "stop", "freeze", "ignore", "demote", "suicide", NULL); |
| } |
| |
| bool |
| pcmk__valid_script(const char *value) |
| { |
| struct stat st; |
| |
| if (pcmk__str_eq(value, "/dev/null", pcmk__str_casei)) { |
| return true; |
| } |
| |
| if (stat(value, &st) != 0) { |
| crm_err("Script %s does not exist", value); |
| return false; |
| } |
| |
| if (S_ISREG(st.st_mode) == 0) { |
| crm_err("Script %s is not a regular file", value); |
| return false; |
| } |
| |
| if ((st.st_mode & (S_IXUSR | S_IXGRP)) == 0) { |
| crm_err("Script %s is not executable", value); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool |
| pcmk__valid_utilization(const char *value) |
| { |
| char *end = NULL; |
| long number = strtol(value, &end, 10); |
| |
| if (end && (end[0] != '%')) { |
| return false; |
| } |
| return number >= 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static const char * |
| cluster_option_value(GHashTable *options, bool (*validate)(const char *), |
| const char *name, const char *old_name, |
| const char *def_value) |
| { |
| const char *value = NULL; |
| char *new_value = NULL; |
| |
| CRM_ASSERT(name != NULL); |
| |
| if (options) { |
| value = g_hash_table_lookup(options, name); |
| |
| if ((value == NULL) && old_name) { |
| value = g_hash_table_lookup(options, old_name); |
| if (value != NULL) { |
| pcmk__config_warn("Support for legacy name '%s' for cluster " |
| "option '%s' is deprecated and will be " |
| "removed in a future release", |
| old_name, name); |
| |
| |
| new_value = strdup(value); |
| g_hash_table_insert(options, strdup(name), new_value); |
| value = new_value; |
| } |
| } |
| |
| if (value && validate && (validate(value) == FALSE)) { |
| pcmk__config_err("Using default value for cluster option '%s' " |
| "because '%s' is invalid", name, value); |
| value = NULL; |
| } |
| |
| if (value) { |
| return value; |
| } |
| } |
| |
| |
| value = def_value; |
| |
| if (value == NULL) { |
| crm_trace("No value or default provided for cluster option '%s'", |
| name); |
| return NULL; |
| } |
| |
| if (validate) { |
| CRM_CHECK(validate(value) != FALSE, |
| crm_err("Bug: default value for cluster option '%s' is invalid", name); |
| return NULL); |
| } |
| |
| crm_trace("Using default value '%s' for cluster option '%s'", |
| value, name); |
| if (options) { |
| new_value = strdup(value); |
| g_hash_table_insert(options, strdup(name), new_value); |
| value = new_value; |
| } |
| return value; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const char * |
| pcmk__cluster_option(GHashTable *options, pcmk__cluster_option_t *option_list, |
| int len, const char *name) |
| { |
| const char *value = NULL; |
| |
| for (int lpc = 0; lpc < len; lpc++) { |
| if (pcmk__str_eq(name, option_list[lpc].name, pcmk__str_casei)) { |
| value = cluster_option_value(options, option_list[lpc].is_valid, |
| option_list[lpc].name, |
| option_list[lpc].alt_name, |
| option_list[lpc].default_value); |
| return value; |
| } |
| } |
| CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name)); |
| return NULL; |
| } |
| |
| void |
| pcmk__print_option_metadata(const char *name, const char *version, |
| const char *desc_short, const char *desc_long, |
| pcmk__cluster_option_t *option_list, int len) |
| { |
| int lpc = 0; |
| |
| fprintf(stdout, "<?xml version=\"1.0\"?>" |
| "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" |
| "<resource-agent name=\"%s\">\n" |
| " <version>%s</version>\n" |
| " <longdesc lang=\"en\">%s</longdesc>\n" |
| " <shortdesc lang=\"en\">%s</shortdesc>\n" |
| " <parameters>\n", name, version, desc_long, desc_short); |
| |
| for (lpc = 0; lpc < len; lpc++) { |
| if ((option_list[lpc].description_long == NULL) |
| && (option_list[lpc].description_short == NULL)) { |
| continue; |
| } |
| fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n" |
| " <shortdesc lang=\"en\">%s</shortdesc>\n" |
| " <content type=\"%s\" default=\"%s\"/>\n" |
| " <longdesc lang=\"en\">%s%s%s</longdesc>\n" |
| " </parameter>\n", |
| option_list[lpc].name, |
| option_list[lpc].description_short, |
| option_list[lpc].type, |
| option_list[lpc].default_value, |
| option_list[lpc].description_long? |
| option_list[lpc].description_long : |
| option_list[lpc].description_short, |
| (option_list[lpc].values? " Allowed values: " : ""), |
| (option_list[lpc].values? option_list[lpc].values : "")); |
| } |
| fprintf(stdout, " </parameters>\n</resource-agent>\n"); |
| } |
| |
| void |
| pcmk__validate_cluster_options(GHashTable *options, |
| pcmk__cluster_option_t *option_list, int len) |
| { |
| for (int lpc = 0; lpc < len; lpc++) { |
| cluster_option_value(options, option_list[lpc].is_valid, |
| option_list[lpc].name, |
| option_list[lpc].alt_name, |
| option_list[lpc].default_value); |
| } |
| } |