pacemaker/SOURCES/006-ipc_refactor.patch

5199 lines
159 KiB
Diff

From f0cfb7b02202b832f5655ee80580d053638e0be1 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Fri, 3 Apr 2020 10:27:29 -0500
Subject: [PATCH 1/8] Refactor: libcrmcommon: combine IPC internal headers
Combine ipcs_internal.h into ipc_internal.h.
---
daemons/attrd/attrd_utils.c | 2 +-
daemons/attrd/pacemaker-attrd.c | 2 +-
daemons/based/based_messages.c | 2 +-
daemons/based/based_remote.c | 2 +-
daemons/based/pacemaker-based.h | 2 +-
daemons/controld/controld_control.c | 2 +-
daemons/controld/controld_fsa.h | 2 +-
daemons/controld/controld_messages.c | 2 +-
daemons/controld/controld_messages.h | 2 +-
daemons/execd/execd_alerts.c | 2 +-
daemons/execd/execd_commands.c | 2 +-
daemons/execd/pacemaker-execd.c | 2 +-
daemons/execd/pacemaker-execd.h | 2 +-
daemons/execd/remoted_proxy.c | 2 +-
daemons/fenced/fenced_commands.c | 2 +-
daemons/fenced/fenced_history.c | 2 +-
daemons/fenced/fenced_remote.c | 2 +-
daemons/fenced/pacemaker-fenced.c | 2 +-
daemons/pacemakerd/pacemakerd.c | 4 +-
daemons/schedulerd/pacemaker-schedulerd.c | 2 +-
include/crm/cib/internal.h | 2 +-
include/crm/common/Makefile.am | 2 +-
include/crm/common/ipc_internal.h | 144 ++++++++++++++++++++++++++++-
include/crm/common/ipcs_internal.h | 149 ------------------------------
include/crm_internal.h | 2 +-
lib/cib/cib_remote.c | 2 +-
lib/common/ipc.c | 4 +-
lib/common/mainloop.c | 2 +-
lib/common/remote.c | 2 +-
lib/lrmd/lrmd_client.c | 2 +-
lib/pacemaker/pcmk_sched_messages.c | 2 +-
maint/mocked/based.c | 2 +-
maint/mocked/based.h | 2 +-
33 files changed, 172 insertions(+), 187 deletions(-)
delete mode 100644 include/crm/common/ipcs_internal.h
diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c
index b60b452..c311ddc 100644
--- a/daemons/attrd/attrd_utils.c
+++ b/daemons/attrd/attrd_utils.c
@@ -17,7 +17,7 @@
#include <sys/types.h>
#include <crm/crm.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include "pacemaker-attrd.h"
diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c
index 61e5493..0e944ed 100644
--- a/daemons/attrd/pacemaker-attrd.c
+++ b/daemons/attrd/pacemaker-attrd.c
@@ -25,7 +25,7 @@
#include <crm/pengine/rules.h>
#include <crm/common/iso8601.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/xml.h>
#include <crm/cluster/internal.h>
diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c
index 4438e28..af0a3a2 100644
--- a/daemons/based/based_messages.c
+++ b/daemons/based/based_messages.c
@@ -24,7 +24,7 @@
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
#include <pacemaker-based.h>
diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c
index ca75b73..70261c3 100644
--- a/daemons/based/based_remote.c
+++ b/daemons/based/based_remote.c
@@ -26,7 +26,7 @@
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/xml.h>
#include <crm/common/remote_internal.h>
#include <crm/cib/internal.h>
diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h
index 0d7a5b9..c88ce7c 100644
--- a/daemons/based/pacemaker-based.h
+++ b/daemons/based/pacemaker-based.h
@@ -22,7 +22,7 @@
#include <crm/cib.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include <crm/cib/internal.h>
diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c
index 1ddcada..7d29205 100644
--- a/daemons/controld/controld_control.c
+++ b/daemons/controld/controld_control.c
@@ -18,7 +18,7 @@
#include <crm/pengine/rules.h>
#include <crm/cluster/internal.h>
#include <crm/cluster/election.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <pacemaker-controld.h>
diff --git a/daemons/controld/controld_fsa.h b/daemons/controld/controld_fsa.h
index b76a7d2..28eea56 100644
--- a/daemons/controld/controld_fsa.h
+++ b/daemons/controld/controld_fsa.h
@@ -16,7 +16,7 @@
# include <crm/common/mainloop.h>
# include <crm/cluster.h>
# include <crm/cluster/election.h>
-# include <crm/common/ipcs_internal.h>
+# include <crm/common/ipc_internal.h>
/*! States the controller can be in */
enum crmd_fsa_state {
diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c
index 62719ad..59b2069 100644
--- a/daemons/controld/controld_messages.c
+++ b/daemons/controld/controld_messages.c
@@ -18,7 +18,7 @@
#include <crm/common/xml.h>
#include <crm/cluster/internal.h>
#include <crm/cib.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <pacemaker-controld.h>
diff --git a/daemons/controld/controld_messages.h b/daemons/controld/controld_messages.h
index db3ade3..4018deb 100644
--- a/daemons/controld/controld_messages.h
+++ b/daemons/controld/controld_messages.h
@@ -11,7 +11,7 @@
# define XML_CRM_MESSAGES__H
# include <crm/crm.h>
-# include <crm/common/ipcs_internal.h>
+# include <crm/common/ipc_internal.h>
# include <crm/common/xml.h>
# include <crm/cluster/internal.h>
# include <controld_fsa.h>
diff --git a/daemons/execd/execd_alerts.c b/daemons/execd/execd_alerts.c
index 10eca36..ffe60b5 100644
--- a/daemons/execd/execd_alerts.c
+++ b/daemons/execd/execd_alerts.c
@@ -14,7 +14,7 @@
#include <crm/crm.h>
#include <crm/services.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/alerts_internal.h>
#include <crm/msg_xml.h>
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index 4d0e457..8da63b4 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.c
@@ -25,7 +25,7 @@
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/msg_xml.h>
#include "pacemaker-execd.h"
diff --git a/daemons/execd/pacemaker-execd.c b/daemons/execd/pacemaker-execd.c
index df27d1a..c06da7a 100644
--- a/daemons/execd/pacemaker-execd.c
+++ b/daemons/execd/pacemaker-execd.c
@@ -18,7 +18,7 @@
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/remote_internal.h>
#include <crm/lrmd_internal.h>
diff --git a/daemons/execd/pacemaker-execd.h b/daemons/execd/pacemaker-execd.h
index 7ba3e78..d86894b 100644
--- a/daemons/execd/pacemaker-execd.h
+++ b/daemons/execd/pacemaker-execd.h
@@ -11,7 +11,7 @@
# define PACEMAKER_EXECD__H
# include <glib.h>
-# include <crm/common/ipcs_internal.h>
+# include <crm/common/ipc_internal.h>
# include <crm/lrmd.h>
# include <crm/stonith-ng.h>
diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c
index ef0d108..dda7eed 100644
--- a/daemons/execd/remoted_proxy.c
+++ b/daemons/execd/remoted_proxy.c
@@ -18,7 +18,7 @@
#include <crm/services.h>
#include <crm/common/mainloop.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/cib/internal.h>
#include <crm/fencing/internal.h>
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
index d13d1a1..5df472f 100644
--- a/daemons/fenced/fenced_commands.c
+++ b/daemons/fenced/fenced_commands.c
@@ -25,7 +25,7 @@
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
#include <crm/common/mainloop.h>
diff --git a/daemons/fenced/fenced_history.c b/daemons/fenced/fenced_history.c
index 710d6fe..b48662c 100644
--- a/daemons/fenced/fenced_history.c
+++ b/daemons/fenced/fenced_history.c
@@ -16,7 +16,7 @@
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
#include <crm/stonith-ng.h>
diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c
index b89c40a..bf24acb 100644
--- a/daemons/fenced/fenced_remote.c
+++ b/daemons/fenced/fenced_remote.c
@@ -26,7 +26,7 @@
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
#include <crm/stonith-ng.h>
diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c
index 450814c..6a2935a 100644
--- a/daemons/fenced/pacemaker-fenced.c
+++ b/daemons/fenced/pacemaker-fenced.c
@@ -24,7 +24,7 @@
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
#include <crm/stonith-ng.h>
diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
index 5ed4626..64c30e2 100644
--- a/daemons/pacemakerd/pacemakerd.c
+++ b/daemons/pacemakerd/pacemakerd.c
@@ -24,13 +24,11 @@
#include <crm/crm.h> /* indirectly: CRM_EX_* */
#include <crm/cib/internal.h> /* cib_channel_ro */
#include <crm/msg_xml.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include <crm/cluster/internal.h>
#include <crm/cluster.h>
-#include <crm/common/ipc_internal.h> /* PCMK__SPECIAL_PID*, ... */
-
#ifdef SUPPORT_COROSYNC
#include <corosync/cfg.h>
#endif
diff --git a/daemons/schedulerd/pacemaker-schedulerd.c b/daemons/schedulerd/pacemaker-schedulerd.c
index 0146ca2..885386d 100644
--- a/daemons/schedulerd/pacemaker-schedulerd.c
+++ b/daemons/schedulerd/pacemaker-schedulerd.c
@@ -21,7 +21,7 @@
#include <libxml/parser.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include <crm/pengine/internal.h>
#include <pacemaker-internal.h>
diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h
index df16280..b43cf08 100644
--- a/include/crm/cib/internal.h
+++ b/include/crm/cib/internal.h
@@ -10,7 +10,7 @@
#ifndef CIB_INTERNAL__H
# define CIB_INTERNAL__H
# include <crm/cib.h>
-# include <crm/common/ipcs_internal.h>
+# include <crm/common/ipc_internal.h>
# define CIB_OP_SLAVE "cib_slave"
# define CIB_OP_SLAVEALL "cib_slave_all"
diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am
index b38a5c5..776e4a7 100644
--- a/include/crm/common/Makefile.am
+++ b/include/crm/common/Makefile.am
@@ -13,7 +13,7 @@ headerdir=$(pkgincludedir)/crm/common
header_HEADERS = xml.h ipc.h util.h iso8601.h mainloop.h logging.h results.h \
nvpair.h acl.h
-noinst_HEADERS = ipcs_internal.h internal.h alerts_internal.h \
+noinst_HEADERS = internal.h alerts_internal.h \
iso8601_internal.h remote_internal.h xml_internal.h \
ipc_internal.h output.h cmdline_internal.h curses_internal.h \
attrd_internal.h options_internal.h
diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h
index 7113d78..a85210d 100644
--- a/include/crm/common/ipc_internal.h
+++ b/include/crm/common/ipc_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 the Pacemaker project contributors
+ * Copyright 2013-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,10 +10,26 @@
#ifndef PCMK__IPC_INTERNAL_H
#define PCMK__IPC_INTERNAL_H
-#include <sys/types.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h> // bool
+#include <stdint.h> // uint32_t
+#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 <crm_config.h> /* US_AUTH_GETPEEREID */
+#include <glib.h> // guint, gpointer, GQueue, ...
+#include <libxml/tree.h> // xmlNode
+#include <qb/qbipcs.h> // qb_ipcs_connection_t, ...
+#include <crm_config.h> // US_AUTH_GETPEEREID
+#include <crm/common/ipc.h>
+#include <crm/common/mainloop.h> // mainloop_io_t
/* denotes "non yieldable PID" on FreeBSD, or actual PID1 in scenarios that
require a delicate handling anyway (socket-based activation with systemd);
@@ -69,4 +85,126 @@
int pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid,
gid_t refgid, pid_t *gotpid);
+typedef struct pcmk__client_s pcmk__client_t;
+
+enum pcmk__client_type {
+ PCMK__CLIENT_IPC = 1,
+ PCMK__CLIENT_TCP = 2,
+# ifdef HAVE_GNUTLS_GNUTLS_H
+ PCMK__CLIENT_TLS = 3,
+# endif
+};
+
+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;
+
+ /* CIB-only */
+ bool authenticated;
+ char *token;
+
+ /* TLS only */
+# ifdef HAVE_GNUTLS_GNUTLS_H
+ gnutls_session_t *tls_session;
+ bool tls_handshake_complete;
+# endif
+};
+
+enum pcmk__client_flags {
+ pcmk__client_proxied = 0x00001, /* ipc_proxy code only */
+ pcmk__client_privileged = 0x00002, /* root or cluster user */
+};
+
+struct pcmk__client_s {
+ unsigned int pid;
+
+ uid_t uid;
+ gid_t gid;
+
+ char *id;
+ char *name;
+ char *user;
+
+ /* Provided for server use (not used by library) */
+ /* @TODO merge options, flags, and kind (reserving lower bits for server) */
+ long long options;
+
+ int request_id;
+ uint32_t flags;
+ void *userdata;
+
+ int event_timer;
+ GQueue *event_queue;
+
+ /* Depending on the value of kind, only some of the following
+ * will be populated/valid
+ */
+ enum pcmk__client_type kind;
+
+ 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 */
+};
+
+guint pcmk__ipc_client_count(void);
+void pcmk__foreach_ipc_client(GHFunc func, gpointer user_data);
+void pcmk__foreach_ipc_client_remove(GHRFunc func, gpointer user_data);
+
+void pcmk__client_cleanup(void);
+
+pcmk__client_t *pcmk__find_client(qb_ipcs_connection_t *c);
+pcmk__client_t *pcmk__find_client_by_id(const char *id);
+const char *pcmk__client_name(pcmk__client_t *c);
+const char *pcmk__client_type_str(enum pcmk__client_type 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);
+
+void pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c,
+ uint32_t request, uint32_t flags, const char *tag);
+#define pcmk__ipc_send_ack(c, req, flags, tag) \
+ pcmk__ipc_send_ack_as(__FUNCTION__, __LINE__, (c), (req), (flags), (tag))
+
+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);
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/include/crm/common/ipcs_internal.h b/include/crm/common/ipcs_internal.h
deleted file mode 100644
index c631dfc..0000000
--- a/include/crm/common/ipcs_internal.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2013-2020 the Pacemaker project contributors
- *
- * The version control history for this file may have further details.
- *
- * This source code is licensed under the GNU Lesser General Public License
- * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
- */
-
-#ifndef CRM_COMMON_IPCS__H
-# define CRM_COMMON_IPCS__H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-# include <stdbool.h>
-# include <qb/qbipcs.h>
-# ifdef HAVE_GNUTLS_GNUTLS_H
-# undef KEYFILE
-# include <gnutls/gnutls.h>
-# endif
-
-# include <crm/common/ipc.h>
-# include <crm/common/mainloop.h>
-
-typedef struct pcmk__client_s pcmk__client_t;
-
-enum pcmk__client_type {
- PCMK__CLIENT_IPC = 1,
- PCMK__CLIENT_TCP = 2,
-# ifdef HAVE_GNUTLS_GNUTLS_H
- PCMK__CLIENT_TLS = 3,
-# endif
-};
-
-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;
-
- /* CIB-only */
- bool authenticated;
- char *token;
-
- /* TLS only */
-# ifdef HAVE_GNUTLS_GNUTLS_H
- gnutls_session_t *tls_session;
- bool tls_handshake_complete;
-# endif
-};
-
-enum pcmk__client_flags {
- pcmk__client_proxied = 0x00001, /* ipc_proxy code only */
- pcmk__client_privileged = 0x00002, /* root or cluster user */
-};
-
-struct pcmk__client_s {
- uint pid;
-
- uid_t uid;
- gid_t gid;
-
- char *id;
- char *name;
- char *user;
-
- /* Provided for server use (not used by library) */
- /* @TODO merge options, flags, and kind (reserving lower bits for server) */
- long long options;
-
- int request_id;
- uint32_t flags;
- void *userdata;
-
- int event_timer;
- GQueue *event_queue;
-
- /* Depending on the value of kind, only some of the following
- * will be populated/valid
- */
- enum pcmk__client_type kind;
-
- 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 */
-};
-
-guint pcmk__ipc_client_count(void);
-void pcmk__foreach_ipc_client(GHFunc func, gpointer user_data);
-void pcmk__foreach_ipc_client_remove(GHRFunc func, gpointer user_data);
-
-void pcmk__client_cleanup(void);
-
-pcmk__client_t *pcmk__find_client(qb_ipcs_connection_t *c);
-pcmk__client_t *pcmk__find_client_by_id(const char *id);
-const char *pcmk__client_name(pcmk__client_t *c);
-const char *pcmk__client_type_str(enum pcmk__client_type 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);
-
-void pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c,
- uint32_t request, uint32_t flags, const char *tag);
-#define pcmk__ipc_send_ack(c, req, flags, tag) \
- pcmk__ipc_send_ack_as(__FUNCTION__, __LINE__, (c), (req), (flags), (tag))
-
-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);
-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);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/crm_internal.h b/include/crm_internal.h
index 882cad8..15f9d2b 100644
--- a/include/crm_internal.h
+++ b/include/crm_internal.h
@@ -26,7 +26,7 @@
# include <crm/lrmd.h>
# include <crm/common/logging.h>
-# include <crm/common/ipcs_internal.h>
+# include <crm/common/ipc_internal.h>
# include <crm/common/options_internal.h>
# include <crm/common/internal.h>
diff --git a/lib/cib/cib_remote.c b/lib/cib/cib_remote.c
index ed93700..a011810 100644
--- a/lib/cib/cib_remote.c
+++ b/lib/cib/cib_remote.c
@@ -23,7 +23,7 @@
#include <crm/crm.h>
#include <crm/cib/internal.h>
#include <crm/msg_xml.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include <crm/common/remote_internal.h>
diff --git a/lib/common/ipc.c b/lib/common/ipc.c
index 34bd594..defaa7e 100644
--- a/lib/common/ipc.c
+++ b/lib/common/ipc.c
@@ -35,9 +35,7 @@
#include <crm/crm.h> /* indirectly: pcmk_err_generic */
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
-#include <crm/common/ipcs_internal.h>
-
-#include <crm/common/ipc_internal.h> /* PCMK__SPECIAL_PID* */
+#include <crm/common/ipc_internal.h>
#define PCMK_IPC_VERSION 1
diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c
index e3640f5..10450e4 100644
--- a/lib/common/mainloop.c
+++ b/lib/common/mainloop.c
@@ -23,7 +23,7 @@
#include <crm/crm.h>
#include <crm/common/xml.h>
#include <crm/common/mainloop.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <qb/qbarray.h>
diff --git a/lib/common/remote.c b/lib/common/remote.c
index 94e06dd..3a97e54 100644
--- a/lib/common/remote.c
+++ b/lib/common/remote.c
@@ -28,7 +28,7 @@
#include <glib.h>
#include <bzlib.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/xml.h>
#include <crm/common/mainloop.h>
#include <crm/common/remote_internal.h>
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index a2c7200..a6c023b 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -27,7 +27,7 @@
#include <crm/lrmd_internal.h>
#include <crm/services.h>
#include <crm/common/mainloop.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
#include <crm/common/remote_internal.h>
#include <crm/msg_xml.h>
diff --git a/lib/pacemaker/pcmk_sched_messages.c b/lib/pacemaker/pcmk_sched_messages.c
index 9d013be..3d09a5e 100644
--- a/lib/pacemaker/pcmk_sched_messages.c
+++ b/lib/pacemaker/pcmk_sched_messages.c
@@ -20,7 +20,7 @@
#include <crm/pengine/status.h>
#include <pacemaker-internal.h>
-#include <crm/common/ipcs_internal.h>
+#include <crm/common/ipc_internal.h>
gboolean show_scores = FALSE;
gboolean show_utilization = FALSE;
diff --git a/maint/mocked/based.c b/maint/mocked/based.c
index 0d5fd2d..2cfad9f 100644
--- a/maint/mocked/based.c
+++ b/maint/mocked/based.c
@@ -23,7 +23,7 @@
#include <crm_internal.h>
#if 0
-#include "crm/common/ipcs_internal.h" /* pcmk__client_t */
+#include "crm/common/ipc_internal.h" /* pcmk__client_t */
#include "crm/common/xml.h" /* crm_xml_add */
#endif
#include "crm/msg_xml.h" /* F_SUBTYPE */
diff --git a/maint/mocked/based.h b/maint/mocked/based.h
index ef1dc95..c214c08 100644
--- a/maint/mocked/based.h
+++ b/maint/mocked/based.h
@@ -11,7 +11,7 @@
#include <stdlib.h> /* size_t */
#include <stdbool.h> /* bool */
-#include <crm/common/ipcs_internal.h> /* pcmk__client_t */
+#include <crm/common/ipc_internal.h> /* pcmk__client_t */
struct module_s;
--
1.8.3.1
From 6743aee47c7b4cfa107ef02512fda7bebdd29efc Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 2 Apr 2020 17:01:50 -0500
Subject: [PATCH 2/8] Refactor: libcrmcommon: separate IPC code into multiple
files
A significant amount of new IPC code will be added soon, so avoid the file
sizes becoming ridiculous.
Before:
210 include/crm/common/ipc_internal.h
1846 lib/common/ipc.c
2056 total
After:
215 include/crm/common/ipc_internal.h
755 lib/common/ipc_client.c
103 lib/common/ipc_common.c
903 lib/common/ipc_server.c
146 lib/common/messages.c
2122 total
Rename newly exposed symbols per current style guidelines.
---
include/crm/common/ipc.h | 25 +-
include/crm/common/ipc_internal.h | 5 +
lib/common/Makefile.am | 5 +-
lib/common/crmcommon_private.h | 37 +-
lib/common/ipc.c | 1846 -------------------------------------
lib/common/ipc_client.c | 755 +++++++++++++++
lib/common/ipc_common.c | 103 +++
lib/common/ipc_server.c | 903 ++++++++++++++++++
lib/common/messages.c | 146 +++
9 files changed, 1969 insertions(+), 1856 deletions(-)
delete mode 100644 lib/common/ipc.c
create mode 100644 lib/common/ipc_client.c
create mode 100644 lib/common/ipc_common.c
create mode 100644 lib/common/ipc_server.c
create mode 100644 lib/common/messages.c
diff --git a/include/crm/common/ipc.h b/include/crm/common/ipc.h
index 79763f6..a0df956 100644
--- a/include/crm/common/ipc.h
+++ b/include/crm/common/ipc.h
@@ -24,17 +24,30 @@ extern "C" {
#include <qb/qbipcc.h>
#include <crm/common/xml.h>
-/* clplumbing based IPC */
+/*
+ * 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, __FUNCTION__);
-xmlNode *create_reply_adv(xmlNode * request, xmlNode * xml_response_data, const char *origin);
+#define create_reply(request, xml_response_data) \
+ create_reply_adv(request, xml_response_data, __FUNCTION__)
-# 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, __FUNCTION__)
+xmlNode *create_reply_adv(xmlNode *request, xmlNode *xml_response_data,
+ const char *origin);
-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,
+#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, \
+ __FUNCTION__)
+
+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);
+
/* *INDENT-OFF* */
enum crm_ipc_flags
{
diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h
index a85210d..6a1fcf3 100644
--- a/include/crm/common/ipc_internal.h
+++ b/include/crm/common/ipc_internal.h
@@ -85,6 +85,11 @@ extern "C" {
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;
enum pcmk__client_type {
diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am
index fae17f5..29404a6 100644
--- a/lib/common/Makefile.am
+++ b/lib/common/Makefile.am
@@ -47,10 +47,13 @@ endif
libcrmcommon_la_SOURCES += cmdline.c
libcrmcommon_la_SOURCES += digest.c
libcrmcommon_la_SOURCES += io.c
-libcrmcommon_la_SOURCES += ipc.c
+libcrmcommon_la_SOURCES += ipc_client.c
+libcrmcommon_la_SOURCES += ipc_common.c
+libcrmcommon_la_SOURCES += ipc_server.c
libcrmcommon_la_SOURCES += iso8601.c
libcrmcommon_la_SOURCES += logging.c
libcrmcommon_la_SOURCES += mainloop.c
+libcrmcommon_la_SOURCES += messages.c
libcrmcommon_la_SOURCES += nvpair.c
libcrmcommon_la_SOURCES += operations.c
libcrmcommon_la_SOURCES += options.c
diff --git a/lib/common/crmcommon_private.h b/lib/common/crmcommon_private.h
index dfb1e54..d06fa20 100644
--- a/lib/common/crmcommon_private.h
+++ b/lib/common/crmcommon_private.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2018-2019 the Pacemaker project contributors
+ * Copyright 2018-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,6 +14,17 @@
* declared with G_GNUC_INTERNAL for efficiency.
*/
+#include <stdint.h> // uint8_t, uint32_t
+#include <stdbool.h> // bool
+#include <sys/types.h> // size_t
+#include <glib.h> // GList
+#include <libxml/tree.h> // xmlNode, xmlAttr
+#include <qb/qbipcc.h> // struct qb_ipc_response_header
+
+/*
+ * XML and ACLs
+ */
+
enum xml_private_flags {
xpf_none = 0x0000,
xpf_dirty = 0x0001,
@@ -40,8 +51,8 @@ typedef struct xml_private_s {
long check;
uint32_t flags;
char *user;
- GListPtr acls;
- GListPtr deleted_objs;
+ GList *acls;
+ GList *deleted_objs;
} xml_private_t;
G_GNUC_INTERNAL
@@ -86,4 +97,24 @@ pcmk__xml_attr_value(const xmlAttr *attr)
: (const char *) attr->children->content;
}
+/*
+ * IPC
+ */
+
+#define PCMK__IPC_VERSION 1
+
+typedef struct pcmk__ipc_header_s {
+ struct qb_ipc_response_header qb;
+ uint32_t size_uncompressed;
+ uint32_t size_compressed;
+ uint32_t flags;
+ uint8_t version;
+} pcmk__ipc_header_t;
+
+G_GNUC_INTERNAL
+unsigned int pcmk__ipc_buffer_size(unsigned int max);
+
+G_GNUC_INTERNAL
+bool pcmk__valid_ipc_header(const pcmk__ipc_header_t *header);
+
#endif // CRMCOMMON_PRIVATE__H
diff --git a/lib/common/ipc.c b/lib/common/ipc.c
deleted file mode 100644
index defaa7e..0000000
--- a/lib/common/ipc.c
+++ /dev/null
@@ -1,1846 +0,0 @@
-/*
- * Copyright 2004-2020 the Pacemaker project contributors
- *
- * The version control history for this file may have further details.
- *
- * This source code is licensed under the GNU Lesser General Public License
- * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
- */
-
-#include <crm_internal.h>
-
-#if defined(US_AUTH_PEERCRED_UCRED) || defined(US_AUTH_PEERCRED_SOCKPEERCRED)
-# ifdef US_AUTH_PEERCRED_UCRED
-# ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-# endif
-# endif
-# include <sys/socket.h>
-#elif defined(US_AUTH_GETPEERUCRED)
-# include <ucred.h>
-#endif
-
-#include <sys/param.h>
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <grp.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <bzlib.h>
-
-#include <crm/crm.h> /* indirectly: pcmk_err_generic */
-#include <crm/msg_xml.h>
-#include <crm/common/ipc.h>
-#include <crm/common/ipc_internal.h>
-
-#define PCMK_IPC_VERSION 1
-
-/* Evict clients whose event queue grows this large (by default) */
-#define PCMK_IPC_DEFAULT_QUEUE_MAX 500
-
-struct crm_ipc_response_header {
- struct qb_ipc_response_header qb;
- uint32_t size_uncompressed;
- uint32_t size_compressed;
- uint32_t flags;
- uint8_t version; /* Protect against version changes for anyone that might bother to statically link us */
-};
-
-static int hdr_offset = 0;
-static unsigned int ipc_buffer_max = 0;
-static unsigned int pick_ipc_buffer(unsigned int max);
-
-static inline void
-crm_ipc_init(void)
-{
- if (hdr_offset == 0) {
- hdr_offset = sizeof(struct crm_ipc_response_header);
- }
- if (ipc_buffer_max == 0) {
- ipc_buffer_max = pick_ipc_buffer(0);
- }
-}
-
-unsigned int
-crm_ipc_default_buffer_size(void)
-{
- return pick_ipc_buffer(0);
-}
-
-static char *
-generateReference(const char *custom1, const char *custom2)
-{
- static uint ref_counter = 0;
-
- return crm_strdup_printf("%s-%s-%lld-%u",
- (custom1? custom1 : "_empty_"),
- (custom2? custom2 : "_empty_"),
- (long long) time(NULL), ref_counter++);
-}
-
-xmlNode *
-create_request_adv(const char *task, xmlNode * msg_data,
- const char *host_to, const char *sys_to,
- const char *sys_from, const char *uuid_from, const char *origin)
-{
- char *true_from = NULL;
- xmlNode *request = NULL;
- char *reference = generateReference(task, sys_from);
-
- if (uuid_from != NULL) {
- true_from = generate_hash_key(sys_from, uuid_from);
- } else if (sys_from != NULL) {
- true_from = strdup(sys_from);
- } else {
- crm_err("No sys from specified");
- }
-
- // host_from will get set for us if necessary by the controller when routed
- request = create_xml_node(NULL, __FUNCTION__);
- crm_xml_add(request, F_CRM_ORIGIN, origin);
- crm_xml_add(request, F_TYPE, T_CRM);
- crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET);
- crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST);
- crm_xml_add(request, F_CRM_REFERENCE, reference);
- crm_xml_add(request, F_CRM_TASK, task);
- crm_xml_add(request, F_CRM_SYS_TO, sys_to);
- crm_xml_add(request, F_CRM_SYS_FROM, true_from);
-
- /* HOSTTO will be ignored if it is to the DC anyway. */
- if (host_to != NULL && strlen(host_to) > 0) {
- crm_xml_add(request, F_CRM_HOST_TO, host_to);
- }
-
- if (msg_data != NULL) {
- add_message_xml(request, F_CRM_DATA, msg_data);
- }
- free(reference);
- free(true_from);
-
- return request;
-}
-
-/*
- * This method adds a copy of xml_response_data
- */
-xmlNode *
-create_reply_adv(xmlNode * original_request, xmlNode * xml_response_data, const char *origin)
-{
- xmlNode *reply = NULL;
-
- const char *host_from = crm_element_value(original_request, F_CRM_HOST_FROM);
- const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM);
- const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO);
- const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE);
- const char *operation = crm_element_value(original_request, F_CRM_TASK);
- const char *crm_msg_reference = crm_element_value(original_request, F_CRM_REFERENCE);
-
- if (type == NULL) {
- crm_err("Cannot create new_message, no message type in original message");
- CRM_ASSERT(type != NULL);
- return NULL;
-#if 0
- } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) {
- crm_err("Cannot create new_message, original message was not a request");
- return NULL;
-#endif
- }
- reply = create_xml_node(NULL, __FUNCTION__);
- if (reply == NULL) {
- crm_err("Cannot create new_message, malloc failed");
- return NULL;
- }
-
- crm_xml_add(reply, F_CRM_ORIGIN, origin);
- crm_xml_add(reply, F_TYPE, T_CRM);
- crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET);
- crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE);
- crm_xml_add(reply, F_CRM_REFERENCE, crm_msg_reference);
- crm_xml_add(reply, F_CRM_TASK, operation);
-
- /* since this is a reply, we reverse the from and to */
- crm_xml_add(reply, F_CRM_SYS_TO, sys_from);
- crm_xml_add(reply, F_CRM_SYS_FROM, sys_to);
-
- /* HOSTTO will be ignored if it is to the DC anyway. */
- if (host_from != NULL && strlen(host_from) > 0) {
- crm_xml_add(reply, F_CRM_HOST_TO, host_from);
- }
-
- if (xml_response_data != NULL) {
- add_message_xml(reply, F_CRM_DATA, xml_response_data);
- }
-
- return reply;
-}
-
-/* Libqb based IPC */
-
-/* Server... */
-
-static GHashTable *client_connections = NULL;
-
-/*!
- * \internal
- * \brief Count IPC clients
- *
- * \return Number of active IPC client connections
- */
-guint
-pcmk__ipc_client_count()
-{
- return client_connections? g_hash_table_size(client_connections) : 0;
-}
-
-/*!
- * \internal
- * \brief Execute a function for each active IPC client connection
- *
- * \param[in] func Function to call
- * \param[in] user_data Pointer to pass to function
- *
- * \note The parameters are the same as for g_hash_table_foreach().
- */
-void
-pcmk__foreach_ipc_client(GHFunc func, gpointer user_data)
-{
- if ((func != NULL) && (client_connections != NULL)) {
- g_hash_table_foreach(client_connections, func, user_data);
- }
-}
-
-/*!
- * \internal
- * \brief Remote IPC clients based on iterative function result
- *
- * \param[in] func Function to call for each active IPC client
- * \param[in] user_data Pointer to pass to function
- *
- * \note The parameters are the same as for g_hash_table_foreach_remove().
- */
-void
-pcmk__foreach_ipc_client_remove(GHRFunc func, gpointer user_data)
-{
- if ((func != NULL) && (client_connections != NULL)) {
- g_hash_table_foreach_remove(client_connections, func, user_data);
- }
-}
-
-pcmk__client_t *
-pcmk__find_client(qb_ipcs_connection_t *c)
-{
- if (client_connections) {
- return g_hash_table_lookup(client_connections, c);
- }
-
- crm_trace("No client found for %p", c);
- return NULL;
-}
-
-pcmk__client_t *
-pcmk__find_client_by_id(const char *id)
-{
- gpointer key;
- pcmk__client_t *client;
- GHashTableIter iter;
-
- if (client_connections && id) {
- g_hash_table_iter_init(&iter, client_connections);
- while (g_hash_table_iter_next(&iter, &key, (gpointer *) & client)) {
- if (strcmp(client->id, id) == 0) {
- return client;
- }
- }
- }
-
- crm_trace("No client found with id=%s", id);
- return NULL;
-}
-
-const char *
-pcmk__client_name(pcmk__client_t *c)
-{
- if (c == NULL) {
- return "null";
- } else if (c->name == NULL && c->id == NULL) {
- return "unknown";
- } else if (c->name == NULL) {
- return c->id;
- } else {
- return c->name;
- }
-}
-
-const char *
-pcmk__client_type_str(enum pcmk__client_type client_type)
-{
- switch (client_type) {
- case PCMK__CLIENT_IPC:
- return "IPC";
- case PCMK__CLIENT_TCP:
- return "TCP";
-#ifdef HAVE_GNUTLS_GNUTLS_H
- case PCMK__CLIENT_TLS:
- return "TLS";
-#endif
- default:
- return "unknown";
- }
-}
-
-void
-pcmk__client_cleanup(void)
-{
- if (client_connections != NULL) {
- int active = g_hash_table_size(client_connections);
-
- if (active) {
- crm_err("Exiting with %d active IPC client%s",
- active, pcmk__plural_s(active));
- }
- g_hash_table_destroy(client_connections); client_connections = NULL;
- }
-}
-
-void
-pcmk__drop_all_clients(qb_ipcs_service_t *service)
-{
- qb_ipcs_connection_t *c = NULL;
-
- if (service == NULL) {
- return;
- }
-
- c = qb_ipcs_connection_first_get(service);
-
- while (c != NULL) {
- qb_ipcs_connection_t *last = c;
-
- c = qb_ipcs_connection_next_get(service, last);
-
- /* There really shouldn't be anyone connected at this point */
- crm_notice("Disconnecting client %p, pid=%d...",
- last, pcmk__client_pid(last));
- qb_ipcs_disconnect(last);
- qb_ipcs_connection_unref(last);
- }
-}
-
-/*!
- * \internal
- * \brief Allocate a new pcmk__client_t object based on an IPC connection
- *
- * \param[in] c IPC connection (or NULL to allocate generic client)
- * \param[in] key Connection table key (or NULL to use sane default)
- * \param[in] uid_client UID corresponding to c (ignored if c is NULL)
- *
- * \return Pointer to new pcmk__client_t (or NULL on error)
- */
-static pcmk__client_t *
-client_from_connection(qb_ipcs_connection_t *c, void *key, uid_t uid_client)
-{
- pcmk__client_t *client = calloc(1, sizeof(pcmk__client_t));
-
- if (client == NULL) {
- crm_perror(LOG_ERR, "Allocating client");
- return NULL;
- }
-
- if (c) {
-#if ENABLE_ACL
- client->user = pcmk__uid2username(uid_client);
- if (client->user == NULL) {
- client->user = strdup("#unprivileged");
- CRM_CHECK(client->user != NULL, free(client); return NULL);
- crm_err("Unable to enforce ACLs for user ID %d, assuming unprivileged",
- uid_client);
- }
-#endif
- client->ipcs = c;
- client->kind = PCMK__CLIENT_IPC;
- client->pid = pcmk__client_pid(c);
- if (key == NULL) {
- key = c;
- }
- }
-
- client->id = crm_generate_uuid();
- if (client->id == NULL) {
- crm_err("Could not generate UUID for client");
- free(client->user);
- free(client);
- return NULL;
- }
- if (key == NULL) {
- key = client->id;
- }
- if (client_connections == NULL) {
- crm_trace("Creating IPC client table");
- client_connections = g_hash_table_new(g_direct_hash, g_direct_equal);
- }
- g_hash_table_insert(client_connections, key, client);
- return client;
-}
-
-/*!
- * \brief Allocate a new pcmk__client_t object and generate its ID
- *
- * \param[in] key What to use as connections hash table key (NULL to use ID)
- *
- * \return Pointer to new pcmk__client_t (asserts on failure)
- */
-pcmk__client_t *
-pcmk__new_unauth_client(void *key)
-{
- pcmk__client_t *client = client_from_connection(NULL, key, 0);
-
- CRM_ASSERT(client != NULL);
- return client;
-}
-
-pcmk__client_t *
-pcmk__new_client(qb_ipcs_connection_t *c, uid_t uid_client, gid_t gid_client)
-{
- gid_t uid_cluster = 0;
- gid_t gid_cluster = 0;
-
- pcmk__client_t *client = NULL;
-
- CRM_CHECK(c != NULL, return NULL);
-
- if (pcmk_daemon_user(&uid_cluster, &gid_cluster) < 0) {
- static bool need_log = TRUE;
-
- if (need_log) {
- crm_warn("Could not find user and group IDs for user %s",
- CRM_DAEMON_USER);
- need_log = FALSE;
- }
- }
-
- if (uid_client != 0) {
- crm_trace("Giving group %u access to new IPC connection", gid_cluster);
- /* Passing -1 to chown(2) means don't change */
- qb_ipcs_connection_auth_set(c, -1, gid_cluster, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
- }
-
- /* TODO: Do our own auth checking, return NULL if unauthorized */
- client = client_from_connection(c, NULL, uid_client);
- if (client == NULL) {
- return NULL;
- }
-
- if ((uid_client == 0) || (uid_client == uid_cluster)) {
- /* Remember when a connection came from root or hacluster */
- set_bit(client->flags, pcmk__client_privileged);
- }
-
- crm_debug("New IPC client %s for PID %u with uid %d and gid %d",
- client->id, client->pid, uid_client, gid_client);
- return client;
-}
-
-static struct iovec *
-pcmk__new_ipc_event(void)
-{
- struct iovec *iov = calloc(2, sizeof(struct iovec));
-
- CRM_ASSERT(iov != NULL);
- return iov;
-}
-
-/*!
- * \brief Free an I/O vector created by pcmk__ipc_prepare_iov()
- *
- * \param[in] event I/O vector to free
- */
-void
-pcmk_free_ipc_event(struct iovec *event)
-{
- if (event != NULL) {
- free(event[0].iov_base);
- free(event[1].iov_base);
- free(event);
- }
-}
-
-static void
-free_event(gpointer data)
-{
- pcmk_free_ipc_event((struct iovec *) data);
-}
-
-static void
-add_event(pcmk__client_t *c, struct iovec *iov)
-{
- if (c->event_queue == NULL) {
- c->event_queue = g_queue_new();
- }
- g_queue_push_tail(c->event_queue, iov);
-}
-
-void
-pcmk__free_client(pcmk__client_t *c)
-{
- if (c == NULL) {
- return;
- }
-
- if (client_connections) {
- if (c->ipcs) {
- crm_trace("Destroying %p/%p (%d remaining)",
- c, c->ipcs, g_hash_table_size(client_connections) - 1);
- g_hash_table_remove(client_connections, c->ipcs);
-
- } else {
- crm_trace("Destroying remote connection %p (%d remaining)",
- c, g_hash_table_size(client_connections) - 1);
- g_hash_table_remove(client_connections, c->id);
- }
- }
-
- if (c->event_timer) {
- g_source_remove(c->event_timer);
- }
-
- if (c->event_queue) {
- crm_debug("Destroying %d events", g_queue_get_length(c->event_queue));
- g_queue_free_full(c->event_queue, free_event);
- }
-
- free(c->id);
- free(c->name);
- free(c->user);
- if (c->remote) {
- if (c->remote->auth_timeout) {
- g_source_remove(c->remote->auth_timeout);
- }
- free(c->remote->buffer);
- free(c->remote);
- }
- free(c);
-}
-
-/*!
- * \internal
- * \brief Raise IPC eviction threshold for a client, if allowed
- *
- * \param[in,out] client Client to modify
- * \param[in] qmax New threshold (as non-NULL string)
- *
- * \return TRUE if change was allowed, FALSE otherwise
- */
-bool
-pcmk__set_client_queue_max(pcmk__client_t *client, const char *qmax)
-{
- if (is_set(client->flags, pcmk__client_privileged)) {
- long long qmax_int;
-
- errno = 0;
- qmax_int = crm_parse_ll(qmax, NULL);
- if ((errno == 0) && (qmax_int > 0)) {
- client->queue_max = (unsigned int) qmax_int;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-int
-pcmk__client_pid(qb_ipcs_connection_t *c)
-{
- struct qb_ipcs_connection_stats stats;
-
- stats.client_pid = 0;
- qb_ipcs_connection_stats_get(c, &stats, 0);
- return stats.client_pid;
-}
-
-/*!
- * \internal
- * \brief Retrieve message XML from data read from client IPC
- *
- * \param[in] c IPC client connection
- * \param[in] data Data read from client connection
- * \param[out] id Where to store message ID from libqb header
- * \param[out] flags Where to store flags from libqb header
- *
- * \return Message XML on success, NULL otherwise
- */
-xmlNode *
-pcmk__client_data2xml(pcmk__client_t *c, void *data, uint32_t *id,
- uint32_t *flags)
-{
- xmlNode *xml = NULL;
- char *uncompressed = NULL;
- char *text = ((char *)data) + sizeof(struct crm_ipc_response_header);
- struct crm_ipc_response_header *header = data;
-
- if (id) {
- *id = ((struct qb_ipc_response_header *)data)->id;
- }
- if (flags) {
- *flags = header->flags;
- }
-
- if (is_set(header->flags, crm_ipc_proxied)) {
- /* Mark this client as being the endpoint of a proxy connection.
- * Proxy connections responses are sent on the event channel, to avoid
- * blocking the controller serving as proxy.
- */
- c->flags |= pcmk__client_proxied;
- }
-
- if(header->version > PCMK_IPC_VERSION) {
- crm_err("Filtering incompatible v%d IPC message, we only support versions <= %d",
- header->version, PCMK_IPC_VERSION);
- return NULL;
- }
-
- if (header->size_compressed) {
- int rc = 0;
- unsigned int size_u = 1 + header->size_uncompressed;
- uncompressed = calloc(1, size_u);
-
- crm_trace("Decompressing message data %u bytes into %u bytes",
- header->size_compressed, size_u);
-
- rc = BZ2_bzBuffToBuffDecompress(uncompressed, &size_u, text, header->size_compressed, 1, 0);
- text = uncompressed;
-
- if (rc != BZ_OK) {
- crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
- bz2_strerror(rc), rc);
- free(uncompressed);
- return NULL;
- }
- }
-
- CRM_ASSERT(text[header->size_uncompressed - 1] == 0);
-
- xml = string2xml(text);
- crm_log_xml_trace(xml, "[IPC received]");
-
- free(uncompressed);
- return xml;
-}
-
-static int crm_ipcs_flush_events(pcmk__client_t *c);
-
-static gboolean
-crm_ipcs_flush_events_cb(gpointer data)
-{
- pcmk__client_t *c = data;
-
- c->event_timer = 0;
- crm_ipcs_flush_events(c);
- return FALSE;
-}
-
-/*!
- * \internal
- * \brief Add progressive delay before next event queue flush
- *
- * \param[in,out] c Client connection to add delay to
- * \param[in] queue_len Current event queue length
- */
-static inline void
-delay_next_flush(pcmk__client_t *c, unsigned int queue_len)
-{
- /* Delay a maximum of 1.5 seconds */
- guint delay = (queue_len < 5)? (1000 + 100 * queue_len) : 1500;
-
- c->event_timer = g_timeout_add(delay, crm_ipcs_flush_events_cb, c);
-}
-
-/*!
- * \internal
- * \brief Send client any messages in its queue
- *
- * \param[in] c Client to flush
- *
- * \return Standard Pacemaker return value
- */
-static int
-crm_ipcs_flush_events(pcmk__client_t *c)
-{
- int rc = pcmk_rc_ok;
- ssize_t qb_rc = 0;
- unsigned int sent = 0;
- unsigned int queue_len = 0;
-
- if (c == NULL) {
- return rc;
-
- } else if (c->event_timer) {
- /* There is already a timer, wait until it goes off */
- crm_trace("Timer active for %p - %d", c->ipcs, c->event_timer);
- return rc;
- }
-
- if (c->event_queue) {
- queue_len = g_queue_get_length(c->event_queue);
- }
- while (sent < 100) {
- struct crm_ipc_response_header *header = NULL;
- struct iovec *event = NULL;
-
- if (c->event_queue) {
- // We don't pop unless send is successful
- event = g_queue_peek_head(c->event_queue);
- }
- if (event == NULL) { // Queue is empty
- break;
- }
-
- qb_rc = qb_ipcs_event_sendv(c->ipcs, event, 2);
- if (qb_rc < 0) {
- rc = (int) -qb_rc;
- break;
- }
- event = g_queue_pop_head(c->event_queue);
-
- sent++;
- header = event[0].iov_base;
- if (header->size_compressed) {
- crm_trace("Event %d to %p[%d] (%lld compressed bytes) sent",
- header->qb.id, c->ipcs, c->pid, (long long) qb_rc);
- } else {
- crm_trace("Event %d to %p[%d] (%lld bytes) sent: %.120s",
- header->qb.id, c->ipcs, c->pid, (long long) qb_rc,
- (char *) (event[1].iov_base));
- }
- pcmk_free_ipc_event(event);
- }
-
- queue_len -= sent;
- if (sent > 0 || queue_len) {
- crm_trace("Sent %d events (%d remaining) for %p[%d]: %s (%lld)",
- sent, queue_len, c->ipcs, c->pid,
- pcmk_rc_str(rc), (long long) qb_rc);
- }
-
- if (queue_len) {
-
- /* Allow clients to briefly fall behind on processing incoming messages,
- * but drop completely unresponsive clients so the connection doesn't
- * consume resources indefinitely.
- */
- if (queue_len > QB_MAX(c->queue_max, PCMK_IPC_DEFAULT_QUEUE_MAX)) {
- if ((c->queue_backlog <= 1) || (queue_len < c->queue_backlog)) {
- /* Don't evict for a new or shrinking backlog */
- crm_warn("Client with process ID %u has a backlog of %u messages "
- CRM_XS " %p", c->pid, queue_len, c->ipcs);
- } else {
- crm_err("Evicting client with process ID %u due to backlog of %u messages "
- CRM_XS " %p", c->pid, queue_len, c->ipcs);
- c->queue_backlog = 0;
- qb_ipcs_disconnect(c->ipcs);
- return rc;
- }
- }
-
- c->queue_backlog = queue_len;
- delay_next_flush(c, queue_len);
-
- } else {
- /* Event queue is empty, there is no backlog */
- c->queue_backlog = 0;
- }
-
- return rc;
-}
-
-/*!
- * \internal
- * \brief Create an I/O vector for sending an IPC XML message
- *
- * \param[in] request Identifier for libqb response header
- * \param[in] message XML message to send
- * \param[in] max_send_size If 0, default IPC buffer size is used
- * \param[out] result Where to store prepared I/O vector
- * \param[out] bytes Size of prepared data in bytes
- *
- * \return Standard Pacemaker return code
- */
-int
-pcmk__ipc_prepare_iov(uint32_t request, xmlNode *message,
- uint32_t max_send_size, struct iovec **result,
- ssize_t *bytes)
-{
- static unsigned int biggest = 0;
- struct iovec *iov;
- unsigned int total = 0;
- char *compressed = NULL;
- char *buffer = NULL;
- struct crm_ipc_response_header *header = NULL;
-
- if ((message == NULL) || (result == NULL)) {
- return EINVAL;
- }
-
- header = calloc(1, sizeof(struct crm_ipc_response_header));
- if (header == NULL) {
- return ENOMEM; /* errno mightn't be set by allocator */
- }
-
- buffer = dump_xml_unformatted(message);
- crm_ipc_init();
-
- if (max_send_size == 0) {
- max_send_size = ipc_buffer_max;
- }
- CRM_LOG_ASSERT(max_send_size != 0);
-
- *result = NULL;
- iov = pcmk__new_ipc_event();
- iov[0].iov_len = hdr_offset;
- iov[0].iov_base = header;
-
- header->version = PCMK_IPC_VERSION;
- header->size_uncompressed = 1 + strlen(buffer);
- total = iov[0].iov_len + header->size_uncompressed;
-
- if (total < max_send_size) {
- iov[1].iov_base = buffer;
- iov[1].iov_len = header->size_uncompressed;
-
- } else {
- unsigned int new_size = 0;
-
- if (pcmk__compress(buffer, (unsigned int) header->size_uncompressed,
- (unsigned int) max_send_size, &compressed,
- &new_size) == pcmk_rc_ok) {
-
- header->flags |= crm_ipc_compressed;
- header->size_compressed = new_size;
-
- iov[1].iov_len = header->size_compressed;
- iov[1].iov_base = compressed;
-
- free(buffer);
-
- biggest = QB_MAX(header->size_compressed, biggest);
-
- } else {
- crm_log_xml_trace(message, "EMSGSIZE");
- biggest = QB_MAX(header->size_uncompressed, biggest);
-
- crm_err("Could not compress %u-byte message into less than IPC "
- "limit of %u bytes; set PCMK_ipc_buffer to higher value "
- "(%u bytes suggested)",
- header->size_uncompressed, max_send_size, 4 * biggest);
-
- free(compressed);
- free(buffer);
- pcmk_free_ipc_event(iov);
- return EMSGSIZE;
- }
- }
-
- header->qb.size = iov[0].iov_len + iov[1].iov_len;
- header->qb.id = (int32_t)request; /* Replying to a specific request */
-
- *result = iov;
- CRM_ASSERT(header->qb.size > 0);
- if (bytes != NULL) {
- *bytes = header->qb.size;
- }
- return pcmk_rc_ok;
-}
-
-int
-pcmk__ipc_send_iov(pcmk__client_t *c, struct iovec *iov, uint32_t flags)
-{
- int rc = pcmk_rc_ok;
- static uint32_t id = 1;
- struct crm_ipc_response_header *header = iov[0].iov_base;
-
- if (c->flags & pcmk__client_proxied) {
- /* _ALL_ replies to proxied connections need to be sent as events */
- if (is_not_set(flags, crm_ipc_server_event)) {
- flags |= crm_ipc_server_event;
- /* this flag lets us know this was originally meant to be a response.
- * even though we're sending it over the event channel. */
- flags |= crm_ipc_proxied_relay_response;
- }
- }
-
- header->flags |= flags;
- if (flags & crm_ipc_server_event) {
- header->qb.id = id++; /* We don't really use it, but doesn't hurt to set one */
-
- if (flags & crm_ipc_server_free) {
- crm_trace("Sending the original to %p[%d]", c->ipcs, c->pid);
- add_event(c, iov);
-
- } else {
- struct iovec *iov_copy = pcmk__new_ipc_event();
-
- crm_trace("Sending a copy to %p[%d]", c->ipcs, c->pid);
- iov_copy[0].iov_len = iov[0].iov_len;
- iov_copy[0].iov_base = malloc(iov[0].iov_len);
- memcpy(iov_copy[0].iov_base, iov[0].iov_base, iov[0].iov_len);
-
- iov_copy[1].iov_len = iov[1].iov_len;
- iov_copy[1].iov_base = malloc(iov[1].iov_len);
- memcpy(iov_copy[1].iov_base, iov[1].iov_base, iov[1].iov_len);
-
- add_event(c, iov_copy);
- }
-
- } else {
- ssize_t qb_rc;
-
- CRM_LOG_ASSERT(header->qb.id != 0); /* Replying to a specific request */
-
- qb_rc = qb_ipcs_response_sendv(c->ipcs, iov, 2);
- if (qb_rc < header->qb.size) {
- if (qb_rc < 0) {
- rc = (int) -qb_rc;
- }
- crm_notice("Response %d to pid %d failed: %s "
- CRM_XS " bytes=%u rc=%lld ipcs=%p",
- header->qb.id, c->pid, pcmk_rc_str(rc),
- header->qb.size, (long long) qb_rc, c->ipcs);
-
- } else {
- crm_trace("Response %d sent, %lld bytes to %p[%d]",
- header->qb.id, (long long) qb_rc, c->ipcs, c->pid);
- }
-
- if (flags & crm_ipc_server_free) {
- pcmk_free_ipc_event(iov);
- }
- }
-
- if (flags & crm_ipc_server_event) {
- rc = crm_ipcs_flush_events(c);
- } else {
- crm_ipcs_flush_events(c);
- }
-
- if ((rc == EPIPE) || (rc == ENOTCONN)) {
- crm_trace("Client %p disconnected", c->ipcs);
- }
- return rc;
-}
-
-int
-pcmk__ipc_send_xml(pcmk__client_t *c, uint32_t request, xmlNode *message,
- uint32_t flags)
-{
- struct iovec *iov = NULL;
- int rc = pcmk_rc_ok;
-
- if (c == NULL) {
- return EINVAL;
- }
- crm_ipc_init();
- rc = pcmk__ipc_prepare_iov(request, message, ipc_buffer_max, &iov, NULL);
- if (rc == pcmk_rc_ok) {
- rc = pcmk__ipc_send_iov(c, iov, flags | crm_ipc_server_free);
- } else {
- pcmk_free_ipc_event(iov);
- crm_notice("IPC message to pid %d failed: %s " CRM_XS " rc=%d",
- c->pid, pcmk_rc_str(rc), rc);
- }
- return rc;
-}
-
-void
-pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c,
- uint32_t request, uint32_t flags, const char *tag)
-{
- if (flags & crm_ipc_client_response) {
- xmlNode *ack = create_xml_node(NULL, tag);
-
- crm_trace("Ack'ing IPC message from %s", pcmk__client_name(c));
- c->request_id = 0;
- crm_xml_add(ack, "function", function);
- crm_xml_add_int(ack, "line", line);
- pcmk__ipc_send_xml(c, request, ack, flags);
- free_xml(ack);
- }
-}
-
-/*!
- * \internal
- * \brief Add an IPC server to the main loop for the pacemaker-based API
- *
- * \param[out] ipcs_ro New IPC server for read-only pacemaker-based API
- * \param[out] ipcs_rw New IPC server for read/write pacemaker-based API
- * \param[out] ipcs_shm New IPC server for shared-memory pacemaker-based API
- * \param[in] ro_cb IPC callbacks for read-only API
- * \param[in] rw_cb IPC callbacks for read/write and shared-memory APIs
- *
- * \note This function exits fatally if unable to create the servers.
- */
-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)
-{
- *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO,
- QB_IPC_NATIVE, ro_cb);
-
- *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW,
- QB_IPC_NATIVE, rw_cb);
-
- *ipcs_shm = mainloop_add_ipc_server(PCMK__SERVER_BASED_SHM,
- QB_IPC_SHM, rw_cb);
-
- if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) {
- crm_err("Failed to create the CIB manager: exiting and inhibiting respawn");
- crm_warn("Verify pacemaker and pacemaker_remote are not both enabled");
- crm_exit(CRM_EX_FATAL);
- }
-}
-
-/*!
- * \internal
- * \brief Destroy IPC servers for pacemaker-based API
- *
- * \param[out] ipcs_ro IPC server for read-only pacemaker-based API
- * \param[out] ipcs_rw IPC server for read/write pacemaker-based API
- * \param[out] ipcs_shm IPC server for shared-memory pacemaker-based API
- *
- * \note This is a convenience function for calling qb_ipcs_destroy() for each
- * argument.
- */
-void
-pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro,
- qb_ipcs_service_t *ipcs_rw,
- qb_ipcs_service_t *ipcs_shm)
-{
- qb_ipcs_destroy(ipcs_ro);
- qb_ipcs_destroy(ipcs_rw);
- qb_ipcs_destroy(ipcs_shm);
-}
-
-/*!
- * \internal
- * \brief Add an IPC server to the main loop for the pacemaker-controld API
- *
- * \param[in] cb IPC callbacks
- *
- * \return Newly created IPC server
- */
-qb_ipcs_service_t *
-pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb)
-{
- return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb);
-}
-
-/*!
- * \internal
- * \brief Add an IPC server to the main loop for the pacemaker-attrd API
- *
- * \param[in] cb IPC callbacks
- *
- * \note This function exits fatally if unable to create the servers.
- */
-void
-pcmk__serve_attrd_ipc(qb_ipcs_service_t **ipcs,
- struct qb_ipcs_service_handlers *cb)
-{
- *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb);
-
- if (*ipcs == NULL) {
- crm_err("Failed to create pacemaker-attrd server: exiting and inhibiting respawn");
- crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
- crm_exit(CRM_EX_FATAL);
- }
-}
-
-/*!
- * \internal
- * \brief Add an IPC server to the main loop for the pacemaker-fenced API
- *
- * \param[in] cb IPC callbacks
- *
- * \note This function exits fatally if unable to create the servers.
- */
-void
-pcmk__serve_fenced_ipc(qb_ipcs_service_t **ipcs,
- struct qb_ipcs_service_handlers *cb)
-{
- *ipcs = mainloop_add_ipc_server_with_prio("stonith-ng", QB_IPC_NATIVE, cb,
- QB_LOOP_HIGH);
-
- if (*ipcs == NULL) {
- crm_err("Failed to create fencer: exiting and inhibiting respawn.");
- crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
- crm_exit(CRM_EX_FATAL);
- }
-}
-
-/* Client... */
-
-#define MIN_MSG_SIZE 12336 /* sizeof(struct qb_ipc_connection_response) */
-#define MAX_MSG_SIZE 128*1024 /* 128k default */
-
-struct crm_ipc_s {
- struct pollfd pfd;
-
- /* the max size we can send/receive over ipc */
- unsigned int max_buf_size;
- /* Size of the allocated 'buffer' */
- unsigned int buf_size;
- int msg_size;
- int need_reply;
- char *buffer;
- char *name;
-
- qb_ipcc_connection_t *ipc;
-
-};
-
-static unsigned int
-pick_ipc_buffer(unsigned int max)
-{
- static unsigned int global_max = 0;
-
- if (global_max == 0) {
- const char *env = getenv("PCMK_ipc_buffer");
-
- if (env) {
- int env_max = crm_parse_int(env, "0");
-
- global_max = (env_max > 0)? QB_MAX(MIN_MSG_SIZE, env_max) : MAX_MSG_SIZE;
-
- } else {
- global_max = MAX_MSG_SIZE;
- }
- }
-
- return QB_MAX(max, global_max);
-}
-
-crm_ipc_t *
-crm_ipc_new(const char *name, size_t max_size)
-{
- crm_ipc_t *client = NULL;
-
- client = calloc(1, sizeof(crm_ipc_t));
-
- client->name = strdup(name);
- client->buf_size = pick_ipc_buffer(max_size);
- client->buffer = malloc(client->buf_size);
-
- /* Clients initiating connection pick the max buf size */
- client->max_buf_size = client->buf_size;
-
- client->pfd.fd = -1;
- client->pfd.events = POLLIN;
- client->pfd.revents = 0;
-
- return client;
-}
-
-/*!
- * \brief Establish an IPC connection to a Pacemaker component
- *
- * \param[in] client Connection instance obtained from crm_ipc_new()
- *
- * \return TRUE on success, FALSE otherwise (in which case errno will be set;
- * specifically, in case of discovering the remote side is not
- * authentic, its value is set to ECONNABORTED).
- */
-bool
-crm_ipc_connect(crm_ipc_t * client)
-{
- uid_t cl_uid = 0;
- gid_t cl_gid = 0;
- pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
- int rv;
-
- client->need_reply = FALSE;
- client->ipc = qb_ipcc_connect(client->name, client->buf_size);
-
- if (client->ipc == NULL) {
- crm_debug("Could not establish %s connection: %s (%d)", client->name, pcmk_strerror(errno), errno);
- return FALSE;
- }
-
- client->pfd.fd = crm_ipc_get_fd(client);
- if (client->pfd.fd < 0) {
- rv = errno;
- /* message already omitted */
- crm_ipc_close(client);
- errno = rv;
- return FALSE;
- }
-
- rv = pcmk_daemon_user(&cl_uid, &cl_gid);
- if (rv < 0) {
- /* message already omitted */
- crm_ipc_close(client);
- errno = -rv;
- return FALSE;
- }
-
- if (!(rv = crm_ipc_is_authentic_process(client->pfd.fd, cl_uid, cl_gid,
- &found_pid, &found_uid,
- &found_gid))) {
- crm_err("Daemon (IPC %s) is not authentic:"
- " process %lld (uid: %lld, gid: %lld)",
- client->name, (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
- (long long) found_uid, (long long) found_gid);
- crm_ipc_close(client);
- errno = ECONNABORTED;
- return FALSE;
-
- } else if (rv < 0) {
- errno = -rv;
- crm_perror(LOG_ERR, "Could not verify authenticity of daemon (IPC %s)",
- client->name);
- crm_ipc_close(client);
- errno = -rv;
- return FALSE;
- }
-
- qb_ipcc_context_set(client->ipc, client);
-
-#ifdef HAVE_IPCS_GET_BUFFER_SIZE
- client->max_buf_size = qb_ipcc_get_buffer_size(client->ipc);
- if (client->max_buf_size > client->buf_size) {
- free(client->buffer);
- client->buffer = calloc(1, client->max_buf_size);
- client->buf_size = client->max_buf_size;
- }
-#endif
-
- return TRUE;
-}
-
-void
-crm_ipc_close(crm_ipc_t * client)
-{
- if (client) {
- crm_trace("Disconnecting %s IPC connection %p (%p)", client->name, client, client->ipc);
-
- if (client->ipc) {
- qb_ipcc_connection_t *ipc = client->ipc;
-
- client->ipc = NULL;
- qb_ipcc_disconnect(ipc);
- }
- }
-}
-
-void
-crm_ipc_destroy(crm_ipc_t * client)
-{
- if (client) {
- if (client->ipc && qb_ipcc_is_connected(client->ipc)) {
- crm_notice("Destroying an active IPC connection to %s", client->name);
- /* The next line is basically unsafe
- *
- * If this connection was attached to mainloop and mainloop is active,
- * the 'disconnected' callback will end up back here and we'll end
- * up free'ing the memory twice - something that can still happen
- * even without this if we destroy a connection and it closes before
- * we call exit
- */
- /* crm_ipc_close(client); */
- }
- crm_trace("Destroying IPC connection to %s: %p", client->name, client);
- free(client->buffer);
- free(client->name);
- free(client);
- }
-}
-
-int
-crm_ipc_get_fd(crm_ipc_t * client)
-{
- int fd = 0;
-
- if (client && client->ipc && (qb_ipcc_fd_get(client->ipc, &fd) == 0)) {
- return fd;
- }
- errno = EINVAL;
- crm_perror(LOG_ERR, "Could not obtain file IPC descriptor for %s",
- (client? client->name : "unspecified client"));
- return -errno;
-}
-
-bool
-crm_ipc_connected(crm_ipc_t * client)
-{
- bool rc = FALSE;
-
- if (client == NULL) {
- crm_trace("No client");
- return FALSE;
-
- } else if (client->ipc == NULL) {
- crm_trace("No connection");
- return FALSE;
-
- } else if (client->pfd.fd < 0) {
- crm_trace("Bad descriptor");
- return FALSE;
- }
-
- rc = qb_ipcc_is_connected(client->ipc);
- if (rc == FALSE) {
- client->pfd.fd = -EINVAL;
- }
- return rc;
-}
-
-/*!
- * \brief Check whether an IPC connection is ready to be read
- *
- * \param[in] client Connection to check
- *
- * \return Positive value if ready to be read, 0 if not ready, -errno on error
- */
-int
-crm_ipc_ready(crm_ipc_t *client)
-{
- int rc;
-
- CRM_ASSERT(client != NULL);
-
- if (crm_ipc_connected(client) == FALSE) {
- return -ENOTCONN;
- }
-
- client->pfd.revents = 0;
- rc = poll(&(client->pfd), 1, 0);
- return (rc < 0)? -errno : rc;
-}
-
-// \return Standard Pacemaker return code
-static int
-crm_ipc_decompress(crm_ipc_t * client)
-{
- struct crm_ipc_response_header *header = (struct crm_ipc_response_header *)(void*)client->buffer;
-
- if (header->size_compressed) {
- int rc = 0;
- unsigned int size_u = 1 + header->size_uncompressed;
- /* never let buf size fall below our max size required for ipc reads. */
- unsigned int new_buf_size = QB_MAX((hdr_offset + size_u), client->max_buf_size);
- char *uncompressed = calloc(1, new_buf_size);
-
- crm_trace("Decompressing message data %u bytes into %u bytes",
- header->size_compressed, size_u);
-
- rc = BZ2_bzBuffToBuffDecompress(uncompressed + hdr_offset, &size_u,
- client->buffer + hdr_offset, header->size_compressed, 1, 0);
-
- if (rc != BZ_OK) {
- crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
- bz2_strerror(rc), rc);
- free(uncompressed);
- return EILSEQ;
- }
-
- /*
- * This assert no longer holds true. For an identical msg, some clients may
- * require compression, and others may not. If that same msg (event) is sent
- * to multiple clients, it could result in some clients receiving a compressed
- * msg even though compression was not explicitly required for them.
- *
- * CRM_ASSERT((header->size_uncompressed + hdr_offset) >= ipc_buffer_max);
- */
- CRM_ASSERT(size_u == header->size_uncompressed);
-
- memcpy(uncompressed, client->buffer, hdr_offset); /* Preserve the header */
- header = (struct crm_ipc_response_header *)(void*)uncompressed;
-
- free(client->buffer);
- client->buf_size = new_buf_size;
- client->buffer = uncompressed;
- }
-
- CRM_ASSERT(client->buffer[hdr_offset + header->size_uncompressed - 1] == 0);
- return pcmk_rc_ok;
-}
-
-long
-crm_ipc_read(crm_ipc_t * client)
-{
- struct crm_ipc_response_header *header = NULL;
-
- CRM_ASSERT(client != NULL);
- CRM_ASSERT(client->ipc != NULL);
- CRM_ASSERT(client->buffer != NULL);
-
- crm_ipc_init();
-
- client->buffer[0] = 0;
- client->msg_size = qb_ipcc_event_recv(client->ipc, client->buffer,
- client->buf_size, 0);
- if (client->msg_size >= 0) {
- int rc = crm_ipc_decompress(client);
-
- if (rc != pcmk_rc_ok) {
- return pcmk_rc2legacy(rc);
- }
-
- header = (struct crm_ipc_response_header *)(void*)client->buffer;
- if(header->version > PCMK_IPC_VERSION) {
- crm_err("Filtering incompatible v%d IPC message, we only support versions <= %d",
- header->version, PCMK_IPC_VERSION);
- return -EBADMSG;
- }
-
- crm_trace("Received %s event %d, size=%u, rc=%d, text: %.100s",
- client->name, header->qb.id, header->qb.size, client->msg_size,
- client->buffer + hdr_offset);
-
- } else {
- crm_trace("No message from %s received: %s", client->name, pcmk_strerror(client->msg_size));
- }
-
- if (crm_ipc_connected(client) == FALSE || client->msg_size == -ENOTCONN) {
- crm_err("Connection to %s failed", client->name);
- }
-
- if (header) {
- /* Data excluding the header */
- return header->size_uncompressed;
- }
- return -ENOMSG;
-}
-
-const char *
-crm_ipc_buffer(crm_ipc_t * client)
-{
- CRM_ASSERT(client != NULL);
- return client->buffer + sizeof(struct crm_ipc_response_header);
-}
-
-uint32_t
-crm_ipc_buffer_flags(crm_ipc_t * client)
-{
- struct crm_ipc_response_header *header = NULL;
-
- CRM_ASSERT(client != NULL);
- if (client->buffer == NULL) {
- return 0;
- }
-
- header = (struct crm_ipc_response_header *)(void*)client->buffer;
- return header->flags;
-}
-
-const char *
-crm_ipc_name(crm_ipc_t * client)
-{
- CRM_ASSERT(client != NULL);
- return client->name;
-}
-
-// \return Standard Pacemaker return code
-static int
-internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout,
- ssize_t *bytes)
-{
- time_t timeout = time(NULL) + 1 + (ms_timeout / 1000);
- int rc = pcmk_rc_ok;
-
- crm_ipc_init();
-
- /* get the reply */
- crm_trace("client %s waiting on reply to msg id %d", client->name, request_id);
- do {
-
- *bytes = qb_ipcc_recv(client->ipc, client->buffer, client->buf_size, 1000);
- if (*bytes > 0) {
- struct crm_ipc_response_header *hdr = NULL;
-
- rc = crm_ipc_decompress(client);
- if (rc != pcmk_rc_ok) {
- return rc;
- }
-
- hdr = (struct crm_ipc_response_header *)(void*)client->buffer;
- if (hdr->qb.id == request_id) {
- /* Got it */
- break;
- } else if (hdr->qb.id < request_id) {
- xmlNode *bad = string2xml(crm_ipc_buffer(client));
-
- crm_err("Discarding old reply %d (need %d)", hdr->qb.id, request_id);
- crm_log_xml_notice(bad, "OldIpcReply");
-
- } else {
- xmlNode *bad = string2xml(crm_ipc_buffer(client));
-
- crm_err("Discarding newer reply %d (need %d)", hdr->qb.id, request_id);
- crm_log_xml_notice(bad, "ImpossibleReply");
- CRM_ASSERT(hdr->qb.id <= request_id);
- }
- } else if (crm_ipc_connected(client) == FALSE) {
- crm_err("Server disconnected client %s while waiting for msg id %d", client->name,
- request_id);
- break;
- }
-
- } while (time(NULL) < timeout);
-
- if (*bytes < 0) {
- rc = (int) -*bytes; // System errno
- }
- return rc;
-}
-
-/*!
- * \brief Send an IPC XML message
- *
- * \param[in] client Connection to IPC server
- * \param[in] message XML message to send
- * \param[in] flags Bitmask of crm_ipc_flags
- * \param[in] ms_timeout Give up if not sent within this much time
- * (5 seconds if 0, or no timeout if negative)
- * \param[out] reply Reply from server (or NULL if none)
- *
- * \return Negative errno on error, otherwise size of reply received in bytes
- * if reply was needed, otherwise number of bytes sent
- */
-int
-crm_ipc_send(crm_ipc_t * client, xmlNode * message, enum crm_ipc_flags flags, int32_t ms_timeout,
- xmlNode ** reply)
-{
- int rc = 0;
- ssize_t qb_rc = 0;
- ssize_t bytes = 0;
- struct iovec *iov;
- static uint32_t id = 0;
- static int factor = 8;
- struct crm_ipc_response_header *header;
-
- crm_ipc_init();
-
- if (client == NULL) {
- crm_notice("Can't send IPC request without connection (bug?): %.100s",
- message);
- return -ENOTCONN;
-
- } else if (crm_ipc_connected(client) == FALSE) {
- /* Don't even bother */
- crm_notice("Can't send IPC request to %s: Connection closed",
- client->name);
- return -ENOTCONN;
- }
-
- if (ms_timeout == 0) {
- ms_timeout = 5000;
- }
-
- if (client->need_reply) {
- qb_rc = qb_ipcc_recv(client->ipc, client->buffer, client->buf_size, ms_timeout);
- if (qb_rc < 0) {
- crm_warn("Sending IPC to %s disabled until pending reply received",
- client->name);
- return -EALREADY;
-
- } else {
- crm_notice("Sending IPC to %s re-enabled after pending reply received",
- client->name);
- client->need_reply = FALSE;
- }
- }
-
- id++;
- CRM_LOG_ASSERT(id != 0); /* Crude wrap-around detection */
- rc = pcmk__ipc_prepare_iov(id, message, client->max_buf_size, &iov, &bytes);
- if (rc != pcmk_rc_ok) {
- crm_warn("Couldn't prepare IPC request to %s: %s " CRM_XS " rc=%d",
- client->name, pcmk_rc_str(rc), rc);
- return pcmk_rc2legacy(rc);
- }
-
- header = iov[0].iov_base;
- header->flags |= flags;
-
- if(is_set(flags, crm_ipc_proxied)) {
- /* Don't look for a synchronous response */
- clear_bit(flags, crm_ipc_client_response);
- }
-
- if(header->size_compressed) {
- if(factor < 10 && (client->max_buf_size / 10) < (bytes / factor)) {
- crm_notice("Compressed message exceeds %d0%% of configured IPC "
- "limit (%u bytes); consider setting PCMK_ipc_buffer to "
- "%u or higher",
- factor, client->max_buf_size, 2 * client->max_buf_size);
- factor++;
- }
- }
-
- crm_trace("Sending %s IPC request %d of %u bytes using %dms timeout",
- client->name, header->qb.id, header->qb.size, ms_timeout);
-
- if (ms_timeout > 0 || is_not_set(flags, crm_ipc_client_response)) {
-
- time_t timeout = time(NULL) + 1 + (ms_timeout / 1000);
-
- do {
- /* @TODO Is this check really needed? Won't qb_ipcc_sendv() return
- * an error if it's not connected?
- */
- if (!crm_ipc_connected(client)) {
- goto send_cleanup;
- }
-
- qb_rc = qb_ipcc_sendv(client->ipc, iov, 2);
- } while ((qb_rc == -EAGAIN) && (time(NULL) < timeout));
-
- rc = (int) qb_rc; // Negative of system errno, or bytes sent
- if (qb_rc <= 0) {
- goto send_cleanup;
-
- } else if (is_not_set(flags, crm_ipc_client_response)) {
- crm_trace("Not waiting for reply to %s IPC request %d",
- client->name, header->qb.id);
- goto send_cleanup;
- }
-
- rc = internal_ipc_get_reply(client, header->qb.id, ms_timeout, &bytes);
- if (rc != pcmk_rc_ok) {
- /* We didn't get the reply in time, so disable future sends for now.
- * The only alternative would be to close the connection since we
- * don't know how to detect and discard out-of-sequence replies.
- *
- * @TODO Implement out-of-sequence detection
- */
- client->need_reply = TRUE;
- }
- rc = (int) bytes; // Negative system errno, or size of reply received
-
- } else {
- // No timeout, and client response needed
- do {
- qb_rc = qb_ipcc_sendv_recv(client->ipc, iov, 2, client->buffer,
- client->buf_size, -1);
- } while ((qb_rc == -EAGAIN) && crm_ipc_connected(client));
- rc = (int) qb_rc; // Negative system errno, or size of reply received
- }
-
- if (rc > 0) {
- struct crm_ipc_response_header *hdr = (struct crm_ipc_response_header *)(void*)client->buffer;
-
- crm_trace("Received %d-byte reply %d to %s IPC %d: %.100s",
- rc, hdr->qb.id, client->name, header->qb.id,
- crm_ipc_buffer(client));
-
- if (reply) {
- *reply = string2xml(crm_ipc_buffer(client));
- }
-
- } else {
- crm_trace("No reply to %s IPC %d: rc=%d",
- client->name, header->qb.id, rc);
- }
-
- send_cleanup:
- if (crm_ipc_connected(client) == FALSE) {
- crm_notice("Couldn't send %s IPC request %d: Connection closed "
- CRM_XS " rc=%d", client->name, header->qb.id, rc);
-
- } else if (rc == -ETIMEDOUT) {
- crm_warn("%s IPC request %d failed: %s after %dms " CRM_XS " rc=%d",
- client->name, header->qb.id, pcmk_strerror(rc), ms_timeout,
- rc);
- crm_write_blackbox(0, NULL);
-
- } else if (rc <= 0) {
- crm_warn("%s IPC request %d failed: %s " CRM_XS " rc=%d",
- client->name, header->qb.id,
- ((rc == 0)? "No bytes sent" : pcmk_strerror(rc)), rc);
- }
-
- pcmk_free_ipc_event(iov);
- return rc;
-}
-
-int
-crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid,
- pid_t *gotpid, uid_t *gotuid, gid_t *gotgid) {
- int ret = 0;
- pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
-#if defined(US_AUTH_PEERCRED_UCRED)
- struct ucred ucred;
- socklen_t ucred_len = sizeof(ucred);
-
- if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
- &ucred, &ucred_len)
- && ucred_len == sizeof(ucred)) {
- found_pid = ucred.pid; found_uid = ucred.uid; found_gid = ucred.gid;
-
-#elif defined(US_AUTH_PEERCRED_SOCKPEERCRED)
- struct sockpeercred sockpeercred;
- socklen_t sockpeercred_len = sizeof(sockpeercred);
-
- if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
- &sockpeercred, &sockpeercred_len)
- && sockpeercred_len == sizeof(sockpeercred_len)) {
- found_pid = sockpeercred.pid;
- found_uid = sockpeercred.uid; found_gid = sockpeercred.gid;
-
-#elif defined(US_AUTH_GETPEEREID)
- if (!getpeereid(sock, &found_uid, &found_gid)) {
- found_pid = PCMK__SPECIAL_PID; /* cannot obtain PID (FreeBSD) */
-
-#elif defined(US_AUTH_GETPEERUCRED)
- ucred_t *ucred;
- if (!getpeerucred(sock, &ucred)) {
- errno = 0;
- found_pid = ucred_getpid(ucred);
- found_uid = ucred_geteuid(ucred); found_gid = ucred_getegid(ucred);
- ret = -errno;
- ucred_free(ucred);
- if (ret) {
- return (ret < 0) ? ret : -pcmk_err_generic;
- }
-
-#else
-# error "No way to authenticate a Unix socket peer"
- errno = 0;
- if (0) {
-#endif
- if (gotpid != NULL) {
- *gotpid = found_pid;
- }
- if (gotuid != NULL) {
- *gotuid = found_uid;
- }
- if (gotgid != NULL) {
- *gotgid = found_gid;
- }
- ret = (found_uid == 0 || found_uid == refuid || found_gid == refgid);
- } else {
- ret = (errno > 0) ? -errno : -pcmk_err_generic;
- }
-
- return ret;
-}
-
-int
-pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid,
- gid_t refgid, pid_t *gotpid)
-{
- static char last_asked_name[PATH_MAX / 2] = ""; /* log spam prevention */
- int fd;
- int rc = pcmk_rc_ipc_unresponsive;
- int auth_rc = 0;
- int32_t qb_rc;
- pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
- qb_ipcc_connection_t *c;
-
- c = qb_ipcc_connect(name, 0);
- if (c == NULL) {
- crm_info("Could not connect to %s IPC: %s", name, strerror(errno));
- rc = pcmk_rc_ipc_unresponsive;
- goto bail;
- }
-
- qb_rc = qb_ipcc_fd_get(c, &fd);
- if (qb_rc != 0) {
- rc = (int) -qb_rc; // System errno
- crm_err("Could not get fd from %s IPC: %s " CRM_XS " rc=%d",
- name, pcmk_rc_str(rc), rc);
- goto bail;
- }
-
- auth_rc = crm_ipc_is_authentic_process(fd, refuid, refgid, &found_pid,
- &found_uid, &found_gid);
- if (auth_rc < 0) {
- rc = pcmk_legacy2rc(auth_rc);
- crm_err("Could not get peer credentials from %s IPC: %s "
- CRM_XS " rc=%d", name, pcmk_rc_str(rc), rc);
- goto bail;
- }
-
- if (gotpid != NULL) {
- *gotpid = found_pid;
- }
-
- if (auth_rc == 0) {
- crm_err("Daemon (IPC %s) effectively blocked with unauthorized"
- " process %lld (uid: %lld, gid: %lld)",
- name, (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
- (long long) found_uid, (long long) found_gid);
- rc = pcmk_rc_ipc_unauthorized;
- goto bail;
- }
-
- rc = pcmk_rc_ok;
- if ((found_uid != refuid || found_gid != refgid)
- && strncmp(last_asked_name, name, sizeof(last_asked_name))) {
- if ((found_uid == 0) && (refuid != 0)) {
- crm_warn("Daemon (IPC %s) runs as root, whereas the expected"
- " credentials are %lld:%lld, hazard of violating"
- " the least privilege principle",
- name, (long long) refuid, (long long) refgid);
- } else {
- crm_notice("Daemon (IPC %s) runs as %lld:%lld, whereas the"
- " expected credentials are %lld:%lld, which may"
- " mean a different set of privileges than expected",
- name, (long long) found_uid, (long long) found_gid,
- (long long) refuid, (long long) refgid);
- }
- memccpy(last_asked_name, name, '\0', sizeof(last_asked_name));
- }
-
-bail:
- if (c != NULL) {
- qb_ipcc_disconnect(c);
- }
- return rc;
-}
-
-
-/* Utils */
-
-xmlNode *
-create_hello_message(const char *uuid,
- const char *client_name, const char *major_version, const char *minor_version)
-{
- xmlNode *hello_node = NULL;
- xmlNode *hello = NULL;
-
- if (pcmk__str_empty(uuid) || pcmk__str_empty(client_name)
- || pcmk__str_empty(major_version) || pcmk__str_empty(minor_version)) {
- crm_err("Could not create IPC hello message from %s (UUID %s): "
- "missing information",
- client_name? client_name : "unknown client",
- uuid? uuid : "unknown");
- return NULL;
- }
-
- hello_node = create_xml_node(NULL, XML_TAG_OPTIONS);
- if (hello_node == NULL) {
- crm_err("Could not create IPC hello message from %s (UUID %s): "
- "Message data creation failed", client_name, uuid);
- return NULL;
- }
-
- crm_xml_add(hello_node, "major_version", major_version);
- crm_xml_add(hello_node, "minor_version", minor_version);
- crm_xml_add(hello_node, "client_name", client_name);
- crm_xml_add(hello_node, "client_uuid", uuid);
-
- hello = create_request(CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid);
- if (hello == NULL) {
- crm_err("Could not create IPC hello message from %s (UUID %s): "
- "Request creation failed", client_name, uuid);
- return NULL;
- }
- free_xml(hello_node);
-
- crm_trace("Created hello message from %s (UUID %s)", client_name, uuid);
- return hello;
-}
diff --git a/lib/common/ipc_client.c b/lib/common/ipc_client.c
new file mode 100644
index 0000000..7737588
--- /dev/null
+++ b/lib/common/ipc_client.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright 2004-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#if defined(US_AUTH_PEERCRED_UCRED) || defined(US_AUTH_PEERCRED_SOCKPEERCRED)
+# ifdef US_AUTH_PEERCRED_UCRED
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+# endif
+# include <sys/socket.h>
+#elif defined(US_AUTH_GETPEERUCRED)
+# include <ucred.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <bzlib.h>
+
+#include <crm/crm.h> /* indirectly: pcmk_err_generic */
+#include <crm/msg_xml.h>
+#include <crm/common/ipc.h>
+#include <crm/common/ipc_internal.h>
+#include "crmcommon_private.h"
+
+struct crm_ipc_s {
+ struct pollfd pfd;
+ unsigned int max_buf_size; // maximum bytes we can send or receive over IPC
+ unsigned int buf_size; // size of allocated buffer
+ int msg_size;
+ int need_reply;
+ char *buffer;
+ char *name;
+ qb_ipcc_connection_t *ipc;
+};
+
+crm_ipc_t *
+crm_ipc_new(const char *name, size_t max_size)
+{
+ crm_ipc_t *client = NULL;
+
+ client = calloc(1, sizeof(crm_ipc_t));
+
+ client->name = strdup(name);
+ client->buf_size = pcmk__ipc_buffer_size(max_size);
+ client->buffer = malloc(client->buf_size);
+
+ /* Clients initiating connection pick the max buf size */
+ client->max_buf_size = client->buf_size;
+
+ client->pfd.fd = -1;
+ client->pfd.events = POLLIN;
+ client->pfd.revents = 0;
+
+ return client;
+}
+
+/*!
+ * \brief Establish an IPC connection to a Pacemaker component
+ *
+ * \param[in] client Connection instance obtained from crm_ipc_new()
+ *
+ * \return TRUE on success, FALSE otherwise (in which case errno will be set;
+ * specifically, in case of discovering the remote side is not
+ * authentic, its value is set to ECONNABORTED).
+ */
+bool
+crm_ipc_connect(crm_ipc_t * client)
+{
+ uid_t cl_uid = 0;
+ gid_t cl_gid = 0;
+ pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
+ int rv;
+
+ client->need_reply = FALSE;
+ client->ipc = qb_ipcc_connect(client->name, client->buf_size);
+
+ if (client->ipc == NULL) {
+ crm_debug("Could not establish %s connection: %s (%d)", client->name, pcmk_strerror(errno), errno);
+ return FALSE;
+ }
+
+ client->pfd.fd = crm_ipc_get_fd(client);
+ if (client->pfd.fd < 0) {
+ rv = errno;
+ /* message already omitted */
+ crm_ipc_close(client);
+ errno = rv;
+ return FALSE;
+ }
+
+ rv = pcmk_daemon_user(&cl_uid, &cl_gid);
+ if (rv < 0) {
+ /* message already omitted */
+ crm_ipc_close(client);
+ errno = -rv;
+ return FALSE;
+ }
+
+ if (!(rv = crm_ipc_is_authentic_process(client->pfd.fd, cl_uid, cl_gid,
+ &found_pid, &found_uid,
+ &found_gid))) {
+ crm_err("Daemon (IPC %s) is not authentic:"
+ " process %lld (uid: %lld, gid: %lld)",
+ client->name, (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
+ (long long) found_uid, (long long) found_gid);
+ crm_ipc_close(client);
+ errno = ECONNABORTED;
+ return FALSE;
+
+ } else if (rv < 0) {
+ errno = -rv;
+ crm_perror(LOG_ERR, "Could not verify authenticity of daemon (IPC %s)",
+ client->name);
+ crm_ipc_close(client);
+ errno = -rv;
+ return FALSE;
+ }
+
+ qb_ipcc_context_set(client->ipc, client);
+
+#ifdef HAVE_IPCS_GET_BUFFER_SIZE
+ client->max_buf_size = qb_ipcc_get_buffer_size(client->ipc);
+ if (client->max_buf_size > client->buf_size) {
+ free(client->buffer);
+ client->buffer = calloc(1, client->max_buf_size);
+ client->buf_size = client->max_buf_size;
+ }
+#endif
+
+ return TRUE;
+}
+
+void
+crm_ipc_close(crm_ipc_t * client)
+{
+ if (client) {
+ crm_trace("Disconnecting %s IPC connection %p (%p)", client->name, client, client->ipc);
+
+ if (client->ipc) {
+ qb_ipcc_connection_t *ipc = client->ipc;
+
+ client->ipc = NULL;
+ qb_ipcc_disconnect(ipc);
+ }
+ }
+}
+
+void
+crm_ipc_destroy(crm_ipc_t * client)
+{
+ if (client) {
+ if (client->ipc && qb_ipcc_is_connected(client->ipc)) {
+ crm_notice("Destroying an active IPC connection to %s", client->name);
+ /* The next line is basically unsafe
+ *
+ * If this connection was attached to mainloop and mainloop is active,
+ * the 'disconnected' callback will end up back here and we'll end
+ * up free'ing the memory twice - something that can still happen
+ * even without this if we destroy a connection and it closes before
+ * we call exit
+ */
+ /* crm_ipc_close(client); */
+ }
+ crm_trace("Destroying IPC connection to %s: %p", client->name, client);
+ free(client->buffer);
+ free(client->name);
+ free(client);
+ }
+}
+
+int
+crm_ipc_get_fd(crm_ipc_t * client)
+{
+ int fd = 0;
+
+ if (client && client->ipc && (qb_ipcc_fd_get(client->ipc, &fd) == 0)) {
+ return fd;
+ }
+ errno = EINVAL;
+ crm_perror(LOG_ERR, "Could not obtain file IPC descriptor for %s",
+ (client? client->name : "unspecified client"));
+ return -errno;
+}
+
+bool
+crm_ipc_connected(crm_ipc_t * client)
+{
+ bool rc = FALSE;
+
+ if (client == NULL) {
+ crm_trace("No client");
+ return FALSE;
+
+ } else if (client->ipc == NULL) {
+ crm_trace("No connection");
+ return FALSE;
+
+ } else if (client->pfd.fd < 0) {
+ crm_trace("Bad descriptor");
+ return FALSE;
+ }
+
+ rc = qb_ipcc_is_connected(client->ipc);
+ if (rc == FALSE) {
+ client->pfd.fd = -EINVAL;
+ }
+ return rc;
+}
+
+/*!
+ * \brief Check whether an IPC connection is ready to be read
+ *
+ * \param[in] client Connection to check
+ *
+ * \return Positive value if ready to be read, 0 if not ready, -errno on error
+ */
+int
+crm_ipc_ready(crm_ipc_t *client)
+{
+ int rc;
+
+ CRM_ASSERT(client != NULL);
+
+ if (crm_ipc_connected(client) == FALSE) {
+ return -ENOTCONN;
+ }
+
+ client->pfd.revents = 0;
+ rc = poll(&(client->pfd), 1, 0);
+ return (rc < 0)? -errno : rc;
+}
+
+// \return Standard Pacemaker return code
+static int
+crm_ipc_decompress(crm_ipc_t * client)
+{
+ pcmk__ipc_header_t *header = (pcmk__ipc_header_t *)(void*)client->buffer;
+
+ if (header->size_compressed) {
+ int rc = 0;
+ unsigned int size_u = 1 + header->size_uncompressed;
+ /* never let buf size fall below our max size required for ipc reads. */
+ unsigned int new_buf_size = QB_MAX((sizeof(pcmk__ipc_header_t) + size_u), client->max_buf_size);
+ char *uncompressed = calloc(1, new_buf_size);
+
+ crm_trace("Decompressing message data %u bytes into %u bytes",
+ header->size_compressed, size_u);
+
+ rc = BZ2_bzBuffToBuffDecompress(uncompressed + sizeof(pcmk__ipc_header_t), &size_u,
+ client->buffer + sizeof(pcmk__ipc_header_t), header->size_compressed, 1, 0);
+
+ if (rc != BZ_OK) {
+ crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
+ bz2_strerror(rc), rc);
+ free(uncompressed);
+ return EILSEQ;
+ }
+
+ /*
+ * This assert no longer holds true. For an identical msg, some clients may
+ * require compression, and others may not. If that same msg (event) is sent
+ * to multiple clients, it could result in some clients receiving a compressed
+ * msg even though compression was not explicitly required for them.
+ *
+ * CRM_ASSERT((header->size_uncompressed + sizeof(pcmk__ipc_header_t)) >= ipc_buffer_max);
+ */
+ CRM_ASSERT(size_u == header->size_uncompressed);
+
+ memcpy(uncompressed, client->buffer, sizeof(pcmk__ipc_header_t)); /* Preserve the header */
+ header = (pcmk__ipc_header_t *)(void*)uncompressed;
+
+ free(client->buffer);
+ client->buf_size = new_buf_size;
+ client->buffer = uncompressed;
+ }
+
+ CRM_ASSERT(client->buffer[sizeof(pcmk__ipc_header_t) + header->size_uncompressed - 1] == 0);
+ return pcmk_rc_ok;
+}
+
+long
+crm_ipc_read(crm_ipc_t * client)
+{
+ pcmk__ipc_header_t *header = NULL;
+
+ CRM_ASSERT(client != NULL);
+ CRM_ASSERT(client->ipc != NULL);
+ CRM_ASSERT(client->buffer != NULL);
+
+ client->buffer[0] = 0;
+ client->msg_size = qb_ipcc_event_recv(client->ipc, client->buffer,
+ client->buf_size, 0);
+ if (client->msg_size >= 0) {
+ int rc = crm_ipc_decompress(client);
+
+ if (rc != pcmk_rc_ok) {
+ return pcmk_rc2legacy(rc);
+ }
+
+ header = (pcmk__ipc_header_t *)(void*)client->buffer;
+ if (!pcmk__valid_ipc_header(header)) {
+ return -EBADMSG;
+ }
+
+ crm_trace("Received %s event %d, size=%u, rc=%d, text: %.100s",
+ client->name, header->qb.id, header->qb.size, client->msg_size,
+ client->buffer + sizeof(pcmk__ipc_header_t));
+
+ } else {
+ crm_trace("No message from %s received: %s", client->name, pcmk_strerror(client->msg_size));
+ }
+
+ if (crm_ipc_connected(client) == FALSE || client->msg_size == -ENOTCONN) {
+ crm_err("Connection to %s failed", client->name);
+ }
+
+ if (header) {
+ /* Data excluding the header */
+ return header->size_uncompressed;
+ }
+ return -ENOMSG;
+}
+
+const char *
+crm_ipc_buffer(crm_ipc_t * client)
+{
+ CRM_ASSERT(client != NULL);
+ return client->buffer + sizeof(pcmk__ipc_header_t);
+}
+
+uint32_t
+crm_ipc_buffer_flags(crm_ipc_t * client)
+{
+ pcmk__ipc_header_t *header = NULL;
+
+ CRM_ASSERT(client != NULL);
+ if (client->buffer == NULL) {
+ return 0;
+ }
+
+ header = (pcmk__ipc_header_t *)(void*)client->buffer;
+ return header->flags;
+}
+
+const char *
+crm_ipc_name(crm_ipc_t * client)
+{
+ CRM_ASSERT(client != NULL);
+ return client->name;
+}
+
+// \return Standard Pacemaker return code
+static int
+internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout,
+ ssize_t *bytes)
+{
+ time_t timeout = time(NULL) + 1 + (ms_timeout / 1000);
+ int rc = pcmk_rc_ok;
+
+ /* get the reply */
+ crm_trace("client %s waiting on reply to msg id %d", client->name, request_id);
+ do {
+
+ *bytes = qb_ipcc_recv(client->ipc, client->buffer, client->buf_size, 1000);
+ if (*bytes > 0) {
+ pcmk__ipc_header_t *hdr = NULL;
+
+ rc = crm_ipc_decompress(client);
+ if (rc != pcmk_rc_ok) {
+ return rc;
+ }
+
+ hdr = (pcmk__ipc_header_t *)(void*)client->buffer;
+ if (hdr->qb.id == request_id) {
+ /* Got it */
+ break;
+ } else if (hdr->qb.id < request_id) {
+ xmlNode *bad = string2xml(crm_ipc_buffer(client));
+
+ crm_err("Discarding old reply %d (need %d)", hdr->qb.id, request_id);
+ crm_log_xml_notice(bad, "OldIpcReply");
+
+ } else {
+ xmlNode *bad = string2xml(crm_ipc_buffer(client));
+
+ crm_err("Discarding newer reply %d (need %d)", hdr->qb.id, request_id);
+ crm_log_xml_notice(bad, "ImpossibleReply");
+ CRM_ASSERT(hdr->qb.id <= request_id);
+ }
+ } else if (crm_ipc_connected(client) == FALSE) {
+ crm_err("Server disconnected client %s while waiting for msg id %d", client->name,
+ request_id);
+ break;
+ }
+
+ } while (time(NULL) < timeout);
+
+ if (*bytes < 0) {
+ rc = (int) -*bytes; // System errno
+ }
+ return rc;
+}
+
+/*!
+ * \brief Send an IPC XML message
+ *
+ * \param[in] client Connection to IPC server
+ * \param[in] message XML message to send
+ * \param[in] flags Bitmask of crm_ipc_flags
+ * \param[in] ms_timeout Give up if not sent within this much time
+ * (5 seconds if 0, or no timeout if negative)
+ * \param[out] reply Reply from server (or NULL if none)
+ *
+ * \return Negative errno on error, otherwise size of reply received in bytes
+ * if reply was needed, otherwise number of bytes sent
+ */
+int
+crm_ipc_send(crm_ipc_t * client, xmlNode * message, enum crm_ipc_flags flags, int32_t ms_timeout,
+ xmlNode ** reply)
+{
+ int rc = 0;
+ ssize_t qb_rc = 0;
+ ssize_t bytes = 0;
+ struct iovec *iov;
+ static uint32_t id = 0;
+ static int factor = 8;
+ pcmk__ipc_header_t *header;
+
+ if (client == NULL) {
+ crm_notice("Can't send IPC request without connection (bug?): %.100s",
+ message);
+ return -ENOTCONN;
+
+ } else if (crm_ipc_connected(client) == FALSE) {
+ /* Don't even bother */
+ crm_notice("Can't send IPC request to %s: Connection closed",
+ client->name);
+ return -ENOTCONN;
+ }
+
+ if (ms_timeout == 0) {
+ ms_timeout = 5000;
+ }
+
+ if (client->need_reply) {
+ qb_rc = qb_ipcc_recv(client->ipc, client->buffer, client->buf_size, ms_timeout);
+ if (qb_rc < 0) {
+ crm_warn("Sending IPC to %s disabled until pending reply received",
+ client->name);
+ return -EALREADY;
+
+ } else {
+ crm_notice("Sending IPC to %s re-enabled after pending reply received",
+ client->name);
+ client->need_reply = FALSE;
+ }
+ }
+
+ id++;
+ CRM_LOG_ASSERT(id != 0); /* Crude wrap-around detection */
+ rc = pcmk__ipc_prepare_iov(id, message, client->max_buf_size, &iov, &bytes);
+ if (rc != pcmk_rc_ok) {
+ crm_warn("Couldn't prepare IPC request to %s: %s " CRM_XS " rc=%d",
+ client->name, pcmk_rc_str(rc), rc);
+ return pcmk_rc2legacy(rc);
+ }
+
+ header = iov[0].iov_base;
+ header->flags |= flags;
+
+ if(is_set(flags, crm_ipc_proxied)) {
+ /* Don't look for a synchronous response */
+ clear_bit(flags, crm_ipc_client_response);
+ }
+
+ if(header->size_compressed) {
+ if(factor < 10 && (client->max_buf_size / 10) < (bytes / factor)) {
+ crm_notice("Compressed message exceeds %d0%% of configured IPC "
+ "limit (%u bytes); consider setting PCMK_ipc_buffer to "
+ "%u or higher",
+ factor, client->max_buf_size, 2 * client->max_buf_size);
+ factor++;
+ }
+ }
+
+ crm_trace("Sending %s IPC request %d of %u bytes using %dms timeout",
+ client->name, header->qb.id, header->qb.size, ms_timeout);
+
+ if (ms_timeout > 0 || is_not_set(flags, crm_ipc_client_response)) {
+
+ time_t timeout = time(NULL) + 1 + (ms_timeout / 1000);
+
+ do {
+ /* @TODO Is this check really needed? Won't qb_ipcc_sendv() return
+ * an error if it's not connected?
+ */
+ if (!crm_ipc_connected(client)) {
+ goto send_cleanup;
+ }
+
+ qb_rc = qb_ipcc_sendv(client->ipc, iov, 2);
+ } while ((qb_rc == -EAGAIN) && (time(NULL) < timeout));
+
+ rc = (int) qb_rc; // Negative of system errno, or bytes sent
+ if (qb_rc <= 0) {
+ goto send_cleanup;
+
+ } else if (is_not_set(flags, crm_ipc_client_response)) {
+ crm_trace("Not waiting for reply to %s IPC request %d",
+ client->name, header->qb.id);
+ goto send_cleanup;
+ }
+
+ rc = internal_ipc_get_reply(client, header->qb.id, ms_timeout, &bytes);
+ if (rc != pcmk_rc_ok) {
+ /* We didn't get the reply in time, so disable future sends for now.
+ * The only alternative would be to close the connection since we
+ * don't know how to detect and discard out-of-sequence replies.
+ *
+ * @TODO Implement out-of-sequence detection
+ */
+ client->need_reply = TRUE;
+ }
+ rc = (int) bytes; // Negative system errno, or size of reply received
+
+ } else {
+ // No timeout, and client response needed
+ do {
+ qb_rc = qb_ipcc_sendv_recv(client->ipc, iov, 2, client->buffer,
+ client->buf_size, -1);
+ } while ((qb_rc == -EAGAIN) && crm_ipc_connected(client));
+ rc = (int) qb_rc; // Negative system errno, or size of reply received
+ }
+
+ if (rc > 0) {
+ pcmk__ipc_header_t *hdr = (pcmk__ipc_header_t *)(void*)client->buffer;
+
+ crm_trace("Received %d-byte reply %d to %s IPC %d: %.100s",
+ rc, hdr->qb.id, client->name, header->qb.id,
+ crm_ipc_buffer(client));
+
+ if (reply) {
+ *reply = string2xml(crm_ipc_buffer(client));
+ }
+
+ } else {
+ crm_trace("No reply to %s IPC %d: rc=%d",
+ client->name, header->qb.id, rc);
+ }
+
+ send_cleanup:
+ if (crm_ipc_connected(client) == FALSE) {
+ crm_notice("Couldn't send %s IPC request %d: Connection closed "
+ CRM_XS " rc=%d", client->name, header->qb.id, rc);
+
+ } else if (rc == -ETIMEDOUT) {
+ crm_warn("%s IPC request %d failed: %s after %dms " CRM_XS " rc=%d",
+ client->name, header->qb.id, pcmk_strerror(rc), ms_timeout,
+ rc);
+ crm_write_blackbox(0, NULL);
+
+ } else if (rc <= 0) {
+ crm_warn("%s IPC request %d failed: %s " CRM_XS " rc=%d",
+ client->name, header->qb.id,
+ ((rc == 0)? "No bytes sent" : pcmk_strerror(rc)), rc);
+ }
+
+ pcmk_free_ipc_event(iov);
+ return rc;
+}
+
+int
+crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid,
+ pid_t *gotpid, uid_t *gotuid, gid_t *gotgid) {
+ int ret = 0;
+ pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
+#if defined(US_AUTH_PEERCRED_UCRED)
+ struct ucred ucred;
+ socklen_t ucred_len = sizeof(ucred);
+
+ if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
+ &ucred, &ucred_len)
+ && ucred_len == sizeof(ucred)) {
+ found_pid = ucred.pid; found_uid = ucred.uid; found_gid = ucred.gid;
+
+#elif defined(US_AUTH_PEERCRED_SOCKPEERCRED)
+ struct sockpeercred sockpeercred;
+ socklen_t sockpeercred_len = sizeof(sockpeercred);
+
+ if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
+ &sockpeercred, &sockpeercred_len)
+ && sockpeercred_len == sizeof(sockpeercred_len)) {
+ found_pid = sockpeercred.pid;
+ found_uid = sockpeercred.uid; found_gid = sockpeercred.gid;
+
+#elif defined(US_AUTH_GETPEEREID)
+ if (!getpeereid(sock, &found_uid, &found_gid)) {
+ found_pid = PCMK__SPECIAL_PID; /* cannot obtain PID (FreeBSD) */
+
+#elif defined(US_AUTH_GETPEERUCRED)
+ ucred_t *ucred;
+ if (!getpeerucred(sock, &ucred)) {
+ errno = 0;
+ found_pid = ucred_getpid(ucred);
+ found_uid = ucred_geteuid(ucred); found_gid = ucred_getegid(ucred);
+ ret = -errno;
+ ucred_free(ucred);
+ if (ret) {
+ return (ret < 0) ? ret : -pcmk_err_generic;
+ }
+
+#else
+# error "No way to authenticate a Unix socket peer"
+ errno = 0;
+ if (0) {
+#endif
+ if (gotpid != NULL) {
+ *gotpid = found_pid;
+ }
+ if (gotuid != NULL) {
+ *gotuid = found_uid;
+ }
+ if (gotgid != NULL) {
+ *gotgid = found_gid;
+ }
+ ret = (found_uid == 0 || found_uid == refuid || found_gid == refgid);
+ } else {
+ ret = (errno > 0) ? -errno : -pcmk_err_generic;
+ }
+
+ return ret;
+}
+
+int
+pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid,
+ gid_t refgid, pid_t *gotpid)
+{
+ static char last_asked_name[PATH_MAX / 2] = ""; /* log spam prevention */
+ int fd;
+ int rc = pcmk_rc_ipc_unresponsive;
+ int auth_rc = 0;
+ int32_t qb_rc;
+ pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
+ qb_ipcc_connection_t *c;
+
+ c = qb_ipcc_connect(name, 0);
+ if (c == NULL) {
+ crm_info("Could not connect to %s IPC: %s", name, strerror(errno));
+ rc = pcmk_rc_ipc_unresponsive;
+ goto bail;
+ }
+
+ qb_rc = qb_ipcc_fd_get(c, &fd);
+ if (qb_rc != 0) {
+ rc = (int) -qb_rc; // System errno
+ crm_err("Could not get fd from %s IPC: %s " CRM_XS " rc=%d",
+ name, pcmk_rc_str(rc), rc);
+ goto bail;
+ }
+
+ auth_rc = crm_ipc_is_authentic_process(fd, refuid, refgid, &found_pid,
+ &found_uid, &found_gid);
+ if (auth_rc < 0) {
+ rc = pcmk_legacy2rc(auth_rc);
+ crm_err("Could not get peer credentials from %s IPC: %s "
+ CRM_XS " rc=%d", name, pcmk_rc_str(rc), rc);
+ goto bail;
+ }
+
+ if (gotpid != NULL) {
+ *gotpid = found_pid;
+ }
+
+ if (auth_rc == 0) {
+ crm_err("Daemon (IPC %s) effectively blocked with unauthorized"
+ " process %lld (uid: %lld, gid: %lld)",
+ name, (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
+ (long long) found_uid, (long long) found_gid);
+ rc = pcmk_rc_ipc_unauthorized;
+ goto bail;
+ }
+
+ rc = pcmk_rc_ok;
+ if ((found_uid != refuid || found_gid != refgid)
+ && strncmp(last_asked_name, name, sizeof(last_asked_name))) {
+ if ((found_uid == 0) && (refuid != 0)) {
+ crm_warn("Daemon (IPC %s) runs as root, whereas the expected"
+ " credentials are %lld:%lld, hazard of violating"
+ " the least privilege principle",
+ name, (long long) refuid, (long long) refgid);
+ } else {
+ crm_notice("Daemon (IPC %s) runs as %lld:%lld, whereas the"
+ " expected credentials are %lld:%lld, which may"
+ " mean a different set of privileges than expected",
+ name, (long long) found_uid, (long long) found_gid,
+ (long long) refuid, (long long) refgid);
+ }
+ memccpy(last_asked_name, name, '\0', sizeof(last_asked_name));
+ }
+
+bail:
+ if (c != NULL) {
+ qb_ipcc_disconnect(c);
+ }
+ return rc;
+}
+
+xmlNode *
+create_hello_message(const char *uuid,
+ const char *client_name, const char *major_version, const char *minor_version)
+{
+ xmlNode *hello_node = NULL;
+ xmlNode *hello = NULL;
+
+ if (pcmk__str_empty(uuid) || pcmk__str_empty(client_name)
+ || pcmk__str_empty(major_version) || pcmk__str_empty(minor_version)) {
+ crm_err("Could not create IPC hello message from %s (UUID %s): "
+ "missing information",
+ client_name? client_name : "unknown client",
+ uuid? uuid : "unknown");
+ return NULL;
+ }
+
+ hello_node = create_xml_node(NULL, XML_TAG_OPTIONS);
+ if (hello_node == NULL) {
+ crm_err("Could not create IPC hello message from %s (UUID %s): "
+ "Message data creation failed", client_name, uuid);
+ return NULL;
+ }
+
+ crm_xml_add(hello_node, "major_version", major_version);
+ crm_xml_add(hello_node, "minor_version", minor_version);
+ crm_xml_add(hello_node, "client_name", client_name);
+ crm_xml_add(hello_node, "client_uuid", uuid);
+
+ hello = create_request(CRM_OP_HELLO, hello_node, NULL, NULL, client_name, uuid);
+ if (hello == NULL) {
+ crm_err("Could not create IPC hello message from %s (UUID %s): "
+ "Request creation failed", client_name, uuid);
+ return NULL;
+ }
+ free_xml(hello_node);
+
+ crm_trace("Created hello message from %s (UUID %s)", client_name, uuid);
+ return hello;
+}
diff --git a/lib/common/ipc_common.c b/lib/common/ipc_common.c
new file mode 100644
index 0000000..78360aa
--- /dev/null
+++ b/lib/common/ipc_common.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2004-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <crm/msg_xml.h>
+#include "crmcommon_private.h"
+
+#define MIN_MSG_SIZE 12336 // sizeof(struct qb_ipc_connection_response)
+#define MAX_MSG_SIZE 128*1024 // 128k default
+
+/*!
+ * \internal
+ * \brief Choose an IPC buffer size in bytes
+ *
+ * \param[in] max Use this value if environment/default is lower
+ *
+ * \return Maximum of max and value of PCMK_ipc_buffer (default 128KB)
+ */
+unsigned int
+pcmk__ipc_buffer_size(unsigned int max)
+{
+ static unsigned int global_max = 0;
+
+ if (global_max == 0) {
+ const char *env = getenv("PCMK_ipc_buffer");
+
+ if (env) {
+ int env_max = crm_parse_int(env, "0");
+
+ global_max = (env_max > 0)? QB_MAX(MIN_MSG_SIZE, env_max) : MAX_MSG_SIZE;
+
+ } else {
+ global_max = MAX_MSG_SIZE;
+ }
+ }
+ return QB_MAX(max, global_max);
+}
+
+/*!
+ * \brief Return pacemaker's default IPC buffer size
+ *
+ * \return IPC buffer size in bytes
+ */
+unsigned int
+crm_ipc_default_buffer_size(void)
+{
+ static unsigned int default_size = 0;
+
+ if (default_size == 0) {
+ default_size = pcmk__ipc_buffer_size(0);
+ }
+ return default_size;
+}
+
+/*!
+ * \internal
+ * \brief Check whether an IPC header is valid
+ *
+ * \param[in] header IPC header to check
+ *
+ * \return true if IPC header has a supported version, false otherwise
+ */
+bool
+pcmk__valid_ipc_header(const pcmk__ipc_header_t *header)
+{
+ if (header == NULL) {
+ crm_err("IPC message without header");
+ return false;
+
+ } else if (header->version > PCMK__IPC_VERSION) {
+ crm_err("Filtering incompatible v%d IPC message (only versions <= %d supported)",
+ header->version, PCMK__IPC_VERSION);
+ return false;
+ }
+ return true;
+}
+
+const char *
+pcmk__client_type_str(enum pcmk__client_type client_type)
+{
+ switch (client_type) {
+ case PCMK__CLIENT_IPC:
+ return "IPC";
+ case PCMK__CLIENT_TCP:
+ return "TCP";
+#ifdef HAVE_GNUTLS_GNUTLS_H
+ case PCMK__CLIENT_TLS:
+ return "TLS";
+#endif
+ default:
+ return "unknown";
+ }
+}
diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c
new file mode 100644
index 0000000..b747be3
--- /dev/null
+++ b/lib/common/ipc_server.c
@@ -0,0 +1,903 @@
+/*
+ * Copyright 2004-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <bzlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <crm/crm.h>
+#include <crm/msg_xml.h>
+#include <crm/common/ipc.h>
+#include <crm/common/ipc_internal.h>
+#include "crmcommon_private.h"
+
+/* Evict clients whose event queue grows this large (by default) */
+#define PCMK_IPC_DEFAULT_QUEUE_MAX 500
+
+static GHashTable *client_connections = NULL;
+
+/*!
+ * \internal
+ * \brief Count IPC clients
+ *
+ * \return Number of active IPC client connections
+ */
+guint
+pcmk__ipc_client_count()
+{
+ return client_connections? g_hash_table_size(client_connections) : 0;
+}
+
+/*!
+ * \internal
+ * \brief Execute a function for each active IPC client connection
+ *
+ * \param[in] func Function to call
+ * \param[in] user_data Pointer to pass to function
+ *
+ * \note The parameters are the same as for g_hash_table_foreach().
+ */
+void
+pcmk__foreach_ipc_client(GHFunc func, gpointer user_data)
+{
+ if ((func != NULL) && (client_connections != NULL)) {
+ g_hash_table_foreach(client_connections, func, user_data);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Remote IPC clients based on iterative function result
+ *
+ * \param[in] func Function to call for each active IPC client
+ * \param[in] user_data Pointer to pass to function
+ *
+ * \note The parameters are the same as for g_hash_table_foreach_remove().
+ */
+void
+pcmk__foreach_ipc_client_remove(GHRFunc func, gpointer user_data)
+{
+ if ((func != NULL) && (client_connections != NULL)) {
+ g_hash_table_foreach_remove(client_connections, func, user_data);
+ }
+}
+
+pcmk__client_t *
+pcmk__find_client(qb_ipcs_connection_t *c)
+{
+ if (client_connections) {
+ return g_hash_table_lookup(client_connections, c);
+ }
+
+ crm_trace("No client found for %p", c);
+ return NULL;
+}
+
+pcmk__client_t *
+pcmk__find_client_by_id(const char *id)
+{
+ gpointer key;
+ pcmk__client_t *client;
+ GHashTableIter iter;
+
+ if (client_connections && id) {
+ g_hash_table_iter_init(&iter, client_connections);
+ while (g_hash_table_iter_next(&iter, &key, (gpointer *) & client)) {
+ if (strcmp(client->id, id) == 0) {
+ return client;
+ }
+ }
+ }
+
+ crm_trace("No client found with id=%s", id);
+ return NULL;
+}
+
+const char *
+pcmk__client_name(pcmk__client_t *c)
+{
+ if (c == NULL) {
+ return "null";
+ } else if (c->name == NULL && c->id == NULL) {
+ return "unknown";
+ } else if (c->name == NULL) {
+ return c->id;
+ } else {
+ return c->name;
+ }
+}
+
+void
+pcmk__client_cleanup(void)
+{
+ if (client_connections != NULL) {
+ int active = g_hash_table_size(client_connections);
+
+ if (active) {
+ crm_err("Exiting with %d active IPC client%s",
+ active, pcmk__plural_s(active));
+ }
+ g_hash_table_destroy(client_connections); client_connections = NULL;
+ }
+}
+
+void
+pcmk__drop_all_clients(qb_ipcs_service_t *service)
+{
+ qb_ipcs_connection_t *c = NULL;
+
+ if (service == NULL) {
+ return;
+ }
+
+ c = qb_ipcs_connection_first_get(service);
+
+ while (c != NULL) {
+ qb_ipcs_connection_t *last = c;
+
+ c = qb_ipcs_connection_next_get(service, last);
+
+ /* There really shouldn't be anyone connected at this point */
+ crm_notice("Disconnecting client %p, pid=%d...",
+ last, pcmk__client_pid(last));
+ qb_ipcs_disconnect(last);
+ qb_ipcs_connection_unref(last);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Allocate a new pcmk__client_t object based on an IPC connection
+ *
+ * \param[in] c IPC connection (or NULL to allocate generic client)
+ * \param[in] key Connection table key (or NULL to use sane default)
+ * \param[in] uid_client UID corresponding to c (ignored if c is NULL)
+ *
+ * \return Pointer to new pcmk__client_t (or NULL on error)
+ */
+static pcmk__client_t *
+client_from_connection(qb_ipcs_connection_t *c, void *key, uid_t uid_client)
+{
+ pcmk__client_t *client = calloc(1, sizeof(pcmk__client_t));
+
+ if (client == NULL) {
+ crm_perror(LOG_ERR, "Allocating client");
+ return NULL;
+ }
+
+ if (c) {
+#if ENABLE_ACL
+ client->user = pcmk__uid2username(uid_client);
+ if (client->user == NULL) {
+ client->user = strdup("#unprivileged");
+ CRM_CHECK(client->user != NULL, free(client); return NULL);
+ crm_err("Unable to enforce ACLs for user ID %d, assuming unprivileged",
+ uid_client);
+ }
+#endif
+ client->ipcs = c;
+ client->kind = PCMK__CLIENT_IPC;
+ client->pid = pcmk__client_pid(c);
+ if (key == NULL) {
+ key = c;
+ }
+ }
+
+ client->id = crm_generate_uuid();
+ if (client->id == NULL) {
+ crm_err("Could not generate UUID for client");
+ free(client->user);
+ free(client);
+ return NULL;
+ }
+ if (key == NULL) {
+ key = client->id;
+ }
+ if (client_connections == NULL) {
+ crm_trace("Creating IPC client table");
+ client_connections = g_hash_table_new(g_direct_hash, g_direct_equal);
+ }
+ g_hash_table_insert(client_connections, key, client);
+ return client;
+}
+
+/*!
+ * \brief Allocate a new pcmk__client_t object and generate its ID
+ *
+ * \param[in] key What to use as connections hash table key (NULL to use ID)
+ *
+ * \return Pointer to new pcmk__client_t (asserts on failure)
+ */
+pcmk__client_t *
+pcmk__new_unauth_client(void *key)
+{
+ pcmk__client_t *client = client_from_connection(NULL, key, 0);
+
+ CRM_ASSERT(client != NULL);
+ return client;
+}
+
+pcmk__client_t *
+pcmk__new_client(qb_ipcs_connection_t *c, uid_t uid_client, gid_t gid_client)
+{
+ gid_t uid_cluster = 0;
+ gid_t gid_cluster = 0;
+
+ pcmk__client_t *client = NULL;
+
+ CRM_CHECK(c != NULL, return NULL);
+
+ if (pcmk_daemon_user(&uid_cluster, &gid_cluster) < 0) {
+ static bool need_log = TRUE;
+
+ if (need_log) {
+ crm_warn("Could not find user and group IDs for user %s",
+ CRM_DAEMON_USER);
+ need_log = FALSE;
+ }
+ }
+
+ if (uid_client != 0) {
+ crm_trace("Giving group %u access to new IPC connection", gid_cluster);
+ /* Passing -1 to chown(2) means don't change */
+ qb_ipcs_connection_auth_set(c, -1, gid_cluster, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ }
+
+ /* TODO: Do our own auth checking, return NULL if unauthorized */
+ client = client_from_connection(c, NULL, uid_client);
+ if (client == NULL) {
+ return NULL;
+ }
+
+ if ((uid_client == 0) || (uid_client == uid_cluster)) {
+ /* Remember when a connection came from root or hacluster */
+ set_bit(client->flags, pcmk__client_privileged);
+ }
+
+ crm_debug("New IPC client %s for PID %u with uid %d and gid %d",
+ client->id, client->pid, uid_client, gid_client);
+ return client;
+}
+
+static struct iovec *
+pcmk__new_ipc_event(void)
+{
+ struct iovec *iov = calloc(2, sizeof(struct iovec));
+
+ CRM_ASSERT(iov != NULL);
+ return iov;
+}
+
+/*!
+ * \brief Free an I/O vector created by pcmk__ipc_prepare_iov()
+ *
+ * \param[in] event I/O vector to free
+ */
+void
+pcmk_free_ipc_event(struct iovec *event)
+{
+ if (event != NULL) {
+ free(event[0].iov_base);
+ free(event[1].iov_base);
+ free(event);
+ }
+}
+
+static void
+free_event(gpointer data)
+{
+ pcmk_free_ipc_event((struct iovec *) data);
+}
+
+static void
+add_event(pcmk__client_t *c, struct iovec *iov)
+{
+ if (c->event_queue == NULL) {
+ c->event_queue = g_queue_new();
+ }
+ g_queue_push_tail(c->event_queue, iov);
+}
+
+void
+pcmk__free_client(pcmk__client_t *c)
+{
+ if (c == NULL) {
+ return;
+ }
+
+ if (client_connections) {
+ if (c->ipcs) {
+ crm_trace("Destroying %p/%p (%d remaining)",
+ c, c->ipcs, g_hash_table_size(client_connections) - 1);
+ g_hash_table_remove(client_connections, c->ipcs);
+
+ } else {
+ crm_trace("Destroying remote connection %p (%d remaining)",
+ c, g_hash_table_size(client_connections) - 1);
+ g_hash_table_remove(client_connections, c->id);
+ }
+ }
+
+ if (c->event_timer) {
+ g_source_remove(c->event_timer);
+ }
+
+ if (c->event_queue) {
+ crm_debug("Destroying %d events", g_queue_get_length(c->event_queue));
+ g_queue_free_full(c->event_queue, free_event);
+ }
+
+ free(c->id);
+ free(c->name);
+ free(c->user);
+ if (c->remote) {
+ if (c->remote->auth_timeout) {
+ g_source_remove(c->remote->auth_timeout);
+ }
+ free(c->remote->buffer);
+ free(c->remote);
+ }
+ free(c);
+}
+
+/*!
+ * \internal
+ * \brief Raise IPC eviction threshold for a client, if allowed
+ *
+ * \param[in,out] client Client to modify
+ * \param[in] qmax New threshold (as non-NULL string)
+ *
+ * \return TRUE if change was allowed, FALSE otherwise
+ */
+bool
+pcmk__set_client_queue_max(pcmk__client_t *client, const char *qmax)
+{
+ if (is_set(client->flags, pcmk__client_privileged)) {
+ long long qmax_int;
+
+ errno = 0;
+ qmax_int = crm_parse_ll(qmax, NULL);
+ if ((errno == 0) && (qmax_int > 0)) {
+ client->queue_max = (unsigned int) qmax_int;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+int
+pcmk__client_pid(qb_ipcs_connection_t *c)
+{
+ struct qb_ipcs_connection_stats stats;
+
+ stats.client_pid = 0;
+ qb_ipcs_connection_stats_get(c, &stats, 0);
+ return stats.client_pid;
+}
+
+/*!
+ * \internal
+ * \brief Retrieve message XML from data read from client IPC
+ *
+ * \param[in] c IPC client connection
+ * \param[in] data Data read from client connection
+ * \param[out] id Where to store message ID from libqb header
+ * \param[out] flags Where to store flags from libqb header
+ *
+ * \return Message XML on success, NULL otherwise
+ */
+xmlNode *
+pcmk__client_data2xml(pcmk__client_t *c, void *data, uint32_t *id,
+ uint32_t *flags)
+{
+ xmlNode *xml = NULL;
+ char *uncompressed = NULL;
+ char *text = ((char *)data) + sizeof(pcmk__ipc_header_t);
+ pcmk__ipc_header_t *header = data;
+
+ if (!pcmk__valid_ipc_header(header)) {
+ return NULL;
+ }
+
+ if (id) {
+ *id = ((struct qb_ipc_response_header *)data)->id;
+ }
+ if (flags) {
+ *flags = header->flags;
+ }
+
+ if (is_set(header->flags, crm_ipc_proxied)) {
+ /* Mark this client as being the endpoint of a proxy connection.
+ * Proxy connections responses are sent on the event channel, to avoid
+ * blocking the controller serving as proxy.
+ */
+ c->flags |= pcmk__client_proxied;
+ }
+
+ if (header->size_compressed) {
+ int rc = 0;
+ unsigned int size_u = 1 + header->size_uncompressed;
+ uncompressed = calloc(1, size_u);
+
+ crm_trace("Decompressing message data %u bytes into %u bytes",
+ header->size_compressed, size_u);
+
+ rc = BZ2_bzBuffToBuffDecompress(uncompressed, &size_u, text, header->size_compressed, 1, 0);
+ text = uncompressed;
+
+ if (rc != BZ_OK) {
+ crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
+ bz2_strerror(rc), rc);
+ free(uncompressed);
+ return NULL;
+ }
+ }
+
+ CRM_ASSERT(text[header->size_uncompressed - 1] == 0);
+
+ xml = string2xml(text);
+ crm_log_xml_trace(xml, "[IPC received]");
+
+ free(uncompressed);
+ return xml;
+}
+
+static int crm_ipcs_flush_events(pcmk__client_t *c);
+
+static gboolean
+crm_ipcs_flush_events_cb(gpointer data)
+{
+ pcmk__client_t *c = data;
+
+ c->event_timer = 0;
+ crm_ipcs_flush_events(c);
+ return FALSE;
+}
+
+/*!
+ * \internal
+ * \brief Add progressive delay before next event queue flush
+ *
+ * \param[in,out] c Client connection to add delay to
+ * \param[in] queue_len Current event queue length
+ */
+static inline void
+delay_next_flush(pcmk__client_t *c, unsigned int queue_len)
+{
+ /* Delay a maximum of 1.5 seconds */
+ guint delay = (queue_len < 5)? (1000 + 100 * queue_len) : 1500;
+
+ c->event_timer = g_timeout_add(delay, crm_ipcs_flush_events_cb, c);
+}
+
+/*!
+ * \internal
+ * \brief Send client any messages in its queue
+ *
+ * \param[in] c Client to flush
+ *
+ * \return Standard Pacemaker return value
+ */
+static int
+crm_ipcs_flush_events(pcmk__client_t *c)
+{
+ int rc = pcmk_rc_ok;
+ ssize_t qb_rc = 0;
+ unsigned int sent = 0;
+ unsigned int queue_len = 0;
+
+ if (c == NULL) {
+ return rc;
+
+ } else if (c->event_timer) {
+ /* There is already a timer, wait until it goes off */
+ crm_trace("Timer active for %p - %d", c->ipcs, c->event_timer);
+ return rc;
+ }
+
+ if (c->event_queue) {
+ queue_len = g_queue_get_length(c->event_queue);
+ }
+ while (sent < 100) {
+ pcmk__ipc_header_t *header = NULL;
+ struct iovec *event = NULL;
+
+ if (c->event_queue) {
+ // We don't pop unless send is successful
+ event = g_queue_peek_head(c->event_queue);
+ }
+ if (event == NULL) { // Queue is empty
+ break;
+ }
+
+ qb_rc = qb_ipcs_event_sendv(c->ipcs, event, 2);
+ if (qb_rc < 0) {
+ rc = (int) -qb_rc;
+ break;
+ }
+ event = g_queue_pop_head(c->event_queue);
+
+ sent++;
+ header = event[0].iov_base;
+ if (header->size_compressed) {
+ crm_trace("Event %d to %p[%d] (%lld compressed bytes) sent",
+ header->qb.id, c->ipcs, c->pid, (long long) qb_rc);
+ } else {
+ crm_trace("Event %d to %p[%d] (%lld bytes) sent: %.120s",
+ header->qb.id, c->ipcs, c->pid, (long long) qb_rc,
+ (char *) (event[1].iov_base));
+ }
+ pcmk_free_ipc_event(event);
+ }
+
+ queue_len -= sent;
+ if (sent > 0 || queue_len) {
+ crm_trace("Sent %d events (%d remaining) for %p[%d]: %s (%lld)",
+ sent, queue_len, c->ipcs, c->pid,
+ pcmk_rc_str(rc), (long long) qb_rc);
+ }
+
+ if (queue_len) {
+
+ /* Allow clients to briefly fall behind on processing incoming messages,
+ * but drop completely unresponsive clients so the connection doesn't
+ * consume resources indefinitely.
+ */
+ if (queue_len > QB_MAX(c->queue_max, PCMK_IPC_DEFAULT_QUEUE_MAX)) {
+ if ((c->queue_backlog <= 1) || (queue_len < c->queue_backlog)) {
+ /* Don't evict for a new or shrinking backlog */
+ crm_warn("Client with process ID %u has a backlog of %u messages "
+ CRM_XS " %p", c->pid, queue_len, c->ipcs);
+ } else {
+ crm_err("Evicting client with process ID %u due to backlog of %u messages "
+ CRM_XS " %p", c->pid, queue_len, c->ipcs);
+ c->queue_backlog = 0;
+ qb_ipcs_disconnect(c->ipcs);
+ return rc;
+ }
+ }
+
+ c->queue_backlog = queue_len;
+ delay_next_flush(c, queue_len);
+
+ } else {
+ /* Event queue is empty, there is no backlog */
+ c->queue_backlog = 0;
+ }
+
+ return rc;
+}
+
+/*!
+ * \internal
+ * \brief Create an I/O vector for sending an IPC XML message
+ *
+ * \param[in] request Identifier for libqb response header
+ * \param[in] message XML message to send
+ * \param[in] max_send_size If 0, default IPC buffer size is used
+ * \param[out] result Where to store prepared I/O vector
+ * \param[out] bytes Size of prepared data in bytes
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__ipc_prepare_iov(uint32_t request, xmlNode *message,
+ uint32_t max_send_size, struct iovec **result,
+ ssize_t *bytes)
+{
+ static unsigned int biggest = 0;
+ struct iovec *iov;
+ unsigned int total = 0;
+ char *compressed = NULL;
+ char *buffer = NULL;
+ pcmk__ipc_header_t *header = NULL;
+
+ if ((message == NULL) || (result == NULL)) {
+ return EINVAL;
+ }
+
+ header = calloc(1, sizeof(pcmk__ipc_header_t));
+ if (header == NULL) {
+ return ENOMEM; /* errno mightn't be set by allocator */
+ }
+
+ buffer = dump_xml_unformatted(message);
+
+ if (max_send_size == 0) {
+ max_send_size = crm_ipc_default_buffer_size();
+ }
+ CRM_LOG_ASSERT(max_send_size != 0);
+
+ *result = NULL;
+ iov = pcmk__new_ipc_event();
+ iov[0].iov_len = sizeof(pcmk__ipc_header_t);
+ iov[0].iov_base = header;
+
+ header->version = PCMK__IPC_VERSION;
+ header->size_uncompressed = 1 + strlen(buffer);
+ total = iov[0].iov_len + header->size_uncompressed;
+
+ if (total < max_send_size) {
+ iov[1].iov_base = buffer;
+ iov[1].iov_len = header->size_uncompressed;
+
+ } else {
+ unsigned int new_size = 0;
+
+ if (pcmk__compress(buffer, (unsigned int) header->size_uncompressed,
+ (unsigned int) max_send_size, &compressed,
+ &new_size) == pcmk_rc_ok) {
+
+ header->flags |= crm_ipc_compressed;
+ header->size_compressed = new_size;
+
+ iov[1].iov_len = header->size_compressed;
+ iov[1].iov_base = compressed;
+
+ free(buffer);
+
+ biggest = QB_MAX(header->size_compressed, biggest);
+
+ } else {
+ crm_log_xml_trace(message, "EMSGSIZE");
+ biggest = QB_MAX(header->size_uncompressed, biggest);
+
+ crm_err("Could not compress %u-byte message into less than IPC "
+ "limit of %u bytes; set PCMK_ipc_buffer to higher value "
+ "(%u bytes suggested)",
+ header->size_uncompressed, max_send_size, 4 * biggest);
+
+ free(compressed);
+ free(buffer);
+ pcmk_free_ipc_event(iov);
+ return EMSGSIZE;
+ }
+ }
+
+ header->qb.size = iov[0].iov_len + iov[1].iov_len;
+ header->qb.id = (int32_t)request; /* Replying to a specific request */
+
+ *result = iov;
+ CRM_ASSERT(header->qb.size > 0);
+ if (bytes != NULL) {
+ *bytes = header->qb.size;
+ }
+ return pcmk_rc_ok;
+}
+
+int
+pcmk__ipc_send_iov(pcmk__client_t *c, struct iovec *iov, uint32_t flags)
+{
+ int rc = pcmk_rc_ok;
+ static uint32_t id = 1;
+ pcmk__ipc_header_t *header = iov[0].iov_base;
+
+ if (c->flags & pcmk__client_proxied) {
+ /* _ALL_ replies to proxied connections need to be sent as events */
+ if (is_not_set(flags, crm_ipc_server_event)) {
+ flags |= crm_ipc_server_event;
+ /* this flag lets us know this was originally meant to be a response.
+ * even though we're sending it over the event channel. */
+ flags |= crm_ipc_proxied_relay_response;
+ }
+ }
+
+ header->flags |= flags;
+ if (flags & crm_ipc_server_event) {
+ header->qb.id = id++; /* We don't really use it, but doesn't hurt to set one */
+
+ if (flags & crm_ipc_server_free) {
+ crm_trace("Sending the original to %p[%d]", c->ipcs, c->pid);
+ add_event(c, iov);
+
+ } else {
+ struct iovec *iov_copy = pcmk__new_ipc_event();
+
+ crm_trace("Sending a copy to %p[%d]", c->ipcs, c->pid);
+ iov_copy[0].iov_len = iov[0].iov_len;
+ iov_copy[0].iov_base = malloc(iov[0].iov_len);
+ memcpy(iov_copy[0].iov_base, iov[0].iov_base, iov[0].iov_len);
+
+ iov_copy[1].iov_len = iov[1].iov_len;
+ iov_copy[1].iov_base = malloc(iov[1].iov_len);
+ memcpy(iov_copy[1].iov_base, iov[1].iov_base, iov[1].iov_len);
+
+ add_event(c, iov_copy);
+ }
+
+ } else {
+ ssize_t qb_rc;
+
+ CRM_LOG_ASSERT(header->qb.id != 0); /* Replying to a specific request */
+
+ qb_rc = qb_ipcs_response_sendv(c->ipcs, iov, 2);
+ if (qb_rc < header->qb.size) {
+ if (qb_rc < 0) {
+ rc = (int) -qb_rc;
+ }
+ crm_notice("Response %d to pid %d failed: %s "
+ CRM_XS " bytes=%u rc=%lld ipcs=%p",
+ header->qb.id, c->pid, pcmk_rc_str(rc),
+ header->qb.size, (long long) qb_rc, c->ipcs);
+
+ } else {
+ crm_trace("Response %d sent, %lld bytes to %p[%d]",
+ header->qb.id, (long long) qb_rc, c->ipcs, c->pid);
+ }
+
+ if (flags & crm_ipc_server_free) {
+ pcmk_free_ipc_event(iov);
+ }
+ }
+
+ if (flags & crm_ipc_server_event) {
+ rc = crm_ipcs_flush_events(c);
+ } else {
+ crm_ipcs_flush_events(c);
+ }
+
+ if ((rc == EPIPE) || (rc == ENOTCONN)) {
+ crm_trace("Client %p disconnected", c->ipcs);
+ }
+ return rc;
+}
+
+int
+pcmk__ipc_send_xml(pcmk__client_t *c, uint32_t request, xmlNode *message,
+ uint32_t flags)
+{
+ struct iovec *iov = NULL;
+ int rc = pcmk_rc_ok;
+
+ if (c == NULL) {
+ return EINVAL;
+ }
+ rc = pcmk__ipc_prepare_iov(request, message, crm_ipc_default_buffer_size(),
+ &iov, NULL);
+ if (rc == pcmk_rc_ok) {
+ rc = pcmk__ipc_send_iov(c, iov, flags | crm_ipc_server_free);
+ } else {
+ pcmk_free_ipc_event(iov);
+ crm_notice("IPC message to pid %d failed: %s " CRM_XS " rc=%d",
+ c->pid, pcmk_rc_str(rc), rc);
+ }
+ return rc;
+}
+
+void
+pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c,
+ uint32_t request, uint32_t flags, const char *tag)
+{
+ if (flags & crm_ipc_client_response) {
+ xmlNode *ack = create_xml_node(NULL, tag);
+
+ crm_trace("Ack'ing IPC message from %s", pcmk__client_name(c));
+ c->request_id = 0;
+ crm_xml_add(ack, "function", function);
+ crm_xml_add_int(ack, "line", line);
+ pcmk__ipc_send_xml(c, request, ack, flags);
+ free_xml(ack);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Add an IPC server to the main loop for the pacemaker-based API
+ *
+ * \param[out] ipcs_ro New IPC server for read-only pacemaker-based API
+ * \param[out] ipcs_rw New IPC server for read/write pacemaker-based API
+ * \param[out] ipcs_shm New IPC server for shared-memory pacemaker-based API
+ * \param[in] ro_cb IPC callbacks for read-only API
+ * \param[in] rw_cb IPC callbacks for read/write and shared-memory APIs
+ *
+ * \note This function exits fatally if unable to create the servers.
+ */
+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)
+{
+ *ipcs_ro = mainloop_add_ipc_server(PCMK__SERVER_BASED_RO,
+ QB_IPC_NATIVE, ro_cb);
+
+ *ipcs_rw = mainloop_add_ipc_server(PCMK__SERVER_BASED_RW,
+ QB_IPC_NATIVE, rw_cb);
+
+ *ipcs_shm = mainloop_add_ipc_server(PCMK__SERVER_BASED_SHM,
+ QB_IPC_SHM, rw_cb);
+
+ if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) {
+ crm_err("Failed to create the CIB manager: exiting and inhibiting respawn");
+ crm_warn("Verify pacemaker and pacemaker_remote are not both enabled");
+ crm_exit(CRM_EX_FATAL);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Destroy IPC servers for pacemaker-based API
+ *
+ * \param[out] ipcs_ro IPC server for read-only pacemaker-based API
+ * \param[out] ipcs_rw IPC server for read/write pacemaker-based API
+ * \param[out] ipcs_shm IPC server for shared-memory pacemaker-based API
+ *
+ * \note This is a convenience function for calling qb_ipcs_destroy() for each
+ * argument.
+ */
+void
+pcmk__stop_based_ipc(qb_ipcs_service_t *ipcs_ro,
+ qb_ipcs_service_t *ipcs_rw,
+ qb_ipcs_service_t *ipcs_shm)
+{
+ qb_ipcs_destroy(ipcs_ro);
+ qb_ipcs_destroy(ipcs_rw);
+ qb_ipcs_destroy(ipcs_shm);
+}
+
+/*!
+ * \internal
+ * \brief Add an IPC server to the main loop for the pacemaker-controld API
+ *
+ * \param[in] cb IPC callbacks
+ *
+ * \return Newly created IPC server
+ */
+qb_ipcs_service_t *
+pcmk__serve_controld_ipc(struct qb_ipcs_service_handlers *cb)
+{
+ return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb);
+}
+
+/*!
+ * \internal
+ * \brief Add an IPC server to the main loop for the pacemaker-attrd API
+ *
+ * \param[in] cb IPC callbacks
+ *
+ * \note This function exits fatally if unable to create the servers.
+ */
+void
+pcmk__serve_attrd_ipc(qb_ipcs_service_t **ipcs,
+ struct qb_ipcs_service_handlers *cb)
+{
+ *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb);
+
+ if (*ipcs == NULL) {
+ crm_err("Failed to create pacemaker-attrd server: exiting and inhibiting respawn");
+ crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
+ crm_exit(CRM_EX_FATAL);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Add an IPC server to the main loop for the pacemaker-fenced API
+ *
+ * \param[in] cb IPC callbacks
+ *
+ * \note This function exits fatally if unable to create the servers.
+ */
+void
+pcmk__serve_fenced_ipc(qb_ipcs_service_t **ipcs,
+ struct qb_ipcs_service_handlers *cb)
+{
+ *ipcs = mainloop_add_ipc_server_with_prio("stonith-ng", QB_IPC_NATIVE, cb,
+ QB_LOOP_HIGH);
+
+ if (*ipcs == NULL) {
+ crm_err("Failed to create fencer: exiting and inhibiting respawn.");
+ crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
+ crm_exit(CRM_EX_FATAL);
+ }
+}
diff --git a/lib/common/messages.c b/lib/common/messages.c
new file mode 100644
index 0000000..c5b5739
--- /dev/null
+++ b/lib/common/messages.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2004-2020 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <crm/msg_xml.h>
+
+/*!
+ * \brief Create a Pacemaker request (for IPC or cluster layer)
+ *
+ * \param[in] task What to set as the request's task
+ * \param[in] msg_data What to add as the request's data contents
+ * \param[in] host_to What to set as the request's destination host
+ * \param[in] sys_to What to set as the request's destination system
+ * \param[in] sys_from If not NULL, set as request's origin system
+ * \param[in] uuid_from If not NULL, use in request's origin system
+ * \param[in] origin Name of function that called this one
+ *
+ * \return XML of new request
+ *
+ * \note One of sys_from or uuid_from must be non-NULL
+ * \note This function should not be called directly, but via the
+ * create_request() wrapper.
+ * \note The caller is responsible for freeing the result using free_xml().
+ */
+xmlNode *
+create_request_adv(const char *task, xmlNode * msg_data,
+ const char *host_to, const char *sys_to,
+ const char *sys_from, const char *uuid_from,
+ const char *origin)
+{
+ static uint ref_counter = 0;
+
+ char *true_from = NULL;
+ xmlNode *request = NULL;
+ char *reference = crm_strdup_printf("%s-%s-%lld-%u",
+ (task? task : "_empty_"),
+ (sys_from? sys_from : "_empty_"),
+ (long long) time(NULL), ref_counter++);
+
+ if (uuid_from != NULL) {
+ true_from = generate_hash_key(sys_from, uuid_from);
+ } else if (sys_from != NULL) {
+ true_from = strdup(sys_from);
+ } else {
+ crm_err("No sys from specified");
+ }
+
+ // host_from will get set for us if necessary by the controller when routed
+ request = create_xml_node(NULL, __FUNCTION__);
+ crm_xml_add(request, F_CRM_ORIGIN, origin);
+ crm_xml_add(request, F_TYPE, T_CRM);
+ crm_xml_add(request, F_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(request, F_CRM_MSG_TYPE, XML_ATTR_REQUEST);
+ crm_xml_add(request, F_CRM_REFERENCE, reference);
+ crm_xml_add(request, F_CRM_TASK, task);
+ crm_xml_add(request, F_CRM_SYS_TO, sys_to);
+ crm_xml_add(request, F_CRM_SYS_FROM, true_from);
+
+ /* HOSTTO will be ignored if it is to the DC anyway. */
+ if (host_to != NULL && strlen(host_to) > 0) {
+ crm_xml_add(request, F_CRM_HOST_TO, host_to);
+ }
+
+ if (msg_data != NULL) {
+ add_message_xml(request, F_CRM_DATA, msg_data);
+ }
+ free(reference);
+ free(true_from);
+
+ return request;
+}
+
+/*!
+ * \brief Create a Pacemaker reply (for IPC or cluster layer)
+ *
+ * \param[in] original_request XML of request this is a reply to
+ * \param[in] xml_response_data XML to copy as data section of reply
+ * \param[in] origin Name of function that called this one
+ *
+ * \return XML of new reply
+ *
+ * \note This function should not be called directly, but via the
+ * create_reply() wrapper.
+ * \note The caller is responsible for freeing the result using free_xml().
+ */
+xmlNode *
+create_reply_adv(xmlNode *original_request, xmlNode *xml_response_data,
+ const char *origin)
+{
+ xmlNode *reply = NULL;
+
+ const char *host_from = crm_element_value(original_request, F_CRM_HOST_FROM);
+ const char *sys_from = crm_element_value(original_request, F_CRM_SYS_FROM);
+ const char *sys_to = crm_element_value(original_request, F_CRM_SYS_TO);
+ const char *type = crm_element_value(original_request, F_CRM_MSG_TYPE);
+ const char *operation = crm_element_value(original_request, F_CRM_TASK);
+ const char *crm_msg_reference = crm_element_value(original_request, F_CRM_REFERENCE);
+
+ if (type == NULL) {
+ crm_err("Cannot create new_message, no message type in original message");
+ CRM_ASSERT(type != NULL);
+ return NULL;
+#if 0
+ } else if (strcasecmp(XML_ATTR_REQUEST, type) != 0) {
+ crm_err("Cannot create new_message, original message was not a request");
+ return NULL;
+#endif
+ }
+ reply = create_xml_node(NULL, __FUNCTION__);
+ if (reply == NULL) {
+ crm_err("Cannot create new_message, malloc failed");
+ return NULL;
+ }
+
+ crm_xml_add(reply, F_CRM_ORIGIN, origin);
+ crm_xml_add(reply, F_TYPE, T_CRM);
+ crm_xml_add(reply, F_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(reply, F_CRM_MSG_TYPE, XML_ATTR_RESPONSE);
+ crm_xml_add(reply, F_CRM_REFERENCE, crm_msg_reference);
+ crm_xml_add(reply, F_CRM_TASK, operation);
+
+ /* since this is a reply, we reverse the from and to */
+ crm_xml_add(reply, F_CRM_SYS_TO, sys_from);
+ crm_xml_add(reply, F_CRM_SYS_FROM, sys_to);
+
+ /* HOSTTO will be ignored if it is to the DC anyway. */
+ if (host_from != NULL && strlen(host_from) > 0) {
+ crm_xml_add(reply, F_CRM_HOST_TO, host_from);
+ }
+
+ if (xml_response_data != NULL) {
+ add_message_xml(reply, F_CRM_DATA, xml_response_data);
+ }
+
+ return reply;
+}
--
1.8.3.1
From 8cfa76d840aff62b2376136a7ddb1cb54d070458 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 7 Apr 2020 11:39:31 -0500
Subject: [PATCH 3/8] Refactor: libcrmcommon: move get_message_xml() and
add_message_xml() definition
recently added messages.c is more logical place than xml.c
---
lib/common/messages.c | 20 ++++++++++++++++++++
lib/common/xml.c | 17 -----------------
2 files changed, 20 insertions(+), 17 deletions(-)
diff --git a/lib/common/messages.c b/lib/common/messages.c
index c5b5739..25569db 100644
--- a/lib/common/messages.c
+++ b/lib/common/messages.c
@@ -12,6 +12,9 @@
#include <stdio.h>
#include <sys/types.h>
+#include <glib.h>
+#include <libxml/tree.h>
+
#include <crm/msg_xml.h>
/*!
@@ -144,3 +147,20 @@ create_reply_adv(xmlNode *original_request, xmlNode *xml_response_data,
return reply;
}
+
+xmlNode *
+get_message_xml(xmlNode *msg, const char *field)
+{
+ xmlNode *tmp = first_named_child(msg, field);
+
+ return __xml_first_child(tmp);
+}
+
+gboolean
+add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
+{
+ xmlNode *holder = create_xml_node(msg, field);
+
+ add_node_copy(holder, xml);
+ return TRUE;
+}
diff --git a/lib/common/xml.c b/lib/common/xml.c
index de0c508..e071f8d 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -2613,23 +2613,6 @@ write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
return write_xml_stream(xml_node, filename, stream, compress);
}
-xmlNode *
-get_message_xml(xmlNode * msg, const char *field)
-{
- xmlNode *tmp = first_named_child(msg, field);
-
- return __xml_first_child(tmp);
-}
-
-gboolean
-add_message_xml(xmlNode * msg, const char *field, xmlNode * xml)
-{
- xmlNode *holder = create_xml_node(msg, field);
-
- add_node_copy(holder, xml);
- return TRUE;
-}
-
static char *
crm_xml_escape_shuffle(char *text, int start, int *length, const char *replace)
{
--
1.8.3.1
From a123da0a978a45b08f0723fd651059a13d26235c Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Wed, 8 Apr 2020 11:09:00 -0500
Subject: [PATCH 4/8] Refactor: libcrmcommon: drop generate_hash_key()
The internal one-liner was only used in one location, and that wasn't even to
generate a hash key.
---
include/crm_internal.h | 2 --
lib/common/messages.c | 5 +++--
lib/common/utils.c | 11 -----------
3 files changed, 3 insertions(+), 15 deletions(-)
diff --git a/include/crm_internal.h b/include/crm_internal.h
index 15f9d2b..fd56fc6 100644
--- a/include/crm_internal.h
+++ b/include/crm_internal.h
@@ -68,8 +68,6 @@ crm_set_bit(const char *function, int line, const char *target, long long word,
# define set_bit(word, bit) word = crm_set_bit(__FUNCTION__, __LINE__, NULL, word, bit)
# define clear_bit(word, bit) word = crm_clear_bit(__FUNCTION__, __LINE__, NULL, word, bit)
-char *generate_hash_key(const char *crm_msg_reference, const char *sys);
-
void strip_text_nodes(xmlNode * xml);
void pcmk_panic(const char *origin);
pid_t pcmk_locate_sbd(void);
diff --git a/lib/common/messages.c b/lib/common/messages.c
index 25569db..d3fa894 100644
--- a/lib/common/messages.c
+++ b/lib/common/messages.c
@@ -51,11 +51,12 @@ create_request_adv(const char *task, xmlNode * msg_data,
(long long) time(NULL), ref_counter++);
if (uuid_from != NULL) {
- true_from = generate_hash_key(sys_from, uuid_from);
+ true_from = crm_strdup_printf("%s_%s", uuid_from,
+ (sys_from? sys_from : "none"));
} else if (sys_from != NULL) {
true_from = strdup(sys_from);
} else {
- crm_err("No sys from specified");
+ crm_err("Cannot create IPC request: No originating system specified");
}
// host_from will get set for us if necessary by the controller when routed
diff --git a/lib/common/utils.c b/lib/common/utils.c
index 13e7cb2..0ac96b8 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -116,17 +116,6 @@ score2char(int score)
return crm_itoa(score);
}
-char *
-generate_hash_key(const char *crm_msg_reference, const char *sys)
-{
- char *hash_key = crm_strdup_printf("%s_%s", (sys? sys : "none"),
- crm_msg_reference);
-
- crm_trace("created hash key: (%s)", hash_key);
- return hash_key;
-}
-
-
int
crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
{
--
1.8.3.1
From dd81ff4b826c662654d6760f20f3ec74f6ae6020 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Thu, 16 Apr 2020 16:47:53 -0500
Subject: [PATCH 5/8] Low: libcrmcommon: new function for draining and quitting
a main loop
We have an existing drain function to drain events based on a check function
and a timeout, which is better suited to daemons. This one drains up to N
events and then quits the main loop, which is better suited to tools.
---
include/crm/common/mainloop.h | 3 ++-
lib/common/mainloop.c | 22 ++++++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h
index b443b4e..9957b25 100644
--- a/include/crm/common/mainloop.h
+++ b/include/crm/common/mainloop.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2019 the Pacemaker project contributors
+ * Copyright 2009-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -146,6 +146,7 @@ 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));
diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c
index 10450e4..634eead 100644
--- a/lib/common/mainloop.c
+++ b/lib/common/mainloop.c
@@ -1345,6 +1345,28 @@ drain_timeout_cb(gpointer user_data)
}
/*!
+ * \brief Drain some remaining main loop events then quit it
+ *
+ * \param[in] mloop Main loop to drain and quit
+ * \param[in] n Drain up to this many pending events
+ */
+void
+pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n)
+{
+ if ((mloop != NULL) && g_main_loop_is_running(mloop)) {
+ GMainContext *ctx = g_main_loop_get_context(mloop);
+
+ /* Drain up to n events in case some memory clean-up is pending
+ * (helpful to reduce noise in valgrind output).
+ */
+ for (int i = 0; (i < n) && g_main_context_pending(ctx); ++i) {
+ g_main_context_dispatch(ctx);
+ }
+ g_main_loop_quit(mloop);
+ }
+}
+
+/*!
* \brief Process main loop events while a certain condition is met
*
* \param[in] mloop Main loop to process
--
1.8.3.1
From 771a0c56df363eb91fa7eb6ac4b3ee833c5dcd5e Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 14 Apr 2020 13:04:45 -0500
Subject: [PATCH 6/8] Low: tools: handle memory cleanup better in crm_node
crm_node had paths that would exit without returning to the main(),
missing the memory cleanup at the end.
---
tools/crm_node.c | 30 ++++++++++++++----------------
1 file changed, 14 insertions(+), 16 deletions(-)
diff --git a/tools/crm_node.c b/tools/crm_node.c
index 34511f3..db31f20 100644
--- a/tools/crm_node.c
+++ b/tools/crm_node.c
@@ -187,15 +187,6 @@ new_mainloop_for_ipc(const char *system, ipc_dispatch_fn dispatch)
return ipc;
}
-static void
-run_mainloop_and_exit(void)
-{
- g_main_loop_run(mainloop);
- g_main_loop_unref(mainloop);
- mainloop = NULL;
- crm_node_exit(exit_code);
-}
-
static int
send_controller_hello(crm_ipc_t *controller)
{
@@ -328,7 +319,9 @@ run_controller_mainloop(uint32_t nodeid)
}
// Run main loop to get controller reply via dispatch_controller()
- run_mainloop_and_exit();
+ g_main_loop_run(mainloop);
+ g_main_loop_unref(mainloop);
+ mainloop = NULL;
}
static void
@@ -339,7 +332,8 @@ print_node_name(void)
if (name != NULL) {
printf("%s\n", name);
- crm_node_exit(CRM_EX_OK);
+ exit_code = CRM_EX_OK;
+ return;
} else {
// Otherwise ask the controller
@@ -486,11 +480,11 @@ remove_node(const char *target_uname)
if (tools_remove_node_cache(node_name, nodeid, daemons[d])) {
crm_err("Failed to connect to %s to remove node '%s'",
daemons[d], target_uname);
- crm_node_exit(CRM_EX_ERROR);
+ exit_code = CRM_EX_ERROR;
return;
}
}
- crm_node_exit(CRM_EX_OK);
+ exit_code = CRM_EX_OK;
}
static gint
@@ -513,7 +507,8 @@ node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
if (msg == NULL) {
fprintf(stderr, "error: Could not understand pacemakerd response\n");
- crm_node_exit(CRM_EX_PROTOCOL);
+ exit_code = CRM_EX_PROTOCOL;
+ g_main_loop_quit(mainloop);
return 0;
}
@@ -544,7 +539,8 @@ node_mcp_dispatch(const char *buffer, ssize_t length, gpointer userdata)
}
free_xml(msg);
- crm_node_exit(CRM_EX_OK);
+ exit_code = CRM_EX_OK;
+ g_main_loop_quit(mainloop);
return 0;
}
@@ -562,7 +558,9 @@ run_pacemakerd_mainloop(void)
free_xml(poke);
// Handle reply via node_mcp_dispatch()
- run_mainloop_and_exit();
+ g_main_loop_run(mainloop);
+ g_main_loop_unref(mainloop);
+ mainloop = NULL;
}
static GOptionContext *
--
1.8.3.1
From ba7abdbe6cd7f2fb73ce096c87f0599e27000bee Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Mon, 20 Apr 2020 15:30:40 -0500
Subject: [PATCH 7/8] Refactor: tools: use proper type for glib timeout value
in crmadmin
---
tools/crmadmin.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tools/crmadmin.c b/tools/crmadmin.c
index c58de59..bd4bfe5 100644
--- a/tools/crmadmin.c
+++ b/tools/crmadmin.c
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
+#include <glib.h> // gboolean, GMainLoop, etc.
#include <crm/crm.h>
#include <crm/msg_xml.h>
@@ -28,9 +29,10 @@
#include <crm/cib.h>
-static int message_timer_id = -1;
-static int message_timeout_ms = 30 * 1000;
+#define DEFAULT_MESSAGE_TIMEOUT_MS 30000
+static guint message_timer_id = 0;
+static guint message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
static GMainLoop *mainloop = NULL;
static crm_ipc_t *crmd_channel = NULL;
static char *admin_uuid = NULL;
@@ -172,9 +174,9 @@ main(int argc, char **argv)
crm_bump_log_level(argc, argv);
break;
case 't':
- message_timeout_ms = atoi(optarg);
+ message_timeout_ms = (guint) atoi(optarg);
if (message_timeout_ms < 1) {
- message_timeout_ms = 30 * 1000;
+ message_timeout_ms = DEFAULT_MESSAGE_TIMEOUT_MS;
}
break;
--
1.8.3.1
From 27d763920a1e12b9b5747c1b64a5dc1395c58768 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Mon, 20 Apr 2020 15:44:20 -0500
Subject: [PATCH 8/8] Refactor: tools: functionize listing nodes from CIB in
crmadmin
---
tools/crmadmin.c | 47 +++++++++++++++++++++++++++--------------------
1 file changed, 27 insertions(+), 20 deletions(-)
diff --git a/tools/crmadmin.c b/tools/crmadmin.c
index bd4bfe5..3e9e959 100644
--- a/tools/crmadmin.c
+++ b/tools/crmadmin.c
@@ -149,6 +149,32 @@ static pcmk__cli_option_t long_options[] = {
{ 0, 0, 0, 0 }
};
+// \return Standard Pacemaker return code
+static int
+list_nodes()
+{
+ cib_t *the_cib = cib_new();
+ xmlNode *output = NULL;
+ int rc;
+
+ if (the_cib == NULL) {
+ return ENOMEM;
+ }
+ rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
+ if (rc != pcmk_ok) {
+ return pcmk_legacy2rc(rc);
+ }
+
+ rc = the_cib->cmds->query(the_cib, NULL, &output,
+ cib_scope_local | cib_sync_call);
+ if (rc == pcmk_ok) {
+ do_find_node_list(output);
+ free_xml(output);
+ }
+ the_cib->cmds->signoff(the_cib);
+ return pcmk_legacy2rc(rc);
+}
+
int
main(int argc, char **argv)
{
@@ -304,26 +330,7 @@ do_work(void)
crmd_operation = CRM_OP_PING;
} else if (DO_NODE_LIST) {
-
- cib_t *the_cib = cib_new();
- xmlNode *output = NULL;
-
- int rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
-
- if (rc != pcmk_ok) {
- fprintf(stderr, "Could not connect to CIB: %s\n",
- pcmk_strerror(rc));
- return -1;
- }
-
- rc = the_cib->cmds->query(the_cib, NULL, &output, cib_scope_local | cib_sync_call);
- if(rc == pcmk_ok) {
- do_find_node_list(output);
-
- free_xml(output);
- }
- the_cib->cmds->signoff(the_cib);
- crm_exit(crm_errno2exit(rc));
+ crm_exit(pcmk_rc2exitc(list_nodes()));
} else if (DO_RESET) {
/* tell dest_node to initiate the shutdown procedure
--
1.8.3.1