diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:53:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:53:20 +0000 |
commit | e5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch) | |
tree | a6716c9275b4b413f6c9194798b34b91affb3cc7 /include/crm | |
parent | Initial commit. (diff) | |
download | pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip |
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
81 files changed, 13162 insertions, 0 deletions
diff --git a/include/crm/Makefile.am b/include/crm/Makefile.am new file mode 100644 index 0000000..6dd52fd --- /dev/null +++ b/include/crm/Makefile.am @@ -0,0 +1,22 @@ +# +# Copyright 2004-2021 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 + +header_HEADERS = cib.h cluster.h compatibility.h crm.h \ + lrmd.h msg_xml.h services.h stonith-ng.h \ + crm_compat.h \ + msg_xml_compat.h \ + services_compat.h + +noinst_HEADERS = lrmd_internal.h services_internal.h + +SUBDIRS = common pengine cib fencing cluster diff --git a/include/crm/cib.h b/include/crm/cib.h new file mode 100644 index 0000000..a93bfde --- /dev/null +++ b/include/crm/cib.h @@ -0,0 +1,60 @@ +/* + * Copyright 2004-2019 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 PCMK__CRM_CIB__H +# define PCMK__CRM_CIB__H + +# include <glib.h> // gboolean +# include <crm/common/ipc.h> +# include <crm/common/xml.h> +# include <crm/cib/cib_types.h> +# include <crm/cib/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Cluster Configuration + * \ingroup cib + */ + +# define CIB_FEATURE_SET "2.0" + +/* use compare_version() for doing comparisons */ + +#define T_CIB_DIFF_NOTIFY "cib_diff_notify" + +/* Core functions */ +cib_t *cib_new(void); +cib_t *cib_native_new(void); +cib_t *cib_file_new(const char *filename); +cib_t *cib_remote_new(const char *server, const char *user, const char *passwd, int port, + gboolean encrypted); + +cib_t *cib_new_no_shadow(void); +char *get_shadow_file(const char *name); +cib_t *cib_shadow_new(const char *name); + +void cib_free_notify(cib_t *cib); +void cib_free_callbacks(cib_t *cib); +void cib_delete(cib_t * cib); + +void cib_dump_pending_callbacks(void); +int num_cib_op_callbacks(void); +void remove_cib_op_callback(int call_id, gboolean all_callbacks); + +# define CIB_LIBRARY "libcib.so.27" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/cib/Makefile.am b/include/crm/cib/Makefile.am new file mode 100644 index 0000000..0cd236c --- /dev/null +++ b/include/crm/cib/Makefile.am @@ -0,0 +1,16 @@ +# +# Copyright 2012-2021 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/cib + +noinst_HEADERS = internal.h +header_HEADERS = cib_types.h \ + util.h \ + util_compat.h diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h new file mode 100644 index 0000000..5bd10e4 --- /dev/null +++ b/include/crm/cib/cib_types.h @@ -0,0 +1,223 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_CIB_CIB_TYPES__H +# define PCMK__CRM_CIB_CIB_TYPES__H + +# include <glib.h> // gboolean, GList +# include <libxml/tree.h> // xmlNode +# include <crm/common/ipc.h> +# include <crm/common/xml.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Data types for Cluster Information Base access + * \ingroup cib + */ + +enum cib_variant { + cib_undefined = 0, + cib_native = 1, + cib_file = 2, + cib_remote = 3, + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + //! \deprecated This value will be removed in a future release + cib_database = 4, +#endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +}; + +enum cib_state { + cib_connected_command, + cib_connected_query, + cib_disconnected +}; + +enum cib_conn_type { + cib_command, + cib_query, + cib_no_connection, + cib_command_nonblocking, +}; + +enum cib_call_options { + cib_none = 0, + cib_verbose = (1 << 0), //!< Prefer stderr to logs + cib_xpath = (1 << 1), + cib_multiple = (1 << 2), + cib_can_create = (1 << 3), + cib_discard_reply = (1 << 4), + cib_no_children = (1 << 5), + cib_xpath_address = (1 << 6), + cib_mixed_update = (1 << 7), + cib_scope_local = (1 << 8), + cib_dryrun = (1 << 9), + cib_sync_call = (1 << 12), + cib_no_mtime = (1 << 13), + cib_zero_copy = (1 << 14), + cib_inhibit_notify = (1 << 16), + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + //! \deprecated This value will be removed in a future release + cib_quorum_override = (1 << 20), +#endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + + //! \deprecated This value will be removed in a future release + cib_inhibit_bcast = (1 << 24), + + cib_force_diff = (1 << 28), +}; + +typedef struct cib_s cib_t; + +typedef struct cib_api_operations_s { + int (*signon) (cib_t *cib, const char *name, enum cib_conn_type type); + int (*signon_raw) (cib_t *cib, const char *name, enum cib_conn_type type, + int *event_fd); + int (*signoff) (cib_t *cib); + int (*free) (cib_t *cib); + int (*set_op_callback) (cib_t *cib, void (*callback) (const xmlNode *msg, + int callid, int rc, + xmlNode *output)); + int (*add_notify_callback) (cib_t *cib, const char *event, + void (*callback) (const char *event, + xmlNode *msg)); + int (*del_notify_callback) (cib_t *cib, const char *event, + void (*callback) (const char *event, + xmlNode *msg)); + int (*set_connection_dnotify) (cib_t *cib, + void (*dnotify) (gpointer user_data)); + int (*inputfd) (cib_t *cib); + int (*noop) (cib_t *cib, int call_options); + int (*ping) (cib_t *cib, xmlNode **output_data, int call_options); + int (*query) (cib_t *cib, const char *section, xmlNode **output_data, + int call_options); + int (*query_from) (cib_t *cib, const char *host, const char *section, + xmlNode **output_data, int call_options); + + //! \deprecated This method will be removed and should not be used + int (*is_master) (cib_t *cib); + + //! \deprecated Use the set_primary() method instead + int (*set_master) (cib_t *cib, int call_options); + + //! \deprecated Use the set_secondary() method instead + int (*set_slave) (cib_t *cib, int call_options); + + //! \deprecated This method will be removed and should not be used + int (*set_slave_all) (cib_t *cib, int call_options); + + int (*sync) (cib_t *cib, const char *section, int call_options); + int (*sync_from) (cib_t *cib, const char *host, const char *section, + int call_options); + int (*upgrade) (cib_t *cib, int call_options); + int (*bump_epoch) (cib_t *cib, int call_options); + int (*create) (cib_t *cib, const char *section, xmlNode *data, + int call_options); + int (*modify) (cib_t *cib, const char *section, xmlNode *data, + int call_options); + + //! \deprecated Use the \p modify() method instead + int (*update) (cib_t *cib, const char *section, xmlNode *data, + int call_options); + + int (*replace) (cib_t *cib, const char *section, xmlNode *data, + int call_options); + int (*remove) (cib_t *cib, const char *section, xmlNode *data, + int call_options); + int (*erase) (cib_t *cib, xmlNode **output_data, int call_options); + + //! \deprecated This method does nothing and should not be called + int (*delete_absolute) (cib_t *cib, const char *section, xmlNode *data, + int call_options); + + int (*quit) (cib_t *cib, int call_options); + int (*register_notification) (cib_t *cib, const char *callback, + int enabled); + gboolean (*register_callback) (cib_t *cib, int call_id, int timeout, + gboolean only_success, void *user_data, + const char *callback_name, + void (*callback) (xmlNode*, int, int, + xmlNode*, void *)); + gboolean (*register_callback_full)(cib_t *cib, int call_id, int timeout, + gboolean only_success, void *user_data, + const char *callback_name, + void (*callback)(xmlNode *, int, int, + xmlNode *, void *), + void (*free_func)(void *)); + + /*! + * \brief Set the local CIB manager as the cluster's primary instance + * + * \param[in,out] cib CIB connection + * \param[in] call_options Group of enum cib_call_options flags + * + * \return Legacy Pacemaker return code (in particular, pcmk_ok on success) + */ + int (*set_primary)(cib_t *cib, int call_options); + + /*! + * \brief Set the local CIB manager as a secondary instance + * + * \param[in,out] cib CIB connection + * \param[in] call_options Group of enum cib_call_options flags + * + * \return Legacy Pacemaker return code (in particular, pcmk_ok on success) + */ + int (*set_secondary)(cib_t *cib, int call_options); + + /*! + * \brief Get the given CIB connection's unique client identifier(s) + * + * These can be used to check whether this client requested the action that + * triggered a CIB notification. + * + * \param[in] cib CIB connection + * \param[out] async_id If not \p NULL, where to store asynchronous client + * ID + * \param[out] sync_id If not \p NULL, where to store synchronous client + * ID + * + * \return Legacy Pacemaker return code + * + * \note The client IDs are assigned by \p pacemaker-based when the client + * connects. \p cib_t variants that don't connect to + * \p pacemaker-based may never be assigned a client ID. + * \note Some variants may have only one client for both asynchronous and + * synchronous requests. + */ + int (*client_id)(const cib_t *cib, const char **async_id, + const char **sync_id); +} cib_api_operations_t; + +struct cib_s { + enum cib_state state; + enum cib_conn_type type; + enum cib_variant variant; + + int call_id; + int call_timeout; + void *variant_opaque; + void *delegate_fn; + + GList *notify_list; + void (*op_callback) (const xmlNode *msg, int call_id, int rc, + xmlNode *output); + cib_api_operations_t *cmds; +}; + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_CIB_CIB_TYPES__H diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h new file mode 100644 index 0000000..374902b --- /dev/null +++ b/include/crm/cib/internal.h @@ -0,0 +1,264 @@ +/* + * Copyright 2004-2023 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 CIB_INTERNAL__H +# define CIB_INTERNAL__H +# include <crm/cib.h> +# include <crm/common/ipc_internal.h> +# include <crm/common/output_internal.h> + +// Request types for CIB manager IPC/CPG +#define PCMK__CIB_REQUEST_SECONDARY "cib_slave" +#define PCMK__CIB_REQUEST_ALL_SECONDARY "cib_slave_all" +#define PCMK__CIB_REQUEST_PRIMARY "cib_master" +#define PCMK__CIB_REQUEST_SYNC_TO_ALL "cib_sync" +#define PCMK__CIB_REQUEST_SYNC_TO_ONE "cib_sync_one" +#define PCMK__CIB_REQUEST_IS_PRIMARY "cib_ismaster" +#define PCMK__CIB_REQUEST_BUMP "cib_bump" +#define PCMK__CIB_REQUEST_QUERY "cib_query" +#define PCMK__CIB_REQUEST_CREATE "cib_create" +#define PCMK__CIB_REQUEST_MODIFY "cib_modify" +#define PCMK__CIB_REQUEST_DELETE "cib_delete" +#define PCMK__CIB_REQUEST_ERASE "cib_erase" +#define PCMK__CIB_REQUEST_REPLACE "cib_replace" +#define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff" +#define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade" +#define PCMK__CIB_REQUEST_ABS_DELETE "cib_delete_alt" +#define PCMK__CIB_REQUEST_NOOP "noop" +#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req" + +# define F_CIB_CLIENTID "cib_clientid" +# define F_CIB_CALLOPTS "cib_callopt" +# define F_CIB_CALLID "cib_callid" +# define F_CIB_CALLDATA "cib_calldata" +# define F_CIB_OPERATION "cib_op" +# define F_CIB_ISREPLY "cib_isreplyto" +# define F_CIB_SECTION "cib_section" +# define F_CIB_HOST "cib_host" +# define F_CIB_RC "cib_rc" +# define F_CIB_UPGRADE_RC "cib_upgrade_rc" +# define F_CIB_DELEGATED "cib_delegated_from" +# define F_CIB_OBJID "cib_object" +# define F_CIB_OBJTYPE "cib_object_type" +# define F_CIB_EXISTING "cib_existing_object" +# define F_CIB_SEENCOUNT "cib_seen" +# define F_CIB_TIMEOUT "cib_timeout" +# define F_CIB_UPDATE "cib_update" +# define F_CIB_GLOBAL_UPDATE "cib_update" +# define F_CIB_UPDATE_RESULT "cib_update_result" +# define F_CIB_CLIENTNAME "cib_clientname" +# define F_CIB_NOTIFY_TYPE "cib_notify_type" +# define F_CIB_NOTIFY_ACTIVATE "cib_notify_activate" +# define F_CIB_UPDATE_DIFF "cib_update_diff" +# define F_CIB_USER "cib_user" +# define F_CIB_LOCAL_NOTIFY_ID "cib_local_notify_id" +# define F_CIB_PING_ID "cib_ping_id" +# define F_CIB_SCHEMA_MAX "cib_schema_max" +# define F_CIB_CHANGE_SECTION "cib_change_section" + +# define T_CIB "cib" +# define T_CIB_NOTIFY "cib_notify" +/* notify sub-types */ +# define T_CIB_PRE_NOTIFY "cib_pre_notify" +# define T_CIB_POST_NOTIFY "cib_post_notify" +# define T_CIB_UPDATE_CONFIRM "cib_update_confirmation" +# define T_CIB_REPLACE_NOTIFY "cib_refresh_notify" + +/*! + * \internal + * \enum cib_change_section_info + * \brief Flags to indicate which sections of the CIB have changed + */ +enum cib_change_section_info { + cib_change_section_none = 0, //!< No sections have changed + cib_change_section_nodes = (1 << 0), //!< The nodes section has changed + cib_change_section_alerts = (1 << 1), //!< The alerts section has changed + cib_change_section_status = (1 << 2), //!< The status section has changed +}; + + +gboolean cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates, + int *_admin_epoch, int *_epoch, int *_updates); + +gboolean cib_read_config(GHashTable * options, xmlNode * current_cib); + +typedef struct cib_notify_client_s { + const char *event; + const char *obj_id; /* implement one day */ + const char *obj_type; /* implement one day */ + void (*callback) (const char *event, xmlNode * msg); + +} cib_notify_client_t; + +typedef struct cib_callback_client_s { + void (*callback) (xmlNode *, int, int, xmlNode *, void *); + const char *id; + void *user_data; + gboolean only_success; + struct timer_rec_s *timer; + void (*free_func)(void *); +} cib_callback_client_t; + +struct timer_rec_s { + int call_id; + int timeout; + guint ref; + cib_t *cib; +}; + +#define cib__set_call_options(cib_call_opts, call_for, flags_to_set) do { \ + cib_call_opts = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "CIB call", (call_for), (cib_call_opts), \ + (flags_to_set), #flags_to_set); \ + } while (0) + +#define cib__clear_call_options(cib_call_opts, call_for, flags_to_clear) do { \ + cib_call_opts = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "CIB call", (call_for), (cib_call_opts), \ + (flags_to_clear), #flags_to_clear); \ + } while (0) + +typedef int (*cib_op_t) (const char *, int, const char *, xmlNode *, + xmlNode *, xmlNode *, xmlNode **, xmlNode **); + +cib_t *cib_new_variant(void); + +int cib_perform_op(const char *op, int call_options, cib_op_t * fn, gboolean is_query, + const char *section, xmlNode * req, xmlNode * input, + gboolean manage_counters, gboolean * config_changed, + xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff, + xmlNode ** output); + +xmlNode *cib_create_op(int call_id, const char *op, const char *host, + const char *section, xmlNode * data, int call_options, + const char *user_name); + +void cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc); +void cib_native_notify(gpointer data, gpointer user_data); + +int cib_process_query(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_erase(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_bump(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_replace(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_create(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_modify(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_delete(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_diff(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req, + xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib, + xmlNode ** answer); + +/*! + * \internal + * \brief Query or modify a CIB + * + * \param[in] op PCMK__CIB_REQUEST_* operation to be performed + * \param[in] options Flag set of \c cib_call_options + * \param[in] section XPath to query or modify + * \param[in] req unused + * \param[in] input Portion of CIB to modify (used with + * PCMK__CIB_REQUEST_CREATE, + * PCMK__CIB_REQUEST_MODIFY, and + * PCMK__CIB_REQUEST_REPLACE) + * \param[in,out] existing_cib Input CIB (used with PCMK__CIB_REQUEST_QUERY) + * \param[in,out] result_cib CIB copy to make changes in (used with + * PCMK__CIB_REQUEST_CREATE, + * PCMK__CIB_REQUEST_MODIFY, + * PCMK__CIB_REQUEST_DELETE, and + * PCMK__CIB_REQUEST_REPLACE) + * \param[out] answer Query result (used with PCMK__CIB_REQUEST_QUERY) + * + * \return Legacy Pacemaker return code + */ +int cib_process_xpath(const char *op, int options, const char *section, + const xmlNode *req, xmlNode *input, xmlNode *existing_cib, + xmlNode **result_cib, xmlNode ** answer); + +bool cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff); + +int cib_internal_op(cib_t * cib, const char *op, const char *host, + const char *section, xmlNode * data, + xmlNode ** output_data, int call_options, const char *user_name); + + +int cib_file_read_and_verify(const char *filename, const char *sigfile, + xmlNode **root); +int cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname, + const char *cib_filename); + +void cib__set_output(cib_t *cib, pcmk__output_t *out); + +cib_callback_client_t* cib__lookup_id (int call_id); + +/*! + * \internal + * \brief Connect to, query, and optionally disconnect from the CIB + * + * Open a read-write connection to the CIB manager if an already connected + * client is not passed in. Then query the CIB and store the resulting XML. + * Finally, disconnect if the CIB connection isn't being returned to the caller. + * + * \param[in,out] out Output object (may be \p NULL) + * \param[in,out] cib If not \p NULL, where to store CIB connection + * \param[out] cib_object Where to store query result + * + * \return Standard Pacemaker return code + * + * \note If \p cib is not \p NULL, the caller is responsible for freeing \p *cib + * using \p cib_delete(). + * \note If \p *cib points to an existing \p cib_t object, this function will + * reuse it instead of creating a new one. If the existing client is + * already connected, the connection will be reused, even if it's + * read-only. + */ +int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object); + +int cib__clean_up_connection(cib_t **cib); + +int cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options, + const char *section, const char *node_uuid, const char *set_type, + const char *set_name, const char *attr_id, const char *attr_name, + const char *attr_value, const char *user_name, + const char *node_type); + +int cib__get_node_attrs(pcmk__output_t *out, cib_t *cib, const char *section, + const char *node_uuid, const char *set_type, const char *set_name, + const char *attr_id, const char *attr_name, const char *user_name, + xmlNode **result); + +int cib__delete_node_attr(pcmk__output_t *out, cib_t *cib, int options, + const char *section, const char *node_uuid, const char *set_type, + const char *set_name, const char *attr_id, const char *attr_name, + const char *attr_value, const char *user_name); + +#endif diff --git a/include/crm/cib/util.h b/include/crm/cib/util.h new file mode 100644 index 0000000..18726bb --- /dev/null +++ b/include/crm/cib/util.h @@ -0,0 +1,73 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_CIB_UTIL__H +# define PCMK__CRM_CIB_UTIL__H + +#include <glib.h> // gboolean +#include <libxml/tree.h> // xmlNode +#include <crm/cib/cib_types.h> // cib_t + +#ifdef __cplusplus +extern "C" { +#endif + +/* Utility functions */ +xmlNode *createEmptyCib(int cib_epoch); + +gboolean cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates); + +int update_attr_delegate(cib_t * the_cib, int call_options, + const char *section, const char *node_uuid, + const char *set_type, const char *set_name, + const char *attr_id, const char *attr_name, + const char *attr_value, gboolean to_console, + const char *user_name, const char *node_type); + +int find_nvpair_attr_delegate(cib_t * the_cib, const char *attr, + const char *section, const char *node_uuid, + const char *set_type, const char *set_name, + const char *attr_id, const char *attr_name, + gboolean to_console, char **value, const char *user_name); + +int read_attr_delegate(cib_t * the_cib, + const char *section, const char *node_uuid, + const char *set_type, const char *set_name, + const char *attr_id, const char *attr_name, + char **attr_value, gboolean to_console, const char *user_name); + +int delete_attr_delegate(cib_t * the_cib, int options, + const char *section, const char *node_uuid, + const char *set_type, const char *set_name, + const char *attr_id, const char *attr_name, + const char *attr_value, gboolean to_console, const char *user_name); + +int query_node_uuid(cib_t * the_cib, const char *uname, char **uuid, int *is_remote_node); + +int query_node_uname(cib_t * the_cib, const char *uuid, char **uname); + +int set_standby(cib_t * the_cib, const char *uuid, const char *scope, const char *standby_value); + +xmlNode *cib_get_generation(cib_t * cib); + +void cib_metadata(void); +const char *cib_pref(GHashTable * options, const char *name); + +int cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output, + int level); + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/cib/util_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/cib/util_compat.h b/include/crm/cib/util_compat.h new file mode 100644 index 0000000..20f1e2d --- /dev/null +++ b/include/crm/cib/util_compat.h @@ -0,0 +1,40 @@ +/* + * Copyright 2021 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 PCMK__CRM_CIB_UTIL_COMPAT__H +# define PCMK__CRM_CIB_UTIL_COMPAT__H + +#include <crm/common/xml.h> +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker configuration utilities + * \ingroup cib + * \deprecated Do not include this header directly. The utilities in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use pcmk_cib_xpath_for() instead +const char *get_object_path(const char *object_type); + +//! \deprecated Use pcmk_cib_parent_name_for() instead +const char *get_object_parent(const char *object_type); + +//! \deprecated Use pcmk_cib_xpath_for() instead +xmlNode *get_object_root(const char *object_type, xmlNode *the_root); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_CIB_UTIL_COMPAT__H diff --git a/include/crm/cluster.h b/include/crm/cluster.h new file mode 100644 index 0000000..bceb9c2 --- /dev/null +++ b/include/crm/cluster.h @@ -0,0 +1,236 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_CLUSTER__H +# define PCMK__CRM_CLUSTER__H + +# include <stdint.h> // uint32_t, uint64_t +# include <glib.h> // gboolean, GHashTable +# include <libxml/tree.h> // xmlNode +# include <crm/common/xml.h> +# include <crm/common/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +# if SUPPORT_COROSYNC +# include <corosync/cpg.h> +# endif + +extern gboolean crm_have_quorum; +extern GHashTable *crm_peer_cache; +extern GHashTable *crm_remote_peer_cache; +extern unsigned long long crm_peer_seq; + +#define CRM_NODE_LOST "lost" +#define CRM_NODE_MEMBER "member" + +enum crm_join_phase { + /* @COMPAT: crm_join_nack_quiet can be replaced by crm_node_t:user_data + * at a compatibility break. + */ + //! Not allowed to join, but don't send a nack message + crm_join_nack_quiet = -2, + + crm_join_nack = -1, + crm_join_none = 0, + crm_join_welcomed = 1, + crm_join_integrated = 2, + crm_join_finalized = 3, + crm_join_confirmed = 4, +}; + +enum crm_node_flags { + /* node is not a cluster node and should not be considered for cluster membership */ + crm_remote_node = 0x0001, + + /* node's cache entry is dirty */ + crm_node_dirty = 0x0010, +}; + +typedef struct crm_peer_node_s { + char *uname; // Node name as known to cluster + char *uuid; // Node UUID to ensure uniqueness + char *state; // @TODO change to enum + uint64_t flags; // Bitmask of crm_node_flags + uint64_t last_seen; // Only needed by cluster nodes + uint32_t processes; // @TODO most not needed, merge into flags + + /* @TODO When we can break public API compatibility, we can make the rest of + * these members separate structs and use void *cluster_data and + * void *user_data here instead, to abstract the cluster layer further. + */ + + // Currently only needed by corosync stack + uint32_t id; // Node ID + time_t when_lost; // When CPG membership was last lost + + // Only used by controller + enum crm_join_phase join; + char *expected; + + time_t peer_lost; + char *conn_host; +} crm_node_t; + +void crm_peer_init(void); +void crm_peer_destroy(void); + +typedef struct crm_cluster_s { + char *uuid; + char *uname; + uint32_t nodeid; + + void (*destroy) (gpointer); + +# if SUPPORT_COROSYNC + /* @TODO When we can break public API compatibility, make these members a + * separate struct and use void *cluster_data here instead, to abstract the + * cluster layer further. + */ + struct cpg_name group; + cpg_callbacks_t cpg; + cpg_handle_t cpg_handle; +# endif + +} crm_cluster_t; + +gboolean crm_cluster_connect(crm_cluster_t *cluster); +void crm_cluster_disconnect(crm_cluster_t *cluster); + +crm_cluster_t *pcmk_cluster_new(void); +void pcmk_cluster_free(crm_cluster_t *cluster); + +enum crm_ais_msg_class { + crm_class_cluster = 0, +}; + +enum crm_ais_msg_types { + crm_msg_none = 0, + crm_msg_ais = 1, + crm_msg_lrmd = 2, + crm_msg_cib = 3, + crm_msg_crmd = 4, + crm_msg_attrd = 5, + crm_msg_stonithd = 6, + crm_msg_te = 7, + crm_msg_pe = 8, + crm_msg_stonith_ng = 9, +}; + +/* used with crm_get_peer_full */ +enum crm_get_peer_flags { + CRM_GET_PEER_CLUSTER = 0x0001, + CRM_GET_PEER_REMOTE = 0x0002, + CRM_GET_PEER_ANY = CRM_GET_PEER_CLUSTER|CRM_GET_PEER_REMOTE, +}; + +gboolean send_cluster_message(const crm_node_t *node, + enum crm_ais_msg_types service, xmlNode *data, + gboolean ordered); + +int crm_remote_peer_cache_size(void); + +/* Initialize and refresh the remote peer cache from a cib config */ +void crm_remote_peer_cache_refresh(xmlNode *cib); +crm_node_t *crm_remote_peer_get(const char *node_name); +void crm_remote_peer_cache_remove(const char *node_name); + +/* allows filtering of remote and cluster nodes using crm_get_peer_flags */ +crm_node_t *crm_get_peer_full(unsigned int id, const char *uname, int flags); + +/* only searches cluster nodes */ +crm_node_t *crm_get_peer(unsigned int id, const char *uname); + +guint crm_active_peers(void); +gboolean crm_is_peer_active(const crm_node_t * node); +guint reap_crm_member(uint32_t id, const char *name); + +# if SUPPORT_COROSYNC +uint32_t get_local_nodeid(cpg_handle_t handle); + +gboolean cluster_connect_cpg(crm_cluster_t *cluster); +void cluster_disconnect_cpg(crm_cluster_t * cluster); + +void pcmk_cpg_membership(cpg_handle_t handle, + const struct cpg_name *groupName, + const struct cpg_address *member_list, size_t member_list_entries, + const struct cpg_address *left_list, size_t left_list_entries, + const struct cpg_address *joined_list, size_t joined_list_entries); +gboolean crm_is_corosync_peer_active(const crm_node_t * node); +gboolean send_cluster_text(enum crm_ais_msg_class msg_class, const char *data, + gboolean local, const crm_node_t *node, + enum crm_ais_msg_types dest); +char *pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, void *msg, + uint32_t *kind, const char **from); +# endif + +const char *crm_peer_uuid(crm_node_t *node); +const char *crm_peer_uname(const char *uuid); +void set_uuid(xmlNode *xml, const char *attr, crm_node_t *node); + +enum crm_status_type { + crm_status_uname, + crm_status_nstate, + crm_status_processes, +}; + +enum crm_ais_msg_types text2msg_type(const char *text); +void crm_set_status_callback(void (*dispatch) (enum crm_status_type, crm_node_t *, const void *)); +void crm_set_autoreap(gboolean autoreap); + +enum cluster_type_e { + pcmk_cluster_unknown = 0x0001, + pcmk_cluster_invalid = 0x0002, + // 0x0004 was heartbeat + // 0x0010 was corosync 1 with plugin + pcmk_cluster_corosync = 0x0020, + // 0x0040 was corosync 1 with CMAN +}; + +enum cluster_type_e get_cluster_type(void); +const char *name_for_cluster_type(enum cluster_type_e type); + +gboolean is_corosync_cluster(void); + +const char *get_local_node_name(void); +char *get_node_name(uint32_t nodeid); + +/*! + * \brief Get log-friendly string equivalent of a join phase + * + * \param[in] phase Join phase + * + * \return Log-friendly string equivalent of \p phase + */ +static inline const char * +crm_join_phase_str(enum crm_join_phase phase) +{ + switch (phase) { + case crm_join_nack_quiet: return "nack_quiet"; + case crm_join_nack: return "nack"; + case crm_join_none: return "none"; + case crm_join_welcomed: return "welcomed"; + case crm_join_integrated: return "integrated"; + case crm_join_finalized: return "finalized"; + case crm_join_confirmed: return "confirmed"; + default: return "invalid"; + } +} + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/cluster/compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/cluster/Makefile.am b/include/crm/cluster/Makefile.am new file mode 100644 index 0000000..96f2bd0 --- /dev/null +++ b/include/crm/cluster/Makefile.am @@ -0,0 +1,14 @@ +# +# Copyright 2012-2021 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/cluster + +noinst_HEADERS = internal.h election_internal.h +header_HEADERS = compat.h diff --git a/include/crm/cluster/compat.h b/include/crm/cluster/compat.h new file mode 100644 index 0000000..9bf14ee --- /dev/null +++ b/include/crm/cluster/compat.h @@ -0,0 +1,37 @@ +/* + * Copyright 2004-2021 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 PCMK__CRM_CLUSTER_COMPAT__H +# define PCMK__CRM_CLUSTER_COMPAT__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker cluster API + * \ingroup cluster + * \deprecated Do not include this header directly. The cluster APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +// \deprecated Use stonith_api_kick() from libstonithd instead +int crm_terminate_member(int nodeid, const char *uname, void *unused); + +// \deprecated Use stonith_api_kick() from libstonithd instead +int crm_terminate_member_no_mainloop(int nodeid, const char *uname, + int *connection); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK_CLUSTER_COMPAT__H diff --git a/include/crm/cluster/election_internal.h b/include/crm/cluster/election_internal.h new file mode 100644 index 0000000..665c3a0 --- /dev/null +++ b/include/crm/cluster/election_internal.h @@ -0,0 +1,86 @@ +/* + * Copyright 2009-2022 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_COMMON_ELECTION__H +# define CRM_COMMON_ELECTION__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Functions for conducting elections + * + * An election is useful for a daemon that runs on all nodes but needs any one + * instance to perform a special role. + * + * Elections are closely tied to the cluster peer cache. Peers in the cache that + * are active members are eligible to vote. Elections are named for logging + * purposes, but only one election may exist at any time, so typically an + * election would be created at daemon start-up and freed at shutdown. + * + * Pacemaker's election procedure has been heavily adapted from the + * Invitation Algorithm variant of the Garcia-Molina Bully Algorithm: + * + * https://en.wikipedia.org/wiki/Bully_algorithm + * + * Elections are conducted via cluster messages. There are two types of + * messages: a "vote" is a declaration of the voting node's candidacy, and is + * always broadcast; a "no-vote" is a concession by the responding node, and is + * always a reply to the preferred node's vote. (These correspond to "invite" + * and "accept" in the traditional algorithm.) + * + * A vote together with any no-vote replies to it is considered an election + * round. Rounds are numbered with a simple counter unique to each node + * (this would be the group number in the traditional algorithm). Concurrent + * election rounds are possible. + * + * An election round is started when any node broadcasts a vote. When a node + * receives another node's vote, it compares itself against the sending node + * according to certain metrics, and either starts a new round (if it prefers + * itself) or replies to the other node with a no-vote (if it prefers that + * node). + * + * If a node receives no-votes from all other active nodes, it declares itself + * the winner. The library API does not notify other nodes of this; callers + * must implement that if desired. + */ + +typedef struct election_s election_t; + +/*! Possible election states */ +enum election_result { + election_start = 0, /*! new election needed */ + election_in_progress, /*! election started but not all peers have voted */ + election_lost, /*! local node lost most recent election */ + election_won, /*! local node won most recent election */ + election_error, /*! election message or election object invalid */ +}; + +void election_fini(election_t *e); +void election_reset(election_t *e); +election_t *election_init(const char *name, const char *uname, guint period_ms, GSourceFunc cb); + +void election_timeout_set_period(election_t *e, guint period_ms); +void election_timeout_stop(election_t *e); + +void election_vote(election_t *e); +bool election_check(election_t *e); +void election_remove(election_t *e, const char *uname); +enum election_result election_state(const election_t *e); +enum election_result election_count_vote(election_t *e, const xmlNode *message, + bool can_win); +void election_clear_dampening(election_t *e); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h new file mode 100644 index 0000000..9bc57c6 --- /dev/null +++ b/include/crm/cluster/internal.h @@ -0,0 +1,133 @@ +/* + * Copyright 2004-2021 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_CLUSTER_INTERNAL__H +# define CRM_CLUSTER_INTERNAL__H + +# include <stdint.h> // uint32_t, uint64_t +# include <crm/cluster.h> + +/* *INDENT-OFF* */ +enum crm_proc_flag { + crm_proc_none = 0x00000001, + + // Cluster layers + crm_proc_cpg = 0x04000000, + + // Daemons + crm_proc_execd = 0x00000010, + crm_proc_based = 0x00000100, + crm_proc_controld = 0x00000200, + crm_proc_attrd = 0x00001000, + crm_proc_schedulerd = 0x00010000, + crm_proc_fenced = 0x00100000, +}; +/* *INDENT-ON* */ + +/*! + * \internal + * \brief Return the process bit corresponding to the current cluster stack + * + * \return Process flag if detectable, otherwise 0 + */ +static inline uint32_t +crm_get_cluster_proc(void) +{ + switch (get_cluster_type()) { + case pcmk_cluster_corosync: + return crm_proc_cpg; + + default: + break; + } + return crm_proc_none; +} + +/*! + * \internal + * \brief Get log-friendly string description of a Corosync return code + * + * \param[in] error Corosync return code + * + * \return Log-friendly string description corresponding to \p error + */ +static inline const char * +pcmk__cs_err_str(int error) +{ +# if SUPPORT_COROSYNC + switch (error) { + case CS_OK: return "OK"; + case CS_ERR_LIBRARY: return "Library error"; + case CS_ERR_VERSION: return "Version error"; + case CS_ERR_INIT: return "Initialization error"; + case CS_ERR_TIMEOUT: return "Timeout"; + case CS_ERR_TRY_AGAIN: return "Try again"; + case CS_ERR_INVALID_PARAM: return "Invalid parameter"; + case CS_ERR_NO_MEMORY: return "No memory"; + case CS_ERR_BAD_HANDLE: return "Bad handle"; + case CS_ERR_BUSY: return "Busy"; + case CS_ERR_ACCESS: return "Access error"; + case CS_ERR_NOT_EXIST: return "Doesn't exist"; + case CS_ERR_NAME_TOO_LONG: return "Name too long"; + case CS_ERR_EXIST: return "Exists"; + case CS_ERR_NO_SPACE: return "No space"; + case CS_ERR_INTERRUPT: return "Interrupt"; + case CS_ERR_NAME_NOT_FOUND: return "Name not found"; + case CS_ERR_NO_RESOURCES: return "No resources"; + case CS_ERR_NOT_SUPPORTED: return "Not supported"; + case CS_ERR_BAD_OPERATION: return "Bad operation"; + case CS_ERR_FAILED_OPERATION: return "Failed operation"; + case CS_ERR_MESSAGE_ERROR: return "Message error"; + case CS_ERR_QUEUE_FULL: return "Queue full"; + case CS_ERR_QUEUE_NOT_AVAILABLE: return "Queue not available"; + case CS_ERR_BAD_FLAGS: return "Bad flags"; + case CS_ERR_TOO_BIG: return "Too big"; + case CS_ERR_NO_SECTIONS: return "No sections"; + } +# endif + return "Corosync error"; +} + +# if SUPPORT_COROSYNC + +#if 0 +/* This is the new way to do it, but we still support all Corosync 2 versions, + * and this isn't always available. A better alternative here would be to check + * for support in the configure script and enable this conditionally. + */ +#define pcmk__init_cmap(handle) cmap_initialize_map((handle), CMAP_MAP_ICMAP) +#else +#define pcmk__init_cmap(handle) cmap_initialize(handle) +#endif + +char *pcmk__corosync_cluster_name(void); +bool pcmk__corosync_add_nodes(xmlNode *xml_parent); +# endif + +crm_node_t *crm_update_peer_proc(const char *source, crm_node_t * peer, + uint32_t flag, const char *status); +crm_node_t *pcmk__update_peer_state(const char *source, crm_node_t *node, + const char *state, uint64_t membership); + +void pcmk__update_peer_expected(const char *source, crm_node_t *node, + const char *expected); +void pcmk__reap_unseen_nodes(uint64_t ring_id); + +void pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long, + gboolean), + void (*destroy) (gpointer)); +crm_node_t *pcmk__search_node_caches(unsigned int id, const char *uname, + uint32_t flags); +crm_node_t *pcmk__search_cluster_node_cache(unsigned int id, const char *uname); + +void pcmk__refresh_node_caches_from_cib(xmlNode *cib); +crm_node_t *pcmk__search_known_node_cache(unsigned int id, const char *uname, + uint32_t flags); + +#endif diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am new file mode 100644 index 0000000..7d417e4 --- /dev/null +++ b/include/crm/common/Makefile.am @@ -0,0 +1,55 @@ +# +# Copyright 2004-2022 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 = acl.h \ + agents.h \ + agents_compat.h \ + cib.h \ + ipc.h \ + ipc_attrd_internal.h \ + ipc_controld.h \ + ipc_pacemakerd.h \ + ipc_schedulerd.h \ + iso8601.h \ + logging.h \ + logging_compat.h \ + mainloop.h \ + mainloop_compat.h \ + nvpair.h \ + output.h \ + results.h \ + results_compat.h \ + util.h \ + util_compat.h \ + xml.h \ + xml_compat.h + +noinst_HEADERS = acl_internal.h \ + alerts_internal.h \ + attrd_internal.h \ + cmdline_internal.h \ + health_internal.h \ + internal.h \ + io_internal.h \ + ipc_internal.h \ + iso8601_internal.h \ + lists_internal.h \ + logging_internal.h \ + messages_internal.h \ + options_internal.h \ + output_internal.h \ + remote_internal.h \ + results_internal.h \ + strings_internal.h \ + unittest_internal.h \ + xml_internal.h diff --git a/include/crm/common/acl.h b/include/crm/common/acl.h new file mode 100644 index 0000000..655ad55 --- /dev/null +++ b/include/crm/common/acl.h @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_COMMON_ACL__H +# define PCMK__CRM_COMMON_ACL__H + +# include <libxml/tree.h> // xmlNode +# include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Low-level API for XML Access Control Lists (ACLs) + * \ingroup core + */ + +bool xml_acl_enabled(const xmlNode *xml); +void xml_acl_disable(xmlNode *xml); +bool xml_acl_denied(const xmlNode *xml); +bool xml_acl_filtered_copy(const char *user, xmlNode* acl_source, xmlNode *xml, + xmlNode **result); + +bool pcmk_acl_required(const char *user); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_ACL__H diff --git a/include/crm/common/acl_internal.h b/include/crm/common/acl_internal.h new file mode 100644 index 0000000..ca67d68 --- /dev/null +++ b/include/crm/common/acl_internal.h @@ -0,0 +1,32 @@ +/* + * Copyright 2015-2022 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_COMMON_ACL_INTERNAL__H +#define CRM_COMMON_ACL_INTERNAL__H + +#include <string.h> // strcmp() + +/* internal ACL-related utilities */ + +char *pcmk__uid2username(uid_t uid); +const char *pcmk__update_acl_user(xmlNode *request, const char *field, + const char *peer_user); + +static inline bool +pcmk__is_privileged(const char *user) +{ + return user && (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")); +} + +void pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user); + +bool pcmk__check_acl(xmlNode *xml, const char *name, + enum xml_private_flags mode); + +#endif /* CRM_COMMON_INTERNAL__H */ diff --git a/include/crm/common/agents.h b/include/crm/common/agents.h new file mode 100644 index 0000000..5a67a87 --- /dev/null +++ b/include/crm/common/agents.h @@ -0,0 +1,83 @@ +/* + * Copyright 2017-2023 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 PCMK__CRM_COMMON_AGENTS__H +# define PCMK__CRM_COMMON_AGENTS__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief API related to resource agents + * \ingroup core + */ + +#include <stdint.h> // uint32_t +#include <stdbool.h> + +// Known resource classes +#define PCMK_RESOURCE_CLASS_OCF "ocf" +#define PCMK_RESOURCE_CLASS_SERVICE "service" +#define PCMK_RESOURCE_CLASS_LSB "lsb" +#define PCMK_RESOURCE_CLASS_SYSTEMD "systemd" +#define PCMK_RESOURCE_CLASS_STONITH "stonith" +#define PCMK_RESOURCE_CLASS_ALERT "alert" +//! \deprecated Do not use +#define PCMK_RESOURCE_CLASS_NAGIOS "nagios" +//! \deprecated Do not use +#define PCMK_RESOURCE_CLASS_UPSTART "upstart" + +/* Special stonith-class agent parameters interpreted directly by Pacemaker + * (not including the pcmk_ACTION_{action,retries,timeout} parameters) + */ +#define PCMK_STONITH_ACTION_LIMIT "pcmk_action_limit" +#define PCMK_STONITH_DELAY_BASE "pcmk_delay_base" +#define PCMK_STONITH_DELAY_MAX "pcmk_delay_max" +#define PCMK_STONITH_HOST_ARGUMENT "pcmk_host_argument" +#define PCMK_STONITH_HOST_CHECK "pcmk_host_check" +#define PCMK_STONITH_HOST_LIST "pcmk_host_list" +#define PCMK_STONITH_HOST_MAP "pcmk_host_map" +#define PCMK_STONITH_PROVIDES "provides" +#define PCMK_STONITH_STONITH_TIMEOUT "stonith-timeout" + +// OCF Resource Agent API standard version that this Pacemaker supports +#define PCMK_OCF_MAJOR_VERSION "1" +#define PCMK_OCF_MINOR_VERSION "1" +#define PCMK_OCF_VERSION PCMK_OCF_MAJOR_VERSION "." PCMK_OCF_MINOR_VERSION + +// Capabilities supported by a resource agent standard +enum pcmk_ra_caps { + pcmk_ra_cap_none = 0, + pcmk_ra_cap_provider = (1 << 0), // Requires provider + pcmk_ra_cap_status = (1 << 1), // Supports status instead of monitor + pcmk_ra_cap_params = (1 << 2), // Supports parameters + pcmk_ra_cap_unique = (1 << 3), // Supports unique clones + pcmk_ra_cap_promotable = (1 << 4), // Supports promotable clones + pcmk_ra_cap_stdin = (1 << 5), // Reads from standard input + pcmk_ra_cap_fence_params = (1 << 6), // Supports pcmk_monitor_timeout, etc. +}; + +uint32_t pcmk_get_ra_caps(const char *standard); +char *crm_generate_ra_key(const char *standard, const char *provider, + const char *type); +int crm_parse_agent_spec(const char *spec, char **standard, char **provider, + char **type); +bool pcmk_stonith_param(const char *param); + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/common/agents_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_AGENTS__H diff --git a/include/crm/common/agents_compat.h b/include/crm/common/agents_compat.h new file mode 100644 index 0000000..05a80f1 --- /dev/null +++ b/include/crm/common/agents_compat.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2021 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 PCMK__CRM_COMMON_AGENTS_COMPAT__H +# define PCMK__CRM_COMMON_AGENTS_COMPAT__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker resource agent API + * \ingroup core + * \deprecated Do not include this header directly. The agent APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +#include <stdbool.h> + +//! \deprecated Use pcmk_get_ra_caps() instead +bool crm_provider_required(const char *standard); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_AGENTS_COMPAT__H diff --git a/include/crm/common/alerts_internal.h b/include/crm/common/alerts_internal.h new file mode 100644 index 0000000..ef64fab --- /dev/null +++ b/include/crm/common/alerts_internal.h @@ -0,0 +1,92 @@ +/* + * Copyright 2015-2022 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 PCMK__ALERT_INTERNAL__H +#define PCMK__ALERT_INTERNAL__H + +#include <glib.h> +#include <stdbool.h> + +/* Default-Timeout to use before killing a alerts script (in milliseconds) */ +# define PCMK__ALERT_DEFAULT_TIMEOUT_MS (30000) + +/* Default-Format-String used to pass timestamps to the alerts scripts */ +# define PCMK__ALERT_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N" + +enum pcmk__alert_flags { + pcmk__alert_none = 0, + pcmk__alert_node = (1 << 0), + pcmk__alert_fencing = (1 << 1), + pcmk__alert_resource = (1 << 2), + pcmk__alert_attribute = (1 << 3), + pcmk__alert_default = pcmk__alert_node|pcmk__alert_fencing| + pcmk__alert_resource, +}; + +typedef struct { + char *id; + char *path; + char *tstamp_format; + char *recipient; + char **select_attribute_name; + GHashTable *envvars; + int timeout; + uint32_t flags; +} pcmk__alert_t; + +enum pcmk__alert_keys_e { + PCMK__alert_key_recipient = 0, + PCMK__alert_key_node, + PCMK__alert_key_nodeid, + PCMK__alert_key_rsc, + PCMK__alert_key_task, + PCMK__alert_key_interval, + PCMK__alert_key_desc, + PCMK__alert_key_status, + PCMK__alert_key_target_rc, + PCMK__alert_key_rc, + PCMK__alert_key_kind, + PCMK__alert_key_version, + PCMK__alert_key_node_sequence, + PCMK__alert_key_timestamp, + PCMK__alert_key_attribute_name, + PCMK__alert_key_attribute_value, + PCMK__alert_key_timestamp_epoch, + PCMK__alert_key_timestamp_usec, + PCMK__alert_key_exec_time, + PCMK__alert_key_select_kind, + PCMK__alert_key_select_attribute_name +}; + +#define PCMK__ALERT_INTERNAL_KEY_MAX 19 +#define PCMK__ALERT_NODE_SEQUENCE "CRM_alert_node_sequence" + +extern const char *pcmk__alert_keys[PCMK__ALERT_INTERNAL_KEY_MAX][3]; + +pcmk__alert_t *pcmk__dup_alert(const pcmk__alert_t *entry); +pcmk__alert_t *pcmk__alert_new(const char *id, const char *path); +void pcmk__free_alert(pcmk__alert_t *entry); +void pcmk__add_alert_key(GHashTable *table, enum pcmk__alert_keys_e name, + const char *value); +void pcmk__add_alert_key_int(GHashTable *table, enum pcmk__alert_keys_e name, + int value); +bool pcmk__alert_in_patchset(xmlNode *msg, bool config); + +static inline const char * +pcmk__alert_flag2text(enum pcmk__alert_flags flag) +{ + switch (flag) { + case pcmk__alert_node: return "node"; + case pcmk__alert_fencing: return "fencing"; + case pcmk__alert_resource: return "resource"; + case pcmk__alert_attribute: return "attribute"; + default: return "unknown"; + } +} +#endif diff --git a/include/crm/common/attrd_internal.h b/include/crm/common/attrd_internal.h new file mode 100644 index 0000000..9d0b730 --- /dev/null +++ b/include/crm/common/attrd_internal.h @@ -0,0 +1,50 @@ +/* + * Copyright 2004-2023 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 PCMK__ATTRD_INTERNAL__H +# define PCMK__ATTRD_INTERNAL__H + +#ifdef __cplusplus +extern "C" { +#endif + +// Options for clients to use with functions below +enum pcmk__node_attr_opts { + pcmk__node_attr_none = 0, + pcmk__node_attr_remote = (1 << 0), + pcmk__node_attr_private = (1 << 1), + pcmk__node_attr_pattern = (1 << 2), + pcmk__node_attr_value = (1 << 3), + pcmk__node_attr_delay = (1 << 4), + pcmk__node_attr_perm = (1 << 5), + pcmk__node_attr_sync_local = (1 << 6), + pcmk__node_attr_sync_cluster = (1 << 7), + pcmk__node_attr_utilization = (1 << 8), + pcmk__node_attr_query_all = (1 << 9), +}; + +#define pcmk__set_node_attr_flags(node_attr_flags, flags_to_set) do { \ + node_attr_flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Node attribute", crm_system_name, \ + (node_attr_flags), (flags_to_set), #flags_to_set); \ + } while (0) + +#define pcmk__clear_node_attr_flags(node_attr_flags, flags_to_clear) do { \ + node_attr_flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Node attribute", crm_system_name, \ + (node_attr_flags), (flags_to_clear), #flags_to_clear); \ + } while (0) + +const char *pcmk__node_attr_target(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/cib.h b/include/crm/common/cib.h new file mode 100644 index 0000000..e1c4471 --- /dev/null +++ b/include/crm/common/cib.h @@ -0,0 +1,27 @@ +/* + * Copyright 2021 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 PCMK__CRM_COMMON_CIB__H +# define PCMK__CRM_COMMON_CIB__H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libxml/tree.h> // xmlNode + +const char *pcmk_cib_xpath_for(const char *element_name); +const char *pcmk_cib_parent_name_for(const char *element_name); +xmlNode *pcmk_find_cib_element(xmlNode *cib, const char *element_name); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__COMMON_CIB__H diff --git a/include/crm/common/cmdline_internal.h b/include/crm/common/cmdline_internal.h new file mode 100644 index 0000000..db4fd59 --- /dev/null +++ b/include/crm/common/cmdline_internal.h @@ -0,0 +1,186 @@ +/* + * Copyright 2019-2022 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 PCMK__CMDLINE_INTERNAL__H +#define PCMK__CMDLINE_INTERNAL__H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <glib.h> + +typedef struct { + char *summary; + char *output_as_descr; + + gboolean version; + gboolean quiet; + unsigned int verbosity; + + char *output_ty; + char *output_dest; +} pcmk__common_args_t; + +/*! + * \internal + * \brief Allocate a new common args object + * + * \param[in] summary Summary description of tool for man page + * + * \return Newly allocated common args object + * \note This function will immediately exit the program if memory allocation + * fails, since the intent is to call it at the very beginning of a + * program, before logging has been set up. + */ +pcmk__common_args_t * +pcmk__new_common_args(const char *summary); + +/*! + * \internal + * \brief Create and return a GOptionContext containing the command line options + * supported by all tools. + * + * \note Formatted output options will be added unless fmts is NULL. This allows + * for using this function in tools that have not yet been converted to + * formatted output. It should not be NULL in any tool that calls + * pcmk__register_formats() as that function adds its own command line + * options. + * + * \param[in,out] common_args A ::pcmk__common_args_t structure where the + * results of handling command options will be written. + * \param[in] fmts The help string for which formats are supported. + * \param[in,out] output_group A ::GOptionGroup that formatted output related + * command line arguments should be added to. + * \param[in] param_string A string describing any remaining command line + * arguments. + */ +GOptionContext * +pcmk__build_arg_context(pcmk__common_args_t *common_args, const char *fmts, + GOptionGroup **output_group, const char *param_string); + +/*! + * \internal + * \brief Clean up after pcmk__build_arg_context(). This should be called + * instead of ::g_option_context_free at program termination. + * + * \param[in,out] context Argument context to free + */ +void +pcmk__free_arg_context(GOptionContext *context); + +/*! + * \internal + * \brief Add options to the main application options + * + * \param[in,out] context Argument context to add options to + * \param[in] entries Option entries to add + * + * \note This is simply a convenience wrapper to reduce duplication + */ +void pcmk__add_main_args(GOptionContext *context, const GOptionEntry entries[]); + +/*! + * \internal + * \brief Add an option group to an argument context + * + * \param[in,out] context Argument context to add group to + * \param[in] name Option group name (to be used in --help-NAME) + * \param[in] header Header for --help-NAME output + * \param[in] desc Short description for --help-NAME option + * \param[in] entries Array of options in group + * + * \note This is simply a convenience wrapper to reduce duplication + */ +void pcmk__add_arg_group(GOptionContext *context, const char *name, + const char *header, const char *desc, + const GOptionEntry entries[]); + +/*! + * \internal + * \brief Prepare the command line for being added to a pcmk__output_t as the + * request + * + * This performs various transformations on the command line arguments, such + * as surrounding arguments containing spaces with quotes and escaping any + * single quotes in the string. + * + * \param[in,out] argv Command line (typically from pcmk__cmdline_preproc()) + */ +gchar *pcmk__quote_cmdline(gchar **argv); + +/*! + * \internal + * \brief Pre-process command line arguments to preserve compatibility with + * getopt behavior. + * + * getopt and glib have slightly different behavior when it comes to processing + * single command line arguments. getopt allows this: -x<val>, while glib will + * try to handle <val> like it is additional single letter arguments. glib + * prefers -x <val> instead. + * + * This function scans argv, looking for any single letter command line options + * (indicated by the 'special' parameter). When one is found, everything after + * that argument to the next whitespace is converted into its own value. Single + * letter command line options can come in a group after a single dash, but + * this function will expand each group into many arguments. + * + * Long options and anything after "--" is preserved. The result of this function + * can then be passed to ::g_option_context_parse_strv for actual processing. + * + * In pseudocode, this: + * + * pcmk__cmdline_preproc(4, ["-XbA", "--blah=foo", "-aF", "-Fval", "--", "--extra", "-args"], "aF") + * + * Would be turned into this: + * + * ["-X", "-b", "-A", "--blah=foo", "-a", "F", "-F", "val", "--", "--extra", "-args"] + * + * This function does not modify argv, and the return value is built of copies + * of all the command line arguments. It is up to the caller to free this memory + * after use. + * + * \note This function calls g_set_prgname assuming it wasn't previously set and + * assuming argv is not NULL. It is not safe to call g_set_prgname more + * than once so clients should not do so after calling this function. + * + * \param[in] argv The command line arguments. + * \param[in] special Single-letter command line arguments that take a value. + * These letters will all have pre-processing applied. + */ +gchar ** +pcmk__cmdline_preproc(char *const *argv, const char *special); + +/*! + * \internal + * \brief Process extra arguments as if they were provided by the user on the + * command line. + * + * \param[in,out] context The command line option processing context. + * \param[out] error A place for errors to be collected. + * \param[in] format The command line to be processed, potentially with + * format specifiers. + * \param[in] ... Arguments to be formatted. + * + * \note The first item in the list of arguments must be the name of the + * program, exactly as if the format string were coming from the + * command line. Otherwise, the first argument will be ignored. + * + * \return TRUE if processing succeeded, or FALSE otherwise. If FALSE, error + * should be checked and displayed to the user. + */ +G_GNUC_PRINTF(3, 4) +gboolean +pcmk__force_args(GOptionContext *context, GError **error, const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/health_internal.h b/include/crm/common/health_internal.h new file mode 100644 index 0000000..277a4c9 --- /dev/null +++ b/include/crm/common/health_internal.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022 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 PCMK__CRM_COMMON_HEALTH_INTERNAL__H +#define PCMK__CRM_COMMON_HEALTH_INTERNAL__H + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \internal + * \brief Possible node health strategies + * + * \note It would be nice to use this in pe_working_set_t but that will have to + * wait for an API backward compatibility break. + */ +enum pcmk__health_strategy { + pcmk__health_strategy_none, + pcmk__health_strategy_no_red, + pcmk__health_strategy_only_green, + pcmk__health_strategy_progressive, + pcmk__health_strategy_custom, +}; + +bool pcmk__validate_health_strategy(const char *value); + +enum pcmk__health_strategy pcmk__parse_health_strategy(const char *value); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_HEALTH_INTERNAL__H diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h new file mode 100644 index 0000000..bd98780 --- /dev/null +++ b/include/crm/common/internal.h @@ -0,0 +1,358 @@ +/* + * Copyright 2015-2022 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_COMMON_INTERNAL__H +#define CRM_COMMON_INTERNAL__H + +#include <unistd.h> // pid_t, getpid() +#include <stdbool.h> // bool +#include <stdint.h> // uint8_t, uint64_t + +#include <glib.h> // guint, GList, GHashTable +#include <libxml/tree.h> // xmlNode + +#include <crm/common/util.h> // crm_strdup_printf() +#include <crm/common/logging.h> // do_crm_log_unlikely(), etc. +#include <crm/common/mainloop.h> // mainloop_io_t, struct ipc_client_callbacks +#include <crm/common/health_internal.h> +#include <crm/common/io_internal.h> +#include <crm/common/iso8601_internal.h> +#include <crm/common/results_internal.h> +#include <crm/common/messages_internal.h> +#include <crm/common/strings_internal.h> +#include <crm/common/acl_internal.h> + +/* This says whether the current application is a Pacemaker daemon or not, + * and is used to change default logging settings such as whether to log to + * stderr, etc., as well as a few other details such as whether blackbox signal + * handling is enabled. + * + * It is set when logging is initialized, and does not need to be set directly. + */ +extern bool pcmk__is_daemon; + +//! Node name of the local node +extern char *pcmk__our_nodename; + +// Number of elements in a statically defined array +#define PCMK__NELEM(a) ((int) (sizeof(a)/sizeof(a[0])) ) + +#if SUPPORT_CIBSECRETS +/* internal CIB utilities (from cib_secrets.c) */ + +int pcmk__substitute_secrets(const char *rsc_id, GHashTable *params); +#endif + + +/* internal digest-related utilities (from digest.c) */ + +bool pcmk__verify_digest(xmlNode *input, const char *expected); + + +/* internal main loop utilities (from mainloop.c) */ + +int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, + const struct ipc_client_callbacks *callbacks, + mainloop_io_t **source); +guint pcmk__mainloop_timer_get_period(const mainloop_timer_t *timer); + + +/* internal node-related XML utilities (from nodes.c) */ + +/*! + * \internal + * \brief Add local node name and ID to an XML node + * + * \param[in,out] request XML node to modify + * \param[in] node The local node's name + * \param[in] nodeid The local node's ID (can be 0) + */ +void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid); + + +/* internal name/value utilities (from nvpair.c) */ + +int pcmk__scan_nvpair(const char *input, char **name, char **value); +char *pcmk__format_nvpair(const char *name, const char *value, + const char *units); + +/*! + * \internal + * \brief Add a boolean attribute to an XML node. + * + * \param[in,out] node XML node to add attributes to + * \param[in] name XML attribute to create + * \param[in] value Value to give to the attribute + */ +void +pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value); + +/*! + * \internal + * \brief Extract a boolean attribute's value from an XML element + * + * \param[in] node XML node to get attribute from + * \param[in] name XML attribute to get + * + * \return True if the given \p name is an attribute on \p node and has + * the value "true", False in all other cases + */ +bool +pcmk__xe_attr_is_true(const xmlNode *node, const char *name); + +/*! + * \internal + * \brief Extract a boolean attribute's value from an XML element, with + * error checking + * + * \param[in] node XML node to get attribute from + * \param[in] name XML attribute to get + * \param[out] value Destination for the value of the attribute + * + * \return EINVAL if \p name or \p value are NULL, ENODATA if \p node is + * NULL or the attribute does not exist, pcmk_rc_unknown_format + * if the attribute is not a boolean, and pcmk_rc_ok otherwise. + * + * \note \p value only has any meaning if the return value is pcmk_rc_ok. + */ +int +pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value); + + +/* internal procfs utilities (from procfs.c) */ + +pid_t pcmk__procfs_pid_of(const char *name); +unsigned int pcmk__procfs_num_cores(void); +int pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size); +bool pcmk__procfs_has_pids(void); + +/* internal XML schema functions (from xml.c) */ + +void crm_schema_init(void); +void crm_schema_cleanup(void); + + +/* internal functions related to process IDs (from pid.c) */ + +/*! + * \internal + * \brief Check whether process exists (by PID and optionally executable path) + * + * \param[in] pid PID of process to check + * \param[in] daemon If not NULL, path component to match with procfs entry + * + * \return Standard Pacemaker return code + * \note Particular return codes of interest include pcmk_rc_ok for alive, + * ESRCH for process is not alive (verified by kill and/or executable path + * match), EACCES for caller unable or not allowed to check. A result of + * "alive" is less reliable when \p daemon is not provided or procfs is + * not available, since there is no guarantee that the PID has not been + * recycled for another process. + * \note This function cannot be used to verify \e authenticity of the process. + */ +int pcmk__pid_active(pid_t pid, const char *daemon); + +int pcmk__read_pidfile(const char *filename, pid_t *pid); +int pcmk__pidfile_matches(const char *filename, pid_t expected_pid, + const char *expected_name, pid_t *pid); +int pcmk__lock_pidfile(const char *filename, const char *name); + + +/* internal functions related to resource operations (from operations.c) */ + +// printf-style format to create operation ID from resource, action, interval +#define PCMK__OP_FMT "%s_%s_%u" + +char *pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms); +char *pcmk__notify_key(const char *rsc_id, const char *notify_type, + const char *op_type); +char *pcmk__transition_key(int transition_id, int action_id, int target_rc, + const char *node); +void pcmk__filter_op_for_digest(xmlNode *param_set); +bool pcmk__is_fencing_action(const char *action); + + +// bitwise arithmetic utilities + +/*! + * \internal + * \brief Set specified flags in a flag group + * + * \param[in] function Function name of caller + * \param[in] line Line number of caller + * \param[in] log_level Log a message at this level + * \param[in] flag_type Label describing this flag group (for logging) + * \param[in] target Name of object whose flags these are (for logging) + * \param[in] flag_group Flag group being manipulated + * \param[in] flags Which flags in the group should be set + * \param[in] flags_str Readable equivalent of \p flags (for logging) + * + * \return Possibly modified flag group + */ +static inline uint64_t +pcmk__set_flags_as(const char *function, int line, uint8_t log_level, + const char *flag_type, const char *target, + uint64_t flag_group, uint64_t flags, const char *flags_str) +{ + uint64_t result = flag_group | flags; + + if (result != flag_group) { + do_crm_log_unlikely(log_level, + "%s flags %#.8llx (%s) for %s set by %s:%d", + ((flag_type == NULL)? "Group of" : flag_type), + (unsigned long long) flags, + ((flags_str == NULL)? "flags" : flags_str), + ((target == NULL)? "target" : target), + function, line); + } + return result; +} + +/*! + * \internal + * \brief Clear specified flags in a flag group + * + * \param[in] function Function name of caller + * \param[in] line Line number of caller + * \param[in] log_level Log a message at this level + * \param[in] flag_type Label describing this flag group (for logging) + * \param[in] target Name of object whose flags these are (for logging) + * \param[in] flag_group Flag group being manipulated + * \param[in] flags Which flags in the group should be cleared + * \param[in] flags_str Readable equivalent of \p flags (for logging) + * + * \return Possibly modified flag group + */ +static inline uint64_t +pcmk__clear_flags_as(const char *function, int line, uint8_t log_level, + const char *flag_type, const char *target, + uint64_t flag_group, uint64_t flags, const char *flags_str) +{ + uint64_t result = flag_group & ~flags; + + if (result != flag_group) { + do_crm_log_unlikely(log_level, + "%s flags %#.8llx (%s) for %s cleared by %s:%d", + ((flag_type == NULL)? "Group of" : flag_type), + (unsigned long long) flags, + ((flags_str == NULL)? "flags" : flags_str), + ((target == NULL)? "target" : target), + function, line); + } + return result; +} + +// miscellaneous utilities (from utils.c) + +void pcmk__daemonize(const char *name, const char *pidfile); +void pcmk__panic(const char *origin); +pid_t pcmk__locate_sbd(void); +void pcmk__sleep_ms(unsigned int ms); + +extern int pcmk__score_red; +extern int pcmk__score_green; +extern int pcmk__score_yellow; + +/*! + * \internal + * \brief Resize a dynamically allocated memory block + * + * \param[in] ptr Memory block to resize (or NULL to allocate new memory) + * \param[in] size New size of memory block in bytes (must be > 0) + * + * \return Pointer to resized memory block + * + * \note This asserts on error, so the result is guaranteed to be non-NULL + * (which is the main advantage of this over directly using realloc()). + */ +static inline void * +pcmk__realloc(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; +} + + +static inline char * +pcmk__getpid_s(void) +{ + return crm_strdup_printf("%lu", (unsigned long) getpid()); +} + +// More efficient than g_list_length(list) == 1 +static inline bool +pcmk__list_of_1(GList *list) +{ + return list && (list->next == NULL); +} + +// More efficient than g_list_length(list) > 1 +static inline bool +pcmk__list_of_multiple(GList *list) +{ + return list && (list->next != NULL); +} + +/* convenience functions for failure-related node attributes */ + +#define PCMK__FAIL_COUNT_PREFIX "fail-count" +#define PCMK__LAST_FAILURE_PREFIX "last-failure" + +/*! + * \internal + * \brief Generate a failure-related node attribute name for a resource + * + * \param[in] prefix Start of attribute name + * \param[in] rsc_id Resource name + * \param[in] op Operation name + * \param[in] interval_ms Operation interval + * + * \return Newly allocated string with attribute name + * + * \note Failure attributes are named like PREFIX-RSC#OP_INTERVAL (for example, + * "fail-count-myrsc#monitor_30000"). The '#' is used because it is not + * a valid character in a resource ID, to reliably distinguish where the + * operation name begins. The '_' is used simply to be more comparable to + * action labels like "myrsc_monitor_30000". + */ +static inline char * +pcmk__fail_attr_name(const char *prefix, const char *rsc_id, const char *op, + guint interval_ms) +{ + CRM_CHECK(prefix && rsc_id && op, return NULL); + return crm_strdup_printf("%s-%s#%s_%u", prefix, rsc_id, op, interval_ms); +} + +static inline char * +pcmk__failcount_name(const char *rsc_id, const char *op, guint interval_ms) +{ + return pcmk__fail_attr_name(PCMK__FAIL_COUNT_PREFIX, rsc_id, op, + interval_ms); +} + +static inline char * +pcmk__lastfailure_name(const char *rsc_id, const char *op, guint interval_ms) +{ + return pcmk__fail_attr_name(PCMK__LAST_FAILURE_PREFIX, rsc_id, op, + interval_ms); +} + +// internal resource agent functions (from agents.c) +int pcmk__effective_rc(int rc); + +#endif /* CRM_COMMON_INTERNAL__H */ diff --git a/include/crm/common/io_internal.h b/include/crm/common/io_internal.h new file mode 100644 index 0000000..a8e1f28 --- /dev/null +++ b/include/crm/common/io_internal.h @@ -0,0 +1,57 @@ +/* + * Copyright 2022 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 PCMK__CRM_COMMON_IO_INTERNAL__H +# define PCMK__CRM_COMMON_IO_INTERNAL__H + +#include <fcntl.h> // open() +#include <stdbool.h> // bool +#include <unistd.h> // uid_t, gid_t + +int pcmk__real_path(const char *path, char **resolved_path); + +char *pcmk__series_filename(const char *directory, const char *series, + int sequence, bool bzip); +int pcmk__read_series_sequence(const char *directory, const char *series, + unsigned int *seq); +void pcmk__write_series_sequence(const char *directory, const char *series, + unsigned int sequence, int max); +int pcmk__chown_series_sequence(const char *directory, const char *series, + uid_t uid, gid_t gid); + +int pcmk__build_path(const char *path_c, mode_t mode); +char *pcmk__full_path(const char *filename, const char *dirname); +bool pcmk__daemon_can_write(const char *dir, const char *file); +void pcmk__sync_directory(const char *name); + +int pcmk__file_contents(const char *filename, char **contents); +int pcmk__write_sync(int fd, const char *contents); +int pcmk__set_nonblocking(int fd); +const char *pcmk__get_tmpdir(void); + +void pcmk__close_fds_in_child(bool); + +/*! + * \internal + * \brief Open /dev/null to consume next available file descriptor + * + * Open /dev/null, disregarding the result. This is intended when daemonizing to + * be able to null stdin, stdout, and stderr. + * + * \param[in] flags O_RDONLY (stdin) or O_WRONLY (stdout and stderr) + */ +static inline void +pcmk__open_devnull(int flags) +{ + // Static analysis clutter + // cppcheck-suppress leakReturnValNotUsed + (void) open("/dev/null", flags); +} + +#endif // PCMK__CRM_COMMON_IO_INTERNAL__H diff --git a/include/crm/common/ipc.h b/include/crm/common/ipc.h new file mode 100644 index 0000000..3d4ee10 --- /dev/null +++ b/include/crm/common/ipc.h @@ -0,0 +1,233 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_COMMON_IPC__H +# define PCMK__CRM_COMMON_IPC__H + + +#include <sys/uio.h> +#include <qb/qbipcc.h> +#include <crm/common/xml.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief IPC interface to Pacemaker daemons + * + * \ingroup core + */ + +/* + * Message creation utilities + * + * These are used for both IPC messages and cluster layer messages. However, + * since this is public API, they stay in this header for backward + * compatibility. + */ + +#define create_reply(request, xml_response_data) \ + create_reply_adv(request, xml_response_data, __func__) + +xmlNode *create_reply_adv(const xmlNode *request, xmlNode *xml_response_data, + const char *origin); + +#define create_request(task, xml_data, host_to, sys_to, sys_from, uuid_from) \ + create_request_adv(task, xml_data, host_to, sys_to, sys_from, uuid_from, \ + __func__) + +xmlNode *create_request_adv(const char *task, xmlNode *xml_data, + const char *host_to, const char *sys_to, + const char *sys_from, const char *uuid_from, + const char *origin); + + +/* + * The library supports two methods of creating IPC connections. The older code + * allows connecting to any arbitrary IPC name. The newer code only allows + * connecting to one of the Pacemaker daemons. + * + * As daemons are converted to use the new model, the old functions should be + * considered deprecated for use with those daemons. Once all daemons are + * converted, the old functions should be officially deprecated as public API + * and eventually made internal API. + */ + +/* + * Pacemaker daemon IPC + */ + +//! Available IPC interfaces +enum pcmk_ipc_server { + pcmk_ipc_attrd, //!< Attribute manager + pcmk_ipc_based, //!< CIB manager + pcmk_ipc_controld, //!< Controller + pcmk_ipc_execd, //!< Executor + pcmk_ipc_fenced, //!< Fencer + pcmk_ipc_pacemakerd, //!< Launcher + pcmk_ipc_schedulerd, //!< Scheduler +}; + +//! Possible event types that an IPC event callback can be called for +enum pcmk_ipc_event { + pcmk_ipc_event_connect, //!< Result of asynchronous connection attempt + pcmk_ipc_event_disconnect, //!< Termination of IPC connection + pcmk_ipc_event_reply, //!< Daemon's reply to client IPC request + pcmk_ipc_event_notify, //!< Notification from daemon +}; + +//! How IPC replies should be dispatched +enum pcmk_ipc_dispatch { + pcmk_ipc_dispatch_main, //!< Attach IPC to GMainLoop for dispatch + pcmk_ipc_dispatch_poll, //!< Caller will poll and dispatch IPC + pcmk_ipc_dispatch_sync, //!< Sending a command will wait for any reply +}; + +//! Client connection to Pacemaker IPC +typedef struct pcmk_ipc_api_s pcmk_ipc_api_t; + +/*! + * \brief Callback function type for Pacemaker daemon IPC APIs + * + * \param[in,out] api IPC API connection + * \param[in] event_type The type of event that occurred + * \param[in] status Event status + * \param[in,out] event_data Event-specific data + * \param[in,out] user_data Caller data provided when callback was registered + * + * \note For connection and disconnection events, event_data may be NULL (for + * local IPC) or the name of the connected node (for remote IPC, for + * daemons that support that). For reply and notify events, event_data is + * defined by the specific daemon API. + */ +typedef void (*pcmk_ipc_callback_t)(pcmk_ipc_api_t *api, + enum pcmk_ipc_event event_type, + crm_exit_t status, + void *event_data, void *user_data); + +int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server); + +void pcmk_free_ipc_api(pcmk_ipc_api_t *api); + +int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type); + +void pcmk_disconnect_ipc(pcmk_ipc_api_t *api); + +int pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms); + +void pcmk_dispatch_ipc(pcmk_ipc_api_t *api); + +void pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb, + void *user_data); + +const char *pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log); + +bool pcmk_ipc_is_connected(pcmk_ipc_api_t *api); + +int pcmk_ipc_purge_node(pcmk_ipc_api_t *api, const char *node_name, + uint32_t nodeid); + + +/* + * Generic IPC API (to eventually be deprecated as public API and made internal) + */ + +/* *INDENT-OFF* */ +enum crm_ipc_flags +{ + crm_ipc_flags_none = 0x00000000, + + crm_ipc_compressed = 0x00000001, /* Message has been compressed */ + + crm_ipc_proxied = 0x00000100, /* _ALL_ replies to proxied connections need to be sent as events */ + crm_ipc_client_response = 0x00000200, /* A Response is expected in reply */ + + // These are options for Pacemaker's internal use only (pcmk__ipc_send_*()) + crm_ipc_server_event = 0x00010000, /* Send an Event instead of a Response */ + crm_ipc_server_free = 0x00020000, /* Free the iovec after sending */ + crm_ipc_proxied_relay_response = 0x00040000, /* all replies to proxied connections are sent as events, this flag preserves whether the event should be treated as an actual event, or a response.*/ + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + crm_ipc_server_info = 0x00100000, //!< \deprecated Unused + crm_ipc_server_error = 0x00200000, //!< \deprecated Unused +#endif +}; +/* *INDENT-ON* */ + +typedef struct crm_ipc_s crm_ipc_t; + +crm_ipc_t *crm_ipc_new(const char *name, size_t max_size); +bool crm_ipc_connect(crm_ipc_t * client); +void crm_ipc_close(crm_ipc_t * client); +void crm_ipc_destroy(crm_ipc_t * client); +void pcmk_free_ipc_event(struct iovec *event); + +int crm_ipc_send(crm_ipc_t * client, xmlNode * message, enum crm_ipc_flags flags, + int32_t ms_timeout, xmlNode ** reply); + +int crm_ipc_get_fd(crm_ipc_t * client); +bool crm_ipc_connected(crm_ipc_t * client); +int crm_ipc_ready(crm_ipc_t * client); +long crm_ipc_read(crm_ipc_t * client); +const char *crm_ipc_buffer(crm_ipc_t * client); +uint32_t crm_ipc_buffer_flags(crm_ipc_t * client); +const char *crm_ipc_name(crm_ipc_t * client); +unsigned int crm_ipc_default_buffer_size(void); + +/*! + * \brief Check the authenticity of the IPC socket peer process (legacy) + * + * If everything goes well, peer's authenticity is verified by the means + * of comparing against provided referential UID and GID (either satisfies), + * and the result of this check can be deduced from the return value. + * As an exception, detected UID of 0 ("root") satisfies arbitrary + * provided referential daemon's credentials. + * + * \param[in] sock IPC related, connected Unix socket to check peer of + * \param[in] refuid referential UID to check against + * \param[in] refgid referential GID to check against + * \param[out] gotpid to optionally store obtained PID of the peer + * (not available on FreeBSD, special value of 1 + * used instead, and the caller is required to + * special case this value respectively) + * \param[out] gotuid to optionally store obtained UID of the peer + * \param[out] gotgid to optionally store obtained GID of the peer + * + * \return 0 if IPC related socket's peer is not authentic given the + * referential credentials (see above), 1 if it is, + * negative value on error (generally expressing -errno unless + * it was zero even on nonhappy path, -pcmk_err_generic is + * returned then; no message is directly emitted) + * + * \note While this function is tolerant on what constitutes authorized + * IPC daemon process (its effective user matches UID=0 or \p refuid, + * or at least its group matches \p refgid), either or both (in case + * of UID=0) mismatches on the expected credentials of such peer + * process \e shall be investigated at the caller when value of 1 + * gets returned there, since higher-than-expected privileges in + * respect to the expected/intended credentials possibly violate + * the least privilege principle and may pose an additional risk + * (i.e. such accidental inconsistency shall be eventually fixed). + */ +int crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid, + pid_t *gotpid, uid_t *gotuid, gid_t *gotgid); + +/* This is controller-specific but is declared in this header for C API + * backward compatibility. + */ +xmlNode *create_hello_message(const char *uuid, const char *client_name, + const char *major_version, const char *minor_version); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/ipc_attrd_internal.h b/include/crm/common/ipc_attrd_internal.h new file mode 100644 index 0000000..b1b7584 --- /dev/null +++ b/include/crm/common/ipc_attrd_internal.h @@ -0,0 +1,198 @@ +/* + * Copyright 2022-2023 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 PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H +# define PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H + +#include <glib.h> // GList +#include <crm/common/ipc.h> // pcmk_ipc_api_t + +#ifdef __cplusplus +extern "C" { +#endif + +//! Possible types of attribute manager replies +enum pcmk__attrd_api_reply { + pcmk__attrd_reply_unknown, + pcmk__attrd_reply_query, +}; + +// Information passed with pcmk__attrd_reply_query +typedef struct { + const char *node; + const char *name; + const char *value; +} pcmk__attrd_query_pair_t; + +/*! + * Attribute manager reply passed to event callback + * + * \note The pointers in the reply are only guaranteed to be meaningful for the + * execution of the callback; if the values are needed for later, the + * callback should copy them. + */ +typedef struct { + enum pcmk__attrd_api_reply reply_type; + + union { + // pcmk__attrd_reply_query + GList *pairs; + } data; +} pcmk__attrd_api_reply_t; + +/*! + * \internal + * \brief Send a request to pacemaker-attrd to clear resource failure + * + * \param[in,out] api pacemaker-attrd IPC object + * \param[in] node Affect only this node (or NULL for all nodes) + * \param[in] resource Name of resource to clear (or NULL for all) + * \param[in] operation Name of operation to clear (or NULL for all) + * \param[in] interval_spec If operation is not NULL, its interval + * \param[in] user_name ACL user to pass to pacemaker-attrd + * \param[in] options Bitmask of pcmk__node_attr_opts + * + * \note If \p api is NULL, a new temporary connection will be created + * just for this operation and destroyed afterwards. If \p api is + * not NULL but is not yet connected to pacemaker-attrd, the object + * will be connected for this operation and left connected afterwards. + * This allows for reusing an IPC connection. + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node, + const char *resource, const char *operation, + const char *interval_spec, const char *user_name, + uint32_t options); + +/*! + * \internal + * + * \brief Delete a previously set attribute by setting its value to NULL + * + * \param[in,out] api Connection to pacemaker-attrd (or NULL to use + * a temporary new connection) + * \param[in] node Delete attribute for this node (or NULL for local) + * \param[in] name Attribute name + * \param[in] options Bitmask of pcmk__node_attr_opts + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, + uint32_t options); + +/*! + * \internal + * \brief Purge a node from pacemaker-attrd + * + * \param[in,out] api pacemaker-attrd IPC object + * \param[in] node Node to remove + * + * \note If \p api is NULL, a new temporary connection will be created + * just for this operation and destroyed afterwards. If \p api is + * not NULL but is not yet connected to pacemaker-attrd, the object + * will be connected for this operation and left connected afterwards. + * This allows for reusing an IPC connection. + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node); + +/*! + * \internal + * \brief Get the value of an attribute from pacemaker-attrd + * + * \param[in,out] api Connection to pacemaker-attrd + * \param[in] node Look up the attribute for this node + * (or NULL for the local node) + * \param[in] name Attribute name + * \param[in] options Bitmask of pcmk__node_attr_opts + * + * \note Passing pcmk__node_attr_query_all will cause the function to query + * the value of \p name on all nodes, regardless of the value of \p node. + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, + uint32_t options); + +/*! + * \internal + * \brief Tell pacemaker-attrd to update the CIB with current values + * + * \param[in,out] api pacemaker-attrd IPC object + * \param[in] node Affect only this node (or NULL for all nodes) + * + * \note If \p api is NULL, a new temporary connection will be created + * just for this operation and destroyed afterwards. If \p api is + * not NULL but is not yet connected to pacemaker-attrd, the object + * will be connected for this operation and left connected afterwards. + * This allows for reusing an IPC connection. + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node); + +/*! + * \internal + * \brief Update an attribute's value, time to wait, or both + * + * \param[in,out] api pacemaker-attrd IPC object + * \param[in] node Affect only this node (or NULL for current node) + * \param[in] name Attribute name + * \param[in] value The attribute's new value, or NULL to unset + * \param[in] dampen The new time to wait value, or NULL to unset + * \param[in] set ID of attribute set to use (or NULL for first) + * \param[in] user_name ACL user to pass to pacemaker-attrd + * \param[in] options Bitmask of pcmk__node_attr_opts + * + * \note If \p api is NULL, a new temporary connection will be created + * just for this operation and destroyed afterwards. If \p api is + * not NULL but is not yet connected to pacemaker-attrd, the object + * will be connected for this operation and left connected afterwards. + * This allows for reusing an IPC connection. + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, + const char *value, const char *dampen, const char *set, + const char *user_name, uint32_t options); + +/*! + * \internal + * \brief Like pcmk__attrd_api_update, but for multiple attributes at once + * + * \param[in,out] api pacemaker-attrd IPC object + * \param[in,out] attrs A list of pcmk__attr_query_pair_t structs + * \param[in] dampen The new time to wait value, or NULL to unset + * \param[in] set ID of attribute set to use (or NULL for first) + * \param[in] user_name ACL user to pass to pacemaker-attrd + * \param[in] options Bitmask of pcmk__node_attr_opts + * + * \note If \p api is NULL, a new temporary connection will be created + * just for this operation and destroyed afterwards. If \p api is + * not NULL but is not yet connected to pacemaker-attrd, the object + * will be connected for this operation and left connected afterwards. + * This allows for reusing an IPC connection. + * + * \note Not all attrd versions support setting multiple attributes at once. + * For those servers that do not, this function will fall back to just + * sending a separate IPC request for each attribute. + * + * \return Standard Pacemaker return code + */ +int pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, + const char *dampen, const char *set, + const char *user_name, uint32_t options); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H diff --git a/include/crm/common/ipc_controld.h b/include/crm/common/ipc_controld.h new file mode 100644 index 0000000..6deba48 --- /dev/null +++ b/include/crm/common/ipc_controld.h @@ -0,0 +1,111 @@ +/* + * Copyright 2020-2021 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 PCMK__CRM_COMMON_IPC_CONTROLD__H +# define PCMK__CRM_COMMON_IPC_CONTROLD__H + + +#include <stdbool.h> // bool +#include <glib.h> // GList +#include <libxml/tree.h> // xmlNode +#include <crm/common/ipc.h> // pcmk_ipc_api_t + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief IPC commands for Pacemaker controller + * + * \ingroup core + */ + +//! Possible types of controller replies +enum pcmk_controld_api_reply { + pcmk_controld_reply_unknown, + pcmk_controld_reply_reprobe, + pcmk_controld_reply_info, + pcmk_controld_reply_resource, + pcmk_controld_reply_ping, + pcmk_controld_reply_nodes, +}; + +// Node information passed with pcmk_controld_reply_nodes +typedef struct { + uint32_t id; + const char *uname; + const char *state; +} pcmk_controld_api_node_t; + +/*! + * Controller reply passed to event callback + * + * \note Shutdown and election calls have no reply. Reprobe calls are + * acknowledged but contain no data (reply_type will be the only item + * set). Node info and ping calls have their own reply data. Fail and + * refresh calls use the resource reply type and reply data. + * \note The pointers in the reply are only guaranteed to be meaningful for the + * execution of the callback; if the values are needed for later, the + * callback should copy them. + */ +typedef struct { + enum pcmk_controld_api_reply reply_type; + const char *feature_set; //!< CRM feature set advertised by controller + const char *host_from; //!< Name of node that sent reply + + union { + // pcmk_controld_reply_info + struct { + bool have_quorum; + bool is_remote; + int id; + const char *uuid; + const char *uname; + const char *state; + } node_info; + + // pcmk_controld_reply_resource + struct { + xmlNode *node_state; //<! Resource operation history XML + } resource; + + // pcmk_controld_reply_ping + struct { + const char *sys_from; + const char *fsa_state; + const char *result; + } ping; + + // pcmk_controld_reply_nodes + GList *nodes; // list of pcmk_controld_api_node_t * + } data; +} pcmk_controld_api_reply_t; + +int pcmk_controld_api_reprobe(pcmk_ipc_api_t *api, const char *target_node, + const char *router_node); +int pcmk_controld_api_node_info(pcmk_ipc_api_t *api, uint32_t nodeid); +int pcmk_controld_api_fail(pcmk_ipc_api_t *api, const char *target_node, + const char *router_node, const char *rsc_id, + const char *rsc_long_id, const char *standard, + const char *provider, const char *type); +int pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node, + const char *router_node, const char *rsc_id, + const char *rsc_long_id, const char *standard, + const char *provider, const char *type, + bool cib_only); +int pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name); +int pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api); +unsigned int pcmk_controld_api_replies_expected(const pcmk_ipc_api_t *api); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_IPC_CONTROLD__H diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h new file mode 100644 index 0000000..5099dda --- /dev/null +++ b/include/crm/common/ipc_internal.h @@ -0,0 +1,293 @@ +/* + * Copyright 2013-2023 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 PCMK__IPC_INTERNAL_H +#define PCMK__IPC_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> // bool +#include <stdint.h> // uint32_t, uint64_t, UINT64_C() +#include <sys/uio.h> // struct iovec +#include <sys/types.h> // uid_t, gid_t, pid_t, size_t + +#ifdef HAVE_GNUTLS_GNUTLS_H +# include <gnutls/gnutls.h> // gnutls_session_t +#endif + +#include <glib.h> // guint, gpointer, GQueue, ... +#include <libxml/tree.h> // xmlNode +#include <qb/qbipcs.h> // qb_ipcs_connection_t, ... + +#include <crm_config.h> // HAVE_GETPEEREID +#include <crm/common/ipc.h> +#include <crm/common/ipc_controld.h> // pcmk_controld_api_reply +#include <crm/common/ipc_pacemakerd.h> // pcmk_pacemakerd_{api_reply,state} +#include <crm/common/mainloop.h> // mainloop_io_t + +/* + * XML attribute names used only by internal code + */ + +#define PCMK__XA_IPC_PROTO_VERSION "ipc-protocol-version" + +/* denotes "non yieldable PID" on FreeBSD, or actual PID1 in scenarios that + require a delicate handling anyway (socket-based activation with systemd); + we can be reasonably sure that this PID is never possessed by the actual + child daemon, as it gets taken either by the proper init, or by pacemakerd + itself (i.e. this precludes anything else); note that value of zero + is meant to carry "unset" meaning, and better not to bet on/conditionalize + over signedness of pid_t */ +#define PCMK__SPECIAL_PID 1 + +// Timeout (in seconds) to use for IPC client sends, reply waits, etc. +#define PCMK__IPC_TIMEOUT 120 + +#if defined(HAVE_GETPEEREID) +/* on FreeBSD, we don't want to expose "non-yieldable PID" (leading to + "IPC liveness check only") as its nominal representation, which could + cause confusion -- this is unambiguous as long as there's no + socket-based activation like with systemd (very improbable) */ +#define PCMK__SPECIAL_PID_AS_0(p) (((p) == PCMK__SPECIAL_PID) ? 0 : (p)) +#else +#define PCMK__SPECIAL_PID_AS_0(p) (p) +#endif + +/*! + * \internal + * \brief Check the authenticity and liveness of the process via IPC end-point + * + * When IPC daemon under given IPC end-point (name) detected, its authenticity + * is verified by the means of comparing against provided referential UID and + * GID, and the result of this check can be deduced from the return value. + * As an exception, referential UID of 0 (~ root) satisfies arbitrary + * detected daemon's credentials. + * + * \param[in] name IPC name to base the search on + * \param[in] refuid referential UID to check against + * \param[in] refgid referential GID to check against + * \param[out] gotpid to optionally store obtained PID of the found process + * upon returning 1 or -2 + * (not available on FreeBSD, special value of 1, + * see PCMK__SPECIAL_PID, used instead, and the caller + * is required to special case this value respectively) + * + * \return Standard Pacemaker return code + * + * \note Return codes of particular interest include pcmk_rc_ipc_unresponsive + * indicating that no trace of IPC liveness was detected, and + * pcmk_rc_ipc_unauthorized indicating that the IPC endpoint is blocked by + * an unauthorized process. + * \note This function emits a log message for return codes other than + * pcmk_rc_ok and pcmk_rc_ipc_unresponsive, and when there isn't a perfect + * match in respect to \p reguid and/or \p refgid, for a possible + * least privilege principle violation. + * + * \see crm_ipc_is_authentic_process + */ +int pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid, + gid_t refgid, pid_t *gotpid); + + +/* + * Server-related + */ + +typedef struct pcmk__client_s pcmk__client_t; + +struct pcmk__remote_s { + /* Shared */ + char *buffer; + size_t buffer_size; + size_t buffer_offset; + int auth_timeout; + int tcp_socket; + mainloop_io_t *source; + time_t uptime; + + /* CIB-only */ + char *token; + + /* TLS only */ +# ifdef HAVE_GNUTLS_GNUTLS_H + gnutls_session_t *tls_session; +# endif +}; + +enum pcmk__client_flags { + // Lower 32 bits are reserved for server (not library) use + + // Next 8 bits are reserved for client type (sort of a cheap enum) + + //! Client uses plain IPC + pcmk__client_ipc = (UINT64_C(1) << 32), + + //! Client uses TCP connection + pcmk__client_tcp = (UINT64_C(1) << 33), + +# ifdef HAVE_GNUTLS_GNUTLS_H + //! Client uses TCP with TLS + pcmk__client_tls = (UINT64_C(1) << 34), +# endif + + // The rest are client attributes + + //! Client IPC is proxied + pcmk__client_proxied = (UINT64_C(1) << 40), + + //! Client is run by root or cluster user + pcmk__client_privileged = (UINT64_C(1) << 41), + + //! Local client to be proxied + pcmk__client_to_proxy = (UINT64_C(1) << 42), + + /*! + * \brief Client IPC connection accepted + * + * Used only for remote CIB connections via \c remote-tls-port. + */ + pcmk__client_authenticated = (UINT64_C(1) << 43), + +# ifdef HAVE_GNUTLS_GNUTLS_H + //! Client TLS handshake is complete + pcmk__client_tls_handshake_complete = (UINT64_C(1) << 44), +# endif +}; + +#define PCMK__CLIENT_TYPE(client) ((client)->flags & UINT64_C(0xff00000000)) + +struct pcmk__client_s { + unsigned int pid; + + char *id; + char *name; + char *user; + uint64_t flags; // Group of pcmk__client_flags + + int request_id; + void *userdata; + + int event_timer; + GQueue *event_queue; + + /* Depending on the client type, only some of the following will be + * populated/valid. @TODO Maybe convert to a union. + */ + + qb_ipcs_connection_t *ipcs; /* IPC */ + + struct pcmk__remote_s *remote; /* TCP/TLS */ + + unsigned int queue_backlog; /* IPC queue length after last flush */ + unsigned int queue_max; /* Evict client whose queue grows this big */ +}; + +#define pcmk__set_client_flags(client, flags_to_set) do { \ + (client)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, \ + "Client", pcmk__client_name(client), \ + (client)->flags, (flags_to_set), #flags_to_set); \ + } while (0) + +#define pcmk__clear_client_flags(client, flags_to_clear) do { \ + (client)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, \ + "Client", pcmk__client_name(client), \ + (client)->flags, (flags_to_clear), #flags_to_clear); \ + } while (0) + +#define pcmk__set_ipc_flags(ipc_flags, ipc_name, flags_to_set) do { \ + ipc_flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ + "IPC", (ipc_name), \ + (ipc_flags), (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define pcmk__clear_ipc_flags(ipc_flags, ipc_name, flags_to_clear) do { \ + ipc_flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \ + "IPC", (ipc_name), \ + (ipc_flags), (flags_to_clear), \ + #flags_to_clear); \ + } while (0) + +guint pcmk__ipc_client_count(void); +void pcmk__foreach_ipc_client(GHFunc func, gpointer user_data); + +void pcmk__client_cleanup(void); + +pcmk__client_t *pcmk__find_client(const qb_ipcs_connection_t *c); +pcmk__client_t *pcmk__find_client_by_id(const char *id); +const char *pcmk__client_name(const pcmk__client_t *c); +const char *pcmk__client_type_str(uint64_t client_type); + +pcmk__client_t *pcmk__new_unauth_client(void *key); +pcmk__client_t *pcmk__new_client(qb_ipcs_connection_t *c, uid_t uid, gid_t gid); +void pcmk__free_client(pcmk__client_t *c); +void pcmk__drop_all_clients(qb_ipcs_service_t *s); +bool pcmk__set_client_queue_max(pcmk__client_t *client, const char *qmax); + +xmlNode *pcmk__ipc_create_ack_as(const char *function, int line, uint32_t flags, + const char *tag, const char *ver, crm_exit_t status); +#define pcmk__ipc_create_ack(flags, tag, ver, st) \ + pcmk__ipc_create_ack_as(__func__, __LINE__, (flags), (tag), (ver), (st)) + +int pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c, + uint32_t request, uint32_t flags, const char *tag, + const char *ver, crm_exit_t status); +#define pcmk__ipc_send_ack(c, req, flags, tag, ver, st) \ + pcmk__ipc_send_ack_as(__func__, __LINE__, (c), (req), (flags), (tag), (ver), (st)) + +int pcmk__ipc_prepare_iov(uint32_t request, xmlNode *message, + uint32_t max_send_size, + struct iovec **result, ssize_t *bytes); +int pcmk__ipc_send_xml(pcmk__client_t *c, uint32_t request, xmlNode *message, + uint32_t flags); +int pcmk__ipc_send_iov(pcmk__client_t *c, struct iovec *iov, uint32_t flags); +xmlNode *pcmk__client_data2xml(pcmk__client_t *c, void *data, + uint32_t *id, uint32_t *flags); + +int pcmk__client_pid(qb_ipcs_connection_t *c); + +void pcmk__serve_attrd_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb); +void pcmk__serve_fenced_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb); +void pcmk__serve_pacemakerd_ipc(qb_ipcs_service_t **ipcs, + struct qb_ipcs_service_handlers *cb); +qb_ipcs_service_t *pcmk__serve_schedulerd_ipc(struct qb_ipcs_service_handlers *cb); +qb_ipcs_service_t *pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb); + +void pcmk__serve_based_ipc(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 pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro, + qb_ipcs_service_t *ipcs_rw, + qb_ipcs_service_t *ipcs_shm); + +static inline const char * +pcmk__ipc_sys_name(const char *ipc_name, const char *fallback) +{ + return ipc_name ? ipc_name : ((crm_system_name ? crm_system_name : fallback)); +} + +const char *pcmk__pcmkd_state_enum2friendly(enum pcmk_pacemakerd_state state); + +const char *pcmk__controld_api_reply2str(enum pcmk_controld_api_reply reply); +const char *pcmk__pcmkd_api_reply2str(enum pcmk_pacemakerd_api_reply reply); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/ipc_pacemakerd.h b/include/crm/common/ipc_pacemakerd.h new file mode 100644 index 0000000..340f9a6 --- /dev/null +++ b/include/crm/common/ipc_pacemakerd.h @@ -0,0 +1,79 @@ +/* + * Copyright 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 PCMK__CRM_COMMON_IPC_PACEMAKERD__H +# define PCMK__CRM_COMMON_IPC_PACEMAKERD__H + +#include <sys/types.h> // time_t +#include <crm/common/ipc.h> // pcmk_ipc_api_t + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief IPC commands for Pacemakerd + * + * \ingroup core + */ + +enum pcmk_pacemakerd_state { + pcmk_pacemakerd_state_invalid = -1, + pcmk_pacemakerd_state_init = 0, + pcmk_pacemakerd_state_starting_daemons, + pcmk_pacemakerd_state_wait_for_ping, + pcmk_pacemakerd_state_running, + pcmk_pacemakerd_state_shutting_down, + pcmk_pacemakerd_state_shutdown_complete, + pcmk_pacemakerd_state_remote, + pcmk_pacemakerd_state_max = pcmk_pacemakerd_state_remote, +}; + +//! Possible types of pacemakerd replies +enum pcmk_pacemakerd_api_reply { + pcmk_pacemakerd_reply_unknown, + pcmk_pacemakerd_reply_ping, + pcmk_pacemakerd_reply_shutdown, +}; + +/*! + * Pacemakerd reply passed to event callback + */ +typedef struct { + enum pcmk_pacemakerd_api_reply reply_type; + + union { + // pcmk_pacemakerd_reply_ping + struct { + const char *sys_from; + enum pcmk_pacemakerd_state state; + time_t last_good; + int status; + } ping; + // pcmk_pacemakerd_reply_shutdown + struct { + int status; + } shutdown; + } data; +} pcmk_pacemakerd_api_reply_t; + +int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name); +int pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name); + +enum pcmk_pacemakerd_state + pcmk_pacemakerd_api_daemon_state_text2enum(const char *state); +const char + *pcmk_pacemakerd_api_daemon_state_enum2text(enum pcmk_pacemakerd_state state); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_IPC_PACEMAKERD__H diff --git a/include/crm/common/ipc_schedulerd.h b/include/crm/common/ipc_schedulerd.h new file mode 100644 index 0000000..303ec59 --- /dev/null +++ b/include/crm/common/ipc_schedulerd.h @@ -0,0 +1,64 @@ +/* + * Copyright 2021-2022 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 PCMK__CRM_COMMON_IPC_SCHEDULERD__H +# define PCMK__CRM_COMMON_IPC_SCHEDULERD__H + +#include <crm/common/ipc.h> // pcmk_ipc_api_t + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief IPC commands for Schedulerd + * + * \ingroup core + */ + + +//! Possible types of schedulerd replies +enum pcmk_schedulerd_api_reply { + pcmk_schedulerd_reply_unknown, + pcmk_schedulerd_reply_graph, +}; + +/*! + * Schedulerd reply passed to event callback + */ +typedef struct { + enum pcmk_schedulerd_api_reply reply_type; + + union { + // pcmk__schedulerd_reply_graph + struct { + xmlNode *tgraph; + const char *reference; + const char *input; + } graph; + } data; +} pcmk_schedulerd_api_reply_t; + +/*! + * \brief Make an IPC request to the scheduler for the transition graph + * + * \param[in,out] api IPC API connection + * \param[in] cib The CIB to create a transition graph for + * \param[out] ref The reference ID a response will have + * + * \return Standard Pacemaker return code + */ +int pcmk_schedulerd_api_graph(pcmk_ipc_api_t *api, xmlNode *cib, char **ref); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_IPC_SCHEDULERD__H diff --git a/include/crm/common/iso8601.h b/include/crm/common/iso8601.h new file mode 100644 index 0000000..78f530b --- /dev/null +++ b/include/crm/common/iso8601.h @@ -0,0 +1,130 @@ +/* + * Copyright 2005-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 PCMK__CRM_COMMON_ISO8601__H +# define PCMK__CRM_COMMON_ISO8601__H + +# include <time.h> +# include <ctype.h> +# include <stdint.h> // uint32_t +# include <stdbool.h> // bool + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief ISO_8601 Date handling + * \ingroup date + */ + +/* + * See https://en.wikipedia.org/wiki/ISO_8601 + */ + +typedef struct crm_time_s crm_time_t; + +typedef struct crm_time_period_s { + crm_time_t *start; + crm_time_t *end; + crm_time_t *diff; +} crm_time_period_t; + +/* Creates a new date/time object conforming to ISO 8601, for example: + * Ordinal: 2010-01 12:00:00 +10:00 + * Gregorian: 2010-01-01 12:00:00 +10:00 + * ISO Week: 2010-W53-6 12:00:00 +10:00 + * + * Notes: + * Only one of date, time is required + * If date or timezone is unspecified, they default to the current one + * Supplying NULL results in the current date/time + * Dashes may be omitted from dates + * Colons may be omitted from times and timezones + * A timezone of 'Z' denotes UTC time + */ +crm_time_t *crm_time_new(const char *string); +crm_time_t *crm_time_new_undefined(void); +void crm_time_free(crm_time_t * dt); + +bool crm_time_is_defined(const crm_time_t *t); +char *crm_time_as_string(const crm_time_t *dt, int flags); + +#define crm_time_log(level, prefix, dt, flags) \ + crm_time_log_alias(level, __FILE__, __func__, __LINE__, prefix, dt, flags) + +void crm_time_log_alias(int log_level, const char *file, const char *function, + int line, const char *prefix, + const crm_time_t *date_time, int flags); + +# define crm_time_log_date 0x001 +# define crm_time_log_timeofday 0x002 +# define crm_time_log_with_timezone 0x004 +# define crm_time_log_duration 0x008 + +# define crm_time_ordinal 0x010 +# define crm_time_weeks 0x020 +# define crm_time_seconds 0x100 +# define crm_time_epoch 0x200 +# define crm_time_usecs 0x400 + +crm_time_t *crm_time_parse_duration(const char *duration_str); +crm_time_t *crm_time_calculate_duration(const crm_time_t *dt, + const crm_time_t *value); +crm_time_period_t *crm_time_parse_period(const char *period_str); +void crm_time_free_period(crm_time_period_t *period); + +int crm_time_compare(const crm_time_t *a, const crm_time_t *b); + +int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, + uint32_t *s); +int crm_time_get_timezone(const crm_time_t *dt, uint32_t *h, uint32_t *m); +int crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m, + uint32_t *d); +int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d); +int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, + uint32_t * d); + +/* Time in seconds since 0000-01-01 00:00:00Z */ +long long crm_time_get_seconds(const crm_time_t *dt); + +/* Time in seconds since 1970-01-01 00:00:00Z */ +long long crm_time_get_seconds_since_epoch(const crm_time_t *dt); + +void crm_time_set(crm_time_t *target, const crm_time_t *source); +void crm_time_set_timet(crm_time_t *target, const time_t *source); + +/* Returns a new time object */ +crm_time_t *pcmk_copy_time(const crm_time_t *source); +crm_time_t *crm_time_add(const crm_time_t *dt, const crm_time_t *value); +crm_time_t *crm_time_subtract(const crm_time_t *dt, const crm_time_t *value); + +/* All crm_time_add_... functions support negative values */ +void crm_time_add_seconds(crm_time_t * dt, int value); +void crm_time_add_minutes(crm_time_t * dt, int value); +void crm_time_add_hours(crm_time_t * dt, int value); +void crm_time_add_days(crm_time_t * dt, int value); +void crm_time_add_weeks(crm_time_t * dt, int value); +void crm_time_add_months(crm_time_t * dt, int value); +void crm_time_add_years(crm_time_t * dt, int value); + +/* Useful helper functions */ +int crm_time_january1_weekday(int year); +int crm_time_weeks_in_year(int year); +int crm_time_days_in_month(int month, int year); + +bool crm_time_leapyear(int year); +bool crm_time_check(const crm_time_t *dt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/iso8601_internal.h b/include/crm/common/iso8601_internal.h new file mode 100644 index 0000000..f924d8a --- /dev/null +++ b/include/crm/common/iso8601_internal.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015-2022 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 PCMK__ISO8601_INTERNAL__H +# define PCMK__ISO8601_INTERNAL__H + +#include <time.h> +#include <sys/time.h> +#include <ctype.h> +#include <crm/common/iso8601.h> + +typedef struct pcmk__time_us pcmk__time_hr_t; + +pcmk__time_hr_t *pcmk__time_hr_convert(pcmk__time_hr_t *target, + const crm_time_t *dt); +void pcmk__time_set_hr_dt(crm_time_t *target, const pcmk__time_hr_t *hr_dt); +pcmk__time_hr_t *pcmk__time_hr_now(time_t *epoch); +pcmk__time_hr_t *pcmk__time_hr_new(const char *date_time); +void pcmk__time_hr_free(pcmk__time_hr_t *hr_dt); +char *pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt); +char *pcmk__epoch2str(const time_t *source, uint32_t flags); +char *pcmk__timespec2str(const struct timespec *ts, uint32_t flags); +const char *pcmk__readable_interval(guint interval_ms); +crm_time_t *pcmk__copy_timet(time_t source); + +struct pcmk__time_us { + int years; + int months; /* Only for durations */ + int days; + int seconds; + int offset; /* Seconds */ + bool duration; + int useconds; +}; + +#endif diff --git a/include/crm/common/lists_internal.h b/include/crm/common/lists_internal.h new file mode 100644 index 0000000..1de695a --- /dev/null +++ b/include/crm/common/lists_internal.h @@ -0,0 +1,36 @@ +/* + * Copyright 2020-2022 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 PCMK__LISTS_INTERNAL__H +#define PCMK__LISTS_INTERNAL__H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <glib.h> + +/*! + * \internal + * \brief Return the list that is \p from - \p items + * + * \param[in] from Source list + * \param[in] items List containing items to remove from \p from + * \param[in] cmp Function used to compare list elements + * + * \return Newly allocated list + */ +GList *pcmk__subtract_lists(GList *from, const GList *items, + GCompareFunc cmp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/logging.h b/include/crm/common/logging.h new file mode 100644 index 0000000..2878fba --- /dev/null +++ b/include/crm/common/logging.h @@ -0,0 +1,411 @@ +/* + * Copyright 2004-2023 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. + */ + +#ifndef PCMK__CRM_COMMON_LOGGING__H +# define PCMK__CRM_COMMON_LOGGING__H + +# include <stdio.h> +# include <glib.h> +# include <qb/qblog.h> +# include <libxml/tree.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Wrappers for and extensions to libqb logging + * \ingroup core + */ + + +/* Define custom log priorities. + * + * syslog(3) uses int for priorities, but libqb's struct qb_log_callsite uses + * uint8_t, so make sure they fit in the latter. + */ + +// Define something even less desired than debug +# ifndef LOG_TRACE +# define LOG_TRACE (LOG_DEBUG+1) +# endif + +// Print message to stdout instead of logging it +# ifndef LOG_STDOUT +# define LOG_STDOUT 254 +# endif + +// Don't send message anywhere +# ifndef LOG_NEVER +# define LOG_NEVER 255 +# endif + +/* "Extended information" logging support */ +#ifdef QB_XS +# define CRM_XS QB_XS +# define crm_extended_logging(t, e) qb_log_ctl((t), QB_LOG_CONF_EXTENDED, (e)) +#else +# define CRM_XS "|" + +/* A caller might want to check the return value, so we can't define this as a + * no-op, and we can't simply define it to be 0 because gcc will then complain + * when the value isn't checked. + */ +static inline int +crm_extended_logging(int t, int e) +{ + return 0; +} +#endif + +extern unsigned int crm_log_level; +extern unsigned int crm_trace_nonlog; + +/*! \deprecated Pacemaker library functions set this when a configuration + * error is found, which turns on extra messages at the end of + * processing. It should not be used directly and will be removed + * from the public C API in a future release. + */ +extern gboolean crm_config_error; + +/*! \deprecated Pacemaker library functions set this when a configuration + * warning is found, which turns on extra messages at the end of + * processing. It should not be used directly and will be removed + * from the public C API in a future release. + */ +extern gboolean crm_config_warning; + +void crm_enable_blackbox(int nsig); +void crm_disable_blackbox(int nsig); +void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite); + +void crm_update_callsites(void); + +void crm_log_deinit(void); + +/*! + * \brief Initializes the logging system and defaults to the least verbose output level + * + * \param[in] entity If not NULL, will be used as the identity for logging purposes + * \param[in] argc The number of command line parameters + * \param[in] argv The command line parameter values + */ +void crm_log_preinit(const char *entity, int argc, char *const *argv); +gboolean crm_log_init(const char *entity, uint8_t level, gboolean daemon, + gboolean to_stderr, int argc, char **argv, gboolean quiet); + +void crm_log_args(int argc, char **argv); +void crm_log_output_fn(const char *file, const char *function, int line, int level, + const char *prefix, const char *output); + +// Log a block of text line by line +#define crm_log_output(level, prefix, output) \ + crm_log_output_fn(__FILE__, __func__, __LINE__, level, prefix, output) + +void crm_bump_log_level(int argc, char **argv); + +void crm_enable_stderr(int enable); + +gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags); + +/* returns the old value */ +unsigned int set_crm_log_level(unsigned int level); + +unsigned int get_crm_log_level(void); + +void pcmk_log_xml_impl(uint8_t level, const char *text, const xmlNode *xml); + +/* + * Throughout the macros below, note the leading, pre-comma, space in the + * various ' , ##args' occurrences to aid portability across versions of 'gcc'. + * https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros + */ +#if defined(__clang__) +# define CRM_TRACE_INIT_DATA(name) +# else +# include <assert.h> // required by QB_LOG_INIT_DATA() macro +# define CRM_TRACE_INIT_DATA(name) QB_LOG_INIT_DATA(name) +#endif + +/*! + * \internal + * \brief Clip log_level to \p uint8_t range + * + * \param[in] level Log level to clip + * + * \return 0 if \p level is less than 0, \p UINT8_MAX if \p level is greater + * than \p UINT8_MAX, or \p level otherwise + */ +/* @COMPAT: Make this function internal at a compatibility break. It's used in + * public macros for now. + */ +static inline uint8_t +pcmk__clip_log_level(int level) +{ + if (level <= 0) { + return 0; + } + if (level >= UINT8_MAX) { + return UINT8_MAX; + } + return level; +} + +/* Using "switch" instead of "if" in these macro definitions keeps + * static analysis from complaining about constant evaluations + */ + +/*! + * \brief Log a message + * + * \param[in] level Priority at which to log the message + * \param[in] fmt printf-style format string literal for message + * \param[in] args Any arguments needed by format string + */ +# define do_crm_log(level, fmt, args...) do { \ + uint8_t _level = pcmk__clip_log_level(level); \ + \ + switch (_level) { \ + case LOG_STDOUT: \ + printf(fmt "\n" , ##args); \ + break; \ + case LOG_NEVER: \ + break; \ + default: \ + qb_log_from_external_source(__func__, __FILE__, fmt, \ + _level, __LINE__, 0 , ##args); \ + break; \ + } \ + } while (0) + +/*! + * \brief Log a message that is likely to be filtered out + * + * \param[in] level Priority at which to log the message + * \param[in] fmt printf-style format string for message + * \param[in] args Any arguments needed by format string + * + * \note This does nothing when level is \p LOG_STDOUT. + */ +# define do_crm_log_unlikely(level, fmt, args...) do { \ + uint8_t _level = pcmk__clip_log_level(level); \ + \ + switch (_level) { \ + case LOG_STDOUT: case LOG_NEVER: \ + break; \ + default: { \ + static struct qb_log_callsite *trace_cs = NULL; \ + if (trace_cs == NULL) { \ + trace_cs = qb_log_callsite_get(__func__, __FILE__, fmt, \ + _level, __LINE__, 0); \ + } \ + if (crm_is_callsite_active(trace_cs, _level, 0)) { \ + qb_log_from_external_source(__func__, __FILE__, fmt, \ + _level, __LINE__, 0 , \ + ##args); \ + } \ + } \ + break; \ + } \ + } while (0) + +# define CRM_LOG_ASSERT(expr) do { \ + if (!(expr)) { \ + static struct qb_log_callsite *core_cs = NULL; \ + if(core_cs == NULL) { \ + core_cs = qb_log_callsite_get(__func__, __FILE__, \ + "log-assert", LOG_TRACE, \ + __LINE__, 0); \ + } \ + crm_abort(__FILE__, __func__, __LINE__, #expr, \ + core_cs?core_cs->targets:FALSE, TRUE); \ + } \ + } while(0) + +/* 'failure_action' MUST NOT be 'continue' as it will apply to the + * macro's do-while loop + */ +# define CRM_CHECK(expr, failure_action) do { \ + if (!(expr)) { \ + static struct qb_log_callsite *core_cs = NULL; \ + if (core_cs == NULL) { \ + core_cs = qb_log_callsite_get(__func__, __FILE__, \ + "check-assert", \ + LOG_TRACE, __LINE__, 0); \ + } \ + crm_abort(__FILE__, __func__, __LINE__, #expr, \ + (core_cs? core_cs->targets: FALSE), TRUE); \ + failure_action; \ + } \ + } while(0) + +/*! + * \brief Log XML line-by-line in a formatted fashion + * + * \param[in] level Priority at which to log the messages + * \param[in] text Prefix for each line + * \param[in] xml XML to log + * + * \note This does nothing when \p level is \p LOG_STDOUT. + */ +# define do_crm_log_xml(level, text, xml) do { \ + uint8_t _level = pcmk__clip_log_level(level); \ + static struct qb_log_callsite *xml_cs = NULL; \ + \ + switch (_level) { \ + case LOG_STDOUT: \ + case LOG_NEVER: \ + break; \ + default: \ + if (xml_cs == NULL) { \ + xml_cs = qb_log_callsite_get(__func__, __FILE__, \ + "xml-blob", _level, \ + __LINE__, 0); \ + } \ + if (crm_is_callsite_active(xml_cs, _level, 0)) { \ + pcmk_log_xml_impl(_level, text, xml); \ + } \ + break; \ + } \ + } while(0) + +/*! + * \brief Log a message as if it came from a different code location + * + * \param[in] level Priority at which to log the message + * \param[in] file Source file name to use instead of __FILE__ + * \param[in] function Source function name to use instead of __func__ + * \param[in] line Source line number to use instead of __line__ + * \param[in] fmt printf-style format string literal for message + * \param[in] args Any arguments needed by format string + */ +# define do_crm_log_alias(level, file, function, line, fmt, args...) do { \ + uint8_t _level = pcmk__clip_log_level(level); \ + \ + switch (_level) { \ + case LOG_STDOUT: \ + printf(fmt "\n" , ##args); \ + break; \ + case LOG_NEVER: \ + break; \ + default: \ + qb_log_from_external_source(function, file, fmt, _level, \ + line, 0 , ##args); \ + break; \ + } \ + } while (0) + +/*! + * \brief Send a system error message to both the log and stderr + * + * \param[in] level Priority at which to log the message + * \param[in] fmt printf-style format string for message + * \param[in] args Any arguments needed by format string + * + * \deprecated One of the other logging functions should be used with + * pcmk_strerror() instead. + * \note This is a macro, and \p level may be evaluated more than once. + * \note Because crm_perror() adds the system error message and error number + * onto the end of fmt, that information will become extended information + * if CRM_XS is used inside fmt and will not show up in syslog. + */ +# define crm_perror(level, fmt, args...) do { \ + uint8_t _level = pcmk__clip_log_level(level); \ + \ + switch (_level) { \ + case LOG_NEVER: \ + break; \ + default: { \ + const char *err = strerror(errno); \ + if (_level <= crm_log_level) { \ + fprintf(stderr, fmt ": %s (%d)\n" , ##args, err, \ + errno); \ + } \ + /* Pass original level arg since do_crm_log() also declares \ + * _level \ + */ \ + do_crm_log((level), fmt ": %s (%d)" , ##args, err, errno); \ + } \ + break; \ + } \ + } while (0) + +/*! + * \brief Log a message with a tag (for use with PCMK_trace_tags) + * + * \param[in] level Priority at which to log the message + * \param[in] tag String to tag message with + * \param[in] fmt printf-style format string for message + * \param[in] args Any arguments needed by format string + * + * \note This does nothing when level is LOG_STDOUT. + */ +# define crm_log_tag(level, tag, fmt, args...) do { \ + uint8_t _level = pcmk__clip_log_level(level); \ + \ + switch (_level) { \ + case LOG_STDOUT: case LOG_NEVER: \ + break; \ + default: { \ + static struct qb_log_callsite *trace_tag_cs = NULL; \ + int converted_tag = g_quark_try_string(tag); \ + if (trace_tag_cs == NULL) { \ + trace_tag_cs = qb_log_callsite_get(__func__, __FILE__, \ + fmt, _level, \ + __LINE__, \ + converted_tag); \ + } \ + if (crm_is_callsite_active(trace_tag_cs, _level, \ + converted_tag)) { \ + qb_log_from_external_source(__func__, __FILE__, fmt, \ + _level, __LINE__, \ + converted_tag , ##args); \ + } \ + } \ + } \ + } while (0) + +# define crm_emerg(fmt, args...) qb_log(LOG_EMERG, fmt , ##args) +# define crm_crit(fmt, args...) qb_logt(LOG_CRIT, 0, fmt , ##args) +# define crm_err(fmt, args...) qb_logt(LOG_ERR, 0, fmt , ##args) +# define crm_warn(fmt, args...) qb_logt(LOG_WARNING, 0, fmt , ##args) +# define crm_notice(fmt, args...) qb_logt(LOG_NOTICE, 0, fmt , ##args) +# define crm_info(fmt, args...) qb_logt(LOG_INFO, 0, fmt , ##args) + +# define crm_debug(fmt, args...) do_crm_log_unlikely(LOG_DEBUG, fmt , ##args) +# define crm_trace(fmt, args...) do_crm_log_unlikely(LOG_TRACE, fmt , ##args) + +# define crm_log_xml_crit(xml, text) do_crm_log_xml(LOG_CRIT, text, xml) +# define crm_log_xml_err(xml, text) do_crm_log_xml(LOG_ERR, text, xml) +# define crm_log_xml_warn(xml, text) do_crm_log_xml(LOG_WARNING, text, xml) +# define crm_log_xml_notice(xml, text) do_crm_log_xml(LOG_NOTICE, text, xml) +# define crm_log_xml_info(xml, text) do_crm_log_xml(LOG_INFO, text, xml) +# define crm_log_xml_debug(xml, text) do_crm_log_xml(LOG_DEBUG, text, xml) +# define crm_log_xml_trace(xml, text) do_crm_log_xml(LOG_TRACE, text, xml) + +# define crm_log_xml_explicit(xml, text) do { \ + static struct qb_log_callsite *digest_cs = NULL; \ + digest_cs = qb_log_callsite_get( \ + __func__, __FILE__, text, LOG_TRACE, __LINE__, \ + crm_trace_nonlog); \ + if (digest_cs && digest_cs->targets) { \ + do_crm_log_xml(LOG_TRACE, text, xml); \ + } \ + } while(0) + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/common/logging_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/logging_compat.h b/include/crm/common/logging_compat.h new file mode 100644 index 0000000..cfdb562 --- /dev/null +++ b/include/crm/common/logging_compat.h @@ -0,0 +1,85 @@ +/* + * Copyright 2004-2023 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. + */ + +#ifndef PCMK__CRM_COMMON_LOGGING_COMPAT__H +# define PCMK__CRM_COMMON_LOGGING_COMPAT__H + +#include <glib.h> +#include <libxml/tree.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker logging API + * \ingroup core + * \deprecated Do not include this header directly. Do not use Pacemaker + * libraries for general-purpose logging; libqb's logging API is a + * suitable replacement. The logging APIs in this header, and the + * header itself, will be removed in a future release. + */ + +//! \deprecated This enum will be removed in a future release +enum xml_log_options { + xml_log_option_filtered = 0x0001, + xml_log_option_formatted = 0x0002, + xml_log_option_text = 0x0004, + xml_log_option_full_fledged = 0x0008, + xml_log_option_diff_plus = 0x0010, + xml_log_option_diff_minus = 0x0020, + xml_log_option_diff_short = 0x0040, + xml_log_option_diff_all = 0x0100, + xml_log_option_dirty_add = 0x1000, + xml_log_option_open = 0x2000, + xml_log_option_children = 0x4000, + xml_log_option_close = 0x8000, +}; + +/*! + * \brief Log a message using constant priority + * + * \param[in] level Priority at which to log the message + * \param[in] fmt printf-style format string literal for message + * \param[in] args Any arguments needed by format string + * + * \deprecated Do not use Pacemaker for general-purpose logging + * \note This is a macro, and \p level may be evaluated more than once. + * This does nothing when level is LOG_STDOUT. + */ +# define do_crm_log_always(level, fmt, args...) do { \ + switch (level) { \ + case LOG_STDOUT: case LOG_NEVER: \ + break; \ + default: \ + qb_log((level), fmt , ##args); \ + break; \ + } \ + } while (0) + +//! \deprecated Do not use Pacemaker for general-purpose string handling +#define crm_str(x) (const char *) ((x)? (x) : "<null>") + +//! \deprecated Do not use Pacemaker for general-purpose logging +gboolean crm_log_cli_init(const char *entity); + +//! \deprecated Do not use Pacemaker for general-purpose logging +gboolean crm_add_logfile(const char *filename); + +//! \deprecated Do not use Pacemaker for general-purpose logging +void log_data_element(int log_level, const char *file, const char *function, + int line, const char *prefix, const xmlNode *data, + int depth, int legacy_options); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_LOGGING_COMPAT__H diff --git a/include/crm/common/logging_internal.h b/include/crm/common/logging_internal.h new file mode 100644 index 0000000..479dcab --- /dev/null +++ b/include/crm/common/logging_internal.h @@ -0,0 +1,95 @@ +/* + * Copyright 2015-2023 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. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PCMK__LOGGING_INTERNAL_H +# define PCMK__LOGGING_INTERNAL_H + +# include <glib.h> + +# include <crm/common/logging.h> +# include <crm/common/output_internal.h> + +/*! + * \internal + * \brief Log a configuration error + * + * \param[in] fmt printf(3)-style format string + * \param[in] ... Arguments for format string + */ +# define pcmk__config_err(fmt...) do { \ + crm_config_error = TRUE; \ + crm_err(fmt); \ + } while (0) + +/*! + * \internal + * \brief Log a configuration warning + * + * \param[in] fmt printf(3)-style format string + * \param[in] ... Arguments for format string + */ +# define pcmk__config_warn(fmt...) do { \ + crm_config_warning = TRUE; \ + crm_warn(fmt); \ + } while (0) + +/*! + * \internal + * \brief Execute code depending on whether trace logging is enabled + * + * This is similar to \p do_crm_log_unlikely() except instead of logging, it + * selects one of two code blocks to execute. + * + * \param[in] if_action Code block to execute if trace logging is enabled + * \param[in] else_action Code block to execute if trace logging is not enabled + * + * \note Neither \p if_action nor \p else_action can contain a \p break or + * \p continue statement. + */ +# define pcmk__if_tracing(if_action, else_action) do { \ + static struct qb_log_callsite *trace_cs = NULL; \ + \ + if (trace_cs == NULL) { \ + trace_cs = qb_log_callsite_get(__func__, __FILE__, \ + "if_tracing", LOG_TRACE, \ + __LINE__, crm_trace_nonlog); \ + } \ + if (crm_is_callsite_active(trace_cs, LOG_TRACE, \ + crm_trace_nonlog)) { \ + if_action; \ + } else { \ + else_action; \ + } \ + } while (0) + +/*! + * \internal + * \brief Initialize logging for command line tools + * + * \param[in] name The name of the program + * \param[in] verbosity How verbose to be in logging + * + * \note \p verbosity is not the same as the logging level (LOG_ERR, etc.). + */ +void pcmk__cli_init_logging(const char *name, unsigned int verbosity); + +int pcmk__add_logfile(const char *filename); +void pcmk__add_logfiles(gchar **log_files, pcmk__output_t *out); + +void pcmk__free_common_logger(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h new file mode 100644 index 0000000..a55bcdf --- /dev/null +++ b/include/crm/common/mainloop.h @@ -0,0 +1,192 @@ +/* + * Copyright 2009-2022 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 PCMK__CRM_COMMON_MAINLOOP__H +# define PCMK__CRM_COMMON_MAINLOOP__H + +# include <signal.h> // sighandler_t +# include <glib.h> +# include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Wrappers for and extensions to glib mainloop + * \ingroup core + */ + +enum mainloop_child_flags { + /* don't kill pid group on timeout, only kill the pid */ + mainloop_leave_pid_group = 0x01, +}; + +typedef struct trigger_s crm_trigger_t; +typedef struct mainloop_io_s mainloop_io_t; +typedef struct mainloop_child_s mainloop_child_t; +typedef struct mainloop_timer_s mainloop_timer_t; + +void mainloop_cleanup(void); + +crm_trigger_t *mainloop_add_trigger(int priority, int (*dispatch) (gpointer user_data), + gpointer userdata); + +void mainloop_set_trigger(crm_trigger_t * source); + +void mainloop_trigger_complete(crm_trigger_t * trig); + +gboolean mainloop_destroy_trigger(crm_trigger_t * source); + +# ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); +# endif + +sighandler_t crm_signal_handler(int sig, sighandler_t dispatch); + +gboolean mainloop_add_signal(int sig, void (*dispatch) (int sig)); + +gboolean mainloop_destroy_signal(int sig); + +bool mainloop_timer_running(mainloop_timer_t *t); + +void mainloop_timer_start(mainloop_timer_t *t); + +void mainloop_timer_stop(mainloop_timer_t *t); + +guint mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms); + +mainloop_timer_t *mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata); + +void mainloop_timer_del(mainloop_timer_t *t); + + +# include <crm/common/ipc.h> +# include <qb/qbipcs.h> + +struct ipc_client_callbacks { + /*! + * \brief Dispatch function for an IPC connection used as mainloop source + * + * \param[in] buffer Message read from IPC connection + * \param[in] length Number of bytes in \p buffer + * \param[in] userdata User data passed when creating mainloop source + * + * \return Negative value to remove source, anything else to keep it + */ + int (*dispatch) (const char *buffer, ssize_t length, gpointer userdata); + + /*! + * \brief Destroy function for mainloop IPC connection client data + * + * \param[in,out] userdata User data passed when creating mainloop source + */ + void (*destroy) (gpointer userdata); +}; + +qb_ipcs_service_t *mainloop_add_ipc_server(const char *name, enum qb_ipc_type type, + struct qb_ipcs_service_handlers *callbacks); + +/*! + * \brief Start server-side API end-point, hooked into the internal event loop + * + * \param[in] name name of the IPC end-point ("address" for the client) + * \param[in] type selects libqb's IPC back-end (or use #QB_IPC_NATIVE) + * \param[in] callbacks defines libqb's IPC service-level handlers + * \param[in] priority priority relative to other events handled in the + * abstract handling loop, use #QB_LOOP_MED when unsure + * + * \return libqb's opaque handle to the created service abstraction + * + * \note For portability concerns, do not use this function if you keep + * \p priority as #QB_LOOP_MED, stick with #mainloop_add_ipc_server + * (with exactly such semantics) instead (once you link with this new + * symbol employed, you can't downgrade the library freely anymore). + * + * \note The intended effect will only get fully reflected when run-time + * linked to patched libqb: https://github.com/ClusterLabs/libqb/pull/352 + */ +qb_ipcs_service_t *mainloop_add_ipc_server_with_prio(const char *name, + enum qb_ipc_type type, + struct qb_ipcs_service_handlers *callbacks, + enum qb_loop_priority prio); + +void mainloop_del_ipc_server(qb_ipcs_service_t * server); + +mainloop_io_t *mainloop_add_ipc_client(const char *name, int priority, size_t max_size, + void *userdata, struct ipc_client_callbacks *callbacks); + +void mainloop_del_ipc_client(mainloop_io_t * client); + +crm_ipc_t *mainloop_get_ipc_client(mainloop_io_t * client); + +struct mainloop_fd_callbacks { + /*! + * \brief Dispatch function for mainloop file descriptor with data ready + * + * \param[in,out] userdata User data passed when creating mainloop source + * + * \return Negative value to remove source, anything else to keep it + */ + int (*dispatch) (gpointer userdata); + + /*! + * \brief Destroy function for mainloop file descriptor client data + * + * \param[in,out] userdata User data passed when creating mainloop source + */ + void (*destroy) (gpointer userdata); +}; + +mainloop_io_t *mainloop_add_fd(const char *name, int priority, int fd, void *userdata, + struct mainloop_fd_callbacks *callbacks); + +void mainloop_del_fd(mainloop_io_t * client); + +/* + * Create a new tracked process + * To track a process group, use -pid + */ +void mainloop_child_add(pid_t pid, + int timeout, + const char *desc, + void *userdata, + void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)); + +void mainloop_child_add_with_flags(pid_t pid, + int timeout, + const char *desc, + void *userdata, + enum mainloop_child_flags, + void (*callback) (mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)); + +void *mainloop_child_userdata(mainloop_child_t * child); +int mainloop_child_timeout(mainloop_child_t * child); +const char *mainloop_child_name(mainloop_child_t * child); + +pid_t mainloop_child_pid(mainloop_child_t * child); +void mainloop_clear_child_userdata(mainloop_child_t * child); +gboolean mainloop_child_kill(pid_t pid); + +void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n); +void pcmk_drain_main_loop(GMainLoop *mloop, guint timer_ms, + bool (*check)(guint)); + +# define G_PRIORITY_MEDIUM (G_PRIORITY_HIGH/2) + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/common/mainloop_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/mainloop_compat.h b/include/crm/common/mainloop_compat.h new file mode 100644 index 0000000..5eb445a --- /dev/null +++ b/include/crm/common/mainloop_compat.h @@ -0,0 +1,36 @@ +/* + * Copyright 2009-2021 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 PCMK__CRM_COMMON_MAINLOOP_COMPAT__H +# define PCMK__CRM_COMMON_MAINLOOP_COMPAT__H + +# include <glib.h> +# include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker mainloop API + * \ingroup core + * \deprecated Do not include this header directly. The main loop APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use crm_signal_handler() instead +gboolean crm_signal(int sig, void (*dispatch) (int sig)); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_MAINLOOP_COMPAT__H diff --git a/include/crm/common/messages_internal.h b/include/crm/common/messages_internal.h new file mode 100644 index 0000000..0d1908f --- /dev/null +++ b/include/crm/common/messages_internal.h @@ -0,0 +1,122 @@ +/* + * Copyright 2018-2022 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 PCMK__CRM_COMMON_MESSAGES_INTERNAL__H +#define PCMK__CRM_COMMON_MESSAGES_INTERNAL__H + +#include <stdint.h> // uint32_t +#include <libxml/tree.h> // xmlNode +#include <crm/common/ipc_internal.h> // pcmk__client_t +#include <crm/common/results_internal.h> // pcmk__action_result_t + +enum pcmk__request_flags { + pcmk__request_none = UINT32_C(0), + + /* It would be nice if we could check for synchronous requests generically, + * but each daemon uses its own call options, so the daemons are responsible + * for setting this flag when appropriate. + */ + pcmk__request_sync = (UINT32_C(1) << 0), + + /* Whether reply must use original call options (the library code does not + * use this, so it is for internal daemon use) + */ + pcmk__request_reuse_options = (UINT32_C(1) << 1), +}; + +// Server request (whether from an IPC client or cluster peer) +typedef struct { + // If request is from an IPC client + pcmk__client_t *ipc_client; // IPC client (NULL if not via IPC) + uint32_t ipc_id; // IPC message ID + uint32_t ipc_flags; // IPC message flags + + // If message is from a cluster peer + const char *peer; // Peer name (NULL if not via cluster) + + // Common information regardless of origin + xmlNode *xml; // Request XML + int call_options; // Call options set on request + uint32_t flags; // Flag group of pcmk__request_flags + pcmk__action_result_t result; // Where to store operation result + + /* It would be nice if we could pull the IPC command from the XML + * generically, but each daemon uses a different XML attribute for it, + * so the daemon is responsible for populating this field. + * + * This must be a copy of the XML field, and not just a pointer into xml, + * because handlers might modify the original XML. + * + * @TODO Create a per-daemon struct with IPC handlers, IPC endpoints, etc., + * and the name of the XML attribute for IPC commands, then replace this + * with a convenience function to copy the command. + */ + char *op; // IPC command name +} pcmk__request_t; + +#define pcmk__set_request_flags(request, flags_to_set) do { \ + (request)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Request", "message", (request)->flags, \ + (flags_to_set), #flags_to_set); \ + } while (0) + +// Type for mapping a server command to a handler +typedef struct { + const char *command; + xmlNode *(*handler)(pcmk__request_t *request); +} pcmk__server_command_t; + +const char *pcmk__message_name(const char *name); +GHashTable *pcmk__register_handlers(const pcmk__server_command_t handlers[]); +xmlNode *pcmk__process_request(pcmk__request_t *request, GHashTable *handlers); +void pcmk__reset_request(pcmk__request_t *request); + +/*! + * \internal + * \brief Get a loggable description of a request's origin + * + * \param[in] request + * + * \return "peer" if request was via CPG, "client" if via IPC, or "originator" + * if unknown + */ +static inline const char * +pcmk__request_origin_type(const pcmk__request_t *request) +{ + if ((request != NULL) && (request->ipc_client != NULL)) { + return "client"; + } else if ((request != NULL) && (request->peer != NULL)) { + return "peer"; + } else { + return "originator"; + } +} + +/*! + * \internal + * \brief Get a loggable name for a request's origin + * + * \param[in] request + * + * \return Peer name if request was via CPG, client name if via IPC, or + * "(unspecified)" if unknown + */ +static inline const char * +pcmk__request_origin(const pcmk__request_t *request) +{ + if ((request != NULL) && (request->ipc_client != NULL)) { + return pcmk__client_name(request->ipc_client); + } else if ((request != NULL) && (request->peer != NULL)) { + return request->peer; + } else { + return "(unspecified)"; + } +} + +#endif // PCMK__CRM_COMMON_MESSAGES_INTERNAL__H diff --git a/include/crm/common/nvpair.h b/include/crm/common/nvpair.h new file mode 100644 index 0000000..aebc199 --- /dev/null +++ b/include/crm/common/nvpair.h @@ -0,0 +1,88 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_COMMON_NVPAIR__H +# define PCMK__CRM_COMMON_NVPAIR__H + +# include <sys/time.h> // struct timeval +# include <glib.h> // gpointer, gboolean, guint +# include <libxml/tree.h> // xmlNode +# include <crm/crm.h> + + +# ifdef __cplusplus +extern "C" { +# endif + +/** + * \file + * \brief Functionality for manipulating name/value pairs + * \ingroup core + */ + +typedef struct pcmk_nvpair_s { + char *name; + char *value; +} pcmk_nvpair_t; + +GSList *pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value); +void pcmk_free_nvpairs(GSList *nvpairs); +GSList *pcmk_sort_nvpairs(GSList *list); +GSList *pcmk_xml_attrs2nvpairs(const xmlNode *xml); +void pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml); + +xmlNode *crm_create_nvpair_xml(xmlNode *parent, const char *id, + const char *name, const char *value); +void hash2nvpair(gpointer key, gpointer value, gpointer user_data); +void hash2field(gpointer key, gpointer value, gpointer user_data); +void hash2metafield(gpointer key, gpointer value, gpointer user_data); +void hash2smartfield(gpointer key, gpointer value, gpointer user_data); +GHashTable *xml2list(const xmlNode *parent); + +const char *crm_xml_add(xmlNode *node, const char *name, const char *value); +const char *crm_xml_replace(xmlNode *node, const char *name, const char *value); +const char *crm_xml_add_int(xmlNode *node, const char *name, int value); +const char *crm_xml_add_ll(xmlNode *node, const char *name, long long value); +const char *crm_xml_add_ms(xmlNode *node, const char *name, guint ms); +const char *crm_xml_add_timeval(xmlNode *xml, const char *name_sec, + const char *name_usec, + const struct timeval *value); + +const char *crm_element_value(const xmlNode *data, const char *name); +int crm_element_value_int(const xmlNode *data, const char *name, int *dest); +int crm_element_value_ll(const xmlNode *data, const char *name, long long *dest); +int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest); +int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest); +int crm_element_value_timeval(const xmlNode *data, const char *name_sec, + const char *name_usec, struct timeval *dest); +char *crm_element_value_copy(const xmlNode *data, const char *name); + +/*! + * \brief Copy an element from one XML object to another + * + * \param[in] obj1 Source XML + * \param[in,out] obj2 Destination XML + * \param[in] element Name of element to copy + * + * \return Pointer to copied value (from source) + */ +static inline const char * +crm_copy_xml_element(const xmlNode *obj1, xmlNode *obj2, const char *element) +{ + const char *value = crm_element_value(obj1, element); + + crm_xml_add(obj2, element, value); + return value; +} + +# ifdef __cplusplus +} +# endif + +#endif // PCMK__CRM_COMMON_NVPAIR__H diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h new file mode 100644 index 0000000..4157b58 --- /dev/null +++ b/include/crm/common/options_internal.h @@ -0,0 +1,118 @@ +/* + * Copyright 2006-2022 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 PCMK__OPTIONS_INTERNAL__H +# define PCMK__OPTIONS_INTERNAL__H + +# ifndef PCMK__CONFIG_H +# define PCMK__CONFIG_H +# include <config.h> // _Noreturn +# endif + +# include <glib.h> // GHashTable +# include <stdbool.h> // bool + +_Noreturn void pcmk__cli_help(char cmd); + + +/* + * Environment variable option handling + */ + +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); + + +/* + * 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, + const pcmk__cluster_option_t *option_list, + int len, const char *name); + +gchar *pcmk__format_option_metadata(const char *name, 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_interval_spec(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_percentage(const char *value); + +// from watchdog.c +long pcmk__get_sbd_timeout(void); +bool pcmk__get_sbd_sync_resource_startup(void); +long pcmk__auto_watchdog_timeout(void); +bool pcmk__valid_sbd_timeout(const char *value); + +// Constants for environment variable names +#define PCMK__ENV_BLACKBOX "blackbox" +#define PCMK__ENV_CLUSTER_TYPE "cluster_type" +#define PCMK__ENV_DEBUG "debug" +#define PCMK__ENV_LOGFACILITY "logfacility" +#define PCMK__ENV_LOGFILE "logfile" +#define PCMK__ENV_LOGPRIORITY "logpriority" +#define PCMK__ENV_MCP "mcp" +#define PCMK__ENV_NODE_START_STATE "node_start_state" +#define PCMK__ENV_PHYSICAL_HOST "physical_host" +#define PCMK__ENV_QUORUM_TYPE "quorum_type" +#define PCMK__ENV_SHUTDOWN_DELAY "shutdown_delay" +#define PCMK__ENV_STDERR "stderr" + +// Constants for cluster option names +#define PCMK__OPT_NODE_HEALTH_BASE "node-health-base" +#define PCMK__OPT_NODE_HEALTH_GREEN "node-health-green" +#define PCMK__OPT_NODE_HEALTH_RED "node-health-red" +#define PCMK__OPT_NODE_HEALTH_STRATEGY "node-health-strategy" +#define PCMK__OPT_NODE_HEALTH_YELLOW "node-health-yellow" + +// Constants for meta-attribute names +#define PCMK__META_ALLOW_UNHEALTHY_NODES "allow-unhealthy-nodes" + +// Constants for enumerated values for various options +#define PCMK__VALUE_CLUSTER "cluster" +#define PCMK__VALUE_CUSTOM "custom" +#define PCMK__VALUE_FENCING "fencing" +#define PCMK__VALUE_GREEN "green" +#define PCMK__VALUE_LOCAL "local" +#define PCMK__VALUE_MIGRATE_ON_RED "migrate-on-red" +#define PCMK__VALUE_NONE "none" +#define PCMK__VALUE_NOTHING "nothing" +#define PCMK__VALUE_ONLY_GREEN "only-green" +#define PCMK__VALUE_PROGRESSIVE "progressive" +#define PCMK__VALUE_QUORUM "quorum" +#define PCMK__VALUE_RED "red" +#define PCMK__VALUE_UNFENCING "unfencing" +#define PCMK__VALUE_YELLOW "yellow" + +#endif // PCMK__OPTIONS_INTERNAL__H diff --git a/include/crm/common/output.h b/include/crm/common/output.h new file mode 100644 index 0000000..112ebcb --- /dev/null +++ b/include/crm/common/output.h @@ -0,0 +1,83 @@ +/* + * Copyright 2021-2023 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 PCMK__CRM_COMMON_OUTPUT__H +# define PCMK__CRM_COMMON_OUTPUT__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Control output from tools + * \ingroup core + */ + +/*! + * \brief Control which sections are output + */ +typedef enum { + pcmk_section_stack = 1 << 0, + pcmk_section_dc = 1 << 1, + pcmk_section_times = 1 << 2, + pcmk_section_counts = 1 << 3, + pcmk_section_options = 1 << 4, + pcmk_section_nodes = 1 << 5, + pcmk_section_resources = 1 << 6, + pcmk_section_attributes = 1 << 7, + pcmk_section_failcounts = 1 << 8, + pcmk_section_operations = 1 << 9, + pcmk_section_fence_failed = 1 << 10, + pcmk_section_fence_pending = 1 << 11, + pcmk_section_fence_worked = 1 << 12, + pcmk_section_tickets = 1 << 13, + pcmk_section_bans = 1 << 14, + pcmk_section_failures = 1 << 15, + pcmk_section_maint_mode = 1 << 16, +} pcmk_section_e; + +#define pcmk_section_fencing_all (pcmk_section_fence_failed | pcmk_section_fence_pending | pcmk_section_fence_worked) +#define pcmk_section_summary (pcmk_section_stack | pcmk_section_dc | pcmk_section_times | \ + pcmk_section_counts | pcmk_section_maint_mode) +#define pcmk_section_all (pcmk_section_summary | pcmk_section_options | pcmk_section_nodes | \ + pcmk_section_resources | pcmk_section_attributes | pcmk_section_failcounts | \ + pcmk_section_operations | pcmk_section_fencing_all | pcmk_section_tickets | \ + pcmk_section_bans | pcmk_section_failures | pcmk_section_maint_mode) + +/*! + * \brief Further modify the output of sections + */ +typedef enum { + pcmk_show_brief = 1 << 0, + pcmk_show_clone_detail = 1 << 1, + pcmk_show_node_id = 1 << 2, + pcmk_show_implicit_rscs = 1 << 3, + pcmk_show_timing = 1 << 4, + pcmk_show_inactive_rscs = 1 << 5, + pcmk_show_rscs_by_node = 1 << 6, + pcmk_show_pending = 1 << 7, + pcmk_show_rsc_only = 1 << 8, + pcmk_show_failed_detail = 1 << 9, + pcmk_show_feature_set = 1 << 10, + pcmk_show_description = 1 << 11, +} pcmk_show_opt_e; + +#define pcmk_show_details ((pcmk_show_clone_detail) \ + | (pcmk_show_node_id) \ + | (pcmk_show_implicit_rscs) \ + | (pcmk_show_failed_detail) \ + | (pcmk_show_feature_set) \ + | (pcmk_show_description)) + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_OUTPUT__H diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h new file mode 100644 index 0000000..e7b631e --- /dev/null +++ b/include/crm/common/output_internal.h @@ -0,0 +1,989 @@ +/* + * Copyright 2019-2023 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 PCMK__OUTPUT_INTERNAL__H +# define PCMK__OUTPUT_INTERNAL__H + +# include <stdbool.h> +# include <stdint.h> +# include <stdio.h> +# include <libxml/tree.h> +# include <libxml/HTMLtree.h> + +# include <glib.h> +# include <crm/common/results.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Formatted output for pacemaker tools + */ + +#if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS) +# define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS))) +#else +# define PCMK__OUTPUT_ARGS(ARGS...) +#endif + +typedef struct pcmk__output_s pcmk__output_t; + +/*! + * \internal + * \brief The type of a function that creates a ::pcmk__output_t. + * + * Instances of this type are passed to pcmk__register_format(), stored in an + * internal data structure, and later accessed by pcmk__output_new(). For + * examples, see pcmk__mk_xml_output() and pcmk__mk_text_output(). + * + * \param[in] argv The list of command line arguments. + */ +typedef pcmk__output_t * (*pcmk__output_factory_t)(char **argv); + +/*! + * \internal + * \brief The type of a custom message formatting function. + * + * These functions are defined by various libraries to support formatting of + * types aside from the basic types provided by a ::pcmk__output_t. + * + * The meaning of the return value will be different for each message. + * In general, however, 0 should be returned on success and a positive value + * on error. + * + * \param[in,out] out Output object to use to display message + * \param[in,out] args Message-specific arguments needed + * + * \note These functions must not call va_start or va_end - that is done + * automatically before the custom formatting function is called. + */ +typedef int (*pcmk__message_fn_t)(pcmk__output_t *out, va_list args); + +/*! + * \internal + * \brief Internal type for tracking custom messages. + * + * Each library can register functions that format custom message types. These + * are commonly used to handle some library-specific type. Registration is + * done by first defining a table of ::pcmk__message_entry_t structures and + * then passing that table to pcmk__register_messages(). Separate handlers + * can be defined for the same message, but for different formats (xml vs. + * text). Unknown formats will be ignored. + * + * Additionally, a "default" value for fmt_table can be used. In this case, + * fn will be registered for all supported formats. It is also possible to + * register a default and then override that registration with a format-specific + * function if necessary. + * + * \note The ::pcmk__message_entry_t table is processed in one pass, in order, + * from top to bottom. This means later entries with the same message_id will + * override previous ones. Thus, any default entry must come before any + * format-specific entries for the same message_id. + */ +typedef struct pcmk__message_entry_s { + /*! + * \brief The message to be handled. + * + * This must be the same ID that is passed to the message function of + * a ::pcmk__output_t. Unknown message IDs will be ignored. + */ + const char *message_id; + + /*! + * \brief The format type this handler is for. + * + * This name must match the fmt_name of the currently active formatter in + * order for the registered function to be called. It is valid to have + * multiple entries for the same message_id but with different fmt_name + * values. + */ + const char *fmt_name; + + /*! + * \brief The function to be called for message_id given a match on + * fmt_name. See comments on ::pcmk__message_fn_t. + */ + pcmk__message_fn_t fn; +} pcmk__message_entry_t; + +/*! + * \internal + * \brief This structure contains everything needed to add support for a + * single output formatter to a command line program. + */ +typedef struct pcmk__supported_format_s { + /*! + * \brief The name of this output formatter, which should match the + * fmt_name parameter in some ::pcmk__output_t structure. + */ + const char *name; + + /*! + * \brief A function that creates a ::pcmk__output_t. + */ + pcmk__output_factory_t create; + + /*! + * \brief Format-specific command line options. This can be NULL if + * no command line options should be supported. + */ + GOptionEntry *options; +} pcmk__supported_format_t; + +/* The following three blocks need to be updated each time a new base formatter + * is added. + */ + +extern GOptionEntry pcmk__html_output_entries[]; +extern GOptionEntry pcmk__log_output_entries[]; +extern GOptionEntry pcmk__none_output_entries[]; +extern GOptionEntry pcmk__text_output_entries[]; +extern GOptionEntry pcmk__xml_output_entries[]; + +pcmk__output_t *pcmk__mk_html_output(char **argv); +pcmk__output_t *pcmk__mk_log_output(char **argv); +pcmk__output_t *pcmk__mk_none_output(char **argv); +pcmk__output_t *pcmk__mk_text_output(char **argv); +pcmk__output_t *pcmk__mk_xml_output(char **argv); + +#define PCMK__SUPPORTED_FORMAT_HTML { "html", pcmk__mk_html_output, pcmk__html_output_entries } +#define PCMK__SUPPORTED_FORMAT_LOG { "log", pcmk__mk_log_output, pcmk__log_output_entries } +#define PCMK__SUPPORTED_FORMAT_NONE { PCMK__VALUE_NONE, pcmk__mk_none_output, \ + pcmk__none_output_entries } +#define PCMK__SUPPORTED_FORMAT_TEXT { "text", pcmk__mk_text_output, pcmk__text_output_entries } +#define PCMK__SUPPORTED_FORMAT_XML { "xml", pcmk__mk_xml_output, pcmk__xml_output_entries } + +/*! + * \brief This structure contains everything that makes up a single output + * formatter. + * + * Instances of this structure may be created by calling pcmk__output_new() + * with the name of the desired formatter. They should later be freed with + * pcmk__output_free(). + */ +struct pcmk__output_s { + /*! + * \brief The name of this output formatter. + */ + const char *fmt_name; + + /*! + * \brief Should this formatter supress most output? + * + * \note This setting is not respected by all formatters. In general, + * machine-readable output formats will not support this while + * user-oriented formats will. Callers should use is_quiet() + * to test whether to print or not. + */ + bool quiet; + + /*! + * \brief A copy of the request that generated this output. + * + * In the case of command line usage, this would be the command line + * arguments. For other use cases, it could be different. + */ + gchar *request; + + /*! + * \brief Where output should be written. + * + * This could be a file handle, or stdout or stderr. This is really only + * useful internally. + */ + FILE *dest; + + /*! + * \brief Custom messages that are currently registered on this formatter. + * + * Keys are the string message IDs, values are ::pcmk__message_fn_t function + * pointers. + */ + GHashTable *messages; + + /*! + * \brief Implementation-specific private data. + * + * Each individual formatter may have some private data useful in its + * implementation. This points to that data. Callers should not rely on + * its contents or structure. + */ + void *priv; + + /*! + * \internal + * \brief Take whatever actions are necessary to prepare out for use. This is + * called by pcmk__output_new(). End users should not need to call this. + * + * \note For formatted output implementers - This function should be written in + * such a way that it can be called repeatedly on an already initialized + * object without causing problems, or on a previously finished object + * without crashing. + * + * \param[in,out] out The output functions structure. + * + * \return true on success, false on error. + */ + bool (*init) (pcmk__output_t *out); + + /*! + * \internal + * \brief Free the private formatter-specific data. + * + * This is called from pcmk__output_free() and does not typically need to be + * called directly. + * + * \param[in,out] out The output functions structure. + */ + void (*free_priv) (pcmk__output_t *out); + + /*! + * \internal + * \brief Take whatever actions are necessary to end formatted output. + * + * This could include flushing output to a file, but does not include freeing + * anything. The finish method can potentially be fairly complicated, adding + * additional information to the internal data structures or doing whatever + * else. It is therefore suggested that finish only be called once. + * + * \note The print parameter will only affect those formatters that do all + * their output at the end. Console-oriented formatters typically print + * a line at a time as they go, so this parameter will not affect them. + * Structured formatters will honor it, however. + * + * \note The copy_dest parameter does not apply to all formatters. Console- + * oriented formatters do not build up a structure as they go, and thus + * do not have anything to return. Structured formatters will honor it, + * however. Note that each type of formatter will return a different + * type of value in this parameter. To use this parameter, call this + * function like so: + * + * \code + * xmlNode *dest = NULL; + * out->finish(out, exit_code, false, (void **) &dest); + * \endcode + * + * \param[in,out] out The output functions structure. + * \param[in] exit_status The exit value of the whole program. + * \param[in] print Whether this function should write any output. + * \param[out] copy_dest A destination to store a copy of the internal + * data structure for this output, or NULL if no + * copy is required. The caller should free this + * memory when done with it. + */ + void (*finish) (pcmk__output_t *out, crm_exit_t exit_status, bool print, + void **copy_dest); + + /*! + * \internal + * \brief Finalize output and then immediately set back up to start a new set + * of output. + * + * This is conceptually the same as calling finish and then init, though in + * practice more be happening behind the scenes. + * + * \note This function differs from finish in that no exit_status is added. + * The idea is that the program is not shutting down, so there is not + * yet a final exit code. Call finish on the last time through if this + * is needed. + * + * \param[in,out] out The output functions structure. + */ + void (*reset) (pcmk__output_t *out); + + /*! + * \internal + * \brief Register a custom message. + * + * \param[in,out] out The output functions structure. + * \param[in] message_id The name of the message to register. This name + * will be used as the message_id parameter to the + * message function in order to call the custom + * format function. + * \param[in] fn The custom format function to call for message_id. + */ + void (*register_message) (pcmk__output_t *out, const char *message_id, + pcmk__message_fn_t fn); + + /*! + * \internal + * \brief Call a previously registered custom message. + * + * \param[in,out] out The output functions structure. + * \param[in] message_id The name of the message to call. This name must + * be the same as the message_id parameter of some + * previous call to register_message. + * \param[in] ... Arguments to be passed to the registered function. + * + * \return A standard Pacemaker return code. Generally: 0 if a function was + * registered for the message, that function was called, and returned + * successfully; EINVAL if no function was registered; or pcmk_rc_no_output + * if a function was called but produced no output. + */ + int (*message) (pcmk__output_t *out, const char *message_id, ...); + + /*! + * \internal + * \brief Format the output of a completed subprocess. + * + * \param[in,out] out The output functions structure. + * \param[in] exit_status The exit value of the subprocess. + * \param[in] proc_stdout stdout from the completed subprocess. + * \param[in] proc_stderr stderr from the completed subprocess. + */ + void (*subprocess_output) (pcmk__output_t *out, int exit_status, + const char *proc_stdout, const char *proc_stderr); + + /*! + * \internal + * \brief Format version information. This is useful for the --version + * argument of command line tools. + * + * \param[in,out] out The output functions structure. + * \param[in] extended Add additional version information. + */ + void (*version) (pcmk__output_t *out, bool extended); + + /*! + * \internal + * \brief Format an informational message that should be shown to + * to an interactive user. Not all formatters will do this. + * + * \note A newline will automatically be added to the end of the format + * string, so callers should not include a newline. + * + * \note It is possible for a formatter that supports this method to + * still not print anything out if is_quiet returns true. + * + * \param[in,out] out The output functions structure. + * \param[in] buf The message to be printed. + * \param[in] ... Arguments to be formatted. + * + * \return A standard Pacemaker return code. Generally: pcmk_rc_ok + * if output was produced and pcmk_rc_no_output if it was not. + * As not all formatters implement this function, those that + * do not will always just return pcmk_rc_no_output. + */ + int (*info) (pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3); + + /*! + * \internal + * \brief Like \p info() but for messages that should appear only + * transiently. Not all formatters will do this. + * + * The originally envisioned use case is for console output, where a + * transient status-related message may be quickly overwritten by a refresh. + * + * \param[in,out] out The output functions structure. + * \param[in] format The format string of the message to be printed. + * \param[in] ... Arguments to be formatted. + * + * \return A standard Pacemaker return code. Generally: \p pcmk_rc_ok if + * output was produced and \p pcmk_rc_no_output if it was not. As + * not all formatters implement this function, those that do not + * will always just return \p pcmk_rc_no_output. + */ + int (*transient) (pcmk__output_t *out, const char *format, ...) + G_GNUC_PRINTF(2, 3); + + /*! + * \internal + * \brief Format an error message that should be shown to an interactive + * user. Not all formatters will do this. + * + * \note A newline will automatically be added to the end of the format + * string, so callers should not include a newline. + * + * \note Formatters that support this method should always generate output, + * even if is_quiet returns true. + * + * \param[in,out] out The output functions structure. + * \param[in] buf The message to be printed. + * \param[in] ... Arguments to be formatted. + */ + void (*err) (pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3); + + /*! + * \internal + * \brief Format already formatted XML. + * + * \param[in,out] out The output functions structure. + * \param[in] name A name to associate with the XML. + * \param[in] buf The XML in a string. + */ + void (*output_xml) (pcmk__output_t *out, const char *name, const char *buf); + + /*! + * \internal + * \brief Start a new list of items. + * + * \note For text output, this corresponds to another level of indentation. For + * XML output, this corresponds to wrapping any following output in another + * layer of tags. + * + * \note If singular_noun and plural_noun are non-NULL, calling end_list will + * result in a summary being added. + * + * \param[in,out] out The output functions structure. + * \param[in] singular_noun When outputting the summary for a list with + * one item, the noun to use. + * \param[in] plural_noun When outputting the summary for a list with + * more than one item, the noun to use. + * \param[in] format The format string. + * \param[in] ... Arguments to be formatted. + */ + void (*begin_list) (pcmk__output_t *out, const char *singular_noun, + const char *plural_noun, const char *format, ...) + G_GNUC_PRINTF(4, 5); + + /*! + * \internal + * \brief Format a single item in a list. + * + * \param[in,out] out The output functions structure. + * \param[in] name A name to associate with this item. + * \param[in] format The format string. + * \param[in] ... Arguments to be formatted. + */ + void (*list_item) (pcmk__output_t *out, const char *name, const char *format, ...) + G_GNUC_PRINTF(3, 4); + + /*! + * \internal + * \brief Increment the internal counter of the current list's length. + * + * Typically, this counter is maintained behind the scenes as a side effect + * of calling list_item(). However, custom functions that maintain lists + * some other way will need to manage this counter manually. This is + * useful for implementing custom message functions and should not be + * needed otherwise. + * + * \param[in,out] out The output functions structure. + */ + void (*increment_list) (pcmk__output_t *out); + + /*! + * \internal + * \brief Conclude a list. + * + * \note If begin_list was called with non-NULL for both the singular_noun + * and plural_noun arguments, this function will output a summary. + * Otherwise, no summary will be added. + * + * \param[in,out] out The output functions structure. + */ + void (*end_list) (pcmk__output_t *out); + + /*! + * \internal + * \brief Should anything be printed to the user? + * + * \note This takes into account both the \p quiet value as well as the + * current formatter. + * + * \param[in,out] out The output functions structure. + * + * \return true if output should be supressed, false otherwise. + */ + bool (*is_quiet) (pcmk__output_t *out); + + /*! + * \internal + * \brief Output a spacer. Not all formatters will do this. + * + * \param[in,out] out The output functions structure. + */ + void (*spacer) (pcmk__output_t *out); + + /*! + * \internal + * \brief Output a progress indicator. This is likely only useful for + * plain text, console based formatters. + * + * \param[in,out] out The output functions structure + * \param[in] end If true, output a newline afterwards (this should + * only be used the last time this function is called) + * + */ + void (*progress) (pcmk__output_t *out, bool end); + + /*! + * \internal + * \brief Prompt the user for input. Not all formatters will do this. + * + * \note This function is part of pcmk__output_t, but unlike all other + * function it does not take that as an argument. In general, a + * prompt will go directly to the screen and therefore bypass any + * need to use the formatted output code to decide where and how + * to display. + * + * \param[in] prompt The prompt to display. This is required. + * \param[in] echo If true, echo the user's input to the screen. Set + * to false for password entry. + * \param[out] dest Where to store the user's response. This is + * required. + */ + void (*prompt) (const char *prompt, bool echo, char **dest); +}; + +/*! + * \internal + * \brief Call a formatting function for a previously registered message. + * + * \note This function is for implementing custom formatters. It should not + * be called directly. Instead, call out->message. + * + * \param[in,out] out The output functions structure. + * \param[in] message_id The message to be handled. Unknown messages + * will be ignored. + * \param[in] ... Arguments to be passed to the registered function. + */ +int +pcmk__call_message(pcmk__output_t *out, const char *message_id, ...); + +/*! + * \internal + * \brief Free a ::pcmk__output_t structure that was previously created by + * pcmk__output_new(). + * + * \note While the create and finish functions are designed in such a way that + * they can be called repeatedly, this function will completely free the + * memory of the object. Once this function has been called, producing + * more output requires starting over from pcmk__output_new(). + * + * \param[in,out] out The output structure. + */ +void pcmk__output_free(pcmk__output_t *out); + +/*! + * \internal + * \brief Create a new ::pcmk__output_t structure. + * + * This also registers message functions from libcrmcommon. + * + * \param[in,out] out The destination of the new ::pcmk__output_t. + * \param[in] fmt_name How should output be formatted? + * \param[in] filename Where should formatted output be written to? This + * can be a filename (which will be overwritten if it + * already exists), or NULL or "-" for stdout. For no + * output, pass a filename of "/dev/null". + * \param[in] argv The list of command line arguments. + * + * \return Standard Pacemaker return code + */ +int pcmk__output_new(pcmk__output_t **out, const char *fmt_name, + const char *filename, char **argv); + +/*! + * \internal + * \brief Register a new output formatter, making it available for use + * the same as a base formatter. + * + * \param[in,out] group A ::GOptionGroup that formatted output related command + * line arguments should be added to. This can be NULL + * for use outside of command line programs. + * \param[in] name The name of the format. This will be used to select a + * format from command line options and for displaying help. + * \param[in] create A function that creates a ::pcmk__output_t. + * \param[in] options Format-specific command line options. These will be + * added to the context. This argument can also be NULL. + * + * \return Standard Pacemaker return code + */ +int +pcmk__register_format(GOptionGroup *group, const char *name, + pcmk__output_factory_t create, + const GOptionEntry *options); + +/*! + * \internal + * \brief Register an entire table of output formatters at once. + * + * \param[in,out] group A ::GOptionGroup that formatted output related command + * line arguments should be added to. This can be NULL + * for use outside of command line programs. + * \param[in] table An array of ::pcmk__supported_format_t which should + * all be registered. This array must be NULL-terminated. + * + */ +void +pcmk__register_formats(GOptionGroup *group, + const pcmk__supported_format_t *table); + +/*! + * \internal + * \brief Unregister a previously registered table of custom formatting + * functions and destroy the internal data structures associated with them. + */ +void +pcmk__unregister_formats(void); + +/*! + * \internal + * \brief Register a function to handle a custom message. + * + * \note This function is for implementing custom formatters. It should not + * be called directly. Instead, call out->register_message. + * + * \param[in,out] out The output functions structure. + * \param[in] message_id The message to be handled. + * \param[in] fn The custom format function to call for message_id. + */ +void +pcmk__register_message(pcmk__output_t *out, const char *message_id, + pcmk__message_fn_t fn); + +/*! + * \internal + * \brief Register an entire table of custom formatting functions at once. + * + * This table can contain multiple formatting functions for the same message ID + * if they are for different format types. + * + * \param[in,out] out The output functions structure. + * \param[in] table An array of ::pcmk__message_entry_t values which should + * all be registered. This array must be NULL-terminated. + */ +void +pcmk__register_messages(pcmk__output_t *out, + const pcmk__message_entry_t *table); + +/* Functions that are useful for implementing custom message formatters */ + +/*! + * \internal + * \brief A printf-like function. + * + * This function writes to out->dest and indents the text to the current level + * of the text formatter's nesting. This function should be used when implementing + * custom message functions for the text output format. It should not be used + * for any other purpose. + * + * Typically, this function should be used instead of printf. + * + * \param[in,out] out The output functions structure. + * \param[in] format The format string. + * \param[in] ... Arguments to be passed to the format string. + */ +void +pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3); + +/*! + * \internal + * \brief A vprintf-like function. + * + * This function is like pcmk__indented_printf(), except it takes a va_list instead + * of a list of arguments. This function should be used when implementing custom + * functions for the text output format. It should not be used for any other purpose. + * + * Typically, this function should be used instead of vprintf. + * + * \param[in,out] out The output functions structure. + * \param[in] format The format string. + * \param[in] args A list of arguments to apply to the format string. + */ +void +pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0); + + +/*! + * \internal + * \brief A printf-like function. + * + * This function writes to out->dest without indenting the text. This function + * should be used when implementing custom message functions for the text output + * format. It should not be used for any other purpose. + * + * \param[in,out] out The output functions structure. + * \param[in] format The format string. + * \param[in] ... Arguments to be passed to the format string. + */ +void +pcmk__formatted_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3); + +/*! + * \internal + * \brief A vprintf-like function. + * + * This function is like pcmk__formatted_printf(), except it takes a va_list instead + * of a list of arguments. This function should be used when implementing custom + * message functions for the text output format. It should not be used for any + * other purpose. + * + * \param[in,out] out The output functions structure. + * \param[in] format The format string. + * \param[in] args A list of arguments to apply to the format string. + */ +void +pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0); + +/*! + * \internal + * \brief Prompt the user for input. + * + * \param[in] prompt The prompt to display + * \param[in] echo If true, echo the user's input to the screen. Set + * to false for password entry. + * \param[out] dest Where to store the user's response. + */ +void +pcmk__text_prompt(const char *prompt, bool echo, char **dest); + +/*! + * \internal + * \brief Get the log level used by the formatted output logger + * + * \param[in] out Output object + * + * \return Log level used by \p out + */ +uint8_t +pcmk__output_get_log_level(const pcmk__output_t *out); + +/*! + * \internal + * \brief Set the log level used by the formatted output logger. + * + * \param[in,out] out The output functions structure. + * \param[in] log_level The log level constant (LOG_INFO, LOG_ERR, etc.) + * to use. + * + * \note By default, LOG_INFO is used. + * \note Almost all formatted output messages will respect this setting. + * However, out->err will always log at LOG_ERR. + */ +void +pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level); + +/*! + * \internal + * \brief Create and return a new XML node with the given name, as a child of the + * current list parent. The new node is then added as the new list parent, + * meaning all subsequent nodes will be its children. This is used when + * implementing custom functions. + * + * \param[in,out] out The output functions structure. + * \param[in] name The name of the node to be created. + * \param[in] ... Name/value pairs to set as XML properties. + */ +xmlNodePtr +pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...) +G_GNUC_NULL_TERMINATED; + +/*! + * \internal + * \brief Add a copy of the given node as a child of the current list parent. + * This is used when implementing custom message functions. + * + * \param[in,out] out The output functions structure. + * \param[in] node An XML node to copy as a child. + */ +void +pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node); + +/*! + * \internal + * \brief Create and return a new XML node with the given name, as a child of the + * current list parent. This is used when implementing custom functions. + * + * \param[in,out] out The output functions structure. + * \param[in] name The name of the node to be created. + * \param[in] ... Name/value pairs to set as XML properties. + */ +xmlNodePtr +pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) +G_GNUC_NULL_TERMINATED; + +/*! + * \internal + * \brief Like pcmk__output_create_xml_node(), but add the given text content to the + * new node. + * + * \param[in,out] out The output functions structure. + * \param[in] name The name of the node to be created. + * \param[in] content The text content of the node. + */ +xmlNodePtr +pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content); + +/*! + * \internal + * \brief Push a parent XML node onto the stack. This is used when implementing + * custom message functions. + * + * The XML output formatter maintains an internal stack to keep track of which nodes + * are parents in order to build up the tree structure. This function can be used + * to temporarily push a new node onto the stack. After calling this function, any + * other formatting functions will have their nodes added as children of this new + * parent. + * + * \param[in,out] out The output functions structure + * \param[in] parent XML node to add + */ +void +pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent); + +/*! + * \internal + * \brief Pop a parent XML node onto the stack. This is used when implementing + * custom message functions. + * + * This function removes a parent node from the stack. See pcmk__xml_push_parent() + * for more details. + * + * \note Little checking is done with this function. Be sure you only pop parents + * that were previously pushed. In general, it is best to keep the code between + * push and pop simple. + * + * \param[in,out] out The output functions structure. + */ +void +pcmk__output_xml_pop_parent(pcmk__output_t *out); + +/*! + * \internal + * \brief Peek a parent XML node onto the stack. This is used when implementing + * custom message functions. + * + * This function peeks a parent node on stack. See pcmk__xml_push_parent() + * for more details. It has no side-effect and can be called for an empty stack. + * + * \note Little checking is done with this function. + * + * \param[in,out] out The output functions structure. + * + * \return NULL if stack is empty, otherwise the parent of the stack. + */ +xmlNodePtr +pcmk__output_xml_peek_parent(pcmk__output_t *out); + +/*! + * \internal + * \brief Create a new XML node consisting of the provided text inside an HTML + * element node of the given name. + * + * \param[in,out] out The output functions structure. + * \param[in] element_name The name of the new HTML element. + * \param[in] id The CSS ID selector to apply to this element. + * If NULL, no ID is added. + * \param[in] class_name The CSS class selector to apply to this element. + * If NULL, no class is added. + * \param[in] text The text content of the node. + */ +xmlNodePtr +pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id, + const char *class_name, const char *text); + +/*! + * \internal + * \brief Add an HTML tag to the <head> section. + * + * The arguments after name are a NULL-terminated list of keys and values, + * all of which will be added as attributes to the given tag. For instance, + * the following code would generate the tag "<meta http-equiv='refresh' content='19'>": + * + * \code + * pcmk__html_add_header("meta", "http-equiv", "refresh", "content", "19", NULL); + * \endcode + * + * \param[in] name The HTML tag for the new node. + * \param[in] ... A NULL-terminated key/value list of attributes. + */ +void +pcmk__html_add_header(const char *name, ...) +G_GNUC_NULL_TERMINATED; + +/*! + * \internal + * \brief Handle end-of-program error reporting + * + * \param[in,out] error A GError object potentially containing some error. + * If NULL, do nothing. + * \param[in,out] out The output functions structure. If NULL, any errors + * will simply be printed to stderr. + */ +void pcmk__output_and_clear_error(GError **error, pcmk__output_t *out); + +int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml); +void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml); +int pcmk__log_output_new(pcmk__output_t **out); +int pcmk__text_output_new(pcmk__output_t **out, const char *filename); + +/*! + * \internal + * \brief Select an updated return code for an operation on a \p pcmk__output_t + * + * This function helps to keep an up-to-date record of the most relevant return + * code from a series of operations on a \p pcmk__output_t object. For example, + * suppose the object has already produced some output, and we've saved a + * \p pcmk_rc_ok return code. A new operation did not produce any output and + * returned \p pcmk_rc_no_output. We can ignore the new \p pcmk_rc_no_output + * return code and keep the previous \p pcmk_rc_ok return code. + * + * It prioritizes return codes as follows (from highest to lowest priority): + * 1. Other return codes (unexpected errors) + * 2. \p pcmk_rc_ok + * 3. \p pcmk_rc_no_output + * + * \param[in] old_rc Saved return code from \p pcmk__output_t operations + * \param[in] new_rc New return code from a \p pcmk__output_t operation + * + * \retval \p old_rc \p new_rc is \p pcmk_rc_no_output, or \p new_rc is + * \p pcmk_rc_ok and \p old_rc is not \p pcmk_rc_no_output + * \retval \p new_rc Otherwise + */ +static inline int +pcmk__output_select_rc(int old_rc, int new_rc) +{ + switch (new_rc) { + case pcmk_rc_no_output: + return old_rc; + case pcmk_rc_ok: + switch (old_rc) { + case pcmk_rc_no_output: + return new_rc; + default: + return old_rc; + } + default: + return new_rc; + } +} + +#if defined(PCMK__UNIT_TESTING) +/* If we are building libcrmcommon_test.a, add this accessor function so we can + * inspect the internal formatters hash table. + */ +GHashTable *pcmk__output_formatters(void); +#endif + +#define PCMK__OUTPUT_SPACER_IF(out_obj, cond) \ + if (cond) { \ + out->spacer(out); \ + } + +#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...) \ + if (retcode == pcmk_rc_no_output) { \ + PCMK__OUTPUT_SPACER_IF(out_obj, cond); \ + retcode = pcmk_rc_ok; \ + out_obj->begin_list(out_obj, NULL, NULL, title); \ + } + +#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode) \ + if (retcode == pcmk_rc_ok) { \ + out_obj->end_list(out_obj); \ + } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/remote_internal.h b/include/crm/common/remote_internal.h new file mode 100644 index 0000000..8473668 --- /dev/null +++ b/include/crm/common/remote_internal.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008-2022 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 PCMK__REMOTE_INTERNAL__H +# define PCMK__REMOTE_INTERNAL__H + +// internal functions from remote.c + +typedef struct pcmk__remote_s pcmk__remote_t; + +int pcmk__remote_send_xml(pcmk__remote_t *remote, xmlNode *msg); +int pcmk__remote_ready(const pcmk__remote_t *remote, int timeout_ms); +int pcmk__read_remote_message(pcmk__remote_t *remote, int timeout_ms); +xmlNode *pcmk__remote_message_xml(pcmk__remote_t *remote); +int pcmk__connect_remote(const char *host, int port, int timeout_ms, + int *timer_id, int *sock_fd, void *userdata, + void (*callback) (void *userdata, int rc, int sock)); +int pcmk__accept_remote_connection(int ssock, int *csock); +void pcmk__sockaddr2str(const void *sa, char *s); + +# ifdef HAVE_GNUTLS_GNUTLS_H +# include <gnutls/gnutls.h> + +gnutls_session_t *pcmk__new_tls_session(int csock, unsigned int conn_type, + gnutls_credentials_type_t cred_type, + void *credentials); +int pcmk__init_tls_dh(gnutls_dh_params_t *dh_params); +int pcmk__read_handshake_data(const pcmk__client_t *client); + +/*! + * \internal + * \brief Perform client TLS handshake after establishing TCP socket + * + * \param[in,out] remote Newly established remote connection + * \param[in] timeout_ms Abort if handshake is not complete within this + * + * \return Standard Pacemaker return code + */ +int pcmk__tls_client_handshake(pcmk__remote_t *remote, int timeout_ms); + +# endif // HAVE_GNUTLS_GNUTLS_H +#endif // PCMK__REMOTE_INTERNAL__H diff --git a/include/crm/common/results.h b/include/crm/common/results.h new file mode 100644 index 0000000..224bcbe --- /dev/null +++ b/include/crm/common/results.h @@ -0,0 +1,393 @@ +/* + * Copyright 2012-2023 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 PCMK__CRM_COMMON_RESULTS__H +# define PCMK__CRM_COMMON_RESULTS__H + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief Function and executable result codes + * \ingroup core + */ + +// Lifted from config.h +/* The _Noreturn keyword of C11. */ +#ifndef _Noreturn +# if (defined __cplusplus \ + && ((201103 <= __cplusplus && !(__GNUC__ == 4 && __GNUC_MINOR__ == 7)) \ + || (defined _MSC_VER && 1900 <= _MSC_VER))) +# define _Noreturn [[noreturn]] +# elif ((!defined __cplusplus || defined __clang__) \ + && (201112 <= (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) \ + || 4 < __GNUC__ + (7 <= __GNUC_MINOR__))) + /* _Noreturn works as-is. */ +# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__) || 0x5110 <= __SUNPRO_C +# define _Noreturn __attribute__ ((__noreturn__)) +# elif 1200 <= (defined _MSC_VER ? _MSC_VER : 0) +# define _Noreturn __declspec (noreturn) +# else +# define _Noreturn +# endif +#endif + +# define CRM_ASSERT(expr) do { \ + if (!(expr)) { \ + crm_abort(__FILE__, __func__, __LINE__, #expr, TRUE, FALSE); \ + abort(); /* crm_abort() doesn't always abort! */ \ + } \ + } while(0) + +/* + * Function return codes + * + * Most Pacemaker API functions return an integer return code. There are two + * alternative interpretations. The legacy interpration is that the absolute + * value of the return code is either a system error number or a custom + * pcmk_err_* number. This is less than ideal because system error numbers are + * constrained only to the positive int range, so there's the possibility that + * system errors and custom errors could collide (which did in fact happen + * already on one architecture). The new intepretation is that negative values + * are from the pcmk_rc_e enum, and positive values are system error numbers. + * Both use 0 for success. + * + * For system error codes, see: + * - /usr/include/asm-generic/errno.h + * - /usr/include/asm-generic/errno-base.h + */ + +// Legacy custom return codes for Pacemaker API functions (deprecated) +# define pcmk_ok 0 +# define PCMK_ERROR_OFFSET 190 /* Replacements on non-linux systems, see include/portability.h */ +# define PCMK_CUSTOM_OFFSET 200 /* Purely custom codes */ +# define pcmk_err_generic 201 +# define pcmk_err_no_quorum 202 +# define pcmk_err_schema_validation 203 +# define pcmk_err_transform_failed 204 +# define pcmk_err_old_data 205 +# define pcmk_err_diff_failed 206 +# define pcmk_err_diff_resync 207 +# define pcmk_err_cib_modified 208 +# define pcmk_err_cib_backup 209 +# define pcmk_err_cib_save 210 +# define pcmk_err_schema_unchanged 211 +# define pcmk_err_cib_corrupt 212 +# define pcmk_err_multiple 213 +# define pcmk_err_node_unknown 214 +# define pcmk_err_already 215 +/* On HPPA 215 is ENOSYM (Unknown error 215), which hopefully never happens. */ +#ifdef __hppa__ +# define pcmk_err_bad_nvpair 250 /* 216 is ENOTSOCK */ +# define pcmk_err_unknown_format 252 /* 217 is EDESTADDRREQ */ +#else +# define pcmk_err_bad_nvpair 216 +# define pcmk_err_unknown_format 217 +#endif + +/*! + * \enum pcmk_rc_e + * \brief Return codes for Pacemaker API functions + * + * Any Pacemaker API function documented as returning a "standard Pacemaker + * return code" will return pcmk_rc_ok (0) on success, and one of this + * enumeration's other (negative) values or a (positive) system error number + * otherwise. The custom codes are at -1001 and lower, so that the caller may + * use -1 through -1000 for their own custom values if desired. While generally + * referred to as "errors", nonzero values simply indicate a result, which might + * or might not be an error depending on the calling context. + */ +enum pcmk_rc_e { + /* When adding new values, use consecutively lower numbers, update the array + * in lib/common/results.c, and test with crm_error. + */ + pcmk_rc_bad_xml_patch = -1036, + pcmk_rc_bad_input = -1035, + pcmk_rc_disabled = -1034, + pcmk_rc_duplicate_id = -1033, + pcmk_rc_unpack_error = -1032, + pcmk_rc_invalid_transition = -1031, + pcmk_rc_graph_error = -1030, + pcmk_rc_dot_error = -1029, + pcmk_rc_underflow = -1028, + pcmk_rc_no_input = -1027, + pcmk_rc_no_output = -1026, + pcmk_rc_after_range = -1025, + pcmk_rc_within_range = -1024, + pcmk_rc_before_range = -1023, + pcmk_rc_undetermined = -1022, + pcmk_rc_op_unsatisfied = -1021, + pcmk_rc_ipc_pid_only = -1020, + pcmk_rc_ipc_unresponsive = -1019, + pcmk_rc_ipc_unauthorized = -1018, + pcmk_rc_no_quorum = -1017, + pcmk_rc_schema_validation = -1016, + pcmk_rc_schema_unchanged = -1015, + pcmk_rc_transform_failed = -1014, + pcmk_rc_old_data = -1013, + pcmk_rc_diff_failed = -1012, + pcmk_rc_diff_resync = -1011, + pcmk_rc_cib_modified = -1010, + pcmk_rc_cib_backup = -1009, + pcmk_rc_cib_save = -1008, + pcmk_rc_cib_corrupt = -1007, + pcmk_rc_multiple = -1006, + pcmk_rc_node_unknown = -1005, + pcmk_rc_already = -1004, + pcmk_rc_bad_nvpair = -1003, + pcmk_rc_unknown_format = -1002, + // Developers: Use a more specific code than pcmk_rc_error whenever possible + pcmk_rc_error = -1001, + + // Values -1 through -1000 reserved for caller use + + pcmk_rc_ok = 0 + + // Positive values reserved for system error numbers +}; + + +/*! + * \enum ocf_exitcode + * \brief Exit status codes for resource agents + * + * The OCF Resource Agent API standard enumerates the possible exit status codes + * that agents should return. Besides being used with OCF agents, these values + * are also used by the executor as a universal status for all agent standards; + * actual results are mapped to these before returning them to clients. + */ +enum ocf_exitcode { + PCMK_OCF_OK = 0, //!< Success + PCMK_OCF_UNKNOWN_ERROR = 1, //!< Unspecified error + PCMK_OCF_INVALID_PARAM = 2, //!< Parameter invalid (in local context) + PCMK_OCF_UNIMPLEMENT_FEATURE = 3, //!< Requested action not implemented + PCMK_OCF_INSUFFICIENT_PRIV = 4, //!< Insufficient privileges + PCMK_OCF_NOT_INSTALLED = 5, //!< Dependencies not available locally + PCMK_OCF_NOT_CONFIGURED = 6, //!< Parameter invalid (inherently) + PCMK_OCF_NOT_RUNNING = 7, //!< Service safely stopped + PCMK_OCF_RUNNING_PROMOTED = 8, //!< Service active and promoted + PCMK_OCF_FAILED_PROMOTED = 9, //!< Service failed and possibly in promoted role + PCMK_OCF_DEGRADED = 190, //!< Service active but more likely to fail soon + PCMK_OCF_DEGRADED_PROMOTED = 191, //!< Service promoted but more likely to fail soon + + /* These two are Pacemaker extensions, not in the OCF standard. The + * controller records PCMK_OCF_UNKNOWN for pending actions. + * PCMK_OCF_CONNECTION_DIED is used only with older DCs that don't support + * PCMK_EXEC_NOT_CONNECTED. + */ + PCMK_OCF_CONNECTION_DIED = 189, //!< \deprecated See PCMK_EXEC_NOT_CONNECTED + PCMK_OCF_UNKNOWN = 193, //!< Action is pending + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + // Former Pacemaker extensions + PCMK_OCF_EXEC_ERROR = 192, //!< \deprecated (Unused) + PCMK_OCF_SIGNAL = 194, //!< \deprecated (Unused) + PCMK_OCF_NOT_SUPPORTED = 195, //!< \deprecated (Unused) + PCMK_OCF_PENDING = 196, //!< \deprecated (Unused) + PCMK_OCF_CANCELLED = 197, //!< \deprecated (Unused) + PCMK_OCF_TIMEOUT = 198, //!< \deprecated (Unused) + PCMK_OCF_OTHER_ERROR = 199, //!< \deprecated (Unused) + + //! \deprecated Use PCMK_OCF_RUNNING_PROMOTED instead + PCMK_OCF_RUNNING_MASTER = PCMK_OCF_RUNNING_PROMOTED, + + //! \deprecated Use PCMK_OCF_FAILED_PROMOTED instead + PCMK_OCF_FAILED_MASTER = PCMK_OCF_FAILED_PROMOTED, + + //! \deprecated Use PCMK_OCF_DEGRADED_PROMOTED instead + PCMK_OCF_DEGRADED_MASTER = PCMK_OCF_DEGRADED_PROMOTED, +#endif +}; + +/*! + * \enum crm_exit_e + * \brief Exit status codes for tools and daemons + * + * We want well-specified (i.e. OS-invariant) exit status codes for our daemons + * and applications so they can be relied on by callers. (Function return codes + * and errno's do not make good exit statuses.) + * + * The only hard rule is that exit statuses must be between 0 and 255; all else + * is convention. Universally, 0 is success, and 1 is generic error (excluding + * OSes we don't support -- for example, OpenVMS considers 1 success!). + * + * For init scripts, the LSB gives meaning to 0-7, and sets aside 150-199 for + * application use. OCF adds 8-9 and 190-191. + * + * sysexits.h was an attempt to give additional meanings, but never really + * caught on. It uses 0 and 64-78. + * + * Bash reserves 2 ("incorrect builtin usage") and 126-255 (126 is "command + * found but not executable", 127 is "command not found", 128 + n is + * "interrupted by signal n"). + * + * tldp.org recommends 64-113 for application use. + * + * We try to overlap with the above conventions when practical. + */ +typedef enum crm_exit_e { + // Common convention + CRM_EX_OK = 0, //!< Success + CRM_EX_ERROR = 1, //!< Unspecified error + + // LSB + OCF + CRM_EX_INVALID_PARAM = 2, //!< Parameter invalid (in local context) + CRM_EX_UNIMPLEMENT_FEATURE = 3, //!< Requested action not implemented + CRM_EX_INSUFFICIENT_PRIV = 4, //!< Insufficient privileges + CRM_EX_NOT_INSTALLED = 5, //!< Dependencies not available locally + CRM_EX_NOT_CONFIGURED = 6, //!< Parameter invalid (inherently) + CRM_EX_NOT_RUNNING = 7, //!< Service safely stopped + CRM_EX_PROMOTED = 8, //!< Service active and promoted + CRM_EX_FAILED_PROMOTED = 9, //!< Service failed and possibly promoted + + // sysexits.h + CRM_EX_USAGE = 64, //!< Command line usage error + CRM_EX_DATAERR = 65, //!< User-supplied data incorrect + CRM_EX_NOINPUT = 66, //!< Input file not available + CRM_EX_NOUSER = 67, //!< User does not exist + CRM_EX_NOHOST = 68, //!< Host unknown + CRM_EX_UNAVAILABLE = 69, //!< Needed service unavailable + CRM_EX_SOFTWARE = 70, //!< Internal software bug + CRM_EX_OSERR = 71, //!< External (OS/environmental) problem + CRM_EX_OSFILE = 72, //!< System file not usable + CRM_EX_CANTCREAT = 73, //!< File couldn't be created + CRM_EX_IOERR = 74, //!< File I/O error + CRM_EX_TEMPFAIL = 75, //!< Try again + CRM_EX_PROTOCOL = 76, //!< Protocol violated + CRM_EX_NOPERM = 77, //!< Non-file permission issue + CRM_EX_CONFIG = 78, //!< Misconfiguration + + // Custom + CRM_EX_FATAL = 100, //!< Do not respawn + CRM_EX_PANIC = 101, //!< Panic the local host + CRM_EX_DISCONNECT = 102, //!< Lost connection to something + CRM_EX_OLD = 103, //!< Update older than existing config + CRM_EX_DIGEST = 104, //!< Digest comparison failed + CRM_EX_NOSUCH = 105, //!< Requested item does not exist + CRM_EX_QUORUM = 106, //!< Local partition does not have quorum + CRM_EX_UNSAFE = 107, //!< Requires --force or new conditions + CRM_EX_EXISTS = 108, //!< Requested item already exists + CRM_EX_MULTIPLE = 109, //!< Requested item has multiple matches + CRM_EX_EXPIRED = 110, //!< Requested item has expired + CRM_EX_NOT_YET_IN_EFFECT = 111, //!< Requested item is not in effect + CRM_EX_INDETERMINATE = 112, //!< Could not determine status + CRM_EX_UNSATISFIED = 113, //!< Requested item does not satisfy constraints + + // Other + CRM_EX_TIMEOUT = 124, //!< Convention from timeout(1) + + /* Anything above 128 overlaps with some shells' use of these values for + * "interrupted by signal N", and so may be unreliable when detected by + * shell scripts. + */ + + // OCF Resource Agent API 1.1 + CRM_EX_DEGRADED = 190, //!< Service active but more likely to fail soon + CRM_EX_DEGRADED_PROMOTED = 191, //!< Service promoted but more likely to fail soon + + /* Custom + * + * This can be used to initialize exit status variables or to indicate that + * a command is pending (which is what the controller uses it for). + */ + CRM_EX_NONE = 193, //!< No exit status available + + CRM_EX_MAX = 255, //!< Ensure crm_exit_t can hold this +} crm_exit_t; + +/*! + * \enum pcmk_exec_status + * \brief Execution status + * + * These codes are used to specify the result of the attempt to execute an + * agent, rather than the agent's result itself. + */ +enum pcmk_exec_status { + PCMK_EXEC_UNKNOWN = -2, //!< Used only to initialize variables + PCMK_EXEC_PENDING = -1, //!< Action is in progress + PCMK_EXEC_DONE, //!< Action completed, result is known + PCMK_EXEC_CANCELLED, //!< Action was cancelled + PCMK_EXEC_TIMEOUT, //!< Action did not complete in time + PCMK_EXEC_NOT_SUPPORTED, //!< Agent does not implement requested action + PCMK_EXEC_ERROR, //!< Execution failed, may be retried + PCMK_EXEC_ERROR_HARD, //!< Execution failed, do not retry on node + PCMK_EXEC_ERROR_FATAL, //!< Execution failed, do not retry anywhere + PCMK_EXEC_NOT_INSTALLED, //!< Agent or dependency not available locally + PCMK_EXEC_NOT_CONNECTED, //!< No connection to executor + PCMK_EXEC_INVALID, //!< Action cannot be attempted (e.g. shutdown) + PCMK_EXEC_NO_FENCE_DEVICE, //!< No fence device is configured for target + PCMK_EXEC_NO_SECRETS, //!< Necessary CIB secrets are unavailable + + // Add new values above here then update this one below + PCMK_EXEC_MAX = PCMK_EXEC_NO_SECRETS, //!< Maximum value for this enum +}; + +/*! + * \enum pcmk_result_type + * \brief Types of Pacemaker result codes + * + * A particular integer can have different meanings within different Pacemaker + * result code families. It may be interpretable within zero, one, or multiple + * families. + * + * These values are useful for specifying how an integer result code should be + * interpreted in situations involving a generic integer value. For example, a + * function that can process multiple types of result codes might accept an + * arbitrary integer argument along with a \p pcmk_result_type argument that + * specifies how to interpret the integer. + */ +enum pcmk_result_type { + pcmk_result_legacy = 0, //!< Legacy API function return code + pcmk_result_rc = 1, //!< Standard Pacemaker return code + pcmk_result_exitcode = 2, //!< Exit status code + pcmk_result_exec_status = 3, //!< Execution status +}; + +int pcmk_result_get_strings(int code, enum pcmk_result_type type, + const char **name, const char **desc); +const char *pcmk_rc_name(int rc); +const char *pcmk_rc_str(int rc); +crm_exit_t pcmk_rc2exitc(int rc); +enum ocf_exitcode pcmk_rc2ocf(int rc); +int pcmk_rc2legacy(int rc); +int pcmk_legacy2rc(int legacy_rc); +const char *pcmk_strerror(int rc); +const char *pcmk_errorname(int rc); +const char *bz2_strerror(int rc); +const char *crm_exit_name(crm_exit_t exit_code); +const char *crm_exit_str(crm_exit_t exit_code); +_Noreturn crm_exit_t crm_exit(crm_exit_t rc); + +static inline const char * +pcmk_exec_status_str(enum pcmk_exec_status status) +{ + switch (status) { + case PCMK_EXEC_PENDING: return "pending"; + case PCMK_EXEC_DONE: return "complete"; + case PCMK_EXEC_CANCELLED: return "Cancelled"; + case PCMK_EXEC_TIMEOUT: return "Timed Out"; + case PCMK_EXEC_NOT_SUPPORTED: return "NOT SUPPORTED"; + case PCMK_EXEC_ERROR: return "Error"; + case PCMK_EXEC_ERROR_HARD: return "Hard error"; + case PCMK_EXEC_ERROR_FATAL: return "Fatal error"; + case PCMK_EXEC_NOT_INSTALLED: return "Not installed"; + case PCMK_EXEC_NOT_CONNECTED: return "Internal communication failure"; + case PCMK_EXEC_INVALID: return "Cannot execute now"; + case PCMK_EXEC_NO_FENCE_DEVICE: return "No fence device"; + case PCMK_EXEC_NO_SECRETS: return "CIB secrets unavailable"; + default: return "UNKNOWN!"; + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/results_compat.h b/include/crm/common/results_compat.h new file mode 100644 index 0000000..00ac6b2 --- /dev/null +++ b/include/crm/common/results_compat.h @@ -0,0 +1,35 @@ +/* + * Copyright 2022 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 PCMK__CRM_COMMON_RESULTS_COMPAT__H +# define PCMK__CRM_COMMON_RESULTS_COMPAT__H + +#include <crm/common/results.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker results API + * \ingroup core + * \deprecated Do not include this header directly. The result APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use pcmk_rc2exitc(pcmk_legacy2rc(rc)) instead +crm_exit_t crm_errno2exit(int rc); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_MAINLOOP_COMPAT__H diff --git a/include/crm/common/results_internal.h b/include/crm/common/results_internal.h new file mode 100644 index 0000000..be62780 --- /dev/null +++ b/include/crm/common/results_internal.h @@ -0,0 +1,88 @@ +/* + * Copyright 2020-2022 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 PCMK__COMMON_RESULTS_INTERNAL__H +#define PCMK__COMMON_RESULTS_INTERNAL__H + +#include <glib.h> // GQuark + +// Generic result code type + +int pcmk__result_bounds(enum pcmk_result_type, int *lower, int *upper); + +// Standard Pacemaker API return codes + +extern const size_t pcmk__n_rc; + +/* Error domains for use with g_set_error */ + +GQuark pcmk__rc_error_quark(void); +GQuark pcmk__exitc_error_quark(void); + +#define PCMK__RC_ERROR pcmk__rc_error_quark() +#define PCMK__EXITC_ERROR pcmk__exitc_error_quark() + +/* Action results */ + +typedef struct { + int exit_status; // Child exit status + enum pcmk_exec_status execution_status; // Execution status + char *exit_reason; // Brief, human-friendly explanation + char *action_stdout; // Action output + char *action_stderr; // Action error output +} pcmk__action_result_t; + +/*! + * \internal + * \brief Static initialization for an action result + * + * \note Importantly, this ensures pcmk__reset_result() won't try to free + * garbage. + */ +#define PCMK__UNKNOWN_RESULT { \ + .exit_status = CRM_EX_OK, \ + .execution_status = PCMK_EXEC_UNKNOWN, \ + .exit_reason = NULL, \ + .action_stdout = NULL, \ + .action_stderr = NULL, \ + } + +void pcmk__set_result(pcmk__action_result_t *result, int exit_status, + enum pcmk_exec_status exec_status, + const char *exit_reason); + +void pcmk__format_result(pcmk__action_result_t *result, int exit_status, + enum pcmk_exec_status exec_status, + const char *format, ...) G_GNUC_PRINTF(4, 5); + +void pcmk__set_result_output(pcmk__action_result_t *result, + char *out, char *err); + +void pcmk__reset_result(pcmk__action_result_t *result); + +void pcmk__copy_result(const pcmk__action_result_t *src, + pcmk__action_result_t *dst); + +/*! + * \internal + * \brief Check whether a result is OK + * + * \param[in] result + * + * \return true if the result's exit status is CRM_EX_OK and its + * execution status is PCMK_EXEC_DONE, otherwise false + */ +static inline bool +pcmk__result_ok(const pcmk__action_result_t *result) +{ + return (result != NULL) && (result->exit_status == CRM_EX_OK) + && (result->execution_status == PCMK_EXEC_DONE); +} + +#endif // PCMK__COMMON_RESULTS_INTERNAL__H diff --git a/include/crm/common/strings_internal.h b/include/crm/common/strings_internal.h new file mode 100644 index 0000000..cd394d9 --- /dev/null +++ b/include/crm/common/strings_internal.h @@ -0,0 +1,214 @@ +/* + * Copyright 2015-2022 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 PCMK__STRINGS_INTERNAL__H +#define PCMK__STRINGS_INTERNAL__H + +#include <stdbool.h> // bool + +#include <glib.h> // guint, GList, GHashTable + +/* internal constants for generic string functions (from strings.c) */ + +#define PCMK__PARSE_INT_DEFAULT -1 +#define PCMK__PARSE_DBL_DEFAULT -1.0 + +/* internal generic string functions (from strings.c) */ + +enum pcmk__str_flags { + pcmk__str_none = 0, + pcmk__str_casei = 1 << 0, + pcmk__str_null_matches = 1 << 1, + pcmk__str_regex = 1 << 2, + pcmk__str_star_matches = 1 << 3, +}; + +int pcmk__scan_double(const char *text, double *result, + const char *default_text, char **end_text); +int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, + guint *result); +bool pcmk__starts_with(const char *str, const char *prefix); +bool pcmk__ends_with(const char *s, const char *match); +bool pcmk__ends_with_ext(const char *s, const char *match); +char *pcmk__trim(char *str); +void pcmk__add_separated_word(GString **list, size_t init_size, + const char *word, const char *separator); +int pcmk__compress(const char *data, unsigned int length, unsigned int max, + char **result, unsigned int *result_len); + +int pcmk__scan_ll(const char *text, long long *result, long long default_value); +int pcmk__scan_min_int(const char *text, int *result, int minimum); +int pcmk__scan_port(const char *text, int *port); +int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end); + +GHashTable *pcmk__strkey_table(GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func); +GHashTable *pcmk__strikey_table(GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func); +GHashTable *pcmk__str_table_dup(GHashTable *old_table); + +/*! + * \internal + * \brief Get a string value with a default if NULL + * + * \param[in] s String to return if non-NULL + * \param[in] default_value String (or NULL) to return if \p s is NULL + * + * \return \p s if \p s is non-NULL, otherwise \p default_value + */ +static inline const char * +pcmk__s(const char *s, const char *default_value) +{ + return (s == NULL)? default_value : s; +} + +/*! + * \internal + * \brief Create a hash table with integer keys + * + * \param[in] value_destroy_func Function to free a value + * + * \return Newly allocated hash table + * \note It is the caller's responsibility to free the result, using + * g_hash_table_destroy(). + */ +static inline GHashTable * +pcmk__intkey_table(GDestroyNotify value_destroy_func) +{ + return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + value_destroy_func); +} + +/*! + * \internal + * \brief Insert a value into a hash table with integer keys + * + * \param[in,out] hash_table Table to insert into + * \param[in] key Integer key to insert + * \param[in] value Value to insert + * + * \return Whether the key/value was already in the table + * \note This has the same semantics as g_hash_table_insert(). If the key + * already exists in the table, the old value is freed and replaced. + */ +static inline gboolean +pcmk__intkey_table_insert(GHashTable *hash_table, int key, gpointer value) +{ + return g_hash_table_insert(hash_table, GINT_TO_POINTER(key), value); +} + +/*! + * \internal + * \brief Look up a value in a hash table with integer keys + * + * \param[in] hash_table Table to check + * \param[in] key Integer key to look for + * + * \return Value in table for \key (or NULL if not found) + */ +static inline gpointer +pcmk__intkey_table_lookup(GHashTable *hash_table, int key) +{ + return g_hash_table_lookup(hash_table, GINT_TO_POINTER(key)); +} + +/*! + * \internal + * \brief Remove a key/value from a hash table with integer keys + * + * \param[in,out] hash_table Table to modify + * \param[in] key Integer key of entry to remove + * + * \return Whether \p key was found and removed from \p hash_table + */ +static inline gboolean +pcmk__intkey_table_remove(GHashTable *hash_table, int key) +{ + return g_hash_table_remove(hash_table, GINT_TO_POINTER(key)); +} + +gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags); + +bool pcmk__strcase_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED; +bool pcmk__str_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED; +bool pcmk__char_in_any_str(int ch, ...) G_GNUC_NULL_TERMINATED; + +int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags); +int pcmk__numeric_strcasecmp(const char *s1, const char *s2); +void pcmk__str_update(char **str, const char *value); +void pcmk__g_strcat(GString *buffer, ...) G_GNUC_NULL_TERMINATED; + +static inline bool +pcmk__str_eq(const char *s1, const char *s2, uint32_t flags) +{ + return pcmk__strcmp(s1, s2, flags) == 0; +} + +// Like pcmk__add_separated_word() but using a space as separator +static inline void +pcmk__add_word(GString **list, size_t init_size, const char *word) +{ + return pcmk__add_separated_word(list, init_size, word, " "); +} + +/* Correctly displaying singular or plural is complicated; consider "1 node has" + * vs. "2 nodes have". A flexible solution is to pluralize entire strings, e.g. + * + * if (a == 1) { + * crm_info("singular message"): + * } else { + * crm_info("plural message"); + * } + * + * though even that's not sufficient for all languages besides English (if we + * ever desire to do translations of output and log messages). But the following + * convenience macros are "good enough" and more concise for many cases. + */ + +/* Example: + * crm_info("Found %d %s", nentries, + * pcmk__plural_alt(nentries, "entry", "entries")); + */ +#define pcmk__plural_alt(i, s1, s2) (((i) == 1)? (s1) : (s2)) + +// Example: crm_info("Found %d node%s", nnodes, pcmk__plural_s(nnodes)); +#define pcmk__plural_s(i) pcmk__plural_alt(i, "", "s") + +static inline int +pcmk__str_empty(const char *s) +{ + return (s == NULL) || (s[0] == '\0'); +} + +static inline char * +pcmk__itoa(int an_int) +{ + return crm_strdup_printf("%d", an_int); +} + +static inline char * +pcmk__ftoa(double a_float) +{ + return crm_strdup_printf("%f", a_float); +} + +static inline char * +pcmk__ttoa(time_t epoch_time) +{ + return crm_strdup_printf("%lld", (long long) epoch_time); +} + +// note this returns const not allocated +static inline const char * +pcmk__btoa(bool condition) +{ + return condition? "true" : "false"; +} + +#endif /* PCMK__STRINGS_INTERNAL__H */ diff --git a/include/crm/common/unittest_internal.h b/include/crm/common/unittest_internal.h new file mode 100644 index 0000000..b8f78cf --- /dev/null +++ b/include/crm/common/unittest_internal.h @@ -0,0 +1,84 @@ +/* + * Copyright 2022 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 <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <cmocka.h> + +#ifndef CRM_COMMON_UNITTEST_INTERNAL__H +#define CRM_COMMON_UNITTEST_INTERNAL__H + +/* internal unit testing related utilities */ + +/*! + * \internal + * \brief Assert that a statement aborts through CRM_ASSERT(). + * + * \param[in] stmt Statement to execute; can be an expression. + * + * A cmocka-like assert macro for use in unit testing. This one verifies that a + * statement aborts through CRM_ASSERT(), erroring out if that is not the case. + * + * This macro works by running the statement in a forked child process with core + * dumps disabled (CRM_ASSERT() calls \c abort(), which will write out a core + * dump). The parent waits for the child to exit and checks why. If the child + * received a \c SIGABRT, the test passes. For all other cases, the test fails. + * + * \note If cmocka's expect_*() or will_return() macros are called along with + * pcmk__assert_asserts(), they must be called within a block that is + * passed as the \c stmt argument. That way, the values are added only to + * the child's queue. Otherwise, values added to the parent's queue will + * never be popped, and the test will fail. + */ +#define pcmk__assert_asserts(stmt) \ + do { \ + pid_t p = fork(); \ + if (p == 0) { \ + struct rlimit cores = { 0, 0 }; \ + setrlimit(RLIMIT_CORE, &cores); \ + stmt; \ + _exit(0); \ + } else if (p > 0) { \ + int wstatus = 0; \ + if (waitpid(p, &wstatus, 0) == -1) { \ + fail_msg("waitpid failed"); \ + } \ + if (!(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)) { \ + fail_msg("statement terminated in child without asserting"); \ + } \ + } else { \ + fail_msg("unable to fork for assert test"); \ + } \ + } while (0); + +/* Generate the main function of most unit test files. Typically, group_setup + * and group_teardown will be NULL. The rest of the arguments are a list of + * calls to cmocka_unit_test or cmocka_unit_test_setup_teardown to run the + * individual unit tests. + */ +#define PCMK__UNIT_TEST(group_setup, group_teardown, ...) \ +int \ +main(int argc, char **argv) \ +{ \ + const struct CMUnitTest t[] = { \ + __VA_ARGS__ \ + }; \ + cmocka_set_message_output(CM_OUTPUT_TAP); \ + return cmocka_run_group_tests(t, group_setup, group_teardown); \ +} + +#endif /* CRM_COMMON_UNITTEST_INTERNAL__H */ diff --git a/include/crm/common/util.h b/include/crm/common/util.h new file mode 100644 index 0000000..8acdff9 --- /dev/null +++ b/include/crm/common/util.h @@ -0,0 +1,153 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_COMMON_UTIL__H +# define PCMK__CRM_COMMON_UTIL__H + +# include <sys/types.h> // gid_t, mode_t, size_t, time_t, uid_t +# include <stdlib.h> +# include <stdbool.h> +# include <stdint.h> // uint32_t +# include <limits.h> +# include <signal.h> +# include <glib.h> + +# include <libxml/tree.h> + +# include <crm/lrmd.h> +# include <crm/common/acl.h> +# include <crm/common/agents.h> +# include <crm/common/results.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Utility functions + * \ingroup core + */ + + +# define ONLINESTATUS "online" // Status of an online client +# define OFFLINESTATUS "offline" // Status of an offline client + +/* public node attribute functions (from attrd_client.c) */ +char *pcmk_promotion_score_name(const char *rsc_id); + +/* public Pacemaker Remote functions (from remote.c) */ +int crm_default_remote_port(void); + +/* public score-related functions (from scores.c) */ +const char *pcmk_readable_score(int score); +int char2score(const char *score); +int pcmk__add_scores(int score1, int score2); + +/* public string functions (from strings.c) */ +gboolean crm_is_true(const char *s); +int crm_str_to_boolean(const char *s, int *ret); +long long crm_get_msec(const char *input); +char * crm_strip_trailing_newline(char *str); +char *crm_strdup_printf(char const *format, ...) G_GNUC_PRINTF(1, 2); + +guint crm_parse_interval_spec(const char *input); + +/* public operation functions (from operations.c) */ +gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, + guint *interval_ms); +gboolean decode_transition_key(const char *key, char **uuid, int *transition_id, + int *action_id, int *target_rc); +gboolean decode_transition_magic(const char *magic, char **uuid, + int *transition_id, int *action_id, + int *op_status, int *op_rc, int *target_rc); +int rsc_op_expected_rc(const lrmd_event_data_t *event); +gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc); +bool crm_op_needs_metadata(const char *rsc_class, const char *op); +xmlNode *crm_create_op_xml(xmlNode *parent, const char *prefix, + const char *task, const char *interval_spec, + const char *timeout); +#define CRM_DEFAULT_OP_TIMEOUT_S "20s" + +bool pcmk_is_probe(const char *task, guint interval); +bool pcmk_xe_is_probe(const xmlNode *xml_op); +bool pcmk_xe_mask_probe_failure(const xmlNode *xml_op); + +int compare_version(const char *version1, const char *version2); + +/* coverity[+kill] */ +void crm_abort(const char *file, const char *function, int line, + const char *condition, gboolean do_core, gboolean do_fork); + +/*! + * \brief Check whether any of specified flags are set in a flag group + * + * \param[in] flag_group The flag group being examined + * \param[in] flags_to_check Which flags in flag_group should be checked + * + * \return true if \p flags_to_check is nonzero and any of its flags are set in + * \p flag_group, or false otherwise + */ +static inline bool +pcmk_any_flags_set(uint64_t flag_group, uint64_t flags_to_check) +{ + return (flag_group & flags_to_check) != 0; +} + +/*! + * \brief Check whether all of specified flags are set in a flag group + * + * \param[in] flag_group The flag group being examined + * \param[in] flags_to_check Which flags in flag_group should be checked + * + * \return true if \p flags_to_check is zero or all of its flags are set in + * \p flag_group, or false otherwise + */ +static inline bool +pcmk_all_flags_set(uint64_t flag_group, uint64_t flags_to_check) +{ + return (flag_group & flags_to_check) == flags_to_check; +} + +/*! + * \brief Convenience alias for pcmk_all_flags_set(), to check single flag + */ +#define pcmk_is_set(g, f) pcmk_all_flags_set((g), (f)) + +char *crm_meta_name(const char *field); +const char *crm_meta_value(GHashTable * hash, const char *field); + +char *crm_md5sum(const char *buffer); + +char *crm_generate_uuid(void); + +// This belongs in ipc.h but is here for backward compatibility +bool crm_is_daemon_name(const char *name); + +int crm_user_lookup(const char *name, uid_t * uid, gid_t * gid); +int pcmk_daemon_user(uid_t *uid, gid_t *gid); + +#ifdef HAVE_GNUTLS_GNUTLS_H +void crm_gnutls_global_init(void); +#endif + +char *pcmk_hostname(void); + +bool pcmk_str_is_infinity(const char *s); +bool pcmk_str_is_minus_infinity(const char *s); + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/common/util_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/util_compat.h b/include/crm/common/util_compat.h new file mode 100644 index 0000000..9e02e12 --- /dev/null +++ b/include/crm/common/util_compat.h @@ -0,0 +1,164 @@ +/* + * Copyright 2004-2021 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 PCMK__CRM_COMMON_UTIL_COMPAT__H +# define PCMK__CRM_COMMON_UTIL_COMPAT__H + +# include <glib.h> +# include <crm/common/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker utilities + * \ingroup core + * \deprecated Do not include this header directly. The utilities in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use crm_parse_interval_spec() instead +#define crm_get_interval crm_parse_interval_spec + +//! \deprecated Use !pcmk_is_set() or !pcmk_all_flags_set() instead +static inline gboolean +is_not_set(long long word, long long bit) +{ + return ((word & bit) == 0); +} + +//! \deprecated Use pcmk_is_set() or pcmk_all_flags_set() instead +static inline gboolean +is_set(long long word, long long bit) +{ + return ((word & bit) == bit); +} + +//! \deprecated Use pcmk_any_flags_set() instead +static inline gboolean +is_set_any(long long word, long long bit) +{ + return ((word & bit) != 0); +} + +//! \deprecated Use strcmp() or strcasecmp() instead +gboolean crm_str_eq(const char *a, const char *b, gboolean use_case); + +//! \deprecated Use strcmp() instead +gboolean safe_str_neq(const char *a, const char *b); + +//! \deprecated Use strcasecmp() instead +#define safe_str_eq(a, b) crm_str_eq(a, b, FALSE) + +//! \deprecated Use snprintf() instead +char *crm_itoa_stack(int an_int, char *buf, size_t len); + +//! \deprecated Use sscanf() instead +int pcmk_scan_nvpair(const char *input, char **name, char **value); + +//! \deprecated Use a standard printf()-style function instead +char *pcmk_format_nvpair(const char *name, const char *value, + const char *units); + +//! \deprecated Use a standard printf()-style function instead +char *pcmk_format_named_time(const char *name, time_t epoch_time); + +//! \deprecated Use strtoll() instead +long long crm_parse_ll(const char *text, const char *default_text); + +//! \deprecated Use strtoll() instead +int crm_parse_int(const char *text, const char *default_text); + +//! \deprecated Use strtoll() instead +# define crm_atoi(text, default_text) crm_parse_int(text, default_text) + +//! \deprecated Use g_str_hash() instead +guint g_str_hash_traditional(gconstpointer v); + +//! \deprecated Use g_str_hash() instead +#define crm_str_hash g_str_hash_traditional + +//! \deprecated Do not use Pacemaker for generic string comparison +gboolean crm_strcase_equal(gconstpointer a, gconstpointer b); + +//! \deprecated Do not use Pacemaker for generic string manipulation +guint crm_strcase_hash(gconstpointer v); + +//! \deprecated Use g_hash_table_new_full() instead +static inline GHashTable * +crm_str_table_new(void) +{ + return g_hash_table_new_full(crm_str_hash, g_str_equal, free, free); +} + +//! \deprecated Use g_hash_table_new_full() instead +static inline GHashTable * +crm_strcase_table_new(void) +{ + return g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, + free, free); +} + +//! \deprecated Do not use Pacemaker for generic hash table manipulation +GHashTable *crm_str_table_dup(GHashTable *old_table); + +//! \deprecated Use g_hash_able_size() instead +static inline guint +crm_hash_table_size(GHashTable *hashtable) +{ + if (hashtable == NULL) { + return 0; + } + return g_hash_table_size(hashtable); +} + +//! \deprecated Don't use Pacemaker for string manipulation +char *crm_strip_trailing_newline(char *str); + +//! \deprecated Don't use Pacemaker for string manipulation +int pcmk_numeric_strcasecmp(const char *s1, const char *s2); + +//! \deprecated Don't use Pacemaker for string manipulation +static inline char * +crm_itoa(int an_int) +{ + return crm_strdup_printf("%d", an_int); +} + +//! \deprecated Don't use Pacemaker for string manipulation +static inline char * +crm_ftoa(double a_float) +{ + return crm_strdup_printf("%f", a_float); +} + +//! \deprecated Don't use Pacemaker for string manipulation +static inline char * +crm_ttoa(time_t epoch_time) +{ + return crm_strdup_printf("%lld", (long long) epoch_time); +} + +//! \deprecated Do not use Pacemaker libraries for generic I/O +void crm_build_path(const char *path_c, mode_t mode); + +//! \deprecated Use pcmk_readable_score() instead +char *score2char(int score); + +//! \deprecated Use pcmk_readable_score() instead +char *score2char_stack(int score, char *buf, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_UTIL_COMPAT__H diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h new file mode 100644 index 0000000..682b31c --- /dev/null +++ b/include/crm/common/xml.h @@ -0,0 +1,306 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_COMMON_XML__H +# define PCMK__CRM_COMMON_XML__H + + +# include <stdio.h> +# include <sys/types.h> +# include <unistd.h> + +# include <stdlib.h> +# include <errno.h> +# include <fcntl.h> + +# include <libxml/tree.h> +# include <libxml/xpath.h> + +# include <crm/crm.h> +# include <crm/common/nvpair.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Wrappers for and extensions to libxml2 + * \ingroup core + */ + +/* Define compression parameters for IPC messages + * + * Compression costs a LOT, so we don't want to do it unless we're hitting + * message limits. Currently, we use 128KB as the threshold, because higher + * values don't play well with the heartbeat stack. With an earlier limit of + * 10KB, compressing 184 of 1071 messages accounted for 23% of the total CPU + * used by the cib. + */ +# define CRM_BZ2_BLOCKS 4 +# define CRM_BZ2_WORK 20 +# define CRM_BZ2_THRESHOLD 128 * 1024 + +typedef const xmlChar *pcmkXmlStr; + +gboolean add_message_xml(xmlNode * msg, const char *field, xmlNode * xml); +xmlNode *get_message_xml(const xmlNode *msg, const char *field); + +xmlDoc *getDocPtr(xmlNode * node); + +/* + * \brief xmlCopyPropList ACLs-sensitive replacement expading i++ notation + * + * The gist is the same as with \c{xmlCopyPropList(target, src->properties)}. + * The function exits prematurely when any attribute cannot be copied for + * ACLs violation. Even without bailing out, the result can possibly be + * incosistent with expectations in that case, hence the caller shall, + * aposteriori, verify that no document-level-tracked denial was indicated + * with \c{xml_acl_denied(target)} and drop whole such intermediate object. + * + * \param[in,out] target Element to receive attributes from #src element + * \param[in] src Element carrying attributes to copy over to #target + * + * \note Original commit 1c632c506 sadly haven't stated which otherwise + * assumed behaviours of xmlCopyPropList were missing beyond otherwise + * custom extensions like said ACLs and "atomic increment" (that landed + * later on, anyway). + */ +void copy_in_properties(xmlNode *target, const xmlNode *src); + +void expand_plus_plus(xmlNode * target, const char *name, const char *value); +void fix_plus_plus_recursive(xmlNode * target); + +/* + * Create a node named "name" as a child of "parent" + * If parent is NULL, creates an unconnected node. + * + * Returns the created node + * + */ +xmlNode *create_xml_node(xmlNode * parent, const char *name); + +/* + * Create a node named "name" as a child of "parent", giving it the provided + * text content. + * If parent is NULL, creates an unconnected node. + * + * Returns the created node + * + */ +xmlNode *pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content); + +/* + * Create a new HTML node named "element_name" as a child of "parent", giving it the + * provided text content. Optionally, apply a CSS #id and #class. + * + * Returns the created node. + */ +xmlNode *pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id, + const char *class_name, const char *text); + +/* + * + */ +void purge_diff_markers(xmlNode * a_node); + +/* + * Returns a deep copy of src_node + * + */ +xmlNode *copy_xml(xmlNode * src_node); + +/* + * Add a copy of xml_node to new_parent + */ +xmlNode *add_node_copy(xmlNode * new_parent, xmlNode * xml_node); + +/* + * XML I/O Functions + * + * Whitespace between tags is discarded. + */ +xmlNode *filename2xml(const char *filename); + +xmlNode *stdin2xml(void); + +xmlNode *string2xml(const char *input); + +int write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress); +int write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress); + +char *dump_xml_formatted(xmlNode * msg); +char *dump_xml_formatted_with_text(xmlNode * msg); +char *dump_xml_unformatted(xmlNode * msg); + +/* + * Diff related Functions + */ +xmlNode *diff_xml_object(xmlNode * left, xmlNode * right, gboolean suppress); + +xmlNode *subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right, + gboolean full, gboolean * changed, const char *marker); + +gboolean can_prune_leaf(xmlNode * xml_node); + +/* + * Searching & Modifying + */ +xmlNode *find_xml_node(const xmlNode *root, const char *search_path, + gboolean must_find); + +void xml_remove_prop(xmlNode * obj, const char *name); + +gboolean replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, + gboolean delete_only); + +gboolean update_xml_child(xmlNode * child, xmlNode * to_update); + +int find_xml_children(xmlNode ** children, xmlNode * root, + const char *tag, const char *field, const char *value, + gboolean search_matches); + +xmlNode *get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level); +xmlNode *get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level); + +static inline const char * +crm_element_name(const xmlNode *xml) +{ + return xml? (const char *)(xml->name) : NULL; +} + +static inline const char * +crm_map_element_name(const xmlNode *xml) +{ + const char *name = crm_element_name(xml); + + if (strcmp(name, "master") == 0) { + return "clone"; + } else { + return name; + } +} + +gboolean xml_has_children(const xmlNode * root); + +char *calculate_on_disk_digest(xmlNode * local_cib); +char *calculate_operation_digest(xmlNode * local_cib, const char *version); +char *calculate_xml_versioned_digest(xmlNode * input, gboolean sort, gboolean do_filter, + const char *version); + +/* schema-related functions (from schemas.c) */ +gboolean validate_xml(xmlNode * xml_blob, const char *validation, gboolean to_logs); +gboolean validate_xml_verbose(xmlNode * xml_blob); + +/*! + * \brief Update CIB XML to most recent schema version + * + * "Update" means either actively employ XSLT-based transformation(s) + * (if intermediate product to transform valid per its declared schema version, + * transformation available, proceeded successfully with a result valid per + * expectated newer schema version), or just try to bump the marked validating + * schema until all gradually rising schema versions attested or the first + * such attempt subsequently fails to validate. Which of the two styles will + * be used depends on \p transform parameter (positive/negative, respectively). + * + * \param[in,out] xml_blob XML tree representing CIB, may be swapped with + * an "updated" one + * \param[out] best The highest configuration version (per its index + * in the global schemas table) it was possible to + * reach during the update steps while ensuring + * the validity of the result; if no validation + * success was observed against possibly multiple + * schemas, the value is less or equal the result + * of \c get_schema_version applied on the input + * \p xml_blob value (unless that function maps it + * to -1, then 0 would be used instead) + * \param[in] max When \p transform is positive, this allows to + * set upper boundary schema (per its index in the + * global schemas table) beyond which it's forbidden + * to update by the means of XSLT transformation + * \param[in] transform Whether to employ XSLT-based transformation so + * as to allow overcoming possible incompatibilities + * between major schema versions (see above) + * \param[in] to_logs If true, output notable progress info to + * internal log streams; if false, to stderr + * + * \return \c pcmk_ok if no non-recoverable error encountered (up to + * caller to evaluate if the update satisfies the requirements + * per returned \p best value), negative value carrying the reason + * otherwise + */ +int update_validation(xmlNode **xml_blob, int *best, int max, + gboolean transform, gboolean to_logs); + +int get_schema_version(const char *name); +const char *get_schema_name(int version); +const char *xml_latest_schema(void); +gboolean cli_config_update(xmlNode ** xml, int *best_version, gboolean to_logs); + +/*! + * \brief Initialize the CRM XML subsystem + * + * This method sets global XML settings and loads pacemaker schemas into the cache. + */ +void crm_xml_init(void); +void crm_xml_cleanup(void); + +void pcmk_free_xml_subtree(xmlNode *xml); +void free_xml(xmlNode * child); + +xmlNode *first_named_child(const xmlNode *parent, const char *name); +xmlNode *crm_next_same_xml(const xmlNode *sibling); + +xmlNode *sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive); +xmlXPathObjectPtr xpath_search(xmlNode * xml_top, const char *path); +void crm_foreach_xpath_result(xmlNode *xml, const char *xpath, + void (*helper)(xmlNode*, void*), void *user_data); +xmlNode *expand_idref(xmlNode * input, xmlNode * top); + +void freeXpathObject(xmlXPathObjectPtr xpathObj); +xmlNode *getXpathResult(xmlXPathObjectPtr xpathObj, int index); +void dedupXpathResults(xmlXPathObjectPtr xpathObj); + +static inline int numXpathResults(xmlXPathObjectPtr xpathObj) +{ + if(xpathObj == NULL || xpathObj->nodesetval == NULL) { + return 0; + } + return xpathObj->nodesetval->nodeNr; +} + +bool xml_tracking_changes(xmlNode * xml); +bool xml_document_dirty(xmlNode *xml); +void xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls); +void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml); +void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml); +void xml_accept_changes(xmlNode * xml); +bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3]); + +xmlNode *xml_create_patchset( + int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version); +int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version); + +void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest); + +void save_xml_to_file(xmlNode * xml, const char *desc, const char *filename); + +char * crm_xml_escape(const char *text); +void crm_xml_sanitize_id(char *id); +void crm_xml_set_id(xmlNode *xml, const char *format, ...) G_GNUC_PRINTF(2, 3); + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/common/xml_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/common/xml_compat.h b/include/crm/common/xml_compat.h new file mode 100644 index 0000000..bb49b68 --- /dev/null +++ b/include/crm/common/xml_compat.h @@ -0,0 +1,65 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_COMMON_XML_COMPAT__H +# define PCMK__CRM_COMMON_XML_COMPAT__H + +#include <glib.h> // gboolean +#include <libxml/tree.h> // xmlNode +#include <crm/common/xml.h> // crm_xml_add() + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker XML API + * \ingroup core + * \deprecated Do not include this header directly. The XML APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Do not use (will be removed in a future release) +#define XML_PARANOIA_CHECKS 0 + +//! \deprecated This function will be removed in a future release +int add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child); + +//! \deprecated This function will be removed in a future release +xmlNode *find_entity(xmlNode *parent, const char *node_name, const char *id); + +//! \deprecated This function will be removed in a future release +char *xml_get_path(const xmlNode *xml); + +//! \deprecated This function will be removed in a future release +void xml_log_changes(uint8_t level, const char *function, const xmlNode *xml); + +//! \deprecated This function will be removed in a future release +void xml_log_patchset(uint8_t level, const char *function, const xmlNode *xml); + +//! \deprecated Use xml_apply_patchset() instead +gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml); + +//! \deprecated Do not use (will be removed in a future release) +void crm_destroy_xml(gpointer data); + +//! \deprecated Use crm_xml_add() with "true" or "false" instead +static inline const char * +crm_xml_add_boolean(xmlNode *node, const char *name, gboolean value) +{ + return crm_xml_add(node, name, (value? "true" : "false")); +} + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_COMMON_XML_COMPAT__H diff --git a/include/crm/common/xml_internal.h b/include/crm/common/xml_internal.h new file mode 100644 index 0000000..43b3b8c --- /dev/null +++ b/include/crm/common/xml_internal.h @@ -0,0 +1,414 @@ +/* + * Copyright 2017-2023 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 PCMK__XML_INTERNAL__H +# define PCMK__XML_INTERNAL__H + +/* + * Internal-only wrappers for and extensions to libxml2 (libxslt) + */ + +# include <stdlib.h> +# include <stdio.h> +# include <string.h> + +# include <crm/crm.h> /* transitively imports qblog.h */ +# include <crm/common/output_internal.h> + + +/*! + * \brief Base for directing lib{xml2,xslt} log into standard libqb backend + * + * This macro implements the core of what can be needed for directing + * libxml2 or libxslt error messaging into standard, preconfigured + * libqb-backed log stream. + * + * It's a bit unfortunate that libxml2 (and more sparsely, also libxslt) + * emits a single message by chunks (location is emitted separatedly from + * the message itself), so we have to take the effort to combine these + * chunks back to single message. Whether to do this or not is driven + * with \p dechunk toggle. + * + * The form of a macro was chosen for implicit deriving of __FILE__, etc. + * and also because static dechunking buffer should be differentiated per + * library (here we assume different functions referring to this macro + * will not ever be using both at once), preferably also per-library + * context of use to avoid clashes altogether. + * + * Note that we cannot use qb_logt, because callsite data have to be known + * at the moment of compilation, which it is not always the case -- xml_log + * (and unfortunately there's no clear explanation of the fail to compile). + * + * Also note that there's no explicit guard against said libraries producing + * never-newline-terminated chunks (which would just keep consuming memory), + * as it's quite improbable. Termination of the program in between the + * same-message chunks will raise a flag with valgrind and the likes, though. + * + * And lastly, regarding how dechunking combines with other non-message + * parameters -- for \p priority, most important running specification + * wins (possibly elevated to LOG_ERR in case of nonconformance with the + * newline-termination "protocol"), \p dechunk is expected to always be + * on once it was at the start, and the rest (\p postemit and \p prefix) + * are picked directly from the last chunk entry finalizing the message + * (also reasonable to always have it the same with all related entries). + * + * \param[in] priority Syslog priority for the message to be logged + * \param[in] dechunk Whether to dechunk new-line terminated message + * \param[in] postemit Code to be executed once message is sent out + * \param[in] prefix How to prefix the message or NULL for raw passing + * \param[in] fmt Format string as with printf-like functions + * \param[in] ap Variable argument list to supplement \p fmt format string + */ +#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap) \ +do { \ + if (!(dechunk) && (prefix) == NULL) { /* quick pass */ \ + qb_log_from_external_source_va(__func__, __FILE__, (fmt), \ + (priority), __LINE__, 0, (ap)); \ + (void) (postemit); \ + } else { \ + int CXLB_len = 0; \ + char *CXLB_buf = NULL; \ + static int CXLB_buffer_len = 0; \ + static char *CXLB_buffer = NULL; \ + static uint8_t CXLB_priority = 0; \ + \ + CXLB_len = vasprintf(&CXLB_buf, (fmt), (ap)); \ + \ + if (CXLB_len <= 0 || CXLB_buf[CXLB_len - 1] == '\n' || !(dechunk)) { \ + if (CXLB_len < 0) { \ + CXLB_buf = (char *) "LOG CORRUPTION HAZARD"; /*we don't modify*/\ + CXLB_priority = QB_MIN(CXLB_priority, LOG_ERR); \ + } else if (CXLB_len > 0 /* && (dechunk) */ \ + && CXLB_buf[CXLB_len - 1] == '\n') { \ + CXLB_buf[CXLB_len - 1] = '\0'; \ + } \ + if (CXLB_buffer) { \ + qb_log_from_external_source(__func__, __FILE__, "%s%s%s", \ + CXLB_priority, __LINE__, 0, \ + (prefix) != NULL ? (prefix) : "", \ + CXLB_buffer, CXLB_buf); \ + free(CXLB_buffer); \ + } else { \ + qb_log_from_external_source(__func__, __FILE__, "%s%s", \ + (priority), __LINE__, 0, \ + (prefix) != NULL ? (prefix) : "", \ + CXLB_buf); \ + } \ + if (CXLB_len < 0) { \ + CXLB_buf = NULL; /* restore temporary override */ \ + } \ + CXLB_buffer = NULL; \ + CXLB_buffer_len = 0; \ + (void) (postemit); \ + \ + } else if (CXLB_buffer == NULL) { \ + CXLB_buffer_len = CXLB_len; \ + CXLB_buffer = CXLB_buf; \ + CXLB_buf = NULL; \ + CXLB_priority = (priority); /* remember as a running severest */ \ + \ + } else { \ + CXLB_buffer = realloc(CXLB_buffer, 1 + CXLB_buffer_len + CXLB_len); \ + memcpy(CXLB_buffer + CXLB_buffer_len, CXLB_buf, CXLB_len); \ + CXLB_buffer_len += CXLB_len; \ + CXLB_buffer[CXLB_buffer_len] = '\0'; \ + CXLB_priority = QB_MIN(CXLB_priority, (priority)); /* severest? */ \ + } \ + free(CXLB_buf); \ + } \ +} while (0) + +/* + * \enum pcmk__xml_fmt_options + * \brief Bit flags to control format in XML logs and dumps + */ +enum pcmk__xml_fmt_options { + //! Exclude certain XML attributes (for calculating digests) + pcmk__xml_fmt_filtered = (1 << 0), + + //! Include indentation and newlines + pcmk__xml_fmt_pretty = (1 << 1), + + //! Include full XML subtree (with any text), using libxml serialization + pcmk__xml_fmt_full = (1 << 2), + + //! Include the opening tag of an XML element, and include XML comments + pcmk__xml_fmt_open = (1 << 3), + + //! Include the children of an XML element + pcmk__xml_fmt_children = (1 << 4), + + //! Include the closing tag of an XML element + pcmk__xml_fmt_close = (1 << 5), + + // @COMPAT Remove when log_data_element() is removed + //! Include XML text nodes + pcmk__xml_fmt_text = (1 << 6), + + // @COMPAT Remove when v1 patchsets are removed + //! Log a created XML subtree + pcmk__xml_fmt_diff_plus = (1 << 7), + + // @COMPAT Remove when v1 patchsets are removed + //! Log a removed XML subtree + pcmk__xml_fmt_diff_minus = (1 << 8), + + // @COMPAT Remove when v1 patchsets are removed + //! Log a minimal version of an XML diff (only showing the changes) + pcmk__xml_fmt_diff_short = (1 << 9), +}; + +int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data, + int depth, uint32_t options); +int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml); + +/* XML search strings for guest, remote and pacemaker_remote nodes */ + +/* search string to find CIB resources entries for cluster nodes */ +#define PCMK__XP_MEMBER_NODE_CONFIG \ + "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES \ + "/" XML_CIB_TAG_NODE "[not(@type) or @type='member']" + +/* search string to find CIB resources entries for guest nodes */ +#define PCMK__XP_GUEST_NODE_CONFIG \ + "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE \ + "//" XML_TAG_META_SETS "//" XML_CIB_TAG_NVPAIR \ + "[@name='" XML_RSC_ATTR_REMOTE_NODE "']" + +/* search string to find CIB resources entries for remote nodes */ +#define PCMK__XP_REMOTE_NODE_CONFIG \ + "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE \ + "[@type='remote'][@provider='pacemaker']" + +/* search string to find CIB node status entries for pacemaker_remote nodes */ +#define PCMK__XP_REMOTE_NODE_STATUS \ + "//" XML_TAG_CIB "//" XML_CIB_TAG_STATUS "//" XML_CIB_TAG_STATE \ + "[@" XML_NODE_IS_REMOTE "='true']" + +enum pcmk__xml_artefact_ns { + pcmk__xml_artefact_ns_legacy_rng = 1, + pcmk__xml_artefact_ns_legacy_xslt, + pcmk__xml_artefact_ns_base_rng, + pcmk__xml_artefact_ns_base_xslt, +}; + +void pcmk__strip_xml_text(xmlNode *xml); +const char *pcmk__xe_add_last_written(xmlNode *xe); + +xmlNode *pcmk__xe_match(const xmlNode *parent, const char *node_name, + const char *attr_n, const char *attr_v); + +void pcmk__xe_remove_matching_attrs(xmlNode *element, + bool (*match)(xmlAttrPtr, void *), + void *user_data); + +GString *pcmk__element_xpath(const xmlNode *xml); + +/*! + * \internal + * \brief Get the root directory to scan XML artefacts of given kind for + * + * \param[in] ns governs the hierarchy nesting against the inherent root dir + * + * \return root directory to scan XML artefacts of given kind for + */ +char * +pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns); + +/*! + * \internal + * \brief Get the fully unwrapped path to particular XML artifact (RNG/XSLT) + * + * \param[in] ns denotes path forming details (parent dir, suffix) + * \param[in] filespec symbolic file specification to be combined with + * #artefact_ns to form the final path + * \return unwrapped path to particular XML artifact (RNG/XSLT) + */ +char *pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns, + const char *filespec); + +/*! + * \internal + * \brief Return first non-text child node of an XML node + * + * \param[in] parent XML node to check + * + * \return First non-text child node of \p parent (or NULL if none) + */ +static inline xmlNode * +pcmk__xml_first_child(const xmlNode *parent) +{ + xmlNode *child = (parent? parent->children : NULL); + + while (child && (child->type == XML_TEXT_NODE)) { + child = child->next; + } + return child; +} + +/*! + * \internal + * \brief Return next non-text sibling node of an XML node + * + * \param[in] child XML node to check + * + * \return Next non-text sibling of \p child (or NULL if none) + */ +static inline xmlNode * +pcmk__xml_next(const xmlNode *child) +{ + xmlNode *next = (child? child->next : NULL); + + while (next && (next->type == XML_TEXT_NODE)) { + next = next->next; + } + return next; +} + +/*! + * \internal + * \brief Return first non-text child element of an XML node + * + * \param[in] parent XML node to check + * + * \return First child element of \p parent (or NULL if none) + */ +static inline xmlNode * +pcmk__xe_first_child(const xmlNode *parent) +{ + xmlNode *child = (parent? parent->children : NULL); + + while (child && (child->type != XML_ELEMENT_NODE)) { + child = child->next; + } + return child; +} + +/*! + * \internal + * \brief Return next non-text sibling element of an XML element + * + * \param[in] child XML element to check + * + * \return Next sibling element of \p child (or NULL if none) + */ +static inline xmlNode * +pcmk__xe_next(const xmlNode *child) +{ + xmlNode *next = child? child->next : NULL; + + while (next && (next->type != XML_ELEMENT_NODE)) { + next = next->next; + } + return next; +} + +/*! + * \internal + * \brief Like pcmk__xe_set_props, but takes a va_list instead of + * arguments directly. + * + * \param[in,out] node XML to add attributes to + * \param[in] pairs NULL-terminated list of name/value pairs to add + */ +void +pcmk__xe_set_propv(xmlNodePtr node, va_list pairs); + +/*! + * \internal + * \brief Add a NULL-terminated list of name/value pairs to the given + * XML node as properties. + * + * \param[in,out] node XML node to add properties to + * \param[in] ... NULL-terminated list of name/value pairs + * + * \note A NULL name terminates the arguments; a NULL value will be skipped. + */ +void +pcmk__xe_set_props(xmlNodePtr node, ...) +G_GNUC_NULL_TERMINATED; + +/*! + * \internal + * \brief Get first attribute of an XML element + * + * \param[in] xe XML element to check + * + * \return First attribute of \p xe (or NULL if \p xe is NULL or has none) + */ +static inline xmlAttr * +pcmk__xe_first_attr(const xmlNode *xe) +{ + return (xe == NULL)? NULL : xe->properties; +} + +/*! + * \internal + * \brief Extract the ID attribute from an XML element + * + * \param[in] xpath String to search + * \param[in] node Node to get the ID for + * + * \return ID attribute of \p node in xpath string \p xpath + */ +char * +pcmk__xpath_node_id(const char *xpath, const char *node); + +/* internal XML-related utilities */ + +enum xml_private_flags { + pcmk__xf_none = 0x0000, + pcmk__xf_dirty = 0x0001, + pcmk__xf_deleted = 0x0002, + pcmk__xf_created = 0x0004, + pcmk__xf_modified = 0x0008, + + pcmk__xf_tracking = 0x0010, + pcmk__xf_processed = 0x0020, + pcmk__xf_skip = 0x0040, + pcmk__xf_moved = 0x0080, + + pcmk__xf_acl_enabled = 0x0100, + pcmk__xf_acl_read = 0x0200, + pcmk__xf_acl_write = 0x0400, + pcmk__xf_acl_deny = 0x0800, + + pcmk__xf_acl_create = 0x1000, + pcmk__xf_acl_denied = 0x2000, + pcmk__xf_lazy = 0x4000, +}; + +void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag); + +/*! + * \internal + * \brief Iterate over child elements of \p xml + * + * This function iterates over the children of \p xml, performing the + * callback function \p handler on each node. If the callback returns + * a value other than pcmk_rc_ok, the iteration stops and the value is + * returned. It is therefore possible that not all children will be + * visited. + * + * \param[in,out] xml The starting XML node. Can be NULL. + * \param[in] child_element_name The name that the node must match in order + * for \p handler to be run. If NULL, all + * child elements will match. + * \param[in] handler The callback function. + * \param[in,out] userdata User data to pass to the callback function. + * Can be NULL. + * + * \return Standard Pacemaker return code + */ +int +pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name, + int (*handler)(xmlNode *xml, void *userdata), + void *userdata); + +#endif // PCMK__XML_INTERNAL__H diff --git a/include/crm/compatibility.h b/include/crm/compatibility.h new file mode 100644 index 0000000..1281a3c --- /dev/null +++ b/include/crm/compatibility.h @@ -0,0 +1,243 @@ +/* + * Copyright 2004-2021 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. + */ +#ifndef PCMK__CRM_COMPATIBILITY__H +# define PCMK__CRM_COMPATIBILITY__H + +#include <crm/msg_xml.h> +#include <crm/pengine/pe_types.h> // enum pe_obj_types + +#ifdef __cplusplus +extern "C" { +#endif + +/* This file allows external code that uses Pacemaker libraries to transition + * more easily from old APIs to current ones. Any code that compiled with an + * earlier API but not with the current API can include this file and have a + * good chance of compiling again. + * + * Everything here is deprecated and will be removed at the next major Pacemaker + * release (i.e. 3.0), so it should only be used during a transitionary period + * while the external code is being updated to the current API. + */ + +/* Heartbeat-specific definitions. Support for heartbeat has been removed + * entirely, so any code branches relying on these should be deleted. + */ +#define ACTIVESTATUS "active" +#define DEADSTATUS "dead" +#define PINGSTATUS "ping" +#define JOINSTATUS "join" +#define LEAVESTATUS "leave" +#define NORMALNODE "normal" +#define CRM_NODE_EVICTED "evicted" +#define CRM_LEGACY_CONFIG_DIR "/var/lib/heartbeat/crm" +#define HA_VARLIBHBDIR "/var/lib/heartbeat" +#define pcmk_cluster_heartbeat 0x0004 + +/* Corosync-version-1-specific definitions */ + +/* Support for corosync version 1 has been removed entirely, so any code + * branches relying on these should be deleted. + */ +#define PCMK_SERVICE_ID 9 +#define CRM_SERVICE PCMK_SERVICE_ID +#define XML_ATTR_EXPECTED_VOTES "expected-quorum-votes" +#define crm_class_members 1 +#define crm_class_notify 2 +#define crm_class_nodeid 3 +#define crm_class_rmpeer 4 +#define crm_class_quorum 5 +#define pcmk_cluster_classic_ais 0x0010 +#define pcmk_cluster_cman 0x0040 +#define ais_fd_sync -1 + +// These are always true now +#define CS_USES_LIBQB 1 +#define HAVE_CMAP 1 +#define SUPPORT_CS_QUORUM 1 +#define SUPPORT_AIS 1 +#define AIS_COROSYNC 1 + +// These are always false now +#define HAVE_CONFDB 0 +#define SUPPORT_CMAN 0 +#define SUPPORT_PLUGIN 0 +#define SUPPORT_STONITH_CONFIG 0 +#define is_classic_ais_cluster() 0 +#define is_cman_cluster() 0 + +// These have newer names +#define is_openais_cluster() is_corosync_cluster() +#if SUPPORT_COROSYNC +#define SUPPORT_CS +#endif + +/* Isolation-specific definitions. Support for the resource isolation feature + * has been removed * entirely, so any code branches relying on these should be + * deleted. + */ +#define XML_RSC_ATTR_ISOLATION_INSTANCE "isolation-instance" +#define XML_RSC_ATTR_ISOLATION_WRAPPER "isolation-wrapper" +#define XML_RSC_ATTR_ISOLATION_HOST "isolation-host" +#define XML_RSC_ATTR_ISOLATION "isolation" + +/* Schema-related definitions */ + +// This has been renamed +#define CRM_DTD_DIRECTORY CRM_SCHEMA_DIRECTORY + +/* Exit-code-related definitions */ + +#define DAEMON_RESPAWN_STOP CRM_EX_FATAL +#define pcmk_err_panic CRM_EX_PANIC + +// Deprecated symbols that were removed +#define APPNAME_LEN 256 +#define CRM_NODE_ACTIVE CRM_NODE_MEMBER +#define CRM_OP_DIE "die_no_respawn" +#define CRM_OP_RETRIVE_CIB "retrieve_cib" +#define CRM_OP_HBEAT "dc_beat" +#define CRM_OP_ABORT "abort" +#define CRM_OP_DEBUG_UP "debug_inc" +#define CRM_OP_DEBUG_DOWN "debug_dec" +#define CRM_OP_EVENTCC "event_cc" +#define CRM_OP_TEABORT "te_abort" +#define CRM_OP_TEABORTED "te_abort_confirmed" +#define CRM_OP_TE_HALT "te_halt" +#define CRM_OP_TECOMPLETE "te_complete" +#define CRM_OP_TETIMEOUT "te_timeout" +#define CRM_OP_TRANSITION "transition" +#define CRM_OP_NODES_PROBED "probe_nodes_complete" +#define DOT_ALL_FSA_INPUTS 1 +#define DOT_FSA_ACTIONS 1 +#define F_LRMD_CANCEL_CALLID "lrmd_cancel_callid" +#define F_LRMD_RSC_METADATA "lrmd_rsc_metadata_res" +#define F_LRMD_IPC_PROXY_NODE "lrmd_ipc_proxy_node" +#define INSTANCE(x) crm_element_value(x, XML_CIB_ATTR_INSTANCE) +#define LOG_DEBUG_2 LOG_TRACE +#define LOG_DEBUG_3 LOG_TRACE +#define LOG_DEBUG_4 LOG_TRACE +#define LOG_DEBUG_5 LOG_TRACE +#define LOG_DEBUG_6 LOG_TRACE +#define LRMD_OP_RSC_CHK_REG "lrmd_rsc_check_register" +#define MAX_IPC_FAIL 5 +#define NAME(x) crm_element_value(x, XML_NVPAIR_ATTR_NAME) +#define MSG_LOG 1 +#define PE_OBJ_T_NATIVE "native" +#define PE_OBJ_T_GROUP "group" +#define PE_OBJ_T_INCARNATION "clone" +#define PE_OBJ_T_MASTER "master" +#define SERVICE_SCRIPT "/sbin/service" +#define SOCKET_LEN 1024 +#define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP) +#define XML_ATTR_TAGNAME F_XML_TAGNAME +#define XML_ATTR_FILTER_TYPE "type-filter" +#define XML_ATTR_FILTER_ID "id-filter" +#define XML_ATTR_FILTER_PRIORITY "priority-filter" +#define XML_ATTR_DC "is_dc" +#define XML_MSG_TAG "crm_message" +#define XML_MSG_TAG_DATA "msg_data" +#define XML_FAIL_TAG_RESOURCE "failed_resource" +#define XML_FAILRES_ATTR_RESID "resource_id" +#define XML_FAILRES_ATTR_REASON "reason" +#define XML_FAILRES_ATTR_RESSTATUS "resource_status" +#define XML_ATTR_RESULT "result" +#define XML_ATTR_SECTION "section" +#define XML_CIB_TAG_DOMAIN "domain" +#define XML_CIB_TAG_CONSTRAINT "constraint" +#define XML_RSC_ATTR_STATE "clone-state" +#define XML_RSC_ATTR_PRIORITY "priority" +#define XML_OP_ATTR_DEPENDENT "dependent-on" +#define XML_LRM_TAG_AGENTS "lrm_agents" +#define XML_LRM_TAG_AGENT "lrm_agent" +#define XML_LRM_TAG_ATTRIBUTES "attributes" +#define XML_CIB_ATTR_HEALTH "health" +#define XML_CIB_ATTR_WEIGHT "weight" +#define XML_CIB_ATTR_CLEAR "clear_on" +#define XML_CIB_ATTR_STONITH "stonith" +#define XML_CIB_ATTR_STANDBY "standby" +#define XML_RULE_ATTR_SCORE_MANGLED "score-attribute-mangled" +#define XML_RULE_ATTR_RESULT "result" +#define XML_NODE_ATTR_STATE "state" +#define XML_ATTR_LRM_PROBE "lrm-is-probe" +#define XML_ATTR_TE_ALLOWFAIL "op_allow_fail" +#define VALUE(x) crm_element_value(x, XML_NVPAIR_ATTR_VALUE) +#define action_wrapper_s pe_action_wrapper_s +#define add_cib_op_callback(cib, id, flag, data, fn) do { \ + cib->cmds->register_callback(cib, id, 120, flag, data, #fn, fn); \ + } while(0) +#define cib_default_options = cib_none +#define crm_remote_baremetal 0x0004 +#define crm_remote_container 0x0002 +#define crm_element_value_const crm_element_value +#define crm_element_value_const_int crm_element_value_int +#define n_object_classes 3 +#define no_quorum_policy_e pe_quorum_policy +#define node_s pe_node_s +#define node_shared_s pe_node_shared_s +#define pe_action_failure_is_fatal 0x00020 +#define pe_rsc_munging 0x00000800ULL +#define pe_rsc_try_reload 0x00001000ULL +#define pe_rsc_shutdown 0x00020000ULL +#define pe_rsc_migrating 0x00400000ULL +#define pe_rsc_unexpectedly_running 0x02000000ULL +#define pe_rsc_have_unfencing 0x80000000ULL +#define resource_s pe_resource_s +#define ticket_s pe_ticket_s + +#define node_score_infinity 1000000 + +/* Clone terminology definitions */ + +// These can no longer be used in a switch together +#define pe_master pe_clone + +static inline enum pe_obj_types +get_resource_type(const char *name) +{ + if (safe_str_eq(name, XML_CIB_TAG_RESOURCE)) { + return pe_native; + + } else if (safe_str_eq(name, XML_CIB_TAG_GROUP)) { + return pe_group; + + } else if (safe_str_eq(name, XML_CIB_TAG_INCARNATION) + || safe_str_eq(name, PCMK_XE_PROMOTABLE_LEGACY)) { + return pe_clone; + + } else if (safe_str_eq(name, XML_CIB_TAG_CONTAINER)) { + return pe_container; + } + + return pe_unknown; +} + +static inline const char * +get_resource_typename(enum pe_obj_types type) +{ + switch (type) { + case pe_native: + return XML_CIB_TAG_RESOURCE; + case pe_group: + return XML_CIB_TAG_GROUP; + case pe_clone: + return XML_CIB_TAG_INCARNATION; + case pe_container: + return XML_CIB_TAG_CONTAINER; + case pe_unknown: + return "unknown"; + } + return "<unknown>"; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/crm.h b/include/crm/crm.h new file mode 100644 index 0000000..e824825 --- /dev/null +++ b/include/crm/crm.h @@ -0,0 +1,238 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_CRM__H +# define PCMK__CRM_CRM__H + +# include <crm_config.h> +# include <stdlib.h> +# include <glib.h> +# include <stdbool.h> + +# include <string.h> + +# include <libxml/tree.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief A dumping ground + * \ingroup core + */ + +#ifndef PCMK_ALLOW_DEPRECATED +/*! + * \brief Allow use of deprecated Pacemaker APIs + * + * By default, external code using Pacemaker headers is allowed to use + * deprecated Pacemaker APIs. If PCMK_ALLOW_DEPRECATED is defined to 0 before + * including any Pacemaker headers, deprecated APIs will be unusable. It is + * strongly recommended to leave this unchanged for production and release + * builds, to avoid breakage when users upgrade to new Pacemaker releases that + * deprecate more APIs. This should be defined to 0 only for development and + * testing builds when desiring to check for usage of currently deprecated APIs. + */ +#define PCMK_ALLOW_DEPRECATED 1 +#endif + +/*! + * The CRM feature set assists with compatibility in mixed-version clusters. + * The major version number increases when nodes with different versions + * would not work (rolling upgrades are not allowed). The minor version + * number increases when mixed-version clusters are allowed only during + * rolling upgrades (a node with the oldest feature set will be elected DC). The + * minor-minor version number is ignored, but allows resource agents to detect + * cluster support for various features. + * + * The feature set also affects the processing of old saved CIBs (such as for + * many scheduler regression tests). + * + * Particular feature points currently tested by Pacemaker code: + * + * >2.1: Operation updates include timing data + * >=3.0.5: XML v2 digests are created + * >=3.0.8: Peers do not need acks for cancellations + * >=3.0.9: DC will send its own shutdown request to all peers + * XML v2 patchsets are created by default + * >=3.0.13: Fail counts include operation name and interval + * >=3.2.0: DC supports PCMK_EXEC_INVALID and PCMK_EXEC_NOT_CONNECTED + */ +# define CRM_FEATURE_SET "3.17.4" + +/* Pacemaker's CPG protocols use fixed-width binary fields for the sender and + * recipient of a CPG message. This imposes an arbitrary limit on cluster node + * names. + */ +//! \brief Maximum length of a Corosync cluster node name (in bytes) +#define MAX_NAME 256 + +# define CRM_META "CRM_meta" + +extern char *crm_system_name; + +/* *INDENT-OFF* */ + +// How we represent "infinite" scores +# define CRM_SCORE_INFINITY 1000000 +# define CRM_INFINITY_S "INFINITY" +# define CRM_PLUS_INFINITY_S "+" CRM_INFINITY_S +# define CRM_MINUS_INFINITY_S "-" CRM_INFINITY_S + +/* @COMPAT API < 2.0.0 Deprecated "infinity" aliases + * + * INFINITY might be defined elsewhere (e.g. math.h), so undefine it first. + * This, of course, complicates any attempt to use the other definition in any + * code that includes this header. + */ +# undef INFINITY +# define INFINITY_S "INFINITY" +# define MINUS_INFINITY_S "-INFINITY" +# define INFINITY 1000000 + +/* Sub-systems */ +# define CRM_SYSTEM_DC "dc" +#define CRM_SYSTEM_DCIB "dcib" // Primary instance of CIB manager +# define CRM_SYSTEM_CIB "cib" +# define CRM_SYSTEM_CRMD "crmd" +# define CRM_SYSTEM_LRMD "lrmd" +# define CRM_SYSTEM_PENGINE "pengine" +# define CRM_SYSTEM_TENGINE "tengine" +# define CRM_SYSTEM_STONITHD "stonithd" +# define CRM_SYSTEM_MCP "pacemakerd" + +// Names of internally generated node attributes +# define CRM_ATTR_UNAME "#uname" +# define CRM_ATTR_ID "#id" +# define CRM_ATTR_KIND "#kind" +# define CRM_ATTR_ROLE "#role" +# define CRM_ATTR_IS_DC "#is_dc" +# define CRM_ATTR_CLUSTER_NAME "#cluster-name" +# define CRM_ATTR_SITE_NAME "#site-name" +# define CRM_ATTR_UNFENCED "#node-unfenced" +# define CRM_ATTR_DIGESTS_ALL "#digests-all" +# define CRM_ATTR_DIGESTS_SECURE "#digests-secure" +# define CRM_ATTR_PROTOCOL "#attrd-protocol" +# define CRM_ATTR_FEATURE_SET "#feature-set" + +/* Valid operations */ +# define CRM_OP_NOOP "noop" +# define CRM_OP_JOIN_ANNOUNCE "join_announce" +# define CRM_OP_JOIN_OFFER "join_offer" +# define CRM_OP_JOIN_REQUEST "join_request" +# define CRM_OP_JOIN_ACKNAK "join_ack_nack" +# define CRM_OP_JOIN_CONFIRM "join_confirm" +# define CRM_OP_PING "ping" +# define CRM_OP_NODE_INFO "node-info" +# define CRM_OP_THROTTLE "throttle" +# define CRM_OP_VOTE "vote" +# define CRM_OP_NOVOTE "no-vote" +# define CRM_OP_HELLO "hello" +# define CRM_OP_PECALC "pe_calc" +# define CRM_OP_QUIT "quit" +# define CRM_OP_LOCAL_SHUTDOWN "start_shutdown" +# define CRM_OP_SHUTDOWN_REQ "req_shutdown" +# define CRM_OP_SHUTDOWN "do_shutdown" +# define CRM_OP_FENCE "stonith" +# define CRM_OP_REGISTER "register" +# define CRM_OP_IPC_FWD "ipc_fwd" +# define CRM_OP_INVOKE_LRM "lrm_invoke" +# define CRM_OP_LRM_REFRESH "lrm_refresh" //!< Deprecated since 1.1.10 +# define CRM_OP_LRM_DELETE "lrm_delete" +# define CRM_OP_LRM_FAIL "lrm_fail" +# define CRM_OP_PROBED "probe_complete" +# define CRM_OP_REPROBE "probe_again" +# define CRM_OP_CLEAR_FAILCOUNT "clear_failcount" +# define CRM_OP_REMOTE_STATE "remote_state" +# define CRM_OP_RELAXED_SET "one-or-more" +# define CRM_OP_RELAXED_CLONE "clone-one-or-more" +# define CRM_OP_RM_NODE_CACHE "rm_node_cache" +# define CRM_OP_MAINTENANCE_NODES "maintenance_nodes" + +/* Possible cluster membership states */ +# define CRMD_JOINSTATE_DOWN "down" +# define CRMD_JOINSTATE_PENDING "pending" +# define CRMD_JOINSTATE_MEMBER "member" +# define CRMD_JOINSTATE_NACK "banned" + +# define CRMD_ACTION_DELETE "delete" +# define CRMD_ACTION_CANCEL "cancel" + +# define CRMD_ACTION_RELOAD "reload" +# define CRMD_ACTION_RELOAD_AGENT "reload-agent" +# define CRMD_ACTION_MIGRATE "migrate_to" +# define CRMD_ACTION_MIGRATED "migrate_from" + +# define CRMD_ACTION_START "start" +# define CRMD_ACTION_STARTED "running" + +# define CRMD_ACTION_STOP "stop" +# define CRMD_ACTION_STOPPED "stopped" + +# define CRMD_ACTION_PROMOTE "promote" +# define CRMD_ACTION_PROMOTED "promoted" +# define CRMD_ACTION_DEMOTE "demote" +# define CRMD_ACTION_DEMOTED "demoted" + +# define CRMD_ACTION_NOTIFY "notify" +# define CRMD_ACTION_NOTIFIED "notified" + +# define CRMD_ACTION_STATUS "monitor" +# define CRMD_ACTION_METADATA "meta-data" +# define CRMD_METADATA_CALL_TIMEOUT 30000 + +/* short names */ +# define RSC_DELETE CRMD_ACTION_DELETE +# define RSC_CANCEL CRMD_ACTION_CANCEL + +# define RSC_MIGRATE CRMD_ACTION_MIGRATE +# define RSC_MIGRATED CRMD_ACTION_MIGRATED + +# define RSC_START CRMD_ACTION_START +# define RSC_STARTED CRMD_ACTION_STARTED + +# define RSC_STOP CRMD_ACTION_STOP +# define RSC_STOPPED CRMD_ACTION_STOPPED + +# define RSC_PROMOTE CRMD_ACTION_PROMOTE +# define RSC_PROMOTED CRMD_ACTION_PROMOTED +# define RSC_DEMOTE CRMD_ACTION_DEMOTE +# define RSC_DEMOTED CRMD_ACTION_DEMOTED + +# define RSC_NOTIFY CRMD_ACTION_NOTIFY +# define RSC_NOTIFIED CRMD_ACTION_NOTIFIED + +# define RSC_STATUS CRMD_ACTION_STATUS +# define RSC_METADATA CRMD_ACTION_METADATA +/* *INDENT-ON* */ + +# include <crm/common/cib.h> +# include <crm/common/logging.h> +# include <crm/common/util.h> + +static inline const char * +crm_action_str(const char *task, guint interval_ms) { + if ((task != NULL) && (interval_ms == 0) + && (strcasecmp(task, RSC_STATUS) == 0)) { + return "probe"; + } + return task; +} + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/crm_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/crm_compat.h b/include/crm/crm_compat.h new file mode 100644 index 0000000..2c0a3dd --- /dev/null +++ b/include/crm/crm_compat.h @@ -0,0 +1,61 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_CRM_COMPAT__H +# define PCMK__CRM_CRM_COMPAT__H + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker utilities + * \ingroup core + * \deprecated Do not include this header directly. The utilities in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use '\0' instead +#define EOS '\0' + +//! \deprecated This defined constant will be removed in a future release +#define MAX_IPC_DELAY 120 + +//! \deprecated This defined constant will be removed in a future release +#define CRM_OP_LRM_QUERY "lrm_query" + +//! \deprecated This defined constant will be removed in a future release +#define CRM_ATTR_RA_VERSION "#ra-version" + +//!@{ +//! \deprecated This macro will be removed in a future release + +# ifndef __GNUC__ +# define __builtin_expect(expr, result) (expr) +# endif + +#define __likely(expr) __builtin_expect(expr, 1) + +#define __unlikely(expr) __builtin_expect(expr, 0) + +// This ends the doxygen deprecation comment +//!@} + +//! \deprecated Use GList * instead +typedef GList *GListPtr; + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_CRM_COMPAT__H diff --git a/include/crm/fencing/Makefile.am b/include/crm/fencing/Makefile.am new file mode 100644 index 0000000..12b434d --- /dev/null +++ b/include/crm/fencing/Makefile.am @@ -0,0 +1,13 @@ +# +# Copyright 2012-2021 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/fencing + +noinst_HEADERS = internal.h diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h new file mode 100644 index 0000000..492c035 --- /dev/null +++ b/include/crm/fencing/internal.h @@ -0,0 +1,222 @@ +/* + * Copyright 2011-2022 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 STONITH_NG_INTERNAL__H +# define STONITH_NG_INTERNAL__H + +# include <glib.h> +# include <crm/common/ipc.h> +# include <crm/common/xml.h> +# include <crm/common/output_internal.h> +# include <crm/stonith-ng.h> + +enum st_device_flags { + st_device_supports_none = (0 << 0), + st_device_supports_list = (1 << 0), + st_device_supports_status = (1 << 1), + st_device_supports_reboot = (1 << 2), + st_device_supports_parameter_plug = (1 << 3), + st_device_supports_parameter_port = (1 << 4), + st_device_supports_on = (1 << 5), +}; + +#define stonith__set_device_flags(device_flags, device_id, flags_to_set) do { \ + device_flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ + "Fence device", device_id, \ + (device_flags), (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define stonith__set_call_options(st_call_opts, call_for, flags_to_set) do { \ + st_call_opts = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ + "Fencer call", (call_for), \ + (st_call_opts), (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define stonith__clear_call_options(st_call_opts, call_for, flags_to_clear) do { \ + st_call_opts = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \ + "Fencer call", (call_for), \ + (st_call_opts), (flags_to_clear), \ + #flags_to_clear); \ + } while (0) + +struct stonith_action_s; +typedef struct stonith_action_s stonith_action_t; + +stonith_action_t *stonith__action_create(const char *agent, + const char *action_name, + const char *target, + uint32_t target_nodeid, + int timeout_sec, + GHashTable *device_args, + GHashTable *port_map, + const char *host_arg); +void stonith__destroy_action(stonith_action_t *action); +pcmk__action_result_t *stonith__action_result(stonith_action_t *action); +int stonith__result2rc(const pcmk__action_result_t *result); +void stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result); +void stonith__xe_get_result(const xmlNode *xml, pcmk__action_result_t *result); +xmlNode *stonith__find_xe_with_result(xmlNode *xml); + +int stonith__execute_async(stonith_action_t *action, void *userdata, + void (*done) (int pid, + const pcmk__action_result_t *result, + void *user_data), + void (*fork_cb) (int pid, void *user_data)); + +int stonith__metadata_async(const char *agent, int timeout_sec, + void (*callback)(int pid, + const pcmk__action_result_t *result, + void *user_data), + void *user_data); + +xmlNode *create_level_registration_xml(const char *node, const char *pattern, + const char *attr, const char *value, + int level, + const stonith_key_value_t *device_list); + +xmlNode *create_device_registration_xml(const char *id, + enum stonith_namespace namespace, + const char *agent, + const stonith_key_value_t *params, + const char *rsc_provides); + +void stonith__register_messages(pcmk__output_t *out); + +GList *stonith__parse_targets(const char *hosts); + +const char *stonith__later_succeeded(const stonith_history_t *event, + const stonith_history_t *top_history); +stonith_history_t *stonith__sort_history(stonith_history_t *history); + +void stonith__device_parameter_flags(uint32_t *device_flags, + const char *device_name, + xmlNode *metadata); + +# define ST_LEVEL_MAX 10 + +# define F_STONITH_CLIENTID "st_clientid" +# define F_STONITH_CALLOPTS "st_callopt" +# define F_STONITH_CALLID "st_callid" +# define F_STONITH_CALLDATA "st_calldata" +# define F_STONITH_OPERATION "st_op" +# define F_STONITH_TARGET "st_target" +# define F_STONITH_REMOTE_OP_ID "st_remote_op" +# define F_STONITH_REMOTE_OP_ID_RELAY "st_remote_op_relay" +# define F_STONITH_RC "st_rc" +# define F_STONITH_OUTPUT "st_output" +/*! Timeout period per a device execution */ +# define F_STONITH_TIMEOUT "st_timeout" +# define F_STONITH_TOLERANCE "st_tolerance" +# define F_STONITH_DELAY "st_delay" +/*! Action specific timeout period returned in query of fencing devices. */ +# define F_STONITH_ACTION_TIMEOUT "st_action_timeout" +/*! Host in query result is not allowed to run this action */ +# define F_STONITH_ACTION_DISALLOWED "st_action_disallowed" +/*! Maximum of random fencing delay for a device */ +# define F_STONITH_DELAY_MAX "st_delay_max" +/*! Base delay used for a fencing delay */ +# define F_STONITH_DELAY_BASE "st_delay_base" +/*! Has this device been verified using a monitor type + * operation (monitor, list, status) */ +# define F_STONITH_DEVICE_VERIFIED "st_monitor_verified" +/*! device is required for this action */ +# define F_STONITH_DEVICE_REQUIRED "st_required" +/*! number of available devices in query result */ +# define F_STONITH_AVAILABLE_DEVICES "st-available-devices" +# define F_STONITH_CALLBACK_TOKEN "st_async_id" +# define F_STONITH_CLIENTNAME "st_clientname" +# define F_STONITH_CLIENTNODE "st_clientnode" +# define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate" +# define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate" +# define F_STONITH_DELEGATE "st_delegate" +# define F_STONITH_DEVICE_SUPPORT_FLAGS "st_device_support_flags" +/*! The node initiating the stonith operation. If an operation + * is relayed, this is the last node the operation lands on. When + * in standalone mode, origin is the client's id that originated the + * operation. */ +# define F_STONITH_ORIGIN "st_origin" +# define F_STONITH_HISTORY_LIST "st_history" +# define F_STONITH_DATE "st_date" +# define F_STONITH_DATE_NSEC "st_date_nsec" +# define F_STONITH_STATE "st_state" +# define F_STONITH_ACTIVE "st_active" +# define F_STONITH_DIFFERENTIAL "st_differential" + +# define F_STONITH_DEVICE "st_device_id" +# define F_STONITH_ACTION "st_device_action" +# define F_STONITH_MERGED "st_op_merged" + +# define T_STONITH_NG "stonith-ng" +# define T_STONITH_REPLY "st-reply" +/*! For async operations, an event from the server containing + * the total amount of time the server is allowing for the operation + * to take place is returned to the client. */ +# define T_STONITH_TIMEOUT_VALUE "st-async-timeout-value" +# define T_STONITH_NOTIFY "st_notify" + +# define STONITH_ATTR_ACTION_OP "action" + +# define STONITH_OP_EXEC "st_execute" +# define STONITH_OP_TIMEOUT_UPDATE "st_timeout_update" +# define STONITH_OP_QUERY "st_query" +# define STONITH_OP_FENCE "st_fence" +# define STONITH_OP_RELAY "st_relay" +# define STONITH_OP_DEVICE_ADD "st_device_register" +# define STONITH_OP_DEVICE_DEL "st_device_remove" +# define STONITH_OP_FENCE_HISTORY "st_fence_history" +# define STONITH_OP_LEVEL_ADD "st_level_add" +# define STONITH_OP_LEVEL_DEL "st_level_remove" + +# define STONITH_WATCHDOG_AGENT "fence_watchdog" +/* Don't change 2 below as it would break rolling upgrade */ +# define STONITH_WATCHDOG_AGENT_INTERNAL "#watchdog" +# define STONITH_WATCHDOG_ID "watchdog" + +stonith_history_t *stonith__first_matching_event(stonith_history_t *history, + bool (*matching_fn)(stonith_history_t *, void *), + void *user_data); +bool stonith__event_state_pending(stonith_history_t *history, void *user_data); +bool stonith__event_state_eq(stonith_history_t *history, void *user_data); +bool stonith__event_state_neq(stonith_history_t *history, void *user_data); + +int stonith__legacy2status(int rc); + +int stonith__exit_status(const stonith_callback_data_t *data); +int stonith__execution_status(const stonith_callback_data_t *data); +const char *stonith__exit_reason(const stonith_callback_data_t *data); + +int stonith__event_exit_status(const stonith_event_t *event); +int stonith__event_execution_status(const stonith_event_t *event); +const char *stonith__event_exit_reason(const stonith_event_t *event); +char *stonith__event_description(const stonith_event_t *event); +gchar *stonith__history_description(const stonith_history_t *event, + bool full_history, + const char *later_succeeded, + uint32_t show_opts); + +/*! + * \internal + * \brief Is a fencing operation in pending state? + * + * \param[in] state State as enum op_state value + * + * \return A boolean + */ +static inline bool +stonith__op_state_pending(enum op_state state) +{ + return state != st_failed && state != st_done; +} + +gboolean stonith__watchdog_fencing_enabled_for_node(const char *node); +gboolean stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node); + +#endif diff --git a/include/crm/lrmd.h b/include/crm/lrmd.h new file mode 100644 index 0000000..dfc2f25 --- /dev/null +++ b/include/crm/lrmd.h @@ -0,0 +1,628 @@ +/* + * Copyright 2012-2022 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 PCMK__CRM_LRMD__H +# define PCMK__CRM_LRMD__H + +#include <stdbool.h> // bool +#include <glib.h> // guint, GList +#include <crm_config.h> +#include <crm/services.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Resource agent executor + * \ingroup lrmd + */ + +typedef struct lrmd_s lrmd_t; +typedef struct lrmd_key_value_s { + char *key; + char *value; + struct lrmd_key_value_s *next; +} lrmd_key_value_t; + +/* This should be bumped every time there is an incompatible change that + * prevents older clients from connecting to this version of the server. + */ +#define LRMD_PROTOCOL_VERSION "1.1" + +/* This is the version that the client version will actually be compared + * against. This should be identical to LRMD_PROTOCOL_VERSION. However, we + * accidentally bumped LRMD_PROTOCOL_VERSION in 6424a647 (1.1.15) when we didn't + * need to, so for now it's different. If we ever have a truly incompatible + * bump, we can drop this and compare against LRMD_PROTOCOL_VERSION. + */ +#define LRMD_MIN_PROTOCOL_VERSION "1.0" + +/* *INDENT-OFF* */ +#define DEFAULT_REMOTE_KEY_LOCATION PACEMAKER_CONFIG_DIR "/authkey" +#define ALT_REMOTE_KEY_LOCATION "/etc/corosync/authkey" +#define DEFAULT_REMOTE_PORT 3121 +#define DEFAULT_REMOTE_USERNAME "lrmd" + +#define F_LRMD_OPERATION "lrmd_op" +#define F_LRMD_CLIENTNAME "lrmd_clientname" +#define F_LRMD_IS_IPC_PROVIDER "lrmd_is_ipc_provider" +#define F_LRMD_CLIENTID "lrmd_clientid" +#define F_LRMD_PROTOCOL_VERSION "lrmd_protocol_version" +#define F_LRMD_REMOTE_MSG_TYPE "lrmd_remote_msg_type" +#define F_LRMD_REMOTE_MSG_ID "lrmd_remote_msg_id" +#define F_LRMD_CALLBACK_TOKEN "lrmd_async_id" +#define F_LRMD_CALLID "lrmd_callid" +#define F_LRMD_CALLOPTS "lrmd_callopt" +#define F_LRMD_CALLDATA "lrmd_calldata" +#define F_LRMD_RC "lrmd_rc" +#define F_LRMD_EXEC_RC "lrmd_exec_rc" +#define F_LRMD_OP_STATUS "lrmd_exec_op_status" +#define F_LRMD_TIMEOUT "lrmd_timeout" +#define F_LRMD_WATCHDOG "lrmd_watchdog" +#define F_LRMD_CLASS "lrmd_class" +#define F_LRMD_PROVIDER "lrmd_provider" +#define F_LRMD_TYPE "lrmd_type" +#define F_LRMD_ORIGIN "lrmd_origin" + +#define F_LRMD_RSC_RUN_TIME "lrmd_run_time" +#define F_LRMD_RSC_RCCHANGE_TIME "lrmd_rcchange_time" +#define F_LRMD_RSC_EXEC_TIME "lrmd_exec_time" +#define F_LRMD_RSC_QUEUE_TIME "lrmd_queue_time" + +#define F_LRMD_RSC_ID "lrmd_rsc_id" +#define F_LRMD_RSC_ACTION "lrmd_rsc_action" +#define F_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str" +#define F_LRMD_RSC_OUTPUT "lrmd_rsc_output" +#define F_LRMD_RSC_EXIT_REASON "lrmd_rsc_exit_reason" +#define F_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay" +#define F_LRMD_RSC_INTERVAL "lrmd_rsc_interval" +#define F_LRMD_RSC_DELETED "lrmd_rsc_deleted" +#define F_LRMD_RSC "lrmd_rsc" + +#define F_LRMD_ALERT_ID "lrmd_alert_id" +#define F_LRMD_ALERT_PATH "lrmd_alert_path" +#define F_LRMD_ALERT "lrmd_alert" + +#define LRMD_OP_RSC_REG "lrmd_rsc_register" +#define LRMD_OP_RSC_EXEC "lrmd_rsc_exec" +#define LRMD_OP_RSC_CANCEL "lrmd_rsc_cancel" +#define LRMD_OP_RSC_UNREG "lrmd_rsc_unregister" +#define LRMD_OP_RSC_INFO "lrmd_rsc_info" +#define LRMD_OP_RSC_METADATA "lrmd_rsc_metadata" +#define LRMD_OP_POKE "lrmd_rsc_poke" +#define LRMD_OP_NEW_CLIENT "lrmd_rsc_new_client" +#define LRMD_OP_CHECK "lrmd_check" +#define LRMD_OP_ALERT_EXEC "lrmd_alert_exec" +#define LRMD_OP_GET_RECURRING "lrmd_get_recurring" + +#define LRMD_IPC_OP_NEW "new" +#define LRMD_IPC_OP_DESTROY "destroy" +#define LRMD_IPC_OP_EVENT "event" +#define LRMD_IPC_OP_REQUEST "request" +#define LRMD_IPC_OP_RESPONSE "response" +#define LRMD_IPC_OP_SHUTDOWN_REQ "shutdown_req" +#define LRMD_IPC_OP_SHUTDOWN_ACK "shutdown_ack" +#define LRMD_IPC_OP_SHUTDOWN_NACK "shutdown_nack" + +#define F_LRMD_IPC_OP "lrmd_ipc_op" +#define F_LRMD_IPC_IPC_SERVER "lrmd_ipc_server" +#define F_LRMD_IPC_SESSION "lrmd_ipc_session" +#define F_LRMD_IPC_CLIENT "lrmd_ipc_client" +#define F_LRMD_IPC_USER "lrmd_ipc_user" +#define F_LRMD_IPC_MSG "lrmd_ipc_msg" +#define F_LRMD_IPC_MSG_ID "lrmd_ipc_msg_id" +#define F_LRMD_IPC_MSG_FLAGS "lrmd_ipc_msg_flags" + +#define T_LRMD "lrmd" +#define T_LRMD_REPLY "lrmd_reply" +#define T_LRMD_NOTIFY "lrmd_notify" +#define T_LRMD_IPC_PROXY "lrmd_ipc_proxy" +#define T_LRMD_RSC_OP "lrmd_rsc_op" +/* *INDENT-ON* */ + +/*! + * \brief Create a new connection to the local executor + */ +lrmd_t *lrmd_api_new(void); + +/*! + * \brief Create a new TLS connection to a remote executor + * + * \param[in] nodename Name of remote node identified with this connection + * \param[in] server Hostname to connect to + * \param[in] port Port number to connect to (or 0 to use default) + * + * \return Newly created executor connection object + * \note If only one of \p nodename and \p server is non-NULL, it will be used + * for both purposes. If both are NULL, a local IPC connection will be + * created instead. + */ +lrmd_t *lrmd_remote_api_new(const char *nodename, const char *server, int port); + +/*! + * \brief Use after lrmd_poll returns 1 to read and dispatch a message + * + * \param[in,out] lrmd Executor connection object + * + * \return TRUE if connection is still up, FALSE if disconnected + */ +bool lrmd_dispatch(lrmd_t *lrmd); + +/*! + * \brief Check whether a message is available on an executor connection + * + * \param[in,out] lrmd Executor connection object to check + * \param[in] timeout Currently ignored + * + * \retval 1 Message is ready + * \retval 0 Timeout occurred + * \retval negative errno Error occurred + * + * \note This is intended for callers that do not use a main loop. + */ +int lrmd_poll(lrmd_t *lrmd, int timeout); + +/*! + * \brief Destroy executor connection object + * + * \param[in,out] lrmd Executor connection object to destroy + */ +void lrmd_api_delete(lrmd_t *lrmd); + +lrmd_key_value_t *lrmd_key_value_add(lrmd_key_value_t * kvp, const char *key, const char *value); + +enum lrmd_call_options { + lrmd_opt_none = 0, + + //! Notify only the client that made the request (rather than all clients) + lrmd_opt_notify_orig_only = (1 << 1), + + /*! + * Drop recurring operations initiated by a client when the client + * disconnects. This option is only valid when registering a resource. When + * used with a connection to a remote executor, recurring operations will be + * dropped once all remote connections disconnect. + * + * @COMPAT This is broken, because these values should be unique bits, and + * this value overlaps lrmd_opt_notify_orig_only (0x02). The impact is low + * since this value is used only with registration requests and the other + * one is used only with execution requests. Regardless, when we can break + * API compatibility, this should be changed to (1 << 0) or (1 << 3). + */ + lrmd_opt_drop_recurring = 0x00000003, + + //! Send notifications for recurring operations only when the result changes + lrmd_opt_notify_changes_only = (1 << 2), +}; + +enum lrmd_callback_event { + lrmd_event_register, + lrmd_event_unregister, + lrmd_event_exec_complete, + lrmd_event_disconnect, + lrmd_event_connect, + lrmd_event_poke, + lrmd_event_new_client, +}; + +typedef struct lrmd_event_data_s { + /*! Type of event, register, unregister, call_completed... */ + enum lrmd_callback_event type; + + /*! The resource this event occurred on. */ + const char *rsc_id; + /*! The action performed, start, stop, monitor... */ + const char *op_type; + /*! The user data passed by caller of exec() API function */ + const char *user_data; + + /*! The client api call id associated with this event */ + int call_id; + /*! The operation's timeout period in ms. */ + int timeout; + /*! The operation's recurring interval in ms. */ + guint interval_ms; + /*! The operation's start delay value in ms. */ + int start_delay; + /*! This operation that just completed is on a deleted rsc. */ + int rsc_deleted; + + /*! The executed ra return code mapped to OCF */ + enum ocf_exitcode rc; + /*! The executor status returned for exec_complete events */ + int op_status; + /*! stdout from resource agent operation */ + const char *output; + /*! Timestamp of when op ran */ + unsigned int t_run; + /*! Timestamp of last rc change */ + unsigned int t_rcchange; + /*! Time in length op took to execute */ + unsigned int exec_time; + /*! Time in length spent in queue */ + unsigned int queue_time; + + /*! int connection result. Used for connection and poke events */ + int connection_rc; + + /* This is a GHashTable containing the + * parameters given to the operation */ + void *params; + + /*! client node name associated with this connection + * (used to match actions to the proper client when there are multiple) + */ + const char *remote_nodename; + + /*! exit failure reason string from resource agent operation */ + const char *exit_reason; +} lrmd_event_data_t; + +lrmd_event_data_t *lrmd_new_event(const char *rsc_id, const char *task, + guint interval_ms); +lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t * event); +void lrmd_free_event(lrmd_event_data_t * event); + +typedef struct lrmd_rsc_info_s { + char *id; + char *type; + char *standard; + char *provider; +} lrmd_rsc_info_t; + +typedef struct lrmd_op_info_s { + char *rsc_id; + char *action; + char *interval_ms_s; + char *timeout_ms_s; +} lrmd_op_info_t; + +lrmd_rsc_info_t *lrmd_new_rsc_info(const char *rsc_id, const char *standard, + const char *provider, const char *type); +lrmd_rsc_info_t *lrmd_copy_rsc_info(lrmd_rsc_info_t * rsc_info); +void lrmd_free_rsc_info(lrmd_rsc_info_t * rsc_info); +void lrmd_free_op_info(lrmd_op_info_t *op_info); + +typedef void (*lrmd_event_callback) (lrmd_event_data_t * event); + +typedef struct lrmd_list_s { + const char *val; + struct lrmd_list_s *next; +} lrmd_list_t; + +void lrmd_list_freeall(lrmd_list_t * head); +void lrmd_key_value_freeall(lrmd_key_value_t * head); + +typedef struct lrmd_api_operations_s { + /*! + * \brief Connect to an executor + * + * \param[in,out] lrmd Executor connection object + * \param[in] client_name Arbitrary identifier to pass to server + * \param[out] fd If not NULL, where to store file descriptor + * for connection's socket + * + * \return Legacy Pacemaker return code + */ + int (*connect) (lrmd_t *lrmd, const char *client_name, int *fd); + + /*! + * \brief Initiate an executor connection without blocking + * + * \param[in,out] lrmd Executor connection object + * \param[in] client_name Arbitrary identifier to pass to server + * \param[in] timeout Error if not connected within this time + * (milliseconds) + * + * \return Legacy Pacemaker return code (if pcmk_ok, the event callback will + * be called later with the result) + * \note This function requires a mainloop. + */ + int (*connect_async) (lrmd_t *lrmd, const char *client_name, + int timeout /*ms */ ); + + /*! + * \brief Check whether connection to executor daemon is (still) active + * + * \param[in,out] lrmd Executor connection object to check + * + * \return 1 if the executor connection is active, 0 otherwise + */ + int (*is_connected) (lrmd_t *lrmd); + + /*! + * \brief Poke executor connection to verify it is still active + * + * \param[in,out] lrmd Executor connection object to check + * + * \return Legacy Pacemaker return code (if pcmk_ok, the event callback will + * be called later with the result) + * \note The response comes in the form of a poke event to the callback. + * + */ + int (*poke_connection) (lrmd_t *lrmd); + + /*! + * \brief Disconnect from the executor. + * + * \param[in,out] lrmd Executor connection object to disconnect + * + * \return Legacy Pacemaker return code + */ + int (*disconnect) (lrmd_t *lrmd); + + /*! + * \brief Register a resource with the executor + * + * \param[in,out] lrmd Executor connection object + * \param[in] rsc_id ID of resource to register + * \param[in] standard Resource's resource agent standard + * \param[in] provider Resource's resource agent provider (or NULL) + * \param[in] agent Resource's resource agent name + * \param[in] options Group of enum lrmd_call_options flags + * + * \note Synchronous, guaranteed to occur in daemon before function returns. + * + * \return Legacy Pacemaker return code + */ + int (*register_rsc) (lrmd_t *lrmd, const char *rsc_id, const char *standard, + const char *provider, const char *agent, + enum lrmd_call_options options); + + /*! + * \brief Retrieve a resource's registration information + * + * \param[in,out] lrmd Executor connection object + * \param[in] rsc_id ID of resource to check + * \param[in] options Group of enum lrmd_call_options flags + * + * \return Resource information on success, otherwise NULL + */ + lrmd_rsc_info_t *(*get_rsc_info) (lrmd_t *lrmd, const char *rsc_id, + enum lrmd_call_options options); + + /*! + * \brief Retrieve recurring operations registered for a resource + * + * \param[in,out] lrmd Executor connection object + * \param[in] rsc_id ID of resource to check + * \param[in] timeout_ms Error if not completed within this time + * \param[in] options Group of enum lrmd_call_options flags + * \param[out] output Where to store list of lrmd_op_info_t + * + * \return Legacy Pacemaker return code + */ + int (*get_recurring_ops) (lrmd_t *lrmd, const char *rsc_id, int timeout_ms, + enum lrmd_call_options options, GList **output); + + /*! + * \brief Unregister a resource from the executor + * + * \param[in,out] lrmd Executor connection object + * \param[in] rsc_id ID of resource to unregister + * \param[in] options Group of enum lrmd_call_options flags + * + * \return Legacy Pacemaker return code (of particular interest, EINPROGRESS + * means that operations are in progress for the resource, and the + * unregistration will be done when they complete) + * \note Pending and recurring operations will be cancelled. + * \note Synchronous, guaranteed to occur in daemon before function returns. + * + */ + int (*unregister_rsc) (lrmd_t *lrmd, const char *rsc_id, + enum lrmd_call_options options); + + /*! + * \brief Set a callback for executor events + * + * \param[in,out] lrmd Executor connection object + * \param[in] callback Callback to set + */ + void (*set_callback) (lrmd_t *lrmd, lrmd_event_callback callback); + + /*! + * \brief Request execution of a resource action + * + * \param[in,out] lrmd Executor connection object + * \param[in] rsc_id ID of resource + * \param[in] action Name of resource action to execute + * \param[in] userdata Arbitrary string to pass to event callback + * \param[in] interval_ms If 0, execute action once, otherwise + * recurring at this interval (in milliseconds) + * \param[in] timeout Error if not complete within this time (in + * milliseconds) + * \param[in] start_delay Wait this long before execution (in + * milliseconds) + * \param[in] options Group of enum lrmd_call_options flags + * \param[in,out] params Parameters to pass to agent (will be freed) + * + * \return A call ID for the action on success (in which case the action is + * queued in the executor, and the event callback will be called + * later with the result), otherwise a negative legacy Pacemaker + * return code + * \note exec() and cancel() operations on an individual resource are + * guaranteed to occur in the order the client API is called. However, + * operations on different resources are not guaranteed to occur in + * any specific order. + */ + int (*exec) (lrmd_t *lrmd, const char *rsc_id, const char *action, + const char *userdata, guint interval_ms, int timeout, + int start_delay, enum lrmd_call_options options, + lrmd_key_value_t *params); + + /*! + * \brief Cancel a recurring resource action + * + * \param[in,out] lrmd Executor connection object + * \param[in] rsc_id ID of resource + * \param[in] action Name of resource action to cancel + * \param[in] interval_ms Action's interval (in milliseconds) + * + * \return Legacy Pacemaker return code (if pcmk_ok, cancellation is queued + * on function return, and the event callback will be called later + * with an exec_complete event with an lrmd_op_status signifying + * that the operation is cancelled) + * + * \note exec() and cancel() operations on an individual resource are + * guaranteed to occur in the order the client API is called. However, + * operations on different resources are not guaranteed to occur in + * any specific order. + */ + int (*cancel) (lrmd_t *lrmd, const char *rsc_id, const char *action, + guint interval_ms); + + /*! + * \brief Retrieve resource agent metadata synchronously + * + * \param[in] lrmd Executor connection (unused) + * \param[in] standard Resource agent class + * \param[in] provider Resource agent provider + * \param[in] agent Resource agent type + * \param[out] output Where to store metadata (must not be NULL) + * \param[in] options Group of enum lrmd_call_options flags (unused) + * + * \return Legacy Pacemaker return code + * + * \note Caller is responsible for freeing output. This call is always + * synchronous (blocking), and always done directly by the library + * (not via the executor connection). This means that it is based on + * the local host environment, even if the executor connection is to a + * remote node, so this may fail if the agent is not installed + * locally. This also means that, if an external agent must be + * executed, it will be executed by the caller's user, not the + * executor's. + */ + int (*get_metadata) (lrmd_t *lrmd, const char *standard, + const char *provider, const char *agent, + char **output, enum lrmd_call_options options); + + /*! + * \brief Retrieve a list of installed resource agents + * + * \param[in] lrmd Executor connection (unused) + * \param[out] agents Where to store agent list (must not be NULL) + * \param[in] standard Resource agent standard to list + * \param[in] provider Resource agent provider to list (or NULL) + * + * \return Number of items in list on success, negative legacy Pacemaker + * return code otherwise + * + * \note if standard is not provided, all known agents will be returned + * \note list must be freed using lrmd_list_freeall() + */ + int (*list_agents) (lrmd_t *lrmd, lrmd_list_t **agents, + const char *standard, const char *provider); + + /*! + * \brief Retrieve a list of resource agent providers + * + * \param[in] lrmd Executor connection (unused) + * \param[in] agent If not NULL, list providers for this agent only + * \param[out] providers Where to store provider list + * + * \return Number of items in list on success, negative legacy Pacemaker + * return code otherwise + * \note The caller is responsible for freeing *providers with + * lrmd_list_freeall(). + */ + int (*list_ocf_providers) (lrmd_t *lrmd, const char *agent, + lrmd_list_t **providers); + + /*! + * \brief Retrieve a list of supported standards + * + * \param[in] lrmd Executor connection (unused) + * \param[out] standards Where to store standards list + * + * \return Number of items in list on success, negative legacy Pacemaker + * return code otherwise + * \note The caller is responsible for freeing *standards with + * lrmd_list_freeall(). + */ + int (*list_standards) (lrmd_t *lrmd, lrmd_list_t **standards); + + /*! + * \brief Execute an alert agent + * + * \param[in,out] lrmd Executor connection + * \param[in] alert_id Name of alert to execute + * \param[in] alert_path Full path to alert executable + * \param[in] timeout Error if not complete within this many + * milliseconds + * \param[in,out] params Parameters to pass to agent (will be freed) + * + * \return Legacy Pacemaker return code (if pcmk_ok, the alert is queued in + * the executor, and the event callback will be called later with + * the result) + * + * \note Operations on individual alerts (by ID) are guaranteed to occur in + * the order the client API is called. Operations on different alerts + * are not guaranteed to occur in any specific order. + */ + int (*exec_alert) (lrmd_t *lrmd, const char *alert_id, + const char *alert_path, int timeout, + lrmd_key_value_t *params); + + /*! + * \brief Retrieve resource agent metadata synchronously with parameters + * + * \param[in] lrmd Executor connection (unused) + * \param[in] standard Resource agent class + * \param[in] provider Resource agent provider + * \param[in] agent Resource agent type + * \param[out] output Where to store metadata (must not be NULL) + * \param[in] options Group of enum lrmd_call_options flags (unused) + * \param[in,out] params Parameters to pass to agent (will be freed) + * + * \return Legacy Pacemaker return code + * + * \note This is identical to the get_metadata() API call, except parameters + * will be passed to the resource agent via environment variables. + */ + int (*get_metadata_params) (lrmd_t *lrmd, const char *standard, + const char *provider, const char *agent, + char **output, enum lrmd_call_options options, + lrmd_key_value_t *params); + +} lrmd_api_operations_t; + +struct lrmd_s { + lrmd_api_operations_t *cmds; + void *lrmd_private; +}; + +static inline const char * +lrmd_event_type2str(enum lrmd_callback_event type) +{ + switch (type) { + case lrmd_event_register: + return "register"; + case lrmd_event_unregister: + return "unregister"; + case lrmd_event_exec_complete: + return "exec_complete"; + case lrmd_event_disconnect: + return "disconnect"; + case lrmd_event_connect: + return "connect"; + case lrmd_event_poke: + return "poke"; + case lrmd_event_new_client: + return "new_client"; + } + return "unknown"; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/lrmd_internal.h b/include/crm/lrmd_internal.h new file mode 100644 index 0000000..5810554 --- /dev/null +++ b/include/crm/lrmd_internal.h @@ -0,0 +1,91 @@ +/* + * Copyright 2015-2023 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 LRMD_INTERNAL__H +#define LRMD_INTERNAL__H + +#include <stdint.h> // uint32_t +#include <glib.h> // GList, GHashTable, gpointer +#include <libxml/tree.h> // xmlNode +#include <crm/common/ipc.h> // crm_ipc_t +#include <crm/common/mainloop.h> // mainloop_io_t, ipc_client_callbacks +#include <crm/common/output_internal.h> // pcmk__output_t +#include <crm/common/remote_internal.h> // pcmk__remote_t +#include <crm/lrmd.h> // lrmd_t, lrmd_event_data_t, lrmd_rsc_info_t + +int lrmd__new(lrmd_t **api, const char *nodename, const char *server, int port); + +int lrmd_send_attribute_alert(lrmd_t *lrmd, const GList *alert_list, + const char *node, uint32_t nodeid, + const char *attr_name, const char *attr_value); +int lrmd_send_node_alert(lrmd_t *lrmd, const GList *alert_list, + const char *node, uint32_t nodeid, const char *state); +int lrmd_send_fencing_alert(lrmd_t *lrmd, const GList *alert_list, + const char *target, const char *task, + const char *desc, int op_rc); +int lrmd_send_resource_alert(lrmd_t *lrmd, const GList *alert_list, + const char *node, const lrmd_event_data_t *op); + +int lrmd__remote_send_xml(pcmk__remote_t *session, xmlNode *msg, uint32_t id, + const char *msg_type); + +int lrmd__metadata_async(const lrmd_rsc_info_t *rsc, + void (*callback)(int pid, + const pcmk__action_result_t *result, + void *user_data), + void *user_data); + +void lrmd__set_result(lrmd_event_data_t *event, enum ocf_exitcode rc, + int op_status, const char *exit_reason); + +void lrmd__reset_result(lrmd_event_data_t *event); + +time_t lrmd__uptime(lrmd_t *lrmd); + +/* Shared functions for IPC proxy back end */ + +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 lrmd__validate_remote_settings(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); + +void lrmd__register_messages(pcmk__output_t *out); + +#ifdef HAVE_GNUTLS_GNUTLS_H +int lrmd__init_remote_key(gnutls_datum_t *key); +#endif + +#endif diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h new file mode 100644 index 0000000..2e50adb --- /dev/null +++ b/include/crm/msg_xml.h @@ -0,0 +1,487 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_MSG_XML__H +# define PCMK__CRM_MSG_XML__H + +# include <crm/common/xml.h> + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/msg_xml_compat.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* This file defines constants for various XML syntax (mainly element and + * attribute names). + * + * For consistency, new constants should start with "PCMK_", followed by "XE" + * for XML element names, "XA" for XML attribute names, and "META" for meta + * attribute names. Old names that don't follow this policy should eventually be + * deprecated and replaced with names that do. + */ + +/* + * XML elements + */ + +#define PCMK_XE_DATE_EXPRESSION "date_expression" +#define PCMK_XE_OP_EXPRESSION "op_expression" + +/* This has been deprecated as a CIB element (an alias for <clone> with + * "promotable" set to "true") since 2.0.0. + */ +#define PCMK_XE_PROMOTABLE_LEGACY "master" + +#define PCMK_XE_RSC_EXPRESSION "rsc_expression" + + +/* + * XML attributes + */ + +/* These have been deprecated as CIB <clone> element attributes (aliases for + * "promoted-max" and "promoted-node-max") since 2.0.0. + */ +#define PCMK_XA_PROMOTED_MAX_LEGACY "master-max" +#define PCMK_XA_PROMOTED_NODE_MAX_LEGACY "master-node-max" + + +/* + * Meta attributes + */ + +#define PCMK_META_ENABLED "enabled" + + +/* + * Older constants that don't follow current naming + */ + +# ifndef F_ORIG +# define F_ORIG "src" +# endif + +# ifndef F_SEQ +# define F_SEQ "seq" +# endif + +# ifndef F_SUBTYPE +# define F_SUBTYPE "subt" +# endif + +# ifndef F_TYPE +# define F_TYPE "t" +# endif + +# ifndef F_CLIENTNAME +# define F_CLIENTNAME "cn" +# endif + +# ifndef F_XML_TAGNAME +# define F_XML_TAGNAME "__name__" +# endif + +# ifndef T_CRM +# define T_CRM "crmd" +# endif + +# ifndef T_ATTRD +# define T_ATTRD "attrd" +# endif + +# define CIB_OPTIONS_FIRST "cib-bootstrap-options" + +# define F_CRM_DATA "crm_xml" +# define F_CRM_TASK "crm_task" +# define F_CRM_HOST_TO "crm_host_to" +# define F_CRM_MSG_TYPE F_SUBTYPE +# define F_CRM_SYS_TO "crm_sys_to" +# define F_CRM_SYS_FROM "crm_sys_from" +# define F_CRM_HOST_FROM F_ORIG +# define F_CRM_REFERENCE XML_ATTR_REFERENCE +# define F_CRM_VERSION XML_ATTR_VERSION +# define F_CRM_ORIGIN "origin" +# define F_CRM_USER "crm_user" +# define F_CRM_JOIN_ID "join_id" +# define F_CRM_DC_LEAVING "dc-leaving" +# define F_CRM_ELECTION_ID "election-id" +# define F_CRM_ELECTION_AGE_S "election-age-sec" +# define F_CRM_ELECTION_AGE_US "election-age-nano-sec" +# define F_CRM_ELECTION_OWNER "election-owner" +# define F_CRM_TGRAPH "crm-tgraph-file" +# define F_CRM_TGRAPH_INPUT "crm-tgraph-in" + +# define F_CRM_THROTTLE_MODE "crm-limit-mode" +# define F_CRM_THROTTLE_MAX "crm-limit-max" + +/*---- Common tags/attrs */ +# define XML_DIFF_MARKER "__crm_diff_marker__" +# define XML_TAG_CIB "cib" +# define XML_TAG_FAILED "failed" + +# define XML_ATTR_CRM_VERSION "crm_feature_set" +# define XML_ATTR_DIGEST "digest" +# define XML_ATTR_VALIDATION "validate-with" + +# define XML_ATTR_QUORUM_PANIC "no-quorum-panic" +# define XML_ATTR_HAVE_QUORUM "have-quorum" +# define XML_ATTR_HAVE_WATCHDOG "have-watchdog" +# define XML_ATTR_GENERATION "epoch" +# define XML_ATTR_GENERATION_ADMIN "admin_epoch" +# define XML_ATTR_NUMUPDATES "num_updates" +# define XML_ATTR_TIMEOUT "timeout" +# define XML_ATTR_ORIGIN "crm-debug-origin" +# define XML_ATTR_TSTAMP "crm-timestamp" +# define XML_CIB_ATTR_WRITTEN "cib-last-written" +# define XML_ATTR_VERSION "version" +# define XML_ATTR_DESC "description" +# define XML_ATTR_ID "id" +# define XML_ATTR_NAME "name" +# define XML_ATTR_IDREF "id-ref" +# define XML_ATTR_ID_LONG "long-id" +# define XML_ATTR_TYPE "type" +# define XML_ATTR_VERBOSE "verbose" +# define XML_ATTR_OP "op" +# define XML_ATTR_DC_UUID "dc-uuid" +# define XML_ATTR_UPDATE_ORIG "update-origin" +# define XML_ATTR_UPDATE_CLIENT "update-client" +# define XML_ATTR_UPDATE_USER "update-user" + +# define XML_BOOLEAN_TRUE "true" +# define XML_BOOLEAN_FALSE "false" +# define XML_BOOLEAN_YES XML_BOOLEAN_TRUE +# define XML_BOOLEAN_NO XML_BOOLEAN_FALSE + +# define XML_TAG_OPTIONS "options" + +/*---- top level tags/attrs */ +# define XML_ATTR_REQUEST "request" +# define XML_ATTR_RESPONSE "response" + +# define XML_ATTR_UNAME "uname" +# define XML_ATTR_REFERENCE "reference" + +# define XML_CRM_TAG_PING "ping_response" +# define XML_PING_ATTR_STATUS "result" +# define XML_PING_ATTR_SYSFROM "crm_subsystem" +# define XML_PING_ATTR_CRMDSTATE "crmd_state" +# define XML_PING_ATTR_PACEMAKERDSTATE "pacemakerd_state" +# define XML_PING_ATTR_PACEMAKERDSTATE_INIT "init" +# define XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS "starting_daemons" +# define XML_PING_ATTR_PACEMAKERDSTATE_WAITPING "wait_for_ping" +# define XML_PING_ATTR_PACEMAKERDSTATE_RUNNING "running" +# define XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN "shutting_down" +# define XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE "shutdown_complete" +# define XML_PING_ATTR_PACEMAKERDSTATE_REMOTE "remote" + +# define XML_TAG_FRAGMENT "cib_fragment" + +# define XML_FAIL_TAG_CIB "failed_update" + +# define XML_FAILCIB_ATTR_ID "id" +# define XML_FAILCIB_ATTR_OBJTYPE "object_type" +# define XML_FAILCIB_ATTR_OP "operation" +# define XML_FAILCIB_ATTR_REASON "reason" + +/*---- CIB specific tags/attrs */ +# define XML_CIB_TAG_SECTION_ALL "all" +# define XML_CIB_TAG_CONFIGURATION "configuration" +# define XML_CIB_TAG_STATUS "status" +# define XML_CIB_TAG_RESOURCES "resources" +# define XML_CIB_TAG_NODES "nodes" +# define XML_CIB_TAG_DOMAINS "domains" +# define XML_CIB_TAG_CONSTRAINTS "constraints" +# define XML_CIB_TAG_CRMCONFIG "crm_config" +# define XML_CIB_TAG_OPCONFIG "op_defaults" +# define XML_CIB_TAG_RSCCONFIG "rsc_defaults" +# define XML_CIB_TAG_ACLS "acls" +# define XML_CIB_TAG_ALERTS "alerts" +# define XML_CIB_TAG_ALERT "alert" +# define XML_CIB_TAG_ALERT_RECIPIENT "recipient" +# define XML_CIB_TAG_ALERT_SELECT "select" +# define XML_CIB_TAG_ALERT_ATTRIBUTES "select_attributes" +# define XML_CIB_TAG_ALERT_FENCING "select_fencing" +# define XML_CIB_TAG_ALERT_NODES "select_nodes" +# define XML_CIB_TAG_ALERT_RESOURCES "select_resources" +# define XML_CIB_TAG_ALERT_ATTR "attribute" + +# define XML_CIB_TAG_STATE "node_state" +# define XML_CIB_TAG_NODE "node" +# define XML_CIB_TAG_NVPAIR "nvpair" + +# define XML_CIB_TAG_PROPSET "cluster_property_set" +# define XML_TAG_ATTR_SETS "instance_attributes" +# define XML_TAG_META_SETS "meta_attributes" +# define XML_TAG_ATTRS "attributes" +# define XML_TAG_PARAMS "parameters" +# define XML_TAG_PARAM "param" +# define XML_TAG_UTILIZATION "utilization" + +# define XML_TAG_RESOURCE_REF "resource_ref" +# define XML_CIB_TAG_RESOURCE "primitive" +# define XML_CIB_TAG_GROUP "group" +# define XML_CIB_TAG_INCARNATION "clone" +# define XML_CIB_TAG_CONTAINER "bundle" + +# define XML_CIB_TAG_RSC_TEMPLATE "template" + +# define XML_RSC_ATTR_TARGET "container-attribute-target" +# define XML_RSC_ATTR_RESTART "restart-type" +# define XML_RSC_ATTR_ORDERED "ordered" +# define XML_RSC_ATTR_INTERLEAVE "interleave" +# define XML_RSC_ATTR_INCARNATION "clone" +# define XML_RSC_ATTR_INCARNATION_MAX "clone-max" +# define XML_RSC_ATTR_INCARNATION_MIN "clone-min" +# define XML_RSC_ATTR_INCARNATION_NODEMAX "clone-node-max" +# define XML_RSC_ATTR_PROMOTABLE "promotable" +# define XML_RSC_ATTR_PROMOTED_MAX "promoted-max" +# define XML_RSC_ATTR_PROMOTED_NODEMAX "promoted-node-max" +# define XML_RSC_ATTR_MANAGED "is-managed" +# define XML_RSC_ATTR_TARGET_ROLE "target-role" +# define XML_RSC_ATTR_UNIQUE "globally-unique" +# define XML_RSC_ATTR_NOTIFY "notify" +# define XML_RSC_ATTR_STICKINESS "resource-stickiness" +# define XML_RSC_ATTR_FAIL_STICKINESS "migration-threshold" +# define XML_RSC_ATTR_FAIL_TIMEOUT "failure-timeout" +# define XML_RSC_ATTR_MULTIPLE "multiple-active" +# define XML_RSC_ATTR_REQUIRES "requires" +# define XML_RSC_ATTR_CONTAINER "container" +# define XML_RSC_ATTR_INTERNAL_RSC "internal_rsc" +# define XML_RSC_ATTR_MAINTENANCE "maintenance" +# define XML_RSC_ATTR_REMOTE_NODE "remote-node" +# define XML_RSC_ATTR_CLEAR_OP "clear_failure_op" +# define XML_RSC_ATTR_CLEAR_INTERVAL "clear_failure_interval" +# define XML_RSC_ATTR_REMOTE_RA_ADDR "addr" +# define XML_RSC_ATTR_REMOTE_RA_SERVER "server" +# define XML_RSC_ATTR_REMOTE_RA_PORT "port" +# define XML_RSC_ATTR_CRITICAL "critical" + +# define XML_REMOTE_ATTR_RECONNECT_INTERVAL "reconnect_interval" + +# define XML_OP_ATTR_ON_FAIL "on-fail" +# define XML_OP_ATTR_START_DELAY "start-delay" +# define XML_OP_ATTR_ALLOW_MIGRATE "allow-migrate" +# define XML_OP_ATTR_ORIGIN "interval-origin" +# define XML_OP_ATTR_PENDING "record-pending" +# define XML_OP_ATTR_DIGESTS_ALL "digests-all" +# define XML_OP_ATTR_DIGESTS_SECURE "digests-secure" + +# define XML_CIB_TAG_LRM "lrm" +# define XML_LRM_TAG_RESOURCES "lrm_resources" +# define XML_LRM_TAG_RESOURCE "lrm_resource" +# define XML_LRM_TAG_RSC_OP "lrm_rsc_op" +# define XML_AGENT_ATTR_CLASS "class" +# define XML_AGENT_ATTR_PROVIDER "provider" + +//! \deprecated Do not use (will be removed in a future release) +# define XML_CIB_ATTR_REPLACE "replace" + +# define XML_CIB_ATTR_SOURCE "source" + +# define XML_CIB_ATTR_PRIORITY "priority" +# define XML_CIB_ATTR_SOURCE "source" + +# define XML_NODE_JOIN_STATE "join" +# define XML_NODE_EXPECTED "expected" +# define XML_NODE_IN_CLUSTER "in_ccm" +# define XML_NODE_IS_PEER "crmd" +# define XML_NODE_IS_REMOTE "remote_node" +# define XML_NODE_IS_FENCED "node_fenced" +# define XML_NODE_IS_MAINTENANCE "node_in_maintenance" + +# define XML_CIB_ATTR_SHUTDOWN "shutdown" + +/* Aside from being an old name for the executor, LRM is a misnomer here because + * the controller and scheduler use these to track actions, which are not always + * executor operations. + */ + +// XML attribute that takes interval specification (user-facing configuration) +# define XML_LRM_ATTR_INTERVAL "interval" + +// XML attribute that takes interval in milliseconds (daemon APIs) +// (identical value as above, but different constant allows clearer code intent) +# define XML_LRM_ATTR_INTERVAL_MS XML_LRM_ATTR_INTERVAL + +# define XML_LRM_ATTR_TASK "operation" +# define XML_LRM_ATTR_TASK_KEY "operation_key" +# define XML_LRM_ATTR_TARGET "on_node" +# define XML_LRM_ATTR_TARGET_UUID "on_node_uuid" +/*! Actions to be executed on Pacemaker Remote nodes are routed through the + * controller on the cluster node hosting the remote connection. That cluster + * node is considered the router node for the action. + */ +# define XML_LRM_ATTR_ROUTER_NODE "router_node" +# define XML_LRM_ATTR_RSCID "rsc-id" +# define XML_LRM_ATTR_OPSTATUS "op-status" +# define XML_LRM_ATTR_RC "rc-code" +# define XML_LRM_ATTR_CALLID "call-id" +# define XML_LRM_ATTR_OP_DIGEST "op-digest" +# define XML_LRM_ATTR_OP_RESTART "op-force-restart" +# define XML_LRM_ATTR_OP_SECURE "op-secure-params" +# define XML_LRM_ATTR_RESTART_DIGEST "op-restart-digest" +# define XML_LRM_ATTR_SECURE_DIGEST "op-secure-digest" +# define XML_LRM_ATTR_EXIT_REASON "exit-reason" + +# define XML_RSC_OP_LAST_CHANGE "last-rc-change" +# define XML_RSC_OP_LAST_RUN "last-run" // deprecated since 2.0.3 +# define XML_RSC_OP_T_EXEC "exec-time" +# define XML_RSC_OP_T_QUEUE "queue-time" + +# define XML_LRM_ATTR_MIGRATE_SOURCE "migrate_source" +# define XML_LRM_ATTR_MIGRATE_TARGET "migrate_target" + +# define XML_TAG_GRAPH "transition_graph" +# define XML_GRAPH_TAG_RSC_OP "rsc_op" +# define XML_GRAPH_TAG_PSEUDO_EVENT "pseudo_event" +# define XML_GRAPH_TAG_CRM_EVENT "crm_event" +# define XML_GRAPH_TAG_DOWNED "downed" +# define XML_GRAPH_TAG_MAINTENANCE "maintenance" + +# define XML_TAG_RULE "rule" +# define XML_RULE_ATTR_SCORE "score" +# define XML_RULE_ATTR_SCORE_ATTRIBUTE "score-attribute" +# define XML_RULE_ATTR_ROLE "role" +# define XML_RULE_ATTR_BOOLEAN_OP "boolean-op" + +# define XML_TAG_EXPRESSION "expression" +# define XML_EXPR_ATTR_ATTRIBUTE "attribute" +# define XML_EXPR_ATTR_OPERATION "operation" +# define XML_EXPR_ATTR_VALUE "value" +# define XML_EXPR_ATTR_TYPE "type" +# define XML_EXPR_ATTR_VALUE_SOURCE "value-source" + +# define XML_CONS_TAG_RSC_DEPEND "rsc_colocation" +# define XML_CONS_TAG_RSC_ORDER "rsc_order" +# define XML_CONS_TAG_RSC_LOCATION "rsc_location" +# define XML_CONS_TAG_RSC_TICKET "rsc_ticket" +# define XML_CONS_TAG_RSC_SET "resource_set" +# define XML_CONS_ATTR_SYMMETRICAL "symmetrical" + +# define XML_LOCATION_ATTR_DISCOVERY "resource-discovery" + +# define XML_COLOC_ATTR_SOURCE "rsc" +# define XML_COLOC_ATTR_SOURCE_ROLE "rsc-role" +# define XML_COLOC_ATTR_TARGET "with-rsc" +# define XML_COLOC_ATTR_TARGET_ROLE "with-rsc-role" +# define XML_COLOC_ATTR_NODE_ATTR "node-attribute" +# define XML_COLOC_ATTR_INFLUENCE "influence" + +//! \deprecated Deprecated since 2.1.5 +# define XML_COLOC_ATTR_SOURCE_INSTANCE "rsc-instance" + +//! \deprecated Deprecated since 2.1.5 +# define XML_COLOC_ATTR_TARGET_INSTANCE "with-rsc-instance" + +# define XML_LOC_ATTR_SOURCE "rsc" +# define XML_LOC_ATTR_SOURCE_PATTERN "rsc-pattern" + +# define XML_ORDER_ATTR_FIRST "first" +# define XML_ORDER_ATTR_THEN "then" +# define XML_ORDER_ATTR_FIRST_ACTION "first-action" +# define XML_ORDER_ATTR_THEN_ACTION "then-action" +# define XML_ORDER_ATTR_KIND "kind" + +//! \deprecated Deprecated since 2.1.5 +# define XML_ORDER_ATTR_FIRST_INSTANCE "first-instance" + +//! \deprecated Deprecated since 2.1.5 +# define XML_ORDER_ATTR_THEN_INSTANCE "then-instance" + +# define XML_TICKET_ATTR_TICKET "ticket" +# define XML_TICKET_ATTR_LOSS_POLICY "loss-policy" + +# define XML_NVPAIR_ATTR_NAME "name" +# define XML_NVPAIR_ATTR_VALUE "value" + +# define XML_NODE_ATTR_RSC_DISCOVERY "resource-discovery-enabled" + +# define XML_CONFIG_ATTR_DC_DEADTIME "dc-deadtime" +# define XML_CONFIG_ATTR_ELECTION_FAIL "election-timeout" +# define XML_CONFIG_ATTR_FORCE_QUIT "shutdown-escalation" +# define XML_CONFIG_ATTR_RECHECK "cluster-recheck-interval" +# define XML_CONFIG_ATTR_FENCE_REACTION "fence-reaction" +# define XML_CONFIG_ATTR_SHUTDOWN_LOCK "shutdown-lock" +# define XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT "shutdown-lock-limit" +# define XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY "priority-fencing-delay" + +# define XML_ALERT_ATTR_PATH "path" +# define XML_ALERT_ATTR_TIMEOUT "timeout" +# define XML_ALERT_ATTR_TSTAMP_FORMAT "timestamp-format" +# define XML_ALERT_ATTR_REC_VALUE "value" + +# define XML_CIB_TAG_GENERATION_TUPPLE "generation_tuple" + +# define XML_ATTR_TRANSITION_MAGIC "transition-magic" +# define XML_ATTR_TRANSITION_KEY "transition-key" + +# define XML_ATTR_TE_NOWAIT "op_no_wait" +# define XML_ATTR_TE_TARGET_RC "op_target_rc" +# define XML_TAG_TRANSIENT_NODEATTRS "transient_attributes" + +# define XML_TAG_DIFF_ADDED "diff-added" +# define XML_TAG_DIFF_REMOVED "diff-removed" + +# define XML_ACL_TAG_USER "acl_target" +# define XML_ACL_TAG_USERv1 "acl_user" +# define XML_ACL_TAG_GROUP "acl_group" +# define XML_ACL_TAG_ROLE "acl_role" +# define XML_ACL_TAG_PERMISSION "acl_permission" +# define XML_ACL_TAG_ROLE_REF "role" +# define XML_ACL_TAG_ROLE_REFv1 "role_ref" +# define XML_ACL_ATTR_KIND "kind" +# define XML_ACL_TAG_READ "read" +# define XML_ACL_TAG_WRITE "write" +# define XML_ACL_TAG_DENY "deny" +# define XML_ACL_ATTR_REF "reference" +# define XML_ACL_ATTR_REFv1 "ref" +# define XML_ACL_ATTR_TAG "object-type" +# define XML_ACL_ATTR_TAGv1 "tag" +# define XML_ACL_ATTR_XPATH "xpath" +# define XML_ACL_ATTR_ATTRIBUTE "attribute" + +# define XML_CIB_TAG_TICKETS "tickets" +# define XML_CIB_TAG_TICKET_STATE "ticket_state" + +# define XML_CIB_TAG_TAGS "tags" +# define XML_CIB_TAG_TAG "tag" +# define XML_CIB_TAG_OBJ_REF "obj_ref" + +# define XML_TAG_FENCING_TOPOLOGY "fencing-topology" +# define XML_TAG_FENCING_LEVEL "fencing-level" +# define XML_ATTR_STONITH_INDEX "index" +# define XML_ATTR_STONITH_TARGET "target" +# define XML_ATTR_STONITH_TARGET_VALUE "target-value" +# define XML_ATTR_STONITH_TARGET_PATTERN "target-pattern" +# define XML_ATTR_STONITH_TARGET_ATTRIBUTE "target-attribute" +# define XML_ATTR_STONITH_DEVICES "devices" + +# define XML_TAG_DIFF "diff" +# define XML_DIFF_VERSION "version" +# define XML_DIFF_VSOURCE "source" +# define XML_DIFF_VTARGET "target" +# define XML_DIFF_CHANGE "change" +# define XML_DIFF_LIST "change-list" +# define XML_DIFF_ATTR "change-attr" +# define XML_DIFF_RESULT "change-result" +# define XML_DIFF_OP "operation" +# define XML_DIFF_PATH "path" +# define XML_DIFF_POSITION "position" + +# define ID(x) crm_element_value(x, XML_ATTR_ID) +# define TYPE(x) crm_element_name(x) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/msg_xml_compat.h b/include/crm/msg_xml_compat.h new file mode 100644 index 0000000..aad98e8 --- /dev/null +++ b/include/crm/msg_xml_compat.h @@ -0,0 +1,65 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_MSG_XML_COMPAT__H +# define PCMK__CRM_MSG_XML_COMPAT__H + +#include <crm/common/agents.h> // PCMK_STONITH_PROVIDES + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker XML constants API + * \ingroup core + * \deprecated Do not include this header directly. The XML constants in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use PCMK_STONITH_PROVIDES instead +#define XML_RSC_ATTR_PROVIDES PCMK_STONITH_PROVIDES + +//! \deprecated Use PCMK_XE_PROMOTABLE_LEGACY instead +#define XML_CIB_TAG_MASTER PCMK_XE_PROMOTABLE_LEGACY + +//! \deprecated Use PCMK_XA_PROMOTED_MAX_LEGACY instead +#define PCMK_XE_PROMOTED_MAX_LEGACY PCMK_XA_PROMOTED_MAX_LEGACY + +//! \deprecated Use PCMK_XA_PROMOTED_MAX_LEGACY instead +#define XML_RSC_ATTR_MASTER_MAX PCMK_XA_PROMOTED_MAX_LEGACY + +//! \deprecated Use PCMK_XA_PROMOTED_NODE_MAX_LEGACY instead +#define PCMK_XE_PROMOTED_NODE_MAX_LEGACY PCMK_XA_PROMOTED_NODE_MAX_LEGACY + +//! \deprecated Use PCMK_XA_PROMOTED_NODE_MAX_LEGACY instead +#define XML_RSC_ATTR_MASTER_NODEMAX PCMK_XA_PROMOTED_NODE_MAX_LEGACY + +//! \deprecated Do not use (will be removed in a future release) +#define XML_ATTR_RA_VERSION "ra-version" + +//! \deprecated Do not use (will be removed in a future release) +#define XML_TAG_RSC_VER_ATTRS "rsc_versioned_attrs" + +//! \deprecated Do not use (will be removed in a future release) +#define XML_TAG_OP_VER_ATTRS "op_versioned_attrs" + +//! \deprecated Do not use (will be removed in a future release) +#define XML_TAG_OP_VER_META "op_versioned_meta" + +//! \deprecated Use \p XML_ATTR_ID instead +#define XML_ATTR_UUID "id" + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_MSG_XML_COMPAT__H diff --git a/include/crm/pengine/Makefile.am b/include/crm/pengine/Makefile.am new file mode 100644 index 0000000..fac6031 --- /dev/null +++ b/include/crm/pengine/Makefile.am @@ -0,0 +1,17 @@ +# +# Copyright 2006-2021 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/pengine + +noinst_HEADERS = internal.h remote_internal.h rules_internal.h +header_HEADERS = common.h complex.h pe_types.h rules.h status.h \ + common_compat.h \ + pe_types_compat.h \ + rules_compat.h diff --git a/include/crm/pengine/common.h b/include/crm/pengine/common.h new file mode 100644 index 0000000..9fe05bd --- /dev/null +++ b/include/crm/pengine/common.h @@ -0,0 +1,209 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_PENGINE_COMMON__H +# define PCMK__CRM_PENGINE_COMMON__H + +# include <glib.h> +# include <regex.h> +# include <crm/common/iso8601.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern gboolean was_processing_error; +extern gboolean was_processing_warning; + +/* The order is (partially) significant here; the values from action_fail_ignore + * through action_fail_fence are in order of increasing severity. + * + * @COMPAT The values should be ordered and numbered per the "TODO" comments + * below, so all values are in order of severity and there is room for + * future additions, but that would break API compatibility. + * @TODO For now, we just use a function to compare the values specially, but + * at the next compatibility break, we should arrange things properly. + */ +enum action_fail_response { + action_fail_ignore, // @TODO = 10 + // @TODO action_fail_demote = 20, + action_fail_recover, // @TODO = 30 + // @TODO action_fail_reset_remote = 40, + // @TODO action_fail_restart_container = 50, + action_fail_migrate, // @TODO = 60 + action_fail_block, // @TODO = 70 + action_fail_stop, // @TODO = 80 + action_fail_standby, // @TODO = 90 + action_fail_fence, // @TODO = 100 + + // @COMPAT Values below here are out of order for API compatibility + + action_fail_restart_container, + + /* This is reserved for internal use for remote node connection resources. + * Fence the remote node if stonith is enabled, otherwise attempt to recover + * the connection resource. This allows us to specify types of connection + * resource failures that should result in fencing the remote node + * (for example, recurring monitor failures). + */ + action_fail_reset_remote, + + action_fail_demote, +}; + +/* the "done" action must be the "pre" action +1 */ +enum action_tasks { + no_action, + monitor_rsc, + stop_rsc, + stopped_rsc, + start_rsc, + started_rsc, + action_notify, + action_notified, + action_promote, + action_promoted, + action_demote, + action_demoted, + shutdown_crm, + stonith_node +}; + +enum rsc_recovery_type { + recovery_stop_start, + recovery_stop_only, + recovery_block, + recovery_stop_unexpected, +}; + +enum rsc_start_requirement { + rsc_req_nothing, /* Allowed by custom_action() */ + rsc_req_quorum, /* Enforced by custom_action() */ + rsc_req_stonith /* Enforced by native_start_constraints() */ +}; + +//! Possible roles that a resource can be in +enum rsc_role_e { + RSC_ROLE_UNKNOWN = 0, + RSC_ROLE_STOPPED = 1, + RSC_ROLE_STARTED = 2, + RSC_ROLE_UNPROMOTED = 3, + RSC_ROLE_PROMOTED = 4, + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + //! \deprecated Use RSC_ROLE_UNPROMOTED instead + RSC_ROLE_SLAVE = RSC_ROLE_UNPROMOTED, + + //! \deprecated Use RSC_ROLE_PROMOTED instead + RSC_ROLE_MASTER = RSC_ROLE_PROMOTED, +#endif +}; + +# define RSC_ROLE_MAX (RSC_ROLE_PROMOTED + 1) + +# define RSC_ROLE_UNKNOWN_S "Unknown" +# define RSC_ROLE_STOPPED_S "Stopped" +# define RSC_ROLE_STARTED_S "Started" +# define RSC_ROLE_UNPROMOTED_S "Unpromoted" +# define RSC_ROLE_PROMOTED_S "Promoted" +# define RSC_ROLE_UNPROMOTED_LEGACY_S "Slave" +# define RSC_ROLE_PROMOTED_LEGACY_S "Master" + +//! Deprecated +enum pe_print_options { + pe_print_log = (1 << 0), + pe_print_html = (1 << 1), + pe_print_ncurses = (1 << 2), + pe_print_printf = (1 << 3), + pe_print_dev = (1 << 4), //! Ignored + pe_print_details = (1 << 5), //! Ignored + pe_print_max_details = (1 << 6), //! Ignored + pe_print_rsconly = (1 << 7), + pe_print_ops = (1 << 8), + pe_print_suppres_nl = (1 << 9), + pe_print_xml = (1 << 10), + pe_print_brief = (1 << 11), + pe_print_pending = (1 << 12), + pe_print_clone_details = (1 << 13), + pe_print_clone_active = (1 << 14), // Print clone instances only if active + pe_print_implicit = (1 << 15) // Print implicitly created resources +}; + +const char *task2text(enum action_tasks task); +enum action_tasks text2task(const char *task); +enum rsc_role_e text2role(const char *role); +const char *role2text(enum rsc_role_e role); +const char *fail2text(enum action_fail_response fail); + +const char *pe_pref(GHashTable * options, const char *name); + +/*! + * \brief Get readable description of a recovery type + * + * \param[in] type Recovery type + * + * \return Static string describing \p type + */ +static inline const char * +recovery2text(enum rsc_recovery_type type) +{ + switch (type) { + case recovery_stop_only: + return "shutting it down"; + case recovery_stop_start: + return "attempting recovery"; + case recovery_block: + return "waiting for an administrator"; + case recovery_stop_unexpected: + return "stopping unexpected instances"; + } + return "Unknown"; +} + +typedef struct pe_re_match_data { + char *string; + int nregs; + regmatch_t *pmatch; +} pe_re_match_data_t; + +typedef struct pe_match_data { + pe_re_match_data_t *re; + GHashTable *params; + GHashTable *meta; +} pe_match_data_t; + +typedef struct pe_rsc_eval_data { + const char *standard; + const char *provider; + const char *agent; +} pe_rsc_eval_data_t; + +typedef struct pe_op_eval_data { + const char *op_name; + guint interval; +} pe_op_eval_data_t; + +typedef struct pe_rule_eval_data { + GHashTable *node_hash; // Only used with g_hash_table_lookup() + enum rsc_role_e role; + crm_time_t *now; // @COMPAT could be const + pe_match_data_t *match_data; // @COMPAT could be const + pe_rsc_eval_data_t *rsc_data; // @COMPAT could be const + pe_op_eval_data_t *op_data; // @COMPAT could be const +} pe_rule_eval_data_t; + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/pengine/common_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/pengine/common_compat.h b/include/crm/pengine/common_compat.h new file mode 100644 index 0000000..773bb3d --- /dev/null +++ b/include/crm/pengine/common_compat.h @@ -0,0 +1,37 @@ +/* + * Copyright 2004-2021 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 PCMK__CRM_PENGINE_COMMON_COMPAT__H +# define PCMK__CRM_PENGINE_COMMON_COMPAT__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker scheduler utilities + * \ingroup pengine + * \deprecated Do not include this header directly. The utilities in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use RSC_ROLE_UNPROMOTED_LEGACY_S instead +# define RSC_ROLE_SLAVE_S RSC_ROLE_UNPROMOTED_LEGACY_S + +//! \deprecated Use RSC_ROLE_PROMOTED_LEGACY_S instead +# define RSC_ROLE_MASTER_S RSC_ROLE_PROMOTED_LEGACY_S + + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_PENGINE_COMMON_COMPAT__H diff --git a/include/crm/pengine/complex.h b/include/crm/pengine/complex.h new file mode 100644 index 0000000..929e4da --- /dev/null +++ b/include/crm/pengine/complex.h @@ -0,0 +1,37 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_PENGINE_COMPLEX__H +# define PCMK__CRM_PENGINE_COMPLEX__H + +#include <glib.h> // gboolean, GHashTable +#include <libxml/tree.h> // xmlNode +#include <crm/pengine/pe_types.h> // pe_node_t, pe_resource_t, etc. + +#ifdef __cplusplus +extern "C" { +#endif + +extern resource_object_functions_t resource_class_functions[]; + +GHashTable *pe_rsc_params(pe_resource_t *rsc, const pe_node_t *node, + pe_working_set_t *data_set); +void get_meta_attributes(GHashTable * meta_hash, pe_resource_t *rsc, + pe_node_t *node, pe_working_set_t *data_set); +void get_rsc_attributes(GHashTable *meta_hash, const pe_resource_t *rsc, + const pe_node_t *node, pe_working_set_t *data_set); + +gboolean is_parent(pe_resource_t *child, pe_resource_t *rsc); +pe_resource_t *uber_parent(pe_resource_t *rsc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h new file mode 100644 index 0000000..1b5f6f1 --- /dev/null +++ b/include/crm/pengine/internal.h @@ -0,0 +1,724 @@ +/* + * Copyright 2004-2023 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 PE_INTERNAL__H +# define PE_INTERNAL__H + +# include <stdint.h> +# include <string.h> +# include <crm/msg_xml.h> +# include <crm/pengine/status.h> +# include <crm/pengine/remote_internal.h> +# include <crm/common/internal.h> +# include <crm/common/options_internal.h> +# include <crm/common/output_internal.h> + +const char *pe__resource_description(const pe_resource_t *rsc, uint32_t show_opts); + +enum pe__clone_flags { + // Whether instances should be started sequentially + pe__clone_ordered = (1 << 0), + + // Whether promotion scores have been added + pe__clone_promotion_added = (1 << 1), + + // Whether promotion constraints have been added + pe__clone_promotion_constrained = (1 << 2), +}; + +bool pe__clone_is_ordered(const pe_resource_t *clone); +int pe__set_clone_flag(pe_resource_t *clone, enum pe__clone_flags flag); + + +enum pe__group_flags { + pe__group_ordered = (1 << 0), // Members start sequentially + pe__group_colocated = (1 << 1), // Members must be on same node +}; + +bool pe__group_flag_is_set(const pe_resource_t *group, uint32_t flags); +pe_resource_t *pe__last_group_member(const pe_resource_t *group); + + +# define pe_rsc_info(rsc, fmt, args...) crm_log_tag(LOG_INFO, rsc ? rsc->id : "<NULL>", fmt, ##args) +# define pe_rsc_debug(rsc, fmt, args...) crm_log_tag(LOG_DEBUG, rsc ? rsc->id : "<NULL>", fmt, ##args) +# define pe_rsc_trace(rsc, fmt, args...) crm_log_tag(LOG_TRACE, rsc ? rsc->id : "<NULL>", fmt, ##args) + +# define pe_err(fmt...) do { \ + was_processing_error = TRUE; \ + pcmk__config_err(fmt); \ + } while (0) + +# define pe_warn(fmt...) do { \ + was_processing_warning = TRUE; \ + pcmk__config_warn(fmt); \ + } while (0) + +# define pe_proc_err(fmt...) { was_processing_error = TRUE; crm_err(fmt); } +# define pe_proc_warn(fmt...) { was_processing_warning = TRUE; crm_warn(fmt); } + +#define pe__set_working_set_flags(working_set, flags_to_set) do { \ + (working_set)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Working set", crm_system_name, \ + (working_set)->flags, (flags_to_set), #flags_to_set); \ + } while (0) + +#define pe__clear_working_set_flags(working_set, flags_to_clear) do { \ + (working_set)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Working set", crm_system_name, \ + (working_set)->flags, (flags_to_clear), #flags_to_clear); \ + } while (0) + +#define pe__set_resource_flags(resource, flags_to_set) do { \ + (resource)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \ + (flags_to_set), #flags_to_set); \ + } while (0) + +#define pe__clear_resource_flags(resource, flags_to_clear) do { \ + (resource)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \ + (flags_to_clear), #flags_to_clear); \ + } while (0) + +#define pe__set_action_flags(action, flags_to_set) do { \ + (action)->flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, \ + "Action", (action)->uuid, \ + (action)->flags, \ + (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define pe__clear_action_flags(action, flags_to_clear) do { \ + (action)->flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, \ + "Action", (action)->uuid, \ + (action)->flags, \ + (flags_to_clear), \ + #flags_to_clear); \ + } while (0) + +#define pe__set_raw_action_flags(action_flags, action_name, flags_to_set) do { \ + action_flags = pcmk__set_flags_as(__func__, __LINE__, \ + LOG_TRACE, "Action", action_name, \ + (action_flags), \ + (flags_to_set), #flags_to_set); \ + } while (0) + +#define pe__clear_raw_action_flags(action_flags, action_name, flags_to_clear) do { \ + action_flags = pcmk__clear_flags_as(__func__, __LINE__, \ + LOG_TRACE, \ + "Action", action_name, \ + (action_flags), \ + (flags_to_clear), \ + #flags_to_clear); \ + } while (0) + +#define pe__set_action_flags_as(function, line, action, flags_to_set) do { \ + (action)->flags = pcmk__set_flags_as((function), (line), \ + LOG_TRACE, \ + "Action", (action)->uuid, \ + (action)->flags, \ + (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define pe__clear_action_flags_as(function, line, action, flags_to_clear) do { \ + (action)->flags = pcmk__clear_flags_as((function), (line), \ + LOG_TRACE, \ + "Action", (action)->uuid, \ + (action)->flags, \ + (flags_to_clear), \ + #flags_to_clear); \ + } while (0) + +#define pe__set_order_flags(order_flags, flags_to_set) do { \ + order_flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ + "Ordering", "constraint", \ + order_flags, (flags_to_set), \ + #flags_to_set); \ + } while (0) + +#define pe__clear_order_flags(order_flags, flags_to_clear) do { \ + order_flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \ + "Ordering", "constraint", \ + order_flags, (flags_to_clear), \ + #flags_to_clear); \ + } while (0) + +// Some warnings we don't want to print every transition + +enum pe_warn_once_e { + pe_wo_blind = (1 << 0), + pe_wo_restart_type = (1 << 1), + pe_wo_role_after = (1 << 2), + pe_wo_poweroff = (1 << 3), + pe_wo_require_all = (1 << 4), + pe_wo_order_score = (1 << 5), + pe_wo_neg_threshold = (1 << 6), + pe_wo_remove_after = (1 << 7), + pe_wo_ping_node = (1 << 8), + pe_wo_order_inst = (1 << 9), + pe_wo_coloc_inst = (1 << 10), + pe_wo_group_order = (1 << 11), + pe_wo_group_coloc = (1 << 12), + pe_wo_upstart = (1 << 13), + pe_wo_nagios = (1 << 14), +}; + +extern uint32_t pe_wo; + +#define pe_warn_once(pe_wo_bit, fmt...) do { \ + if (!pcmk_is_set(pe_wo, pe_wo_bit)) { \ + if (pe_wo_bit == pe_wo_blind) { \ + crm_warn(fmt); \ + } else { \ + pe_warn(fmt); \ + } \ + pe_wo = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \ + "Warn-once", "logging", pe_wo, \ + (pe_wo_bit), #pe_wo_bit); \ + } \ + } while (0); + + +typedef struct pe__location_constraint_s { + char *id; // Constraint XML ID + pe_resource_t *rsc_lh; // Resource being located + enum rsc_role_e role_filter; // Role to locate + enum pe_discover_e discover_mode; // Resource discovery + GList *node_list_rh; // List of pe_node_t* +} pe__location_t; + +typedef struct pe__order_constraint_s { + int id; + uint32_t flags; // Group of enum pe_ordering flags + + void *lh_opaque; + pe_resource_t *lh_rsc; + pe_action_t *lh_action; + char *lh_action_task; + + void *rh_opaque; + pe_resource_t *rh_rsc; + pe_action_t *rh_action; + char *rh_action_task; +} pe__ordering_t; + +const pe_resource_t *pe__const_top_resource(const pe_resource_t *rsc, + bool include_bundle); + +int pe__clone_max(const pe_resource_t *clone); +int pe__clone_node_max(const pe_resource_t *clone); +int pe__clone_promoted_max(const pe_resource_t *clone); +int pe__clone_promoted_node_max(const pe_resource_t *clone); +void pe__create_clone_notifications(pe_resource_t *clone); +void pe__free_clone_notification_data(pe_resource_t *clone); +void pe__create_clone_notif_pseudo_ops(pe_resource_t *clone, + pe_action_t *start, pe_action_t *started, + pe_action_t *stop, pe_action_t *stopped); + + +pe_action_t *pe__new_rsc_pseudo_action(pe_resource_t *rsc, const char *task, + bool optional, bool runnable); + +void pe__create_promotable_pseudo_ops(pe_resource_t *clone, bool any_promoting, + bool any_demoting); + +bool pe_can_fence(const pe_working_set_t *data_set, const pe_node_t *node); + +void add_hash_param(GHashTable * hash, const char *name, const char *value); + +char *native_parameter(pe_resource_t * rsc, pe_node_t * node, gboolean create, const char *name, + pe_working_set_t * data_set); +pe_node_t *native_location(const pe_resource_t *rsc, GList **list, int current); + +void pe_metadata(pcmk__output_t *out); +void verify_pe_options(GHashTable * options); + +void native_add_running(pe_resource_t * rsc, pe_node_t * node, pe_working_set_t * data_set, gboolean failed); + +gboolean native_unpack(pe_resource_t * rsc, pe_working_set_t * data_set); +gboolean group_unpack(pe_resource_t * rsc, pe_working_set_t * data_set); +gboolean clone_unpack(pe_resource_t * rsc, pe_working_set_t * data_set); +gboolean pe__unpack_bundle(pe_resource_t *rsc, pe_working_set_t *data_set); + +pe_resource_t *native_find_rsc(pe_resource_t *rsc, const char *id, const pe_node_t *node, + int flags); + +gboolean native_active(pe_resource_t * rsc, gboolean all); +gboolean group_active(pe_resource_t * rsc, gboolean all); +gboolean clone_active(pe_resource_t * rsc, gboolean all); +gboolean pe__bundle_active(pe_resource_t *rsc, gboolean all); + +//! \deprecated This function will be removed in a future release +void native_print(pe_resource_t *rsc, const char *pre_text, long options, + void *print_data); + +//! \deprecated This function will be removed in a future release +void group_print(pe_resource_t *rsc, const char *pre_text, long options, + void *print_data); + +//! \deprecated This function will be removed in a future release +void clone_print(pe_resource_t *rsc, const char *pre_text, long options, + void *print_data); + +//! \deprecated This function will be removed in a future release +void pe__print_bundle(pe_resource_t *rsc, const char *pre_text, long options, + void *print_data); + +gchar *pcmk__native_output_string(const pe_resource_t *rsc, const char *name, + const pe_node_t *node, uint32_t show_opts, + const char *target_role, bool show_nodes); + +int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name + , size_t pairs_count, ...); +char *pe__node_display_name(pe_node_t *node, bool print_detail); + + +// Clone notifications (pe_notif.c) +void pe__order_notifs_after_fencing(const pe_action_t *action, + pe_resource_t *rsc, + pe_action_t *stonith_op); + + +static inline const char * +pe__rsc_bool_str(const pe_resource_t *rsc, uint64_t rsc_flag) +{ + return pcmk__btoa(pcmk_is_set(rsc->flags, rsc_flag)); +} + +int pe__clone_xml(pcmk__output_t *out, va_list args); +int pe__clone_default(pcmk__output_t *out, va_list args); +int pe__group_xml(pcmk__output_t *out, va_list args); +int pe__group_default(pcmk__output_t *out, va_list args); +int pe__bundle_xml(pcmk__output_t *out, va_list args); +int pe__bundle_html(pcmk__output_t *out, va_list args); +int pe__bundle_text(pcmk__output_t *out, va_list args); +int pe__node_html(pcmk__output_t *out, va_list args); +int pe__node_text(pcmk__output_t *out, va_list args); +int pe__node_xml(pcmk__output_t *out, va_list args); +int pe__resource_xml(pcmk__output_t *out, va_list args); +int pe__resource_html(pcmk__output_t *out, va_list args); +int pe__resource_text(pcmk__output_t *out, va_list args); + +void native_free(pe_resource_t * rsc); +void group_free(pe_resource_t * rsc); +void clone_free(pe_resource_t * rsc); +void pe__free_bundle(pe_resource_t *rsc); + +enum rsc_role_e native_resource_state(const pe_resource_t * rsc, gboolean current); +enum rsc_role_e group_resource_state(const pe_resource_t * rsc, gboolean current); +enum rsc_role_e clone_resource_state(const pe_resource_t * rsc, gboolean current); +enum rsc_role_e pe__bundle_resource_state(const pe_resource_t *rsc, + gboolean current); + +void pe__count_common(pe_resource_t *rsc); +void pe__count_bundle(pe_resource_t *rsc); + +void common_free(pe_resource_t * rsc); + +pe_node_t *pe__copy_node(const pe_node_t *this_node); +extern time_t get_effective_time(pe_working_set_t * data_set); + +/* Failure handling utilities (from failcounts.c) */ + +// bit flags for fail count handling options +enum pe_fc_flags_e { + pe_fc_default = (1 << 0), + pe_fc_effective = (1 << 1), // don't count expired failures + pe_fc_fillers = (1 << 2), // if container, include filler failures in count +}; + +int pe_get_failcount(const pe_node_t *node, pe_resource_t *rsc, + time_t *last_failure, uint32_t flags, + const xmlNode *xml_op); + +pe_action_t *pe__clear_failcount(pe_resource_t *rsc, const pe_node_t *node, + const char *reason, + pe_working_set_t *data_set); + +/* Functions for finding/counting a resource's active nodes */ + +bool pe__count_active_node(const pe_resource_t *rsc, pe_node_t *node, + pe_node_t **active, unsigned int *count_all, + unsigned int *count_clean); + +pe_node_t *pe__find_active_requires(const pe_resource_t *rsc, + unsigned int *count); + +static inline pe_node_t * +pe__current_node(const pe_resource_t *rsc) +{ + return (rsc == NULL)? NULL : rsc->fns->active_node(rsc, NULL, NULL); +} + + +/* Binary like operators for lists of nodes */ +extern void node_list_exclude(GHashTable * list, GList *list2, gboolean merge_scores); + +GHashTable *pe__node_list2table(const GList *list); + +static inline gpointer +pe_hash_table_lookup(GHashTable * hash, gconstpointer key) +{ + if (hash) { + return g_hash_table_lookup(hash, key); + } + return NULL; +} + +extern pe_action_t *get_pseudo_op(const char *name, pe_working_set_t * data_set); +extern gboolean order_actions(pe_action_t * lh_action, pe_action_t * rh_action, enum pe_ordering order); + +void pe__show_node_weights_as(const char *file, const char *function, + int line, bool to_log, const pe_resource_t *rsc, + const char *comment, GHashTable *nodes, + pe_working_set_t *data_set); + +#define pe__show_node_weights(level, rsc, text, nodes, data_set) \ + pe__show_node_weights_as(__FILE__, __func__, __LINE__, \ + (level), (rsc), (text), (nodes), (data_set)) + +xmlNode *find_rsc_op_entry(const pe_resource_t *rsc, const char *key); + +pe_action_t *custom_action(pe_resource_t *rsc, char *key, const char *task, + const pe_node_t *on_node, gboolean optional, + gboolean foo, pe_working_set_t *data_set); + +# define delete_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_DELETE, 0) +# define delete_action(rsc, node, optional) custom_action( \ + rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \ + optional, TRUE, rsc->cluster); + +# define stopped_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_STOPPED, 0) +# define stopped_action(rsc, node, optional) custom_action( \ + rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node, \ + optional, TRUE, rsc->cluster); + +# define stop_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_STOP, 0) +# define stop_action(rsc, node, optional) custom_action( \ + rsc, stop_key(rsc), CRMD_ACTION_STOP, node, \ + optional, TRUE, rsc->cluster); + +# define reload_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_RELOAD_AGENT, 0) +# define start_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_START, 0) +# define start_action(rsc, node, optional) custom_action( \ + rsc, start_key(rsc), CRMD_ACTION_START, node, \ + optional, TRUE, rsc->cluster) + +# define started_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_STARTED, 0) +# define started_action(rsc, node, optional) custom_action( \ + rsc, started_key(rsc), CRMD_ACTION_STARTED, node, \ + optional, TRUE, rsc->cluster) + +# define promote_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_PROMOTE, 0) +# define promote_action(rsc, node, optional) custom_action( \ + rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node, \ + optional, TRUE, rsc->cluster) + +# define promoted_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_PROMOTED, 0) +# define promoted_action(rsc, node, optional) custom_action( \ + rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node, \ + optional, TRUE, rsc->cluster) + +# define demote_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_DEMOTE, 0) +# define demote_action(rsc, node, optional) custom_action( \ + rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node, \ + optional, TRUE, rsc->cluster) + +# define demoted_key(rsc) pcmk__op_key(rsc->id, CRMD_ACTION_DEMOTED, 0) +# define demoted_action(rsc, node, optional) custom_action( \ + rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node, \ + optional, TRUE, rsc->cluster) + +extern int pe_get_configured_timeout(pe_resource_t *rsc, const char *action, + pe_working_set_t *data_set); + +pe_action_t *find_first_action(const GList *input, const char *uuid, + const char *task, const pe_node_t *on_node); + +enum action_tasks get_complex_task(const pe_resource_t *rsc, const char *name); + +extern GList *find_actions(GList *input, const char *key, const pe_node_t *on_node); +GList *find_actions_exact(GList *input, const char *key, + const pe_node_t *on_node); +GList *pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node, + const char *task, bool require_node); + +extern void pe_free_action(pe_action_t * action); + +void resource_location(pe_resource_t *rsc, const pe_node_t *node, int score, + const char *tag, pe_working_set_t *data_set); + +extern int pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, + bool same_node_default); +extern gint sort_op_by_callid(gconstpointer a, gconstpointer b); +gboolean get_target_role(const pe_resource_t *rsc, enum rsc_role_e *role); +void pe__set_next_role(pe_resource_t *rsc, enum rsc_role_e role, + const char *why); + +pe_resource_t *find_clone_instance(const pe_resource_t *rsc, + const char *sub_id); + +extern void destroy_ticket(gpointer data); +extern pe_ticket_t *ticket_new(const char *ticket_id, pe_working_set_t * data_set); + +// Resources for manipulating resource names +const char *pe_base_name_end(const char *id); +char *clone_strip(const char *last_rsc_id); +char *clone_zero(const char *last_rsc_id); + +static inline bool +pe_base_name_eq(const pe_resource_t *rsc, const char *id) +{ + if (id && rsc && rsc->id) { + // Number of characters in rsc->id before any clone suffix + size_t base_len = pe_base_name_end(rsc->id) - rsc->id + 1; + + return (strlen(id) == base_len) && !strncmp(id, rsc->id, base_len); + } + return false; +} + +int pe__target_rc_from_xml(const xmlNode *xml_op); + +gint pe__cmp_node_name(gconstpointer a, gconstpointer b); +bool is_set_recursive(const pe_resource_t *rsc, long long flag, bool any); + +enum rsc_digest_cmp_val { + /*! Digests are the same */ + RSC_DIGEST_MATCH = 0, + /*! Params that require a restart changed */ + RSC_DIGEST_RESTART, + /*! Some parameter changed. */ + RSC_DIGEST_ALL, + /*! rsc op didn't have a digest associated with it, so + * it is unknown if parameters changed or not. */ + RSC_DIGEST_UNKNOWN, +}; + +typedef struct op_digest_cache_s { + enum rsc_digest_cmp_val rc; + xmlNode *params_all; + xmlNode *params_secure; + xmlNode *params_restart; + char *digest_all_calc; + char *digest_secure_calc; + char *digest_restart_calc; +} op_digest_cache_t; + +op_digest_cache_t *pe__calculate_digests(pe_resource_t *rsc, const char *task, + guint *interval_ms, + const pe_node_t *node, + const xmlNode *xml_op, + GHashTable *overrides, + bool calc_secure, + pe_working_set_t *data_set); + +void pe__free_digests(gpointer ptr); + +op_digest_cache_t *rsc_action_digest_cmp(pe_resource_t *rsc, + const xmlNode *xml_op, + pe_node_t *node, + pe_working_set_t *data_set); + +pe_action_t *pe_fence_op(pe_node_t *node, const char *op, bool optional, + const char *reason, bool priority_delay, + pe_working_set_t *data_set); +void trigger_unfencing(pe_resource_t *rsc, pe_node_t *node, + const char *reason, pe_action_t *dependency, + pe_working_set_t *data_set); + +char *pe__action2reason(const pe_action_t *action, enum pe_action_flags flag); +void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite); +void pe__add_action_expected_result(pe_action_t *action, int expected_result); + +void pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags); +void pe__clear_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags); +void pe__clear_resource_flags_on_all(pe_working_set_t *data_set, uint64_t flag); + +gboolean add_tag_ref(GHashTable * tags, const char * tag_name, const char * obj_ref); + +//! \deprecated This function will be removed in a future release +void print_rscs_brief(GList *rsc_list, const char * pre_text, long options, + void * print_data, gboolean print_all); +int pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, unsigned int options); +void pe_fence_node(pe_working_set_t * data_set, pe_node_t * node, const char *reason, bool priority_delay); + +pe_node_t *pe_create_node(const char *id, const char *uname, const char *type, + const char *score, pe_working_set_t * data_set); + +//! \deprecated This function will be removed in a future release +void common_print(pe_resource_t *rsc, const char *pre_text, const char *name, + const pe_node_t *node, long options, void *print_data); +int pe__common_output_text(pcmk__output_t *out, const pe_resource_t *rsc, + const char *name, const pe_node_t *node, + unsigned int options); +int pe__common_output_html(pcmk__output_t *out, const pe_resource_t *rsc, + const char *name, const pe_node_t *node, + unsigned int options); + +GList *pe__bundle_containers(const pe_resource_t *bundle); + +int pe__bundle_max(const pe_resource_t *rsc); +int pe__bundle_max_per_node(const pe_resource_t *rsc); + +pe_resource_t *pe__find_bundle_replica(const pe_resource_t *bundle, + const pe_node_t *node); +bool pe__bundle_needs_remote_name(pe_resource_t *rsc); +const char *pe__add_bundle_remote_name(pe_resource_t *rsc, + pe_working_set_t *data_set, + xmlNode *xml, const char *field); +const char *pe_node_attribute_calculated(const pe_node_t *node, + const char *name, + const pe_resource_t *rsc); +const char *pe_node_attribute_raw(const pe_node_t *node, const char *name); +bool pe__is_universal_clone(const pe_resource_t *rsc, + const pe_working_set_t *data_set); +void pe__add_param_check(const xmlNode *rsc_op, pe_resource_t *rsc, + pe_node_t *node, enum pe_check_parameters, + pe_working_set_t *data_set); +void pe__foreach_param_check(pe_working_set_t *data_set, + void (*cb)(pe_resource_t*, pe_node_t*, + const xmlNode*, + enum pe_check_parameters)); +void pe__free_param_checks(pe_working_set_t *data_set); + +bool pe__shutdown_requested(const pe_node_t *node); +void pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set); + +/*! + * \internal + * \brief Register xml formatting message functions. + * + * \param[in,out] out Output object to register messages with + */ +void pe__register_messages(pcmk__output_t *out); + +void pe__unpack_dataset_nvpairs(const xmlNode *xml_obj, const char *set_name, + const pe_rule_eval_data_t *rule_data, + GHashTable *hash, const char *always_first, + gboolean overwrite, pe_working_set_t *data_set); + +bool pe__resource_is_disabled(const pe_resource_t *rsc); +pe_action_t *pe__clear_resource_history(pe_resource_t *rsc, + const pe_node_t *node, + pe_working_set_t *data_set); + +GList *pe__rscs_with_tag(pe_working_set_t *data_set, const char *tag_name); +GList *pe__unames_with_tag(pe_working_set_t *data_set, const char *tag_name); +bool pe__rsc_has_tag(pe_working_set_t *data_set, const char *rsc, const char *tag); +bool pe__uname_has_tag(pe_working_set_t *data_set, const char *node, const char *tag); + +bool pe__rsc_running_on_only(const pe_resource_t *rsc, const pe_node_t *node); +bool pe__rsc_running_on_any(pe_resource_t *rsc, GList *node_list); +GList *pe__filter_rsc_list(GList *rscs, GList *filter); +GList * pe__build_node_name_list(pe_working_set_t *data_set, const char *s); +GList * pe__build_rsc_list(pe_working_set_t *data_set, const char *s); + +bool pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node); + +gboolean pe__bundle_is_filtered(const pe_resource_t *rsc, GList *only_rsc, + gboolean check_parent); +gboolean pe__clone_is_filtered(const pe_resource_t *rsc, GList *only_rsc, + gboolean check_parent); +gboolean pe__group_is_filtered(const pe_resource_t *rsc, GList *only_rsc, + gboolean check_parent); +gboolean pe__native_is_filtered(const pe_resource_t *rsc, GList *only_rsc, + gboolean check_parent); + +xmlNode *pe__failed_probe_for_rsc(const pe_resource_t *rsc, const char *name); + +const char *pe__clone_child_id(const pe_resource_t *rsc); + +int pe__sum_node_health_scores(const pe_node_t *node, int base_health); +int pe__node_health(pe_node_t *node); + +static inline enum pcmk__health_strategy +pe__health_strategy(pe_working_set_t *data_set) +{ + return pcmk__parse_health_strategy(pe_pref(data_set->config_hash, + PCMK__OPT_NODE_HEALTH_STRATEGY)); +} + +static inline int +pe__health_score(const char *option, pe_working_set_t *data_set) +{ + return char2score(pe_pref(data_set->config_hash, option)); +} + +/*! + * \internal + * \brief Return a string suitable for logging as a node name + * + * \param[in] node Node to return a node name string for + * + * \return Node name if available, otherwise node ID if available, + * otherwise "unspecified node" if node is NULL or "unidentified node" + * if node has neither a name nor ID. + */ +static inline const char * +pe__node_name(const pe_node_t *node) +{ + if (node == NULL) { + return "unspecified node"; + + } else if (node->details->uname != NULL) { + return node->details->uname; + + } else if (node->details->id != NULL) { + return node->details->id; + + } else { + return "unidentified node"; + } +} + +/*! + * \internal + * \brief Check whether two node objects refer to the same node + * + * \param[in] node1 First node object to compare + * \param[in] node2 Second node object to compare + * + * \return true if \p node1 and \p node2 refer to the same node + */ +static inline bool +pe__same_node(const pe_node_t *node1, const pe_node_t *node2) +{ + return (node1 != NULL) && (node2 != NULL) + && (node1->details == node2->details); +} + +/*! + * \internal + * \brief Get the operation key from an action history entry + * + * \param[in] xml Action history entry + * + * \return Entry's operation key + */ +static inline const char * +pe__xe_history_key(const xmlNode *xml) +{ + if (xml == NULL) { + return NULL; + } else { + /* @COMPAT Pacemaker <= 1.1.5 did not add the key, and used the ID + * instead. Checking for that allows us to process old saved CIBs, + * including some regression tests. + */ + const char *key = crm_element_value(xml, XML_LRM_ATTR_TASK_KEY); + + return pcmk__str_empty(key)? ID(xml) : key; + } +} + +#endif diff --git a/include/crm/pengine/pe_types.h b/include/crm/pengine/pe_types.h new file mode 100644 index 0000000..cc626c8 --- /dev/null +++ b/include/crm/pengine/pe_types.h @@ -0,0 +1,568 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_PENGINE_PE_TYPES__H +# define PCMK__CRM_PENGINE_PE_TYPES__H + + +# include <stdbool.h> // bool +# include <sys/types.h> // time_t +# include <libxml/tree.h> // xmlNode +# include <glib.h> // gboolean, guint, GList, GHashTable +# include <crm/common/iso8601.h> +# include <crm/pengine/common.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief Data types for cluster status + * \ingroup pengine + */ + +typedef struct pe_node_s pe_node_t; +typedef struct pe_action_s pe_action_t; +typedef struct pe_resource_s pe_resource_t; +typedef struct pe_working_set_s pe_working_set_t; + +enum pe_obj_types { + pe_unknown = -1, + pe_native = 0, + pe_group = 1, + pe_clone = 2, + pe_container = 3, +}; + +typedef struct resource_object_functions_s { + gboolean (*unpack) (pe_resource_t*, pe_working_set_t*); + pe_resource_t *(*find_rsc) (pe_resource_t *parent, const char *search, + const pe_node_t *node, int flags); + /* parameter result must be free'd */ + char *(*parameter) (pe_resource_t*, pe_node_t*, gboolean, const char*, + pe_working_set_t*); + //! \deprecated will be removed in a future release + void (*print) (pe_resource_t*, const char*, long, void*); + gboolean (*active) (pe_resource_t*, gboolean); + enum rsc_role_e (*state) (const pe_resource_t*, gboolean); + pe_node_t *(*location) (const pe_resource_t*, GList**, int); + void (*free) (pe_resource_t*); + void (*count) (pe_resource_t*); + gboolean (*is_filtered) (const pe_resource_t*, GList *, gboolean); + + /*! + * \brief + * \internal Find a node (and optionally count all) where resource is active + * + * \param[in] rsc Resource to check + * \param[out] count_all If not NULL, set this to count of active nodes + * \param[out] count_clean If not NULL, set this to count of clean nodes + * + * \return A node where the resource is active, preferring the source node + * if the resource is involved in a partial migration or a clean, + * online node if the resource's "requires" is "quorum" or + * "nothing", or NULL if the resource is inactive. + */ + pe_node_t *(*active_node)(const pe_resource_t *rsc, unsigned int *count_all, + unsigned int *count_clean); +} resource_object_functions_t; + +typedef struct resource_alloc_functions_s resource_alloc_functions_t; + +enum pe_quorum_policy { + no_quorum_freeze, + no_quorum_stop, + no_quorum_ignore, + no_quorum_suicide, + no_quorum_demote +}; + +enum node_type { + node_ping, //! \deprecated Do not use + node_member, + node_remote +}; + +//! \deprecated will be removed in a future release +enum pe_restart { + pe_restart_restart, //! \deprecated will be removed in a future release + pe_restart_ignore //! \deprecated will be removed in a future release +}; + +//! Determine behavior of pe_find_resource_with_flags() +enum pe_find { + pe_find_renamed = 0x001, //!< match resource ID or LRM history ID + pe_find_anon = 0x002, //!< match base name of anonymous clone instances + pe_find_clone = 0x004, //!< match only clone instances + pe_find_current = 0x008, //!< match resource active on specified node + pe_find_inactive = 0x010, //!< match resource not running anywhere + pe_find_any = 0x020, //!< match base name of any clone instance +}; + +// @TODO Make these an enum + +# define pe_flag_have_quorum 0x00000001ULL +# define pe_flag_symmetric_cluster 0x00000002ULL +# define pe_flag_maintenance_mode 0x00000008ULL + +# define pe_flag_stonith_enabled 0x00000010ULL +# define pe_flag_have_stonith_resource 0x00000020ULL +# define pe_flag_enable_unfencing 0x00000040ULL +# define pe_flag_concurrent_fencing 0x00000080ULL + +# define pe_flag_stop_rsc_orphans 0x00000100ULL +# define pe_flag_stop_action_orphans 0x00000200ULL +# define pe_flag_stop_everything 0x00000400ULL + +# define pe_flag_start_failure_fatal 0x00001000ULL + +//! \deprecated +# define pe_flag_remove_after_stop 0x00002000ULL + +# define pe_flag_startup_fencing 0x00004000ULL +# define pe_flag_shutdown_lock 0x00008000ULL + +# define pe_flag_startup_probes 0x00010000ULL +# define pe_flag_have_status 0x00020000ULL +# define pe_flag_have_remote_nodes 0x00040000ULL + +# define pe_flag_quick_location 0x00100000ULL +# define pe_flag_sanitized 0x00200000ULL + +//! \deprecated +# define pe_flag_stdout 0x00400000ULL + +//! Don't count total, disabled and blocked resource instances +# define pe_flag_no_counts 0x00800000ULL + +/*! Skip deprecated code that is kept solely for backward API compatibility. + * (Internal code should always set this.) + */ +# define pe_flag_no_compat 0x01000000ULL + +# define pe_flag_show_scores 0x02000000ULL +# define pe_flag_show_utilization 0x04000000ULL + +/*! + * When scheduling, only unpack the CIB (including constraints), calculate + * as much cluster status as possible, and apply node health. + */ +# define pe_flag_check_config 0x08000000ULL + +struct pe_working_set_s { + xmlNode *input; + crm_time_t *now; + + /* options extracted from the input */ + char *dc_uuid; + pe_node_t *dc_node; + const char *stonith_action; + const char *placement_strategy; + + unsigned long long flags; + + int stonith_timeout; + enum pe_quorum_policy no_quorum_policy; + + GHashTable *config_hash; + GHashTable *tickets; + + // Actions for which there can be only one (e.g. fence nodeX) + GHashTable *singletons; + + GList *nodes; + GList *resources; + GList *placement_constraints; + GList *ordering_constraints; + GList *colocation_constraints; + GList *ticket_constraints; + + GList *actions; + xmlNode *failed; + xmlNode *op_defaults; + xmlNode *rsc_defaults; + + /* stats */ + int num_synapse; + int max_valid_nodes; //! Deprecated (will be removed in a future release) + int order_id; + int action_id; + + /* final output */ + xmlNode *graph; + + GHashTable *template_rsc_sets; + const char *localhost; + GHashTable *tags; + + int blocked_resources; + int disabled_resources; + + GList *param_check; // History entries that need to be checked + GList *stop_needed; // Containers that need stop actions + time_t recheck_by; // Hint to controller to re-run scheduler by this time + int ninstances; // Total number of resource instances + guint shutdown_lock;// How long (seconds) to lock resources to shutdown node + int priority_fencing_delay; // Priority fencing delay + + void *priv; +}; + +enum pe_check_parameters { + /* Clear fail count if parameters changed for un-expired start or monitor + * last_failure. + */ + pe_check_last_failure, + + /* Clear fail count if parameters changed for start, monitor, promote, or + * migrate_from actions for active resources. + */ + pe_check_active, +}; + +struct pe_node_shared_s { + const char *id; + const char *uname; + enum node_type type; + + /* @TODO convert these flags into a bitfield */ + gboolean online; + gboolean standby; + gboolean standby_onfail; + gboolean pending; + gboolean unclean; + gboolean unseen; + gboolean shutdown; + gboolean expected_up; + gboolean is_dc; + gboolean maintenance; + gboolean rsc_discovery_enabled; + gboolean remote_requires_reset; + gboolean remote_was_fenced; + gboolean remote_maintenance; /* what the remote-rsc is thinking */ + gboolean unpacked; + + int num_resources; + pe_resource_t *remote_rsc; + GList *running_rsc; /* pe_resource_t* */ + GList *allocated_rsc; /* pe_resource_t* */ + + GHashTable *attrs; /* char* => char* */ + GHashTable *utilization; + GHashTable *digest_cache; //!< cache of calculated resource digests + int priority; // calculated based on the priority of resources running on the node + pe_working_set_t *data_set; //!< Cluster that this node is part of +}; + +struct pe_node_s { + int weight; + gboolean fixed; //!< \deprecated Will be removed in a future release + int count; + struct pe_node_shared_s *details; + int rsc_discover_mode; +}; + +# define pe_rsc_orphan 0x00000001ULL +# define pe_rsc_managed 0x00000002ULL +# define pe_rsc_block 0x00000004ULL +# define pe_rsc_orphan_container_filler 0x00000008ULL + +# define pe_rsc_notify 0x00000010ULL +# define pe_rsc_unique 0x00000020ULL +# define pe_rsc_fence_device 0x00000040ULL +# define pe_rsc_promotable 0x00000080ULL + +# define pe_rsc_provisional 0x00000100ULL +# define pe_rsc_allocating 0x00000200ULL +# define pe_rsc_merging 0x00000400ULL +# define pe_rsc_restarting 0x00000800ULL + +# define pe_rsc_stop 0x00001000ULL +# define pe_rsc_reload 0x00002000ULL +# define pe_rsc_allow_remote_remotes 0x00004000ULL +# define pe_rsc_critical 0x00008000ULL + +# define pe_rsc_failed 0x00010000ULL +# define pe_rsc_detect_loop 0x00020000ULL +# define pe_rsc_runnable 0x00040000ULL +# define pe_rsc_start_pending 0x00080000ULL + +//!< \deprecated Do not use +# define pe_rsc_starting 0x00100000ULL + +//!< \deprecated Do not use +# define pe_rsc_stopping 0x00200000ULL + +# define pe_rsc_stop_unexpected 0x00400000ULL +# define pe_rsc_allow_migrate 0x00800000ULL + +# define pe_rsc_failure_ignored 0x01000000ULL +# define pe_rsc_replica_container 0x02000000ULL +# define pe_rsc_maintenance 0x04000000ULL +# define pe_rsc_is_container 0x08000000ULL + +# define pe_rsc_needs_quorum 0x10000000ULL +# define pe_rsc_needs_fencing 0x20000000ULL +# define pe_rsc_needs_unfencing 0x40000000ULL + +/* *INDENT-OFF* */ +enum pe_action_flags { + pe_action_pseudo = 0x00001, + pe_action_runnable = 0x00002, + pe_action_optional = 0x00004, + pe_action_print_always = 0x00008, + + pe_action_have_node_attrs = 0x00010, + pe_action_implied_by_stonith = 0x00040, + pe_action_migrate_runnable = 0x00080, + + pe_action_dumped = 0x00100, + pe_action_processed = 0x00200, +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + pe_action_clear = 0x00400, //! \deprecated Unused +#endif + pe_action_dangle = 0x00800, + + /* This action requires one or more of its dependencies to be runnable. + * We use this to clear the runnable flag before checking dependencies. + */ + pe_action_requires_any = 0x01000, + + pe_action_reschedule = 0x02000, + pe_action_tracking = 0x04000, + pe_action_dedup = 0x08000, //! Internal state tracking when creating graph + + pe_action_dc = 0x10000, //! Action may run on DC instead of target +}; +/* *INDENT-ON* */ + +struct pe_resource_s { + char *id; + char *clone_name; + xmlNode *xml; + xmlNode *orig_xml; + xmlNode *ops_xml; + + pe_working_set_t *cluster; + pe_resource_t *parent; + + enum pe_obj_types variant; + void *variant_opaque; + resource_object_functions_t *fns; + resource_alloc_functions_t *cmds; + + enum rsc_recovery_type recovery_type; + + enum pe_restart restart_type; //!< \deprecated will be removed in future release + + int priority; + int stickiness; + int sort_index; + int failure_timeout; + int migration_threshold; + guint remote_reconnect_ms; + char *pending_task; + + unsigned long long flags; + + // @TODO merge these into flags + gboolean is_remote_node; + gboolean exclusive_discover; + + /* Pay special attention to whether you want to use rsc_cons_lhs and + * rsc_cons directly, which include only colocations explicitly involving + * this resource, or call libpacemaker's pcmk__with_this_colocations() and + * pcmk__this_with_colocations() functions, which may return relevant + * colocations involving the resource's ancestors as well. + */ + + //!@{ + //! This field should be treated as internal to Pacemaker + GList *rsc_cons_lhs; // List of pcmk__colocation_t* + GList *rsc_cons; // List of pcmk__colocation_t* + GList *rsc_location; // List of pe__location_t* + GList *actions; // List of pe_action_t* + GList *rsc_tickets; // List of rsc_ticket* + //!@} + + pe_node_t *allocated_to; + pe_node_t *partial_migration_target; + pe_node_t *partial_migration_source; + GList *running_on; /* pe_node_t* */ + GHashTable *known_on; /* pe_node_t* */ + GHashTable *allowed_nodes; /* pe_node_t* */ + + enum rsc_role_e role; + enum rsc_role_e next_role; + + GHashTable *meta; + GHashTable *parameters; //! \deprecated Use pe_rsc_params() instead + GHashTable *utilization; + + GList *children; /* pe_resource_t* */ + GList *dangling_migrations; /* pe_node_t* */ + + pe_resource_t *container; + GList *fillers; + + // @COMPAT These should be made const at next API compatibility break + pe_node_t *pending_node; // Node on which pending_task is happening + pe_node_t *lock_node; // Resource is shutdown-locked to this node + + time_t lock_time; // When shutdown lock started + + /* Resource parameters may have node-attribute-based rules, which means the + * values can vary by node. This table is a cache of parameter name/value + * tables for each node (as needed). Use pe_rsc_params() to get the table + * for a given node. + */ + GHashTable *parameter_cache; // Key = node name, value = parameters table +}; + +struct pe_action_s { + int id; + int priority; + + pe_resource_t *rsc; + pe_node_t *node; + xmlNode *op_entry; + + char *task; + char *uuid; + char *cancel_task; + char *reason; + + enum pe_action_flags flags; + enum rsc_start_requirement needs; + enum action_fail_response on_fail; + enum rsc_role_e fail_role; + + GHashTable *meta; + GHashTable *extra; + + /* + * These two varables are associated with the constraint logic + * that involves first having one or more actions runnable before + * then allowing this action to execute. + * + * These varables are used with features such as 'clone-min' which + * requires at minimum X number of cloned instances to be running + * before an order dependency can run. Another option that uses + * this is 'require-all=false' in ordering constrants. This option + * says "only require one instance of a resource to start before + * allowing dependencies to start" -- basically, require-all=false is + * the same as clone-min=1. + */ + + /* current number of known runnable actions in the before list. */ + int runnable_before; + /* the number of "before" runnable actions required for this action + * to be considered runnable */ + int required_runnable_before; + + GList *actions_before; /* pe_action_wrapper_t* */ + GList *actions_after; /* pe_action_wrapper_t* */ + + /* Some of the above fields could be moved to the details, + * except for API backward compatibility. + */ + void *action_details; // varies by type of action +}; + +typedef struct pe_ticket_s { + char *id; + gboolean granted; + time_t last_granted; + gboolean standby; + GHashTable *state; +} pe_ticket_t; + +typedef struct pe_tag_s { + char *id; + GList *refs; +} pe_tag_t; + +//! Internal tracking for transition graph creation +enum pe_link_state { + pe_link_not_dumped, //! Internal tracking for transition graph creation + pe_link_dumped, //! Internal tracking for transition graph creation + pe_link_dup, //! \deprecated No longer used by Pacemaker +}; + +enum pe_discover_e { + pe_discover_always = 0, + pe_discover_never, + pe_discover_exclusive, +}; + +/* *INDENT-OFF* */ +enum pe_ordering { + pe_order_none = 0x0, /* deleted */ + pe_order_optional = 0x1, /* pure ordering, nothing implied */ + pe_order_apply_first_non_migratable = 0x2, /* Only apply this constraint's ordering if first is not migratable. */ + + pe_order_implies_first = 0x10, /* If 'then' is required, ensure 'first' is too */ + pe_order_implies_then = 0x20, /* If 'first' is required, ensure 'then' is too */ + pe_order_promoted_implies_first = 0x40, /* If 'then' is required and then's rsc is promoted, ensure 'first' becomes required too */ + + /* first requires then to be both runnable and migrate runnable. */ + pe_order_implies_first_migratable = 0x80, + + pe_order_runnable_left = 0x100, /* 'then' requires 'first' to be runnable */ + + pe_order_pseudo_left = 0x200, /* 'then' can only be pseudo if 'first' is runnable */ + pe_order_implies_then_on_node = 0x400, /* If 'first' is required on 'nodeX', + * ensure instances of 'then' on 'nodeX' are too. + * Only really useful if 'then' is a clone and 'first' is not + */ + pe_order_probe = 0x800, /* If 'first->rsc' is + * - running but about to stop, ignore the constraint + * - otherwise, behave as runnable_left + */ + + pe_order_restart = 0x1000, /* 'then' is runnable if 'first' is optional or runnable */ + pe_order_stonith_stop = 0x2000, //<! \deprecated Will be removed in future release + pe_order_serialize_only = 0x4000, /* serialize */ + pe_order_same_node = 0x8000, /* applies only if 'first' and 'then' are on same node */ + + pe_order_implies_first_printed = 0x10000, /* Like ..implies_first but only ensures 'first' is printed, not mandatory */ + pe_order_implies_then_printed = 0x20000, /* Like ..implies_then but only ensures 'then' is printed, not mandatory */ + + pe_order_asymmetrical = 0x100000, /* Indicates asymmetrical one way ordering constraint. */ + pe_order_load = 0x200000, /* Only relevant if... */ + pe_order_one_or_more = 0x400000, /* 'then' is runnable only if one or more of its dependencies are too */ + pe_order_anti_colocation = 0x800000, + + pe_order_preserve = 0x1000000, /* Hack for breaking user ordering constraints with container resources */ + pe_order_then_cancels_first = 0x2000000, // if 'then' becomes required, 'first' becomes optional + pe_order_trace = 0x4000000, /* test marker */ + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + // \deprecated Use pe_order_promoted_implies_first instead + pe_order_implies_first_master = pe_order_promoted_implies_first, +#endif +}; +/* *INDENT-ON* */ + +typedef struct pe_action_wrapper_s { + enum pe_ordering type; + enum pe_link_state state; + pe_action_t *action; +} pe_action_wrapper_t; + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/pengine/pe_types_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_PENGINE_PE_TYPES__H diff --git a/include/crm/pengine/pe_types_compat.h b/include/crm/pengine/pe_types_compat.h new file mode 100644 index 0000000..6f174c4 --- /dev/null +++ b/include/crm/pengine/pe_types_compat.h @@ -0,0 +1,63 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_PENGINE_PE_TYPES_COMPAT__H +# define PCMK__CRM_PENGINE_PE_TYPES_COMPAT__H + +#include <crm/pengine/pe_types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker scheduler API + * \ingroup pengine + * \deprecated Do not include this header directly. The scheduler APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +//!@{ +//! \deprecated Do not use (unused by Pacemaker) +enum pe_graph_flags { + pe_graph_none = 0x00000, + pe_graph_updated_first = 0x00001, + pe_graph_updated_then = 0x00002, + pe_graph_disable = 0x00004, +}; +//!@} + +//!< \deprecated Use pe_action_t instead +typedef struct pe_action_s action_t; + +//!< \deprecated Use pe_action_wrapper_t instead +typedef struct pe_action_wrapper_s action_wrapper_t; + +//!< \deprecated Use pe_node_t instead +typedef struct pe_node_s node_t; + +//!< \deprecated Use enum pe_quorum_policy instead +typedef enum pe_quorum_policy no_quorum_policy_t; + +//!< \deprecated use pe_resource_t instead +typedef struct pe_resource_s resource_t; + +//!< \deprecated Use pe_tag_t instead +typedef struct pe_tag_s tag_t; + +//!< \deprecated Use pe_ticket_t instead +typedef struct pe_ticket_s ticket_t; + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_PENGINE_PE_TYPES_COMPAT__H diff --git a/include/crm/pengine/remote_internal.h b/include/crm/pengine/remote_internal.h new file mode 100644 index 0000000..46d58fc --- /dev/null +++ b/include/crm/pengine/remote_internal.h @@ -0,0 +1,41 @@ +/* + * Copyright 2013-2019 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 PE_REMOTE__H +# define PE_REMOTE__H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <glib.h> // gboolean +#include <libxml/tree.h> // xmlNode +#include <crm/pengine/status.h> + +bool xml_contains_remote_node(xmlNode *xml); +bool pe__is_remote_node(const pe_node_t *node); +bool pe__is_guest_node(const pe_node_t *node); +bool pe__is_guest_or_remote_node(const pe_node_t *node); +bool pe__is_bundle_node(const pe_node_t *node); +bool pe__resource_is_remote_conn(const pe_resource_t *rsc, + const pe_working_set_t *data_set); +pe_resource_t *pe__resource_contains_guest_node(const pe_working_set_t *data_set, + const pe_resource_t *rsc); +void pe_foreach_guest_node(const pe_working_set_t *data_set, const pe_node_t *host, + void (*helper)(const pe_node_t*, void*), void *user_data); +xmlNode *pe_create_remote_xml(xmlNode *parent, const char *uname, + const char *container_id, const char *migrateable, + const char *is_managed, const char *start_timeout, + const char *server, const char *port); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/pengine/rules.h b/include/crm/pengine/rules.h new file mode 100644 index 0000000..264bd69 --- /dev/null +++ b/include/crm/pengine/rules.h @@ -0,0 +1,80 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_PENGINE_RULES__H +# define PCMK__CRM_PENGINE_RULES__H + +# include <glib.h> +# include <crm/crm.h> +# include <crm/common/iso8601.h> +# include <crm/pengine/common.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum expression_type { + not_expr = 0, + nested_rule = 1, + attr_expr = 2, + loc_expr = 3, + role_expr = 4, + time_expr = 5, +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + //! \deprecated Do not use (will be removed in a future release) + version_expr = 6, +#endif + rsc_expr = 7, + op_expr = 8, +}; + +enum expression_type find_expression_type(xmlNode * expr); + +gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, + crm_time_t *now, crm_time_t *next_change); + +gboolean pe_test_rule(xmlNode *rule, GHashTable *node_hash, + enum rsc_role_e role, crm_time_t *now, + crm_time_t *next_change, pe_match_data_t *match_data); + +gboolean pe_test_expression(xmlNode *expr, GHashTable *node_hash, + enum rsc_role_e role, crm_time_t *now, + crm_time_t *next_change, + pe_match_data_t *match_data); + +void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, + const pe_rule_eval_data_t *rule_data, GHashTable *hash, + const char *always_first, gboolean overwrite, + crm_time_t *next_change); + +void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, + const char *set_name, GHashTable *node_hash, + GHashTable *hash, const char *always_first, + gboolean overwrite, crm_time_t *now, + crm_time_t *next_change); + +char *pe_expand_re_matches(const char *string, + const pe_re_match_data_t *match_data); + +gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data, + crm_time_t *next_change); +gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, + crm_time_t *next_change); +gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, + crm_time_t *next_change); + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/pengine/rules_compat.h> +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/pengine/rules_compat.h b/include/crm/pengine/rules_compat.h new file mode 100644 index 0000000..95fc9ac --- /dev/null +++ b/include/crm/pengine/rules_compat.h @@ -0,0 +1,72 @@ +/* + * Copyright 2004-2021 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 PCMK__CRM_PENGINE_RULES_COMPAT__H +# define PCMK__CRM_PENGINE_RULES_COMPAT__H + +#include <glib.h> +#include <libxml/tree.h> // xmlNode +#include <crm/common/iso8601.h> // crm_time_t +#include <crm/pengine/pe_types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Deprecated Pacemaker rule API + * \ingroup pengine + * \deprecated Do not include this header directly. The rule APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +//! \deprecated Use pe_evaluate_rules() instead +gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now); + +//! \deprecated Use pe_test_rule() instead +gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, + crm_time_t *now); + +//! \deprecated Use pe_test_rule() instead +gboolean pe_test_rule_re(xmlNode *rule, GHashTable *node_hash, + enum rsc_role_e role, crm_time_t *now, + pe_re_match_data_t *re_match_data); + +//! \deprecated Use pe_test_rule() instead +gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, + enum rsc_role_e role, crm_time_t *now, + pe_match_data_t *match_data); + +//! \deprecated Use pe_test_expression() instead +gboolean test_expression(xmlNode *expr, GHashTable *node_hash, + enum rsc_role_e role, crm_time_t *now); + +//! \deprecated Use pe_test_expression() instead +gboolean pe_test_expression_re(xmlNode *expr, GHashTable *node_hash, + enum rsc_role_e role, crm_time_t *now, + pe_re_match_data_t *re_match_data); + +//! \deprecated Use pe_test_expression() instead +gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash, + enum rsc_role_e role, + crm_time_t *now, pe_match_data_t *match_data); + +//! \deprecated Use pe_unpack_nvpairs() instead +void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, + const char *set_name, GHashTable *node_hash, + GHashTable *hash, const char *always_first, + gboolean overwrite, crm_time_t *now); + +#ifdef __cplusplus +} +#endif + +#endif // PCMK__CRM_PENGINE_RULES_COMPAT__H diff --git a/include/crm/pengine/rules_internal.h b/include/crm/pengine/rules_internal.h new file mode 100644 index 0000000..9b81963 --- /dev/null +++ b/include/crm/pengine/rules_internal.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015-2022 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 RULES_INTERNAL_H +#define RULES_INTERNAL_H + +#include <glib.h> +#include <libxml/tree.h> + +#include <crm/common/iso8601.h> +#include <crm/pengine/common.h> +#include <crm/pengine/rules.h> + +GList *pe_unpack_alerts(const xmlNode *alerts); +void pe_free_alert_list(GList *alert_list); + +gboolean pe__eval_attr_expr(const xmlNode *expr, + const pe_rule_eval_data_t *rule_data); +int pe__eval_date_expr(const xmlNode *expr, + const pe_rule_eval_data_t *rule_data, + crm_time_t *next_change); +gboolean pe__eval_op_expr(const xmlNode *expr, + const pe_rule_eval_data_t *rule_data); +gboolean pe__eval_role_expr(const xmlNode *expr, + const pe_rule_eval_data_t *rule_data); +gboolean pe__eval_rsc_expr(const xmlNode *expr, + const pe_rule_eval_data_t *rule_data); + +int pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec); + +#endif diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h new file mode 100644 index 0000000..145a166 --- /dev/null +++ b/include/crm/pengine/status.h @@ -0,0 +1,112 @@ +/* + * Copyright 2004-2023 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 PCMK__CRM_PENGINE_STATUS__H +# define PCMK__CRM_PENGINE_STATUS__H + +# include <glib.h> // gboolean +# include <stdbool.h> // bool +# include <crm/common/util.h> // pcmk_is_set() +# include <crm/common/iso8601.h> +# include <crm/pengine/common.h> +# include <crm/pengine/pe_types.h> // pe_node_t, pe_resource_t, etc. +# include <crm/pengine/complex.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief Cluster status and scheduling + * \ingroup pengine + */ + +const char *rsc_printable_id(const pe_resource_t *rsc); +gboolean cluster_status(pe_working_set_t * data_set); +pe_working_set_t *pe_new_working_set(void); +void pe_free_working_set(pe_working_set_t *data_set); +void set_working_set_defaults(pe_working_set_t * data_set); +void cleanup_calculations(pe_working_set_t * data_set); +void pe_reset_working_set(pe_working_set_t *data_set); +pe_resource_t *pe_find_resource(GList *rsc_list, const char *id_rh); +pe_resource_t *pe_find_resource_with_flags(GList *rsc_list, const char *id, enum pe_find flags); +pe_node_t *pe_find_node(const GList *node_list, const char *node_name); +pe_node_t *pe_find_node_id(const GList *node_list, const char *id); +pe_node_t *pe_find_node_any(const GList *node_list, const char *id, + const char *node_name); +GList *find_operations(const char *rsc, const char *node, gboolean active_filter, + pe_working_set_t * data_set); +void calculate_active_ops(const GList *sorted_op_list, int *start_index, + int *stop_index); +int pe_bundle_replicas(const pe_resource_t *rsc); + +/*! + * \brief Check whether a resource is any clone type + * + * \param[in] rsc Resource to check + * + * \return true if resource is clone, false otherwise + */ +static inline bool +pe_rsc_is_clone(const pe_resource_t *rsc) +{ + return rsc && (rsc->variant == pe_clone); +} + +/*! + * \brief Check whether a resource is a globally unique clone + * + * \param[in] rsc Resource to check + * + * \return true if resource is unique clone, false otherwise + */ +static inline bool +pe_rsc_is_unique_clone(const pe_resource_t *rsc) +{ + return pe_rsc_is_clone(rsc) && pcmk_is_set(rsc->flags, pe_rsc_unique); +} + +/*! + * \brief Check whether a resource is an anonymous clone + * + * \param[in] rsc Resource to check + * + * \return true if resource is anonymous clone, false otherwise + */ +static inline bool +pe_rsc_is_anon_clone(const pe_resource_t *rsc) +{ + return pe_rsc_is_clone(rsc) && !pcmk_is_set(rsc->flags, pe_rsc_unique); +} + +/*! + * \brief Check whether a resource is part of a bundle + * + * \param[in] rsc Resource to check + * + * \return true if resource is part of a bundle, false otherwise + */ +static inline bool +pe_rsc_is_bundled(const pe_resource_t *rsc) +{ + if (rsc == NULL) { + return false; + } + while (rsc->parent != NULL) { + rsc = rsc->parent; + } + return rsc->variant == pe_container; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/services.h b/include/crm/services.h new file mode 100644 index 0000000..4f0a5f0 --- /dev/null +++ b/include/crm/services.h @@ -0,0 +1,432 @@ +/* + * Copyright 2010-2023 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 PCMK__CRM_SERVICES__H +# define PCMK__CRM_SERVICES__H + + +# include <glib.h> +# include <stdio.h> +# include <stdint.h> +# include <string.h> +# include <stdbool.h> +# include <sys/types.h> + +# include <crm_config.h> // OCF_ROOT_DIR +# include <crm/common/agents.h> +# include <crm/common/results.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief Services API + * \ingroup core + */ + +/* TODO: Autodetect these two ?*/ +# ifndef SYSTEMCTL +# define SYSTEMCTL "/bin/systemctl" +# endif + +/* This is the string passed in the OCF_EXIT_REASON_PREFIX environment variable. + * The stderr output that occurs after this prefix is encountered is considered + * the exit reason for a completed operation. + */ +#define PCMK_OCF_REASON_PREFIX "ocf-exit-reason:" + +// Agent version to use if agent doesn't specify one +#define PCMK_DEFAULT_AGENT_VERSION "0.1" + +enum lsb_exitcode { + PCMK_LSB_OK = 0, + PCMK_LSB_UNKNOWN_ERROR = 1, + PCMK_LSB_INVALID_PARAM = 2, + PCMK_LSB_UNIMPLEMENT_FEATURE = 3, + PCMK_LSB_INSUFFICIENT_PRIV = 4, + PCMK_LSB_NOT_INSTALLED = 5, + PCMK_LSB_NOT_CONFIGURED = 6, + PCMK_LSB_NOT_RUNNING = 7, +}; + +// LSB uses different return codes for status actions +enum lsb_status_exitcode { + PCMK_LSB_STATUS_OK = 0, + PCMK_LSB_STATUS_VAR_PID = 1, + PCMK_LSB_STATUS_VAR_LOCK = 2, + PCMK_LSB_STATUS_NOT_RUNNING = 3, + PCMK_LSB_STATUS_UNKNOWN = 4, + + /* custom codes should be in the 150-199 range reserved for application use */ + PCMK_LSB_STATUS_NOT_INSTALLED = 150, + PCMK_LSB_STATUS_INSUFFICIENT_PRIV = 151, +}; + +//!@{ +//! \deprecated Do not use + +enum nagios_exitcode { + NAGIOS_STATE_OK = 0, + NAGIOS_STATE_WARNING = 1, + NAGIOS_STATE_CRITICAL = 2, + NAGIOS_STATE_UNKNOWN = 3, + + /* This is a custom Pacemaker value (not a nagios convention), used as an + * intermediate value between the services library and the executor, so the + * executor can map it to the corresponding OCF code. + */ + NAGIOS_INSUFFICIENT_PRIV = 100, + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + NAGIOS_STATE_DEPENDENT = 4, + NAGIOS_NOT_INSTALLED = 101, +#endif +}; + +//!@} + +enum svc_action_flags { + /* On timeout, only kill pid, do not kill entire pid group */ + SVC_ACTION_LEAVE_GROUP = 0x01, + SVC_ACTION_NON_BLOCKED = 0x02, +}; + +typedef struct svc_action_private_s svc_action_private_t; + +/*! + * \brief Object for executing external actions + * + * \note This object should never be instantiated directly, but instead created + * using one of the constructor functions (resources_action_create() for + * resource agents, services_alert_create() for alert agents, or + * services_action_create_generic() for generic executables). Similarly, + * do not use sizeof() on this struct. + */ +/* + * NOTE: Internally, services__create_resource_action() is preferable to + * resources_action_create(). + */ +typedef struct svc_action_s { + /*! Operation key (<resource>_<action>_<interval>) for resource actions, + * XML ID for alert actions, or NULL for generic actions + */ + char *id; + + //! XML ID of resource being executed for resource actions, otherwise NULL + char *rsc; + + //! Name of action being executed for resource actions, otherwise NULL + char *action; + + //! Action interval for recurring resource actions, otherwise 0 + guint interval_ms; + + //! Resource standard for resource actions, otherwise NULL + char *standard; + + //! Resource provider for resource actions that require it, otherwise NULL + char *provider; + + //! Resource agent name for resource actions, otherwise NULL + char *agent; + + int timeout; //!< Action timeout (in milliseconds) + + /*! A hash table of name/value pairs to use as parameters for resource and + * alert actions, otherwise NULL. These will be used to set environment + * variables for non-fencing resource agents and alert agents, and to send + * stdin to fence agents. + */ + GHashTable *params; + + int rc; //!< Exit status of action (set by library upon completion) + + //!@{ + //! This field should be treated as internal to Pacemaker + int pid; // Process ID of child + int cancel; // Whether this is a cancellation of a recurring action + //!@} + + int status; //!< Execution status (enum pcmk_exec_status set by library) + + /*! Action counter (set by library for resource actions, or by caller + * otherwise) + */ + int sequence; + + //!@{ + //! This field should be treated as internal to Pacemaker + int expected_rc; // Unused + int synchronous; // Whether execution should be synchronous (blocking) + //!@} + + enum svc_action_flags flags; //!< Flag group of enum svc_action_flags + char *stderr_data; //!< Action stderr (set by library) + char *stdout_data; //!< Action stdout (set by library) + void *cb_data; //!< For caller's use (not used by library) + + //! This field should be treated as internal to Pacemaker + svc_action_private_t *opaque; +} svc_action_t; + +/*! + * \brief Get a list of files or directories in a given path + * + * \param[in] root Full path to a directory to read + * \param[in] files Return list of files if TRUE or directories if FALSE + * \param[in] executable If TRUE and files is TRUE, only return executable files + * + * \return List of what was found as char * items. + * \note The caller is responsibile for freeing the result with + * g_list_free_full(list, free). + */ +GList *get_directory_list(const char *root, gboolean files, + gboolean executable); + +/*! + * \brief Get a list of providers + * + * \param[in] standard List providers of this resource agent standard + * + * \return List of providers as char * list items (or NULL if standard does not + * support providers) + * \note The caller is responsible for freeing the result using + * g_list_free_full(list, free). + */ +GList *resources_list_providers(const char *standard); + +/*! + * \brief Get a list of resource agents + * + * \param[in] standard List agents of this standard (or NULL for all) + * \param[in] provider List agents of this provider (or NULL for all) + * + * \return List of resource agents as char * items. + * \note The caller is responsible for freeing the result using + * g_list_free_full(list, free). + */ +GList *resources_list_agents(const char *standard, const char *provider); + +/*! + * Get list of available standards + * + * \return List of resource standards as char * items. + * \note The caller is responsible for freeing the result using + * g_list_free_full(list, free). + */ +GList *resources_list_standards(void); + +/*! + * \brief Check whether a resource agent exists on the local host + * + * \param[in] standard Resource agent standard of agent to check + * \param[in] provider Provider of agent to check (or NULL) + * \param[in] agent Name of agent to check + * + * \return TRUE if agent exists locally, otherwise FALSE + */ +gboolean resources_agent_exists(const char *standard, const char *provider, + const char *agent); + +/*! + * \brief Create a new resource action + * + * \param[in] name Name of resource that action is for + * \param[in] standard Resource agent standard + * \param[in] provider Resource agent provider + * \param[in] agent Resource agent name + * \param[in] action Name of action to create + * \param[in] interval_ms How often to repeat action (if 0, execute once) + * \param[in] timeout Error if not complete within this time (ms) + * \param[in,out] params Action parameters + * \param[in] flags Group of enum svc_action_flags + * + * \return Newly allocated action + * \note This function assumes ownership of (and may free) \p params. + * \note The caller is responsible for freeing the return value using + * services_action_free(). + */ +svc_action_t *resources_action_create(const char *name, const char *standard, + const char *provider, const char *agent, + const char *action, guint interval_ms, + int timeout, GHashTable *params, + enum svc_action_flags flags); + +/*! + * \brief Reschedule a recurring action for immediate execution + * + * \param[in] name Name of resource that action is for + * \param[in] action Action's name + * \param[in] interval_ms Action's interval (in milliseconds) + * + * \return TRUE on success, otherwise FALSE + */ +gboolean services_action_kick(const char *name, const char *action, + guint interval_ms); + +const char *resources_find_service_class(const char *agent); + +/*! + * \brief Request execution of an arbitrary command + * + * This API has useful infrastructure in place to be able to run a command + * in the background and get notified via a callback when the command finishes. + * + * \param[in] exec Full path to command executable + * \param[in] args NULL-terminated list of arguments to pass to command + * + * \return Newly allocated action object + */ +svc_action_t *services_action_create_generic(const char *exec, + const char *args[]); + +void services_action_cleanup(svc_action_t *op); +void services_action_free(svc_action_t *op); +int services_action_user(svc_action_t *op, const char *user); +gboolean services_action_sync(svc_action_t *op); + +/*! + * \brief Run an action asynchronously, with callback after process is forked + * + * \param[in,out] op Action to run + * \param[in] action_callback Function to call when action completes + * (if NULL, any previously set callback will + * continue to be used) + * \param[in] action_fork_callback Function to call after child process is + * forked for action (if NULL, any + * previously set callback will continue to + * be used) + * + * \retval TRUE if the caller should not free or otherwise use \p op again, + * because one of these conditions is true: + * + * * \p op is NULL. + * * The action was successfully initiated, in which case + * \p action_fork_callback has been called, but \p action_callback has + * not (it will be called when the action completes). + * * The action's ID matched an existing recurring action. The existing + * action has taken over the callback and callback data from \p op + * and has been re-initiated asynchronously, and \p op has been freed. + * * Another action for the same resource is in flight, and \p op will + * be blocked until it completes. + * * The action could not be initiated, and is either non-recurring or + * being cancelled. \p action_fork_callback has not been called, but + * \p action_callback has, and \p op has been freed. + * + * \retval FALSE if \op is still valid, because the action cannot be initiated, + * and is a recurring action that is not being cancelled. + * \p action_fork_callback has not been called, but \p action_callback + * has, and a timer has been set for the next invocation of \p op. + */ +gboolean services_action_async_fork_notify(svc_action_t *op, + void (*action_callback) (svc_action_t *), + void (*action_fork_callback) (svc_action_t *)); + +/*! + * \brief Request asynchronous execution of an action + * + * \param[in,out] op Action to execute + * \param[in] action_callback Function to call when the action completes + * (if NULL, any previously set callback will + * continue to be used) + * + * \retval TRUE if the caller should not free or otherwise use \p op again, + * because one of these conditions is true: + * + * * \p op is NULL. + * * The action was successfully initiated, in which case + * \p action_callback has not been called (it will be called when the + * action completes). + * * The action's ID matched an existing recurring action. The existing + * action has taken over the callback and callback data from \p op + * and has been re-initiated asynchronously, and \p op has been freed. + * * Another action for the same resource is in flight, and \p op will + * be blocked until it completes. + * * The action could not be initiated, and is either non-recurring or + * being cancelled. \p action_callback has been called, and \p op has + * been freed. + * + * \retval FALSE if \op is still valid, because the action cannot be initiated, + * and is a recurring action that is not being cancelled. + * \p action_callback has been called, and a timer has been set for the + * next invocation of \p op. + */ +gboolean services_action_async(svc_action_t *op, + void (*action_callback) (svc_action_t *)); + +gboolean services_action_cancel(const char *name, const char *action, + guint interval_ms); + +/* functions for alert agents */ +svc_action_t *services_alert_create(const char *id, const char *exec, + int timeout, GHashTable *params, + int sequence, void *cb_data); +gboolean services_alert_async(svc_action_t *action, + void (*cb)(svc_action_t *op)); + +enum ocf_exitcode services_result2ocf(const char *standard, const char *action, + int exit_status); + + static inline const char *services_ocf_exitcode_str(enum ocf_exitcode code) { + switch (code) { + case PCMK_OCF_OK: + return "ok"; + case PCMK_OCF_UNKNOWN_ERROR: + return "error"; + case PCMK_OCF_INVALID_PARAM: + return "invalid parameter"; + case PCMK_OCF_UNIMPLEMENT_FEATURE: + return "unimplemented feature"; + case PCMK_OCF_INSUFFICIENT_PRIV: + return "insufficient privileges"; + case PCMK_OCF_NOT_INSTALLED: + return "not installed"; + case PCMK_OCF_NOT_CONFIGURED: + return "not configured"; + case PCMK_OCF_NOT_RUNNING: + return "not running"; + case PCMK_OCF_RUNNING_PROMOTED: + return "promoted"; + case PCMK_OCF_FAILED_PROMOTED: + return "promoted (failed)"; + case PCMK_OCF_DEGRADED: + return "OCF_DEGRADED"; + case PCMK_OCF_DEGRADED_PROMOTED: + return "promoted (degraded)"; + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) + case PCMK_OCF_NOT_SUPPORTED: + return "not supported (DEPRECATED STATUS)"; + case PCMK_OCF_CANCELLED: + return "cancelled (DEPRECATED STATUS)"; + case PCMK_OCF_OTHER_ERROR: + return "other error (DEPRECATED STATUS)"; + case PCMK_OCF_SIGNAL: + return "interrupted by signal (DEPRECATED STATUS)"; + case PCMK_OCF_PENDING: + return "pending (DEPRECATED STATUS)"; + case PCMK_OCF_TIMEOUT: + return "timeout (DEPRECATED STATUS)"; +#endif + default: + return "unknown"; + } + } + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +#include <crm/services_compat.h> +#endif + +# ifdef __cplusplus +} +# endif + +#endif /* __PCMK_SERVICES__ */ diff --git a/include/crm/services_compat.h b/include/crm/services_compat.h new file mode 100644 index 0000000..97310f4 --- /dev/null +++ b/include/crm/services_compat.h @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2022 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 PCMK__CRM_SERVICES_COMPAT__H +# define PCMK__CRM_SERVICES_COMPAT__H + + +#include <crm/common/results.h> +#include <crm/services.h> +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief Deprecated services API + * \ingroup core + * \deprecated Do not include this header directly. The service APIs in this + * header, and the header itself, will be removed in a future + * release. + */ + +# ifndef LSB_ROOT_DIR + //! \deprecated Do not use +# define LSB_ROOT_DIR "/etc/init.d" +# endif + +//! \deprecated Use enum pcmk_exec_status instead +enum op_status { + PCMK_LRM_OP_UNKNOWN = PCMK_EXEC_UNKNOWN, + PCMK_LRM_OP_PENDING = PCMK_EXEC_PENDING, + PCMK_LRM_OP_DONE = PCMK_EXEC_DONE, + PCMK_LRM_OP_CANCELLED = PCMK_EXEC_CANCELLED, + PCMK_LRM_OP_TIMEOUT = PCMK_EXEC_TIMEOUT, + PCMK_LRM_OP_NOTSUPPORTED = PCMK_EXEC_NOT_SUPPORTED, + PCMK_LRM_OP_ERROR = PCMK_EXEC_ERROR, + PCMK_LRM_OP_ERROR_HARD = PCMK_EXEC_ERROR_HARD, + PCMK_LRM_OP_ERROR_FATAL = PCMK_EXEC_ERROR_FATAL, + PCMK_LRM_OP_NOT_INSTALLED = PCMK_EXEC_NOT_INSTALLED, + PCMK_LRM_OP_NOT_CONNECTED = PCMK_EXEC_NOT_CONNECTED, + PCMK_LRM_OP_INVALID = PCMK_EXEC_INVALID, +}; + +//! \deprecated Use resources_action_create() instead +svc_action_t *services_action_create(const char *name, const char *action, + guint interval_ms, int timeout); + +//! \deprecated Use resources_list_agents() instead +GList *services_list(void); + +//! \deprecated Use pcmk_exec_status_str() instead +static inline const char * +services_lrm_status_str(enum op_status status) +{ + return pcmk_exec_status_str((enum pcmk_exec_status) status); +} + +//! \deprecated Use services_result2ocf() instead +static inline enum ocf_exitcode +services_get_ocf_exitcode(const char *action, int lsb_exitcode) +{ + /* For non-status actions, LSB and OCF share error code meaning <= 7 */ + if (action && strcmp(action, "status") && strcmp(action, "monitor")) { + if ((lsb_exitcode < 0) || (lsb_exitcode > PCMK_LSB_NOT_RUNNING)) { + return PCMK_OCF_UNKNOWN_ERROR; + } + return (enum ocf_exitcode)lsb_exitcode; + } + + /* status has different return codes */ + switch (lsb_exitcode) { + case PCMK_LSB_STATUS_OK: + return PCMK_OCF_OK; + case PCMK_LSB_STATUS_NOT_INSTALLED: + return PCMK_OCF_NOT_INSTALLED; + case PCMK_LSB_STATUS_INSUFFICIENT_PRIV: + return PCMK_OCF_INSUFFICIENT_PRIV; + case PCMK_LSB_STATUS_VAR_PID: + case PCMK_LSB_STATUS_VAR_LOCK: + case PCMK_LSB_STATUS_NOT_RUNNING: + return PCMK_OCF_NOT_RUNNING; + } + return PCMK_OCF_UNKNOWN_ERROR; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm/services_internal.h b/include/crm/services_internal.h new file mode 100644 index 0000000..ada97e1 --- /dev/null +++ b/include/crm/services_internal.h @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2022 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 PCMK__SERVICES_INTERNAL__H +# define PCMK__SERVICES_INTERNAL__H + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \brief Create a new resource action + * + * \param[in] name Name of resource + * \param[in] standard Resource agent standard + * \param[in] provider Resource agent provider + * \param[in] agent Resource agent name + * \param[in] action Name of action + * \param[in] interval_ms How often to repeat action (if 0, execute once) + * \param[in] timeout Error if not complete within this time (ms) + * \param[in,out] params Action parameters + * \param[in] flags Group of enum svc_action_flags + * + * \return NULL if not enough memory, otherwise newly allocated action instance + * (if its rc member is not PCMK_OCF_UNKNOWN, the action is invalid) + * + * \note This function assumes ownership of (and may free) \p params. + * \note The caller is responsible for freeing the return value using + * services_action_free(). + */ +svc_action_t *services__create_resource_action(const char *name, + const char *standard, + const char *provider, + const char *agent, + const char *action, + guint interval_ms, + int timeout, GHashTable *params, + enum svc_action_flags flags); + +const char *services__exit_reason(const svc_action_t *action); +char *services__grab_stdout(svc_action_t *action); +char *services__grab_stderr(svc_action_t *action); + +void services__set_result(svc_action_t *action, int agent_status, + enum pcmk_exec_status exec_status, + const char *exit_reason); + +void services__format_result(svc_action_t *action, int agent_status, + enum pcmk_exec_status exec_status, + const char *format, ...) G_GNUC_PRINTF(4, 5); + +# ifdef __cplusplus +} +# endif + +#endif /* PCMK__SERVICES_INTERNAL__H */ diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h new file mode 100644 index 0000000..fa87599 --- /dev/null +++ b/include/crm/stonith-ng.h @@ -0,0 +1,707 @@ +/* + * Copyright 2004-2022 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 PCMK__CRM_STONITH_NG__H +# define PCMK__CRM_STONITH_NG__H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file + * \brief Fencing aka. STONITH + * \ingroup fencing + */ + +/* IMPORTANT: DLM source code includes this file directly, without having access + * to other Pacemaker headers on its include path, so this file should *not* + * include any other Pacemaker headers. (DLM might be updated to avoid the + * issue, but we should still follow this guideline for a long time after.) + */ + +# include <dlfcn.h> +# include <errno.h> +# include <stdbool.h> // bool +# include <stdint.h> // uint32_t +# include <time.h> // time_t + +# define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect" +# define T_STONITH_NOTIFY_FENCE "st_notify_fence" +# define T_STONITH_NOTIFY_HISTORY "st_notify_history" +# define T_STONITH_NOTIFY_HISTORY_SYNCED "st_notify_history_synced" + +/* *INDENT-OFF* */ +enum stonith_state { + stonith_connected_command, + stonith_connected_query, + stonith_disconnected, +}; + +enum stonith_call_options { + st_opt_none = 0x00000000, + st_opt_verbose = 0x00000001, + st_opt_allow_suicide = 0x00000002, + + st_opt_manual_ack = 0x00000008, + st_opt_discard_reply = 0x00000010, +/* st_opt_all_replies = 0x00000020, */ + st_opt_topology = 0x00000040, + st_opt_scope_local = 0x00000100, + st_opt_cs_nodeid = 0x00000200, + st_opt_sync_call = 0x00001000, + /*! Allow the timeout period for a callback to be adjusted + * based on the time the server reports the operation will take. */ + st_opt_timeout_updates = 0x00002000, + /*! Only report back if operation is a success in callback */ + st_opt_report_only_success = 0x00004000, + /* used where ever apropriate - e.g. cleanup of history */ + st_opt_cleanup = 0x000080000, + /* used where ever apropriate - e.g. send out a history query to all nodes */ + st_opt_broadcast = 0x000100000, +}; + +/*! Order matters here, do not change values */ +enum op_state +{ + st_query, + st_exec, + st_done, + st_duplicate, + st_failed, +}; + +// Supported fence agent interface standards +enum stonith_namespace { + st_namespace_invalid, + st_namespace_any, + st_namespace_internal, // Implemented internally by Pacemaker + + /* Neither of these projects are active any longer, but the fence agent + * interfaces they created are still in use and supported by Pacemaker. + */ + st_namespace_rhcs, // Red Hat Cluster Suite compatible + st_namespace_lha, // Linux-HA compatible +}; + +enum stonith_namespace stonith_text2namespace(const char *namespace_s); +const char *stonith_namespace2text(enum stonith_namespace st_namespace); +enum stonith_namespace stonith_get_namespace(const char *agent, + const char *namespace_s); + +typedef struct stonith_key_value_s { + char *key; + char *value; + struct stonith_key_value_s *next; +} stonith_key_value_t; + +typedef struct stonith_history_s { + char *target; + char *action; + char *origin; + char *delegate; + char *client; + int state; + time_t completed; + struct stonith_history_s *next; + long completed_nsec; + char *exit_reason; +} stonith_history_t; + +typedef struct stonith_s stonith_t; + +typedef struct stonith_event_s +{ + char *id; + char *type; //!< \deprecated Will be removed in future release + char *message; //!< \deprecated Will be removed in future release + char *operation; + + int result; + char *origin; + char *target; + char *action; + char *executioner; + + char *device; + + /*! The name of the client that initiated the action. */ + char *client_origin; + + //! \internal This field should be treated as internal to Pacemaker + void *opaque; +} stonith_event_t; + +typedef struct stonith_callback_data_s { + int rc; + int call_id; + void *userdata; + + //! \internal This field should be treated as internal to Pacemaker + void *opaque; +} stonith_callback_data_t; + +typedef struct stonith_api_operations_s +{ + /*! + * \brief Destroy a fencer connection + * + * \param[in,out] st Fencer connection to destroy + */ + int (*free) (stonith_t *st); + + /*! + * \brief Connect to the local fencer + * + * \param[in,out] st Fencer connection to connect + * \param[in] name Client name to use + * \param[out] stonith_fd If NULL, use a main loop, otherwise + * store IPC file descriptor here + * + * \return Legacy Pacemaker return code + */ + int (*connect) (stonith_t *st, const char *name, int *stonith_fd); + + /*! + * \brief Disconnect from the local stonith daemon. + * + * \param[in,out] st Fencer connection to disconnect + * + * \return Legacy Pacemaker return code + */ + int (*disconnect)(stonith_t *st); + + /*! + * \brief Unregister a fence device with the local fencer + * + * \param[in,out] st Fencer connection to disconnect + * \param[in] options Group of enum stonith_call_options + * \param[in] name ID of fence device to unregister + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*remove_device)(stonith_t *st, int options, const char *name); + + /*! + * \brief Register a fence device with the local fencer + * + * \param[in,out] st Fencer connection to use + * \param[in] options Group of enum stonith_call_options + * \param[in] id ID of fence device to register + * \param[in] namespace Type of fence agent to search for ("redhat" + * or "stonith-ng" for RHCS-style, "internal" for + * Pacemaker-internal devices, "heartbeat" for + * LHA-style, or "any" or NULL for any) + * \param[in] agent Name of fence agent for device + * \param[in] params Fence agent parameters for device + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*register_device)(stonith_t *st, int options, const char *id, + const char *namespace, const char *agent, + const stonith_key_value_t *params); + + /*! + * \brief Unregister a fencing level for specified node with local fencer + * + * \param[in,out] st Fencer connection to use + * \param[in] options Group of enum stonith_call_options + * \param[in] node Target node to unregister level for + * \param[in] level Topology level number to unregister + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*remove_level)(stonith_t *st, int options, const char *node, + int level); + + /*! + * \brief Register a fencing level for specified node with local fencer + * + * \param[in,out] st Fencer connection to use + * \param[in] options Group of enum stonith_call_options + * \param[in] node Target node to register level for + * \param[in] level Topology level number to register + * \param[in] device_list Devices to register in level + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*register_level)(stonith_t *st, int options, const char *node, + int level, const stonith_key_value_t *device_list); + + /*! + * \brief Retrieve a fence agent's metadata + * + * \param[in,out] stonith Fencer connection + * \param[in] call_options Group of enum stonith_call_options + * (currently ignored) + * \param[in] agent Fence agent to query + * \param[in] namespace Type of fence agent to search for ("redhat" + * or "stonith-ng" for RHCS-style, "internal" + * for Pacemaker-internal devices, "heartbeat" + * for LHA-style, or "any" or NULL for any) + * \param[out] output Where to store metadata + * \param[in] timeout_sec Error if not complete within this time + * + * \return Legacy Pacemaker return code + * \note The caller is responsible for freeing *output using free(). + */ + int (*metadata)(stonith_t *stonith, int call_options, const char *agent, + const char *namespace, char **output, int timeout_sec); + + /*! + * \brief Retrieve a list of installed fence agents + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * (currently ignored) + * \param[in] namespace Type of fence agents to list ("redhat" + * or "stonith-ng" for RHCS-style, "internal" for + * Pacemaker-internal devices, "heartbeat" for + * LHA-style, or "any" or NULL for all) + * \param[out] devices Where to store agent list + * \param[in] timeout Error if unable to complete within this + * (currently ignored) + * + * \return Number of items in list on success, or negative errno otherwise + * \note The caller is responsible for freeing the returned list with + * stonith_key_value_freeall(). + */ + int (*list_agents)(stonith_t *stonith, int call_options, + const char *namespace, stonith_key_value_t **devices, + int timeout); + + /*! + * \brief Get the output of a fence device's list action + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] id Fence device ID to run list for + * \param[out] list_info Where to store list output + * \param[in] timeout Error if unable to complete within this + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*list)(stonith_t *stonith, int call_options, const char *id, + char **list_info, int timeout); + + /*! + * \brief Check whether a fence device is reachable by monitor action + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] id Fence device ID to run monitor for + * \param[in] timeout Error if unable to complete within this + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*monitor)(stonith_t *stonith, int call_options, const char *id, + int timeout); + + /*! + * \brief Check whether a fence device target is reachable by status action + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] id Fence device ID to run status for + * \param[in] port Fence target to run status for + * \param[in] timeout Error if unable to complete within this + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*status)(stonith_t *stonith, int call_options, const char *id, + const char *port, int timeout); + + /*! + * \brief List registered fence devices + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] target Fence target to run status for + * \param[out] devices Where to store list of fence devices + * \param[in] timeout Error if unable to complete within this + * + * \note If node is provided, only devices that can fence the node id + * will be returned. + * + * \return Number of items in list on success, or negative errno otherwise + */ + int (*query)(stonith_t *stonith, int call_options, const char *target, + stonith_key_value_t **devices, int timeout); + + /*! + * \brief Request that a target get fenced + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] node Fence target + * \param[in] action "on", "off", or "reboot" + * \param[in] timeout Default per-device timeout to use with + * each executed device + * \param[in] tolerance Accept result of identical fence action + * completed within this time + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*fence)(stonith_t *stonith, int call_options, const char *node, + const char *action, int timeout, int tolerance); + + /*! + * \brief Manually confirm that a node has been fenced + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] target Fence target + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*confirm)(stonith_t *stonith, int call_options, const char *target); + + /*! + * \brief List fencing actions that have occurred for a target + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] node Fence target + * \param[out] history Where to store list of fencing actions + * \param[in] timeout Error if unable to complete within this + * + * \return Legacy Pacemaker return code + */ + int (*history)(stonith_t *stonith, int call_options, const char *node, + stonith_history_t **history, int timeout); + + /*! + * \brief Register a callback for fence notifications + * + * \param[in,out] stonith Fencer connection to use + * \param[in] event Event to register for + * \param[in] callback Callback to register + * + * \return Legacy Pacemaker return code + */ + int (*register_notification)(stonith_t *stonith, const char *event, + void (*callback)(stonith_t *st, + stonith_event_t *e)); + + /*! + * \brief Unregister callbacks for fence notifications + * + * \param[in,out] stonith Fencer connection to use + * \param[in] event Event to unregister callbacks for (NULL for all) + * + * \return Legacy Pacemaker return code + */ + int (*remove_notification)(stonith_t *stonith, const char *event); + + /*! + * \brief Register a callback for an asynchronous fencing result + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_id Call ID to register callback for + * \param[in] timeout Error if result not received in this time + * \param[in] options Group of enum stonith_call_options + * (respects \c st_opt_timeout_updates and + * \c st_opt_report_only_success) + * \param[in,out] user_data Pointer to pass to callback + * \param[in] callback_name Unique identifier for callback + * \param[in] callback Callback to register (may be called + * immediately if \p call_id indicates error) + * + * \return \c TRUE on success, \c FALSE if call_id indicates error, + * or -EINVAL if \p stonith is not valid + */ + int (*register_callback)(stonith_t *stonith, int call_id, int timeout, + int options, void *user_data, + const char *callback_name, + void (*callback)(stonith_t *st, + stonith_callback_data_t *data)); + + /*! + * \brief Unregister callbacks for asynchronous fencing results + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_id If \p all_callbacks is false, call ID + * to unregister callback for + * \param[in] all_callbacks If true, unregister all callbacks + * + * \return pcmk_ok + */ + int (*remove_callback)(stonith_t *stonith, int call_id, bool all_callbacks); + + /*! + * \brief Unregister fencing level for specified node, pattern or attribute + * + * \param[in,out] st Fencer connection to use + * \param[in] options Group of enum stonith_call_options + * \param[in] node If not NULL, unregister level targeting this node + * \param[in] pattern If not NULL, unregister level targeting nodes + * whose names match this regular expression + * \param[in] attr If this and \p value are not NULL, unregister + * level targeting nodes with this node attribute + * set to \p value + * \param[in] value If this and \p attr are not NULL, unregister + * level targeting nodes with node attribute \p attr + * set to this + * \param[in] level Topology level number to remove + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + * \note The caller should set only one of \p node, \p pattern, or \p attr + * and \p value. + */ + int (*remove_level_full)(stonith_t *st, int options, + const char *node, const char *pattern, + const char *attr, const char *value, int level); + + /*! + * \brief Register fencing level for specified node, pattern or attribute + * + * \param[in,out] st Fencer connection to use + * \param[in] options Group of enum stonith_call_options + * \param[in] node If not NULL, register level targeting this + * node by name + * \param[in] pattern If not NULL, register level targeting nodes + * whose names match this regular expression + * \param[in] attr If this and \p value are not NULL, register + * level targeting nodes with this node + * attribute set to \p value + * \param[in] value If this and \p attr are not NULL, register + * level targeting nodes with node attribute + * \p attr set to this + * \param[in] level Topology level number to remove + * \param[in] device_list Devices to use in level + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + * + * \note The caller should set only one of node, pattern or attr/value. + */ + int (*register_level_full)(stonith_t *st, int options, + const char *node, const char *pattern, + const char *attr, const char *value, int level, + const stonith_key_value_t *device_list); + + /*! + * \brief Validate an arbitrary stonith device configuration + * + * \param[in,out] st Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] rsc_id ID used to replace CIB secrets in \p params + * \param[in] namespace_s Type of fence agent to validate ("redhat" + * or "stonith-ng" for RHCS-style, "internal" + * for Pacemaker-internal devices, "heartbeat" + * for LHA-style, or "any" or NULL for any) + * \param[in] agent Fence agent to validate + * \param[in] params Configuration parameters to pass to agent + * \param[in] timeout Fail if no response within this many seconds + * \param[out] output If non-NULL, where to store any agent output + * \param[out] error_output If non-NULL, where to store agent error output + * + * \return pcmk_ok if validation succeeds, -errno otherwise + * \note If pcmk_ok is returned, the caller is responsible for freeing + * the output (if requested) with free(). + */ + int (*validate)(stonith_t *st, int call_options, const char *rsc_id, + const char *namespace_s, const char *agent, + const stonith_key_value_t *params, int timeout, + char **output, char **error_output); + + /*! + * \brief Request delayed fencing of a target + * + * \param[in,out] stonith Fencer connection to use + * \param[in] call_options Group of enum stonith_call_options + * \param[in] node Fence target + * \param[in] action "on", "off", or "reboot" + * \param[in] timeout Default per-device timeout to use with + * each executed device + * \param[in] tolerance Accept result of identical fence action + * completed within this time + * \param[in] delay Execute fencing after this delay (-1 + * disables any delay from pcmk_delay_base + * and pcmk_delay_max) + * + * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous) + * on success, otherwise a negative legacy Pacemaker return code + */ + int (*fence_with_delay)(stonith_t *stonith, int call_options, + const char *node, const char *action, int timeout, + int tolerance, int delay); + +} stonith_api_operations_t; + +struct stonith_s +{ + enum stonith_state state; + + int call_id; + int call_timeout; //!< \deprecated Unused + void *st_private; + + stonith_api_operations_t *cmds; +}; +/* *INDENT-ON* */ + +/* Core functions */ +stonith_t *stonith_api_new(void); +void stonith_api_delete(stonith_t * st); + +void stonith_dump_pending_callbacks(stonith_t * st); + +bool stonith_dispatch(stonith_t * st); + +stonith_key_value_t *stonith_key_value_add(stonith_key_value_t * kvp, const char *key, + const char *value); +void stonith_key_value_freeall(stonith_key_value_t * kvp, int keys, int values); + +void stonith_history_free(stonith_history_t *history); + +// Convenience functions +int stonith_api_connect_retry(stonith_t *st, const char *name, + int max_attempts); +const char *stonith_op_state_str(enum op_state state); + +/* Basic helpers that allows nodes to be fenced and the history to be + * queried without mainloop or the caller understanding the full API + * + * At least one of nodeid and uname are required + */ +int stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off); +time_t stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress); + +/* + * Helpers for using the above functions without install-time dependencies + * + * Usage: + * #include <crm/stonith-ng.h> + * + * To turn a node off by corosync nodeid: + * stonith_api_kick_helper(nodeid, 120, 1); + * + * To check the last fence date/time (also by nodeid): + * last = stonith_api_time_helper(nodeid, 0); + * + * To check if fencing is in progress: + * if(stonith_api_time_helper(nodeid, 1) > 0) { ... } + * + * eg. + + #include <stdio.h> + #include <time.h> + #include <crm/stonith-ng.h> + int + main(int argc, char ** argv) + { + int rc = 0; + int nodeid = 102; + + rc = stonith_api_time_helper(nodeid, 0); + printf("%d last fenced at %s\n", nodeid, ctime(rc)); + + rc = stonith_api_kick_helper(nodeid, 120, 1); + printf("%d fence result: %d\n", nodeid, rc); + + rc = stonith_api_time_helper(nodeid, 0); + printf("%d last fenced at %s\n", nodeid, ctime(rc)); + + return 0; + } + + */ + +# define STONITH_LIBRARY "libstonithd.so.26" + +typedef int (*st_api_kick_fn) (int nodeid, const char *uname, int timeout, bool off); +typedef time_t (*st_api_time_fn) (int nodeid, const char *uname, bool in_progress); + +static inline int +stonith_api_kick_helper(uint32_t nodeid, int timeout, bool off) +{ + static void *st_library = NULL; + static st_api_kick_fn st_kick_fn; + + if (st_library == NULL) { + st_library = dlopen(STONITH_LIBRARY, RTLD_LAZY); + } + if (st_library && st_kick_fn == NULL) { + st_kick_fn = (st_api_kick_fn) dlsym(st_library, "stonith_api_kick"); + } + if (st_kick_fn == NULL) { +#ifdef ELIBACC + return -ELIBACC; +#else + return -ENOSYS; +#endif + } + + return (*st_kick_fn) (nodeid, NULL, timeout, off); +} + +static inline time_t +stonith_api_time_helper(uint32_t nodeid, bool in_progress) +{ + static void *st_library = NULL; + static st_api_time_fn st_time_fn; + + if (st_library == NULL) { + st_library = dlopen(STONITH_LIBRARY, RTLD_LAZY); + } + if (st_library && st_time_fn == NULL) { + st_time_fn = (st_api_time_fn) dlsym(st_library, "stonith_api_time"); + } + if (st_time_fn == NULL) { + return 0; + } + + return (*st_time_fn) (nodeid, NULL, in_progress); +} + +/** + * Does the given agent describe a stonith resource that can exist? + * + * \param[in] agent What is the name of the agent? + * \param[in] timeout Timeout to use when querying. If 0 is given, + * use a default of 120. + * + * \return A boolean + */ +bool stonith_agent_exists(const char *agent, int timeout); + +/*! + * \brief Turn fence action into a more readable string + * + * \param[in] action Fence action + */ +const char *stonith_action_str(const char *action); + +#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1) +/* Normally we'd put this section in a separate file (crm/fencing/compat.h), but + * we can't do that for the reason noted at the top of this file. That does mean + * we have to duplicate these declarations where they're implemented. + */ + +//! \deprecated Use stonith_get_namespace() instead +const char *get_stonith_provider(const char *agent, const char *provider); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/crm_config.h.in b/include/crm_config.h.in new file mode 100644 index 0000000..a229226 --- /dev/null +++ b/include/crm_config.h.in @@ -0,0 +1,75 @@ +/* + * Copyright 2006-2021 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. + */ + +#ifndef PCMK__CRM_CONFIG__H +#define PCMK__CRM_CONFIG__H + +/****** Versions ******/ + +/* Current pacemaker version */ +#undef PACEMAKER_VERSION + +/* Build version */ +#undef BUILD_VERSION + +/****** Other ******/ + +/* Group to run Pacemaker daemons as */ +#undef CRM_DAEMON_GROUP + +/* User to run Pacemaker daemons as */ +#undef CRM_DAEMON_USER + +/****** Directories ******/ + +/* Where Pacemaker can store log files */ +#undef CRM_LOG_DIR + +/* Location for Pacemaker daemons */ +#undef CRM_DAEMON_DIR + +/* Where to keep blackbox dumps */ +#undef CRM_BLACKBOX_DIR + +/* Where to keep configuration files */ +#undef CRM_CONFIG_DIR + +/* Where to keep scheduler outputs */ +#undef PE_STATE_DIR + +/* Location to store core files produced by Pacemaker daemons */ +#undef CRM_CORE_DIR + +/* Where to keep state files and sockets */ +#undef CRM_STATE_DIR + +/* Location for the Pacemaker Relax-NG Schema */ +#undef CRM_SCHEMA_DIRECTORY + +/* Where to keep configuration files like authkey */ +#undef PACEMAKER_CONFIG_DIR + +/* Where OCF resource agents and libraries can be found */ +#undef OCF_ROOT_DIR + +/****** Features ******/ + +/* Set of enabled features */ +#undef CRM_FEATURES + +/* Support the Corosync messaging and membership layer */ +#undef SUPPORT_COROSYNC + +/* Support systemd based system services */ +#undef SUPPORT_SYSTEMD + +/* Support upstart based system services */ +#undef SUPPORT_UPSTART + +#endif /* CRM_CONFIG__H */ diff --git a/include/crm_internal.h b/include/crm_internal.h new file mode 100644 index 0000000..5f6531f --- /dev/null +++ b/include/crm_internal.h @@ -0,0 +1,118 @@ +/* + * Copyright 2006-2023 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 + +# ifndef PCMK__CONFIG_H +# define PCMK__CONFIG_H +# include <config.h> +# endif + +# include <portability.h> + +/* Our minimum glib dependency is 2.42. Define that as both the minimum and + * maximum glib APIs that are allowed (i.e. APIs that were already deprecated + * in 2.42, and APIs introduced after 2.42, cannot be used by Pacemaker code). + */ +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_42 +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_42 + +# include <glib.h> +# include <stdbool.h> +# include <libxml/tree.h> + +/* Public API headers can guard including deprecated API headers with this + * symbol, thus preventing internal code (which includes this header) from using + * deprecated APIs, while still allowing external code to use them by default. + */ +#define PCMK_ALLOW_DEPRECATED 0 + +# include <crm/lrmd.h> +# include <crm/common/logging.h> +# include <crm/common/logging_internal.h> +# include <crm/common/ipc_internal.h> +# include <crm/common/options_internal.h> +# include <crm/common/output_internal.h> +# include <crm/common/xml_internal.h> +# include <crm/common/internal.h> +# include <locale.h> +# include <gettext.h> + +#define N_(String) (String) + +#ifdef ENABLE_NLS +# define _(String) gettext(String) +#else +# define _(String) (String) +#endif + + +/* + * XML attribute names used only by internal code + */ + +#define PCMK__XA_ATTR_DAMPENING "attr_dampening" +#define PCMK__XA_ATTR_FORCE "attrd_is_force_write" +#define PCMK__XA_ATTR_INTERVAL "attr_clear_interval" +#define PCMK__XA_ATTR_IS_PRIVATE "attr_is_private" +#define PCMK__XA_ATTR_IS_REMOTE "attr_is_remote" +#define PCMK__XA_ATTR_NAME "attr_name" +#define PCMK__XA_ATTR_NODE_ID "attr_host_id" +#define PCMK__XA_ATTR_NODE_NAME "attr_host" +#define PCMK__XA_ATTR_OPERATION "attr_clear_operation" +#define PCMK__XA_ATTR_PATTERN "attr_regex" +#define PCMK__XA_ATTR_RESOURCE "attr_resource" +#define PCMK__XA_ATTR_SECTION "attr_section" +#define PCMK__XA_ATTR_SET "attr_set" +#define PCMK__XA_ATTR_SET_TYPE "attr_set_type" +#define PCMK__XA_ATTR_SYNC_POINT "attr_sync_point" +#define PCMK__XA_ATTR_USER "attr_user" +#define PCMK__XA_ATTR_UUID "attr_key" +#define PCMK__XA_ATTR_VALUE "attr_value" +#define PCMK__XA_ATTR_VERSION "attr_version" +#define PCMK__XA_ATTR_WRITER "attr_writer" +#define PCMK__XA_CONFIG_ERRORS "config-errors" +#define PCMK__XA_CONFIG_WARNINGS "config-warnings" +#define PCMK__XA_CONFIRM "confirm" +#define PCMK__XA_GRAPH_ERRORS "graph-errors" +#define PCMK__XA_GRAPH_WARNINGS "graph-warnings" +#define PCMK__XA_MODE "mode" +#define PCMK__XA_TASK "task" +#define PCMK__XA_UPTIME "uptime" +#define PCMK__XA_CONN_HOST "connection_host" + + +/* + * IPC service names that are only used internally + */ + +# define PCMK__SERVER_BASED_RO "cib_ro" +# define PCMK__SERVER_BASED_RW "cib_rw" +# define PCMK__SERVER_BASED_SHM "cib_shm" + +/* + * IPC commands that can be sent to Pacemaker daemons + */ + +#define PCMK__ATTRD_CMD_PEER_REMOVE "peer-remove" +#define PCMK__ATTRD_CMD_UPDATE "update" +#define PCMK__ATTRD_CMD_UPDATE_BOTH "update-both" +#define PCMK__ATTRD_CMD_UPDATE_DELAY "update-delay" +#define PCMK__ATTRD_CMD_QUERY "query" +#define PCMK__ATTRD_CMD_REFRESH "refresh" +#define PCMK__ATTRD_CMD_FLUSH "flush" +#define PCMK__ATTRD_CMD_SYNC "sync" +#define PCMK__ATTRD_CMD_SYNC_RESPONSE "sync-response" +#define PCMK__ATTRD_CMD_CLEAR_FAILURE "clear-failure" +#define PCMK__ATTRD_CMD_CONFIRM "confirm" + +#define PCMK__CONTROLD_CMD_NODES "list-nodes" + +#endif /* CRM_INTERNAL__H */ |