diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am index 6198cae63c..652c290748 100644 --- a/include/crm/common/Makefile.am +++ b/include/crm/common/Makefile.am @@ -1,19 +1,19 @@ # # Copyright 2004-2020 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # MAINTAINERCLEANFILES = Makefile.in headerdir=$(pkgincludedir)/crm/common header_HEADERS = xml.h ipc.h util.h iso8601.h mainloop.h logging.h results.h \ nvpair.h noinst_HEADERS = ipcs_internal.h internal.h alerts_internal.h \ iso8601_internal.h remote_internal.h xml_internal.h \ ipc_internal.h output.h cmdline_internal.h curses_internal.h \ - attrd_internal.h + attrd_internal.h options_internal.h diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h new file mode 100644 index 0000000000..952f752df4 --- /dev/null +++ b/include/crm/common/options_internal.h @@ -0,0 +1,109 @@ +/* + * Copyright 2006-2020 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef OPTIONS_INTERNAL__H +# define OPTIONS_INTERNAL__H + +# include // HAVE_GETOPT, _Noreturn +# include // GHashTable +# include // bool + +/* + * Command-line option handling + * + * This will all eventually go away as everything is converted to use GOption + */ + +# ifdef HAVE_GETOPT_H +# include +# else +# define no_argument 0 +# define required_argument 1 +# endif + +enum pcmk__cli_option_flags { + pcmk__option_default = (1 << 0), + pcmk__option_hidden = (1 << 1), + pcmk__option_paragraph = (1 << 2), + pcmk__option_example = (1 << 3), +}; + +typedef struct pcmk__cli_option_s { + /* Fields from 'struct option' in getopt.h */ + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; + + /* Custom fields */ + const char *desc; + long flags; +} pcmk__cli_option_t; + +void pcmk__set_cli_options(const char *short_options, const char *usage, + pcmk__cli_option_t *long_options, + const char *app_desc); +int pcmk__next_cli_option(int argc, char **argv, int *index, + const char **longname); +_Noreturn void pcmk__cli_help(char cmd, crm_exit_t exit_code); +void pcmk__cli_option_cleanup(void); + + +/* + * Cluster option handling + */ + +typedef struct pcmk__cluster_option_s { + const char *name; + const char *alt_name; + const char *type; + const char *values; + const char *default_value; + + bool (*is_valid)(const char *); + + const char *description_short; + const char *description_long; + +} pcmk__cluster_option_t; + +const char *pcmk__cluster_option(GHashTable *options, + pcmk__cluster_option_t *option_list, int len, + const char *name); + +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); + +void pcmk__validate_cluster_options(GHashTable *options, + pcmk__cluster_option_t *option_list, + int len); + +bool pcmk__valid_time(const char *value); +bool pcmk__valid_timer(const char *value); +bool pcmk__valid_boolean(const char *value); +bool pcmk__valid_number(const char *value); +bool pcmk__valid_positive_number(const char *value); +bool pcmk__valid_quorum(const char *value); +bool pcmk__valid_script(const char *value); +bool pcmk__valid_utilization(const char *value); + +// from watchdog.c +long pcmk__get_sbd_timeout(void); +long pcmk__auto_watchdog_timeout(void); +bool pcmk__valid_sbd_timeout(const char *value); + +#endif // OPTIONS_INTERNAL__H diff --git a/include/crm_internal.h b/include/crm_internal.h index 72d99169e5..e67702c3f1 100644 --- a/include/crm_internal.h +++ b/include/crm_internal.h @@ -1,318 +1,238 @@ /* * Copyright 2006-2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef CRM_INTERNAL__H # define CRM_INTERNAL__H # include # include # include # include # include # include # include # include +# include # include /* This symbol allows us to deprecate public API and prevent internal code from * using it while still keeping it for backward compatibility. */ #define PCMK__NO_COMPAT /* Dynamic loading of libraries */ void *find_library_function(void **handle, const char *lib, const char *fn, int fatal); /* For ACLs */ char *pcmk__uid2username(uid_t uid); const char *crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user); # if ENABLE_ACL # include static inline gboolean is_privileged(const char *user) { if (user == NULL) { return FALSE; } else if (strcmp(user, CRM_DAEMON_USER) == 0) { return TRUE; } else if (strcmp(user, "root") == 0) { return TRUE; } return FALSE; } # endif -/* CLI option processing*/ -# ifdef HAVE_GETOPT_H -# include -# else -# define no_argument 0 -# define required_argument 1 -# endif - -enum pcmk__cli_option_flags { - pcmk__option_default = (1 << 0), - pcmk__option_hidden = (1 << 1), - pcmk__option_paragraph = (1 << 2), - pcmk__option_example = (1 << 3), -}; - -typedef struct pcmk__cli_option_s { - /* Fields from 'struct option' in getopt.h */ - /* name of long option */ - const char *name; - /* - * one of no_argument, required_argument, and optional_argument: - * whether option takes an argument - */ - int has_arg; - /* if not NULL, set *flag to val when option found */ - int *flag; - /* if flag not NULL, value to set *flag to; else return value */ - int val; - - /* Custom fields */ - const char *desc; - long flags; -} pcmk__cli_option_t; - -void pcmk__set_cli_options(const char *short_options, const char *usage, - pcmk__cli_option_t *long_options, - const char *app_desc); -int pcmk__next_cli_option(int argc, char **argv, int *index, - const char **longname); -_Noreturn void pcmk__cli_help(char cmd, crm_exit_t exit_code); -void pcmk__cli_option_cleanup(void); - -/* Cluster Option Processing */ -typedef struct pcmk__cluster_option_s { - const char *name; - const char *alt_name; - const char *type; - const char *values; - const char *default_value; - - bool (*is_valid)(const char *); - - const char *description_short; - const char *description_long; - -} pcmk__cluster_option_t; - -const char *pcmk__cluster_option(GHashTable *options, - pcmk__cluster_option_t *option_list, int len, - const char *name); - -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); - -void pcmk__validate_cluster_options(GHashTable *options, - pcmk__cluster_option_t *option_list, - int len); - -bool pcmk__valid_time(const char *value); -bool pcmk__valid_timer(const char *value); -bool pcmk__valid_boolean(const char *value); -bool pcmk__valid_number(const char *value); -bool pcmk__valid_positive_number(const char *value); -bool pcmk__valid_quorum(const char *value); -bool pcmk__valid_script(const char *value); -bool pcmk__valid_utilization(const char *value); -long pcmk__get_sbd_timeout(void); -long pcmk__auto_watchdog_timeout(void); -bool pcmk__valid_sbd_timeout(const char *value); - /* char2score */ extern int node_score_red; extern int node_score_green; extern int node_score_yellow; /* Assorted convenience functions */ void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile); // printf-style format to create operation ID from resource, action, interval #define CRM_OP_FMT "%s_%s_%u" static inline long long crm_clear_bit(const char *function, int line, const char *target, long long word, long long bit) { long long rc = (word & ~bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s cleared by %s:%d", bit, target, function, line); } else { crm_trace("Bit 0x%.8llx cleared by %s:%d", bit, function, line); } return rc; } static inline long long crm_set_bit(const char *function, int line, const char *target, long long word, long long bit) { long long rc = (word | bit); if (rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s set by %s:%d", bit, target, function, line); } else { crm_trace("Bit 0x%.8llx set by %s:%d", bit, function, line); } return rc; } # define set_bit(word, bit) word = crm_set_bit(__FUNCTION__, __LINE__, NULL, word, bit) # define clear_bit(word, bit) word = crm_clear_bit(__FUNCTION__, __LINE__, NULL, word, bit) char *generate_hash_key(const char *crm_msg_reference, const char *sys); const char *pcmk__env_option(const char *option); void pcmk__set_env_option(const char *option, const char *value); bool pcmk__env_option_enabled(const char *daemon, const char *option); void strip_text_nodes(xmlNode * xml); void pcmk_panic(const char *origin); pid_t pcmk_locate_sbd(void); # define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } # define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } # define F_ATTRD_KEY "attr_key" # define F_ATTRD_ATTRIBUTE "attr_name" # define F_ATTRD_REGEX "attr_regex" # define F_ATTRD_TASK "task" # define F_ATTRD_VALUE "attr_value" # define F_ATTRD_SET "attr_set" # define F_ATTRD_IS_REMOTE "attr_is_remote" # define F_ATTRD_IS_PRIVATE "attr_is_private" # define F_ATTRD_SECTION "attr_section" # define F_ATTRD_DAMPEN "attr_dampening" # define F_ATTRD_HOST "attr_host" # define F_ATTRD_HOST_ID "attr_host_id" # define F_ATTRD_USER "attr_user" # define F_ATTRD_WRITER "attr_writer" # define F_ATTRD_VERSION "attr_version" # define F_ATTRD_RESOURCE "attr_resource" # define F_ATTRD_OPERATION "attr_clear_operation" # define F_ATTRD_INTERVAL "attr_clear_interval" # define F_ATTRD_IS_FORCE_WRITE "attrd_is_force_write" /* attrd operations */ # define ATTRD_OP_PEER_REMOVE "peer-remove" # define ATTRD_OP_UPDATE "update" # define ATTRD_OP_UPDATE_BOTH "update-both" # define ATTRD_OP_UPDATE_DELAY "update-delay" # define ATTRD_OP_QUERY "query" # define ATTRD_OP_REFRESH "refresh" # define ATTRD_OP_FLUSH "flush" # define ATTRD_OP_SYNC "sync" # define ATTRD_OP_SYNC_RESPONSE "sync-response" # define ATTRD_OP_CLEAR_FAILURE "clear-failure" # define PCMK__XA_MODE "mode" # define PCMK_ENV_PHYSICAL_HOST "physical_host" # if SUPPORT_COROSYNC # include # include typedef struct qb_ipc_request_header cs_ipc_header_request_t; typedef struct qb_ipc_response_header cs_ipc_header_response_t; # else typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_request_t; typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); int error __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_response_t; # endif void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb); qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb); void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb); void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm); static inline void * realloc_safe(void *ptr, size_t size) { void *new_ptr; // realloc(p, 0) can replace free(p) but this wrapper can't CRM_ASSERT(size > 0); new_ptr = realloc(ptr, size); if (new_ptr == NULL) { free(ptr); abort(); } return new_ptr; } const char *crm_xml_add_last_written(xmlNode *xml_node); void crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth); void crm_buffer_add_char(char **buffer, int *offset, int *max, char c); bool pcmk__verify_digest(xmlNode *input, const char *expected); /* IPC Proxy Backend Shared Functions */ typedef struct remote_proxy_s { char *node_name; char *session_id; gboolean is_local; crm_ipc_t *ipc; mainloop_io_t *source; uint32_t last_request_id; lrmd_t *lrm; } remote_proxy_t; remote_proxy_t *remote_proxy_new( lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks, const char *node_name, const char *session_id, const char *channel); int remote_proxy_check(lrmd_t *lrmd, GHashTable *hash); void remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg); void remote_proxy_ack_shutdown(lrmd_t *lrmd); void remote_proxy_nack_shutdown(lrmd_t *lrmd); int remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata); void remote_proxy_disconnected(gpointer data); void remote_proxy_free(gpointer data); void remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg); void remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id); #endif /* CRM_INTERNAL__H */ diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 144a506518..5e72eaddd0 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -1,82 +1,83 @@ # -# Copyright 2004-2019 the Pacemaker project contributors +# Copyright 2004-2020 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # include $(top_srcdir)/mk/common.mk AM_CPPFLAGS += -I$(top_builddir)/lib/gnu -I$(top_srcdir)/lib/gnu -DPCMK_SCHEMAS_EMERGENCY_XSLT=0 MOSTLYCLEANFILES = md5.c ## libraries lib_LTLIBRARIES = libcrmcommon.la # Disable -Wcast-qual if used, because we do some hacky casting, # and because libxml2 has some signatures that should be const but aren't # for backward compatibility reasons. # s390 needs -fPIC # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC CFLAGS = $(CFLAGS_COPY:-Wcast-qual=) -fPIC SUBDIRS = tests noinst_HEADERS = crmcommon_private.h libcrmcommon_la_LDFLAGS = -version-info 37:0:3 libcrmcommon_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libcrmcommon_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) libcrmcommon_la_LIBADD = @LIBADD_DL@ # Use += rather than backlashed continuation lines for parsing by bumplibs.sh libcrmcommon_la_SOURCES = libcrmcommon_la_SOURCES += acl.c libcrmcommon_la_SOURCES += agents.c libcrmcommon_la_SOURCES += alerts.c libcrmcommon_la_SOURCES += attrd_client.c if BUILD_CIBSECRETS libcrmcommon_la_SOURCES += cib_secrets.c endif libcrmcommon_la_SOURCES += cmdline.c libcrmcommon_la_SOURCES += digest.c libcrmcommon_la_SOURCES += io.c libcrmcommon_la_SOURCES += ipc.c libcrmcommon_la_SOURCES += iso8601.c libcrmcommon_la_SOURCES += logging.c libcrmcommon_la_SOURCES += mainloop.c libcrmcommon_la_SOURCES += nvpair.c libcrmcommon_la_SOURCES += operations.c +libcrmcommon_la_SOURCES += options.c libcrmcommon_la_SOURCES += output.c libcrmcommon_la_SOURCES += output_html.c libcrmcommon_la_SOURCES += output_log.c libcrmcommon_la_SOURCES += output_none.c libcrmcommon_la_SOURCES += output_text.c libcrmcommon_la_SOURCES += output_xml.c libcrmcommon_la_SOURCES += pid.c libcrmcommon_la_SOURCES += procfs.c libcrmcommon_la_SOURCES += remote.c libcrmcommon_la_SOURCES += results.c libcrmcommon_la_SOURCES += schemas.c libcrmcommon_la_SOURCES += strings.c libcrmcommon_la_SOURCES += utils.c libcrmcommon_la_SOURCES += watchdog.c libcrmcommon_la_SOURCES += xml.c libcrmcommon_la_SOURCES += xpath.c # It's possible to build the library adding ../gnu/md5.c directly to SOURCES, # but distclean chokes on that because it tries to include the source's .Plo # file, which may have already been cleaned. nodist_libcrmcommon_la_SOURCES = md5.c md5.c: ../gnu/md5.c cp "$<" "$@" clean-generic: rm -f *.log *.debug *.xml *~ diff --git a/lib/common/options.c b/lib/common/options.c new file mode 100644 index 0000000000..4f15ee6901 --- /dev/null +++ b/lib/common/options.c @@ -0,0 +1,513 @@ +/* + * Copyright 2004-2020 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_GETOPT_H +# include +#endif + +#include + + +/* + * Command-line option handling + */ + +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; + + /* + * A previous, possibly poor, choice of '?' as the short form of --help + * means that getopt_long() returns '?' for both --help and for "unknown option" + * + * This dummy entry allows us to differentiate between the two in + * pcmk__next_cli_option() and exit with the correct error code. + */ + long_opts = realloc_safe(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 = realloc_safe(long_opts, (index + 1) * sizeof(struct option)); + /*fprintf(stderr, "Creating %d %s = %c\n", index, + * long_options[lpc].name, long_options[lpc].val); */ + 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++; + } + + /* Now create the list terminator */ + long_opts = realloc_safe(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; +} + +/*! + * \internal + * \brief Define the command-line options a daemon or tool accepts + * + * \param[in] short_options getopt(3)-style short option list + * \param[in] app_usage summary of how command is invoked (for help) + * \param[in] long_options definition of options accepted + * \param[in] app_desc brief command description (for help) + */ +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 = realloc_safe(local_short_options, opt_string_len + 4); + local_short_options[opt_string_len++] = long_options[lpc].val; + /* getopt(3) says: Two colons mean an option takes an optional arg; */ + 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: /* End of option processing */ + 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 { + /* is val printable as char ? */ + 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 /* 0 */; + + if (crm_short_options[i + 1] == ':') { + if (crm_short_options[i + 2] == ':') + has_arg = optional_argument /* 2 */; + else + has_arg = required_argument /* 1 */; + } + + 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); // above does not return +} + + +/* + * Cluster option handling + */ + +bool +pcmk__valid_time(const char *value) +{ + return crm_get_msec(value) >= 5000; +} + +bool +pcmk__valid_timer(const char *value) +{ + return crm_get_msec(value) >= 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 (safe_str_eq(value, CRM_MINUS_INFINITY_S) + || safe_str_eq(value, CRM_INFINITY_S)) { + return true; + } + + errno = 0; + crm_parse_ll(value, NULL); + return errno == 0; +} + +bool +pcmk__valid_positive_number(const char *value) +{ + return safe_str_eq(value, CRM_INFINITY_S) + || (crm_parse_ll(value, NULL) > 0); +} + +bool +pcmk__valid_quorum(const char *value) +{ + return safe_str_eq(value, "stop") + || safe_str_eq(value, "freeze") + || safe_str_eq(value, "ignore") + || safe_str_eq(value, "suicide"); +} + +bool +pcmk__valid_script(const char *value) +{ + struct stat st; + + if (safe_str_eq(value, "/dev/null")) { + 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; +} + +/*! + * \internal + * \brief Check a table of configured options for a particular option + * + * \param[in] options Name/value pairs for configured options + * \param[in] validate If not NULL, validator function for option value + * \param[in] name Option name to look for + * \param[in] old_name Alternative option name to look for + * \param[in] def_value Default to use if option not configured + * + * \return Option value (from supplied options table or default value) + */ +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) { + crm_config_warn("Support for legacy name '%s' for cluster option '%s'" + " is deprecated and will be removed in a future release", + old_name, name); + + // Inserting copy with current name ensures we only warn once + new_value = strdup(value); + g_hash_table_insert(options, strdup(name), new_value); + value = new_value; + } + } + + if (value && validate && (validate(value) == FALSE)) { + crm_config_err("Resetting cluster option '%s' to default: value '%s' is invalid", + name, value); + value = NULL; + } + + if (value) { + return value; + } + } + + // No value found, use default + 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; +} + +/*! + * \internal + * \brief Get the value of a cluster option + * + * \param[in] options Name/value pairs for configured options + * \param[in] option_list Possible cluster options + * \param[in] name (Primary) option name to look for + * + * \return Option 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 (safe_str_eq(name, option_list[lpc].name)) { + 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, "" + "\n" + "\n" + " %s\n" + " %s\n" + " %s\n" + " \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, " \n" + " %s\n" + " \n" + " %s%s%s\n" + " \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, " \n\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); + } +} diff --git a/lib/common/utils.c b/lib/common/utils.c index 8aa0c53919..5ed7905593 100644 --- a/lib/common/utils.c +++ b/lib/common/utils.c @@ -1,1181 +1,700 @@ /* * Copyright 2004-2020 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MAXLINE # define MAXLINE 512 #endif -#ifdef HAVE_GETOPT_H -# include -#endif - #ifndef PW_BUFFER_LEN # define PW_BUFFER_LEN 500 #endif CRM_TRACE_INIT_DATA(common); gboolean crm_config_error = FALSE; gboolean crm_config_warning = FALSE; char *crm_system_name = NULL; int node_score_red = 0; int node_score_green = 0; int node_score_yellow = 0; -static pcmk__cli_option_t *crm_long_options = NULL; -static const char *crm_app_description = NULL; -static char *crm_short_options = NULL; -static const char *crm_app_usage = NULL; - -bool -pcmk__valid_time(const char *value) -{ - return crm_get_msec(value) >= 5000; -} - -bool -pcmk__valid_timer(const char *value) -{ - return crm_get_msec(value) >= 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 (safe_str_eq(value, CRM_MINUS_INFINITY_S) - || safe_str_eq(value, CRM_INFINITY_S)) { - return true; - } - - errno = 0; - crm_parse_ll(value, NULL); - return errno == 0; -} - -bool -pcmk__valid_positive_number(const char *value) -{ - return safe_str_eq(value, CRM_INFINITY_S) - || (crm_parse_ll(value, NULL) > 0); -} - -bool -pcmk__valid_quorum(const char *value) -{ - return safe_str_eq(value, "stop") - || safe_str_eq(value, "freeze") - || safe_str_eq(value, "ignore") - || safe_str_eq(value, "suicide"); -} - -bool -pcmk__valid_script(const char *value) -{ - struct stat st; - - if (safe_str_eq(value, "/dev/null")) { - 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; -} - -void -pcmk__cli_option_cleanup() -{ - free(crm_short_options); - crm_short_options = NULL; -} - int char2score(const char *score) { int score_f = 0; if (score == NULL) { } else if (safe_str_eq(score, CRM_MINUS_INFINITY_S)) { score_f = -CRM_SCORE_INFINITY; } else if (safe_str_eq(score, CRM_INFINITY_S)) { score_f = CRM_SCORE_INFINITY; } else if (safe_str_eq(score, CRM_PLUS_INFINITY_S)) { score_f = CRM_SCORE_INFINITY; } else if (safe_str_eq(score, "red")) { score_f = node_score_red; } else if (safe_str_eq(score, "yellow")) { score_f = node_score_yellow; } else if (safe_str_eq(score, "green")) { score_f = node_score_green; } else { score_f = crm_parse_int(score, NULL); if (score_f > 0 && score_f > CRM_SCORE_INFINITY) { score_f = CRM_SCORE_INFINITY; } else if (score_f < 0 && score_f < -CRM_SCORE_INFINITY) { score_f = -CRM_SCORE_INFINITY; } } return score_f; } char * score2char_stack(int score, char *buf, size_t len) { if (score >= CRM_SCORE_INFINITY) { strncpy(buf, CRM_INFINITY_S, 9); } else if (score <= -CRM_SCORE_INFINITY) { strncpy(buf, CRM_MINUS_INFINITY_S , 10); } else { return crm_itoa_stack(score, buf, len); } return buf; } char * score2char(int score) { if (score >= CRM_SCORE_INFINITY) { return strdup(CRM_INFINITY_S); } else if (score <= -CRM_SCORE_INFINITY) { return strdup(CRM_MINUS_INFINITY_S); } return crm_itoa(score); } -/*! - * \internal - * \brief Check a table of configured options for a particular option - * - * \param[in] options Name/value pairs for configured options - * \param[in] validate If not NULL, validator function for option value - * \param[in] name Option name to look for - * \param[in] old_name Alternative option name to look for - * \param[in] def_value Default to use if option not configured - * - * \return Option value (from supplied options table or default value) - */ -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) { - crm_config_warn("Support for legacy name '%s' for cluster option '%s'" - " is deprecated and will be removed in a future release", - old_name, name); - - // Inserting copy with current name ensures we only warn once - new_value = strdup(value); - g_hash_table_insert(options, strdup(name), new_value); - value = new_value; - } - } - - if (value && validate && (validate(value) == FALSE)) { - crm_config_err("Resetting cluster option '%s' to default: value '%s' is invalid", - name, value); - value = NULL; - } - - if (value) { - return value; - } - } - - // No value found, use default - 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; -} - -/*! - * \internal - * \brief Get the value of a cluster option - * - * \param[in] options Name/value pairs for configured options - * \param[in] option_list Possible cluster options - * \param[in] name (Primary) option name to look for - * - * \return Option 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 (safe_str_eq(name, option_list[lpc].name)) { - 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, "" - "\n" - "\n" - " %s\n" - " %s\n" - " %s\n" - " \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, " \n" - " %s\n" - " \n" - " %s%s%s\n" - " \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, " \n\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); - } -} - char * generate_hash_key(const char *crm_msg_reference, const char *sys) { char *hash_key = crm_concat(sys ? sys : "none", crm_msg_reference, '_'); crm_trace("created hash key: (%s)", hash_key); return hash_key; } int crm_user_lookup(const char *name, uid_t * uid, gid_t * gid) { int rc = pcmk_ok; char *buffer = NULL; struct passwd pwd; struct passwd *pwentry = NULL; buffer = calloc(1, PW_BUFFER_LEN); if (buffer == NULL) { return -ENOMEM; } rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry); if (pwentry) { if (uid) { *uid = pwentry->pw_uid; } if (gid) { *gid = pwentry->pw_gid; } crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid); } else { rc = rc? -rc : -EINVAL; crm_info("User %s lookup: %s", name, pcmk_strerror(rc)); } free(buffer); return rc; } /*! * \brief Get user and group IDs of pacemaker daemon user * * \param[out] uid If non-NULL, where to store daemon user ID * \param[out] gid If non-NULL, where to store daemon group ID * * \return pcmk_ok on success, -errno otherwise */ int pcmk_daemon_user(uid_t *uid, gid_t *gid) { static uid_t daemon_uid; static gid_t daemon_gid; static bool found = false; int rc = pcmk_err_generic; if (!found) { rc = crm_user_lookup(CRM_DAEMON_USER, &daemon_uid, &daemon_gid); if (rc == pcmk_ok) { found = true; } } if (found) { if (uid) { *uid = daemon_uid; } if (gid) { *gid = daemon_gid; } } return rc; } static int crm_version_helper(const char *text, const char **end_text) { int atoi_result = -1; CRM_ASSERT(end_text != NULL); errno = 0; if (text != NULL && text[0] != 0) { /* seemingly sacrificing const-correctness -- because while strtol doesn't modify the input, it doesn't want to artificially taint the "end_text" pointer-to-pointer-to-first-char-in-string with constness in case the input wasn't actually constant -- by semantic definition not a single character will get modified so it shall be perfectly safe to make compiler happy with dropping "const" qualifier here */ atoi_result = (int) strtol(text, (char **) end_text, 10); if (errno == EINVAL) { crm_err("Conversion of '%s' %c failed", text, text[0]); atoi_result = -1; } } return atoi_result; } /* * version1 < version2 : -1 * version1 = version2 : 0 * version1 > version2 : 1 */ int compare_version(const char *version1, const char *version2) { int rc = 0; int lpc = 0; const char *ver1_iter, *ver2_iter; if (version1 == NULL && version2 == NULL) { return 0; } else if (version1 == NULL) { return -1; } else if (version2 == NULL) { return 1; } ver1_iter = version1; ver2_iter = version2; while (1) { int digit1 = 0; int digit2 = 0; lpc++; if (ver1_iter == ver2_iter) { break; } if (ver1_iter != NULL) { digit1 = crm_version_helper(ver1_iter, &ver1_iter); } if (ver2_iter != NULL) { digit2 = crm_version_helper(ver2_iter, &ver2_iter); } if (digit1 < digit2) { rc = -1; break; } else if (digit1 > digit2) { rc = 1; break; } if (ver1_iter != NULL && *ver1_iter == '.') { ver1_iter++; } if (ver1_iter != NULL && *ver1_iter == '\0') { ver1_iter = NULL; } if (ver2_iter != NULL && *ver2_iter == '.') { ver2_iter++; } if (ver2_iter != NULL && *ver2_iter == 0) { ver2_iter = NULL; } } if (rc == 0) { crm_trace("%s == %s (%d)", version1, version2, lpc); } else if (rc < 0) { crm_trace("%s < %s (%d)", version1, version2, lpc); } else if (rc > 0) { crm_trace("%s > %s (%d)", version1, version2, lpc); } return rc; } guint crm_parse_interval_spec(const char *input) { long long msec = 0; if (input == NULL) { return 0; } else if (input[0] != 'P') { long long tmp = crm_get_msec(input); if(tmp > 0) { msec = tmp; } } else { crm_time_t *period_s = crm_time_parse_duration(input); if (period_s) { msec = 1000 * crm_time_get_seconds(period_s); crm_time_free(period_s); } else { // Reason why not valid has already been logged crm_warn("Using 0 instead of '%s'", input); } } return (msec <= 0)? 0 : ((msec >= G_MAXUINT)? G_MAXUINT : (guint) msec); } extern bool crm_is_daemon; /* coverity[+kill] */ void crm_abort(const char *file, const char *function, int line, const char *assert_condition, gboolean do_core, gboolean do_fork) { int rc = 0; int pid = 0; int status = 0; /* Implied by the parent's error logging below */ /* crm_write_blackbox(0); */ if(crm_is_daemon == FALSE) { /* This is a command line tool - do not fork */ /* crm_add_logfile(NULL); * Record it to a file? */ crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */ do_fork = FALSE; /* Just crash if needed */ } if (do_core == FALSE) { crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition); return; } else if (do_fork) { pid = fork(); } else { crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition); } if (pid == -1) { crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s", function, file, line, assert_condition); return; } else if(pid == 0) { /* Child process */ abort(); return; } /* Parent process */ crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s", function, pid, file, line, assert_condition); crm_write_blackbox(SIGTRAP, NULL); do { rc = waitpid(pid, &status, 0); if(rc == pid) { return; /* Job done */ } } while(errno == EINTR); if (errno == ECHILD) { /* crm_mon does this */ crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid); return; } crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid); } void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile) { int rc; pid_t pid; const char *devnull = "/dev/null"; if (daemonize == FALSE) { return; } /* Check before we even try... */ rc = pcmk__pidfile_matches(pidfile, 1, name, &pid); if ((rc != pcmk_rc_ok) && (rc != ENOENT)) { crm_err("%s: already running [pid %lld in %s]", name, (long long) pid, pidfile); printf("%s: already running [pid %lld in %s]\n", name, (long long) pid, pidfile); crm_exit(CRM_EX_ERROR); } pid = fork(); if (pid < 0) { fprintf(stderr, "%s: could not start daemon\n", name); crm_perror(LOG_ERR, "fork"); crm_exit(CRM_EX_OSERR); } else if (pid > 0) { crm_exit(CRM_EX_OK); } rc = pcmk__lock_pidfile(pidfile, name); if (rc != pcmk_rc_ok) { crm_err("Could not lock '%s' for %s: %s " CRM_XS " rc=%d", pidfile, name, pcmk_rc_str(rc), rc); printf("Could not lock '%s' for %s: %s (%d)\n", pidfile, name, pcmk_rc_str(rc), rc); crm_exit(CRM_EX_ERROR); } umask(S_IWGRP | S_IWOTH | S_IROTH); close(STDIN_FILENO); (void)open(devnull, O_RDONLY); /* Stdin: fd 0 */ close(STDOUT_FILENO); (void)open(devnull, O_WRONLY); /* Stdout: fd 1 */ close(STDERR_FILENO); (void)open(devnull, O_WRONLY); /* Stderr: fd 2 */ } char * crm_meta_name(const char *field) { int lpc = 0; int max = 0; char *crm_name = NULL; CRM_CHECK(field != NULL, return NULL); crm_name = crm_concat(CRM_META, field, '_'); /* Massage the names so they can be used as shell variables */ max = strlen(crm_name); for (; lpc < max; lpc++) { switch (crm_name[lpc]) { case '-': crm_name[lpc] = '_'; break; } } return crm_name; } const char * crm_meta_value(GHashTable * hash, const char *field) { char *key = NULL; const char *value = NULL; key = crm_meta_name(field); if (key) { value = g_hash_table_lookup(hash, key); free(key); } return value; } -static struct option * -crm_create_long_opts(pcmk__cli_option_t *long_options) -{ - struct option *long_opts = NULL; - -#ifdef HAVE_GETOPT_H - int index = 0, lpc = 0; - - /* - * A previous, possibly poor, choice of '?' as the short form of --help - * means that getopt_long() returns '?' for both --help and for "unknown option" - * - * This dummy entry allows us to differentiate between the two in - * pcmk__next_cli_option() and exit with the correct error code. - */ - long_opts = realloc_safe(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 = realloc_safe(long_opts, (index + 1) * sizeof(struct option)); - /*fprintf(stderr, "Creating %d %s = %c\n", index, - * long_options[lpc].name, long_options[lpc].val); */ - 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++; - } - - /* Now create the list terminator */ - long_opts = realloc_safe(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; -} - -/*! - * \internal - * \brief Define the command-line options a daemon or tool accepts - * - * \param[in] short_options getopt(3)-style short option list - * \param[in] app_usage summary of how command is invoked (for help) - * \param[in] long_options definition of options accepted - * \param[in] app_desc brief command description (for help) - */ -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 = realloc_safe(local_short_options, opt_string_len + 4); - local_short_options[opt_string_len++] = long_options[lpc].val; - /* getopt(3) says: Two colons mean an option takes an optional arg; */ - 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 = crm_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: /* End of option processing */ - 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 { - /* is val printable as char ? */ - 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 /* 0 */; - - if (crm_short_options[i + 1] == ':') { - if (crm_short_options[i + 2] == ':') - has_arg = optional_argument /* 2 */; - else - has_arg = required_argument /* 1 */; - } - - 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); // above does not return -} - void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro, qb_ipcs_service_t **ipcs_rw, qb_ipcs_service_t **ipcs_shm, struct qb_ipcs_service_handlers *ro_cb, struct qb_ipcs_service_handlers *rw_cb) { *ipcs_ro = mainloop_add_ipc_server(CIB_CHANNEL_RO, QB_IPC_NATIVE, ro_cb); *ipcs_rw = mainloop_add_ipc_server(CIB_CHANNEL_RW, QB_IPC_NATIVE, rw_cb); *ipcs_shm = mainloop_add_ipc_server(CIB_CHANNEL_SHM, QB_IPC_SHM, rw_cb); if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) { crm_err("Failed to create the CIB manager: exiting and inhibiting respawn"); crm_warn("Verify pacemaker and pacemaker_remote are not both enabled"); crm_exit(CRM_EX_FATAL); } } void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro, qb_ipcs_service_t *ipcs_rw, qb_ipcs_service_t *ipcs_shm) { qb_ipcs_destroy(ipcs_ro); qb_ipcs_destroy(ipcs_rw); qb_ipcs_destroy(ipcs_shm); } qb_ipcs_service_t * crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb) { return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb); } void attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb); if (*ipcs == NULL) { crm_err("Failed to create pacemaker-attrd server: exiting and inhibiting respawn"); crm_warn("Verify pacemaker and pacemaker_remote are not both enabled."); crm_exit(CRM_EX_FATAL); } } void stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb) { *ipcs = mainloop_add_ipc_server_with_prio("stonith-ng", QB_IPC_NATIVE, cb, QB_LOOP_HIGH); if (*ipcs == NULL) { crm_err("Failed to create fencer: exiting and inhibiting respawn."); crm_warn("Verify pacemaker and pacemaker_remote are not both enabled."); crm_exit(CRM_EX_FATAL); } } void * find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal) { char *error; void *a_function; if (*handle == NULL) { *handle = dlopen(lib, RTLD_LAZY); } if (!(*handle)) { crm_err("%sCould not open %s: %s", fatal ? "Fatal: " : "", lib, dlerror()); if (fatal) { crm_exit(CRM_EX_FATAL); } return NULL; } a_function = dlsym(*handle, fn); if (a_function == NULL) { error = dlerror(); crm_err("%sCould not find %s in %s: %s", fatal ? "Fatal: " : "", fn, lib, error); if (fatal) { crm_exit(CRM_EX_FATAL); } } return a_function; } #ifdef HAVE_UUID_UUID_H # include #endif char * crm_generate_uuid(void) { unsigned char uuid[16]; char *buffer = malloc(37); /* Including NUL byte */ uuid_generate(uuid); uuid_unparse(uuid, buffer); return buffer; } /*! * \brief Get name to be used as identifier for cluster messages * * \param[in] name Actual system name to check * * \return Non-NULL cluster message identifier corresponding to name * * \note The Pacemaker daemons were renamed in version 2.0.0, but the old names * must continue to be used as the identifier for cluster messages, so * that mixed-version clusters are possible during a rolling upgrade. */ const char * pcmk_message_name(const char *name) { if (name == NULL) { return "unknown"; } else if (!strcmp(name, "pacemaker-attrd")) { return "attrd"; } else if (!strcmp(name, "pacemaker-based")) { return CRM_SYSTEM_CIB; } else if (!strcmp(name, "pacemaker-controld")) { return CRM_SYSTEM_CRMD; } else if (!strcmp(name, "pacemaker-execd")) { return CRM_SYSTEM_LRMD; } else if (!strcmp(name, "pacemaker-fenced")) { return "stonith-ng"; } else if (!strcmp(name, "pacemaker-schedulerd")) { return CRM_SYSTEM_PENGINE; } else { return name; } } /*! * \brief Check whether a string represents a cluster daemon name * * \param[in] name String to check * * \return TRUE if name is standard client name used by daemons, FALSE otherwise */ bool crm_is_daemon_name(const char *name) { name = pcmk_message_name(name); return (!strcmp(name, CRM_SYSTEM_CRMD) || !strcmp(name, CRM_SYSTEM_STONITHD) || !strcmp(name, "stonith-ng") || !strcmp(name, "attrd") || !strcmp(name, CRM_SYSTEM_CIB) || !strcmp(name, CRM_SYSTEM_MCP) || !strcmp(name, CRM_SYSTEM_DC) || !strcmp(name, CRM_SYSTEM_TENGINE) || !strcmp(name, CRM_SYSTEM_LRMD)); } #include char * crm_md5sum(const char *buffer) { int lpc = 0, len = 0; char *digest = NULL; unsigned char raw_digest[MD5_DIGEST_SIZE]; if (buffer == NULL) { buffer = ""; } len = strlen(buffer); crm_trace("Beginning digest of %d bytes", len); digest = malloc(2 * MD5_DIGEST_SIZE + 1); if(digest) { md5_buffer(buffer, len, raw_digest); for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) { sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]); } digest[(2 * MD5_DIGEST_SIZE)] = 0; crm_trace("Digest %s.", digest); } else { crm_err("Could not create digest"); } return digest; } #ifdef HAVE_GNUTLS_GNUTLS_H void crm_gnutls_global_init(void) { signal(SIGPIPE, SIG_IGN); gnutls_global_init(); } #endif /*! * \brief Get the local hostname * * \return Newly allocated string with name, or NULL (and set errno) on error */ char * pcmk_hostname() { struct utsname hostinfo; return (uname(&hostinfo) < 0)? NULL : strdup(hostinfo.nodename); }