diff options
Diffstat (limited to '')
-rw-r--r-- | include/crm/common/ipc_internal.h | 293 |
1 files changed, 293 insertions, 0 deletions
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 |