summaryrefslogtreecommitdiff
path: root/lib/lvmpolld/lvmpolld-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lvmpolld/lvmpolld-client.c')
-rw-r--r--lib/lvmpolld/lvmpolld-client.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/lib/lvmpolld/lvmpolld-client.c b/lib/lvmpolld/lvmpolld-client.c
new file mode 100644
index 000000000..f7faa8b1e
--- /dev/null
+++ b/lib/lvmpolld/lvmpolld-client.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include "daemon-io.h"
+#include "lvmpolld-client.h"
+#include "lvmpolld-protocol.h"
+#include "metadata-exported.h"
+#include "polldaemon.h"
+#include "toolcontext.h"
+#include "lvm2cmd.h"
+
+struct progress_info {
+ unsigned error:1;
+ unsigned finished:1;
+ int cmd_signal;
+ int cmd_retcode;
+};
+
+static int _lvmpolld_use;
+static int _lvmpolld_connected;
+static const char* _lvmpolld_socket;
+
+static daemon_handle _lvmpolld = { .error = 0 };
+
+static daemon_handle _lvmpolld_open(const char *socket)
+{
+ daemon_info lvmpolld_info = {
+ .path = "lvmpolld",
+ .socket = socket ?: LVMPOLLD_SOCKET,
+ .protocol = LVMPOLLD_PROTOCOL,
+ .protocol_version = LVMPOLLD_PROTOCOL_VERSION
+ };
+
+ return daemon_open(lvmpolld_info);
+}
+
+void lvmpolld_set_active(int active)
+{
+ _lvmpolld_use = active;
+}
+
+void lvmpolld_set_socket(const char *socket)
+{
+ _lvmpolld_socket = socket;
+}
+
+static void _lvmpolld_connect_or_warn(void)
+{
+ if (!_lvmpolld_connected && !_lvmpolld.error) {
+ _lvmpolld = _lvmpolld_open(_lvmpolld_socket);
+ if ( _lvmpolld.socket_fd >= 0 && !_lvmpolld.error) {
+ log_debug_lvmpolld("Sucessfully connected to lvmpolld on fd %d.", _lvmpolld.socket_fd);
+ _lvmpolld_connected = 1;
+ } else {
+ log_warn("WARNING: Failed to connect to lvmpolld. Proceeding with polling without using lvmpolld.");
+ log_warn("WARNING: Check global/use_lvmpolld in lvm.conf or the lvmpolld daemon state.");
+ }
+ }
+}
+
+int lvmpolld_use(void)
+{
+ if (!_lvmpolld_use || !_lvmpolld_socket)
+ return 0;
+
+ _lvmpolld_connect_or_warn();
+
+ return _lvmpolld_connected;
+}
+
+void lvmpolld_disconnect(void)
+{
+ if (_lvmpolld_connected) {
+ daemon_close(_lvmpolld);
+ _lvmpolld_connected = 0;
+ }
+}
+
+static void _explain_error_codes(int retcode)
+{
+ switch (retcode) {
+ /* LVM2 return codes */
+ case LVM2_NO_SUCH_COMMAND:
+ log_error("LVM command run by lvmpolld responded with: 'No such command.'");
+ break;
+ case LVM2_INVALID_PARAMETERS:
+ log_error("LVM command run by lvmpolld failed due to invalid parameters.");
+ break;
+ case LVM2_PROCESSING_FAILED:
+ log_error("LVM command executed by lvmpolld failed.");
+ break;
+
+ /* lvmpolld specific return codes */
+ case LVMPD_RET_DUP_FAILED:
+ log_error("lvmpolld failed to duplicate file descriptors.");
+ /* fall through */
+ case LVMPD_RET_EXC_FAILED:
+ log_error("lvmpolld failed to exec() lvm binary.");
+ break;
+ default:
+ log_error("lvmpolld responded with unexpected return code.");
+ }
+
+ log_print_unless_silent("For more information see lvmpolld messages in syslog or lvmpolld log file.");
+}
+
+static void _process_error_response(daemon_reply rep)
+{
+ if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_FAILED))
+ log_error("lvmpolld failed to process a request. The reason was: %s.",
+ daemon_reply_str(rep, "reason", "<empty>"));
+ else if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_EINVAL))
+ log_error("lvmpolld couldn't handle a request. "
+ "It might be due to daemon internal state. The reason was: %s.",
+ daemon_reply_str(rep, "reason", "<empty>"));
+ else
+ log_error("Unexpected response %s. The reason: %s.",
+ daemon_reply_str(rep, "response", "<empty>"),
+ daemon_reply_str(rep, "reason", "<empty>"));
+
+ log_print_unless_silent("For more information see lvmpolld messages in syslog or lvmpolld log file.");
+}
+
+static struct progress_info _request_progress_info(const char *uuid, unsigned abort_polling)
+{
+ daemon_reply rep;
+ const char *e = getenv("LVM_SYSTEM_DIR");
+ struct progress_info ret = { .error = 1, .finished = 1 };
+ daemon_request req = daemon_request_make(LVMPD_REQ_PROGRESS);
+
+ if (!daemon_request_extend(req, LVMPD_PARM_LVID " = %s", uuid, NULL)) {
+ log_error("Failed to create " LVMPD_REQ_PROGRESS " request.");
+ goto out_req;
+ }
+
+ if (abort_polling &&
+ !daemon_request_extend(req, LVMPD_PARM_ABORT " = %d", (int64_t)abort_polling, NULL)) {
+ log_error("Failed to create " LVMPD_REQ_PROGRESS " request.");
+ goto out_req;
+ }
+
+ if (e &&
+ !(daemon_request_extend(req, LVMPD_PARM_SYSDIR " = %s",
+ e, NULL))) {
+ log_error("Failed to create " LVMPD_REQ_PROGRESS " request.");
+ goto out_req;
+ }
+
+ rep = daemon_send(_lvmpolld, req);
+ if (rep.error) {
+ log_error("Failed to process request with error %s (errno: %d).",
+ strerror(rep.error), rep.error);
+ goto out_rep;
+ }
+
+ if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_IN_PROGRESS)) {
+ ret.finished = 0;
+ ret.error = 0;
+ } else if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_FINISHED)) {
+ if (!strcmp(daemon_reply_str(rep, "reason", ""), LVMPD_REAS_SIGNAL))
+ ret.cmd_signal = daemon_reply_int(rep, LVMPD_PARM_VALUE, 0);
+ else
+ ret.cmd_retcode = daemon_reply_int(rep, LVMPD_PARM_VALUE, -1);
+ ret.error = 0;
+ } else if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_NOT_FOUND)) {
+ log_verbose("No polling operation in progress regarding LV %s.", uuid);
+ ret.error = 0;
+ } else {
+ _process_error_response(rep);
+ stack;
+ }
+
+out_rep:
+ daemon_reply_destroy(rep);
+out_req:
+ daemon_request_destroy(req);
+
+ return ret;
+}
+
+/*
+ * interval in seconds long
+ * enough for more than a year
+ * of waiting
+ */
+#define INTERV_SIZE 10
+
+static int _process_poll_init(const struct cmd_context *cmd, const char *poll_type,
+ const struct poll_operation_id *id, const struct daemon_parms *parms)
+{
+ char *str;
+ daemon_reply rep;
+ daemon_request req;
+ const char *e = getenv("LVM_SYSTEM_DIR");
+ int r = 0;
+
+ str = dm_malloc(INTERV_SIZE * sizeof(char));
+ if (!str)
+ return r;
+
+ if (snprintf(str, INTERV_SIZE, "%u", parms->interval) >= INTERV_SIZE) {
+ log_warn("Interval string conversion got truncated.");
+ str[INTERV_SIZE - 1] = '\0';
+ }
+
+ req = daemon_request_make(poll_type);
+ if (!daemon_request_extend(req, LVMPD_PARM_LVID " = %s", id->uuid,
+ LVMPD_PARM_VGNAME " = %s", id->vg_name,
+ LVMPD_PARM_LVNAME " = %s", id->lv_name,
+ LVMPD_PARM_INTERVAL " = %s", str,
+ "cmdline = %s", cmd->cmd_line, /* FIXME: debug param only */
+ NULL)) {
+ log_error("Failed to create %s request.", poll_type);
+ goto out_req;
+ }
+
+ if (parms->aborting &&
+ !(daemon_request_extend(req, LVMPD_PARM_ABORT " = %d", (int64_t)(parms->aborting), NULL))) {
+ log_error("Failed to create %s request." , poll_type);
+ goto out_req;
+ }
+
+ if (cmd->handles_missing_pvs &&
+ !(daemon_request_extend(req, LVMPD_PARM_HANDLE_MISSING_PVS " = %d",
+ (int64_t)(cmd->handles_missing_pvs), NULL))) {
+ log_error("Failed to create %s request." , poll_type);
+ goto out_req;
+ }
+
+ if (e &&
+ !(daemon_request_extend(req, LVMPD_PARM_SYSDIR " = %s",
+ e, NULL))) {
+ log_error("Failed to create %s request." , poll_type);
+ goto out_req;
+ }
+
+ rep = daemon_send(_lvmpolld, req);
+
+ if (rep.error) {
+ log_error("Failed to process request with error %s (errno: %d).",
+ strerror(rep.error), rep.error);
+ goto out_rep;
+ }
+
+ if (!strcmp(daemon_reply_str(rep, "response", ""), LVMPD_RESP_OK))
+ r = 1;
+ else {
+ _process_error_response(rep);
+ stack;
+ }
+
+out_rep:
+ daemon_reply_destroy(rep);
+out_req:
+ daemon_request_destroy(req);
+ dm_free(str);
+
+ return r;
+}
+
+int lvmpolld_poll_init(const struct cmd_context *cmd, const struct poll_operation_id *id,
+ const struct daemon_parms *parms)
+{
+ int r = 0;
+
+ if (!id->uuid) {
+ log_error(INTERNAL_ERROR "Use of lvmpolld requires uuid set");
+ return 0;
+ }
+
+ if (!id->vg_name) {
+ log_error(INTERNAL_ERROR "Use of lvmpolld requires vgname set");
+ return 0;
+ }
+
+ if (!id->lv_name) {
+ log_error(INTERNAL_ERROR "Use of lvmpolld requires lvname set");
+ return 0;
+ }
+
+ if (parms->lv_type & PVMOVE) {
+ log_debug_lvmpolld("Asking lvmpolld for pvmove%s on %s/%s.",
+ parms->aborting ? " abort" : "", id->vg_name, id->lv_name);
+ r = _process_poll_init(cmd, LVMPD_REQ_PVMOVE, id, parms);
+ } else if (parms->lv_type & CONVERTING) {
+ log_debug_lvmpolld("Asking lvmpolld for mirror conversion on %s/%s.",
+ id->vg_name, id->lv_name);
+ r = _process_poll_init(cmd, LVMPD_REQ_CONVERT, id, parms);
+ } else if (parms->lv_type & MERGING) {
+ if (parms->lv_type & SNAPSHOT) {
+ log_debug_lvmpolld("Asking lvmpolld for snapshot merge on %s/%s.",
+ id->vg_name, id->lv_name);
+ r = _process_poll_init(cmd, LVMPD_REQ_MERGE, id, parms);
+ }
+ else if (parms->lv_type & THIN_VOLUME) {
+ log_debug_lvmpolld("Asking lvmpolld for thin snapshot merge on %s/%s.",
+ id->vg_name, id->lv_name);
+ r = _process_poll_init(cmd, LVMPD_REQ_MERGE_THIN, id, parms);
+ }
+ else {
+ log_error(INTERNAL_ERROR "Unsupported poll operation.");
+ }
+ } else
+ log_error(INTERNAL_ERROR "Unsupported poll operation");
+
+ return r;
+}
+
+int lvmpolld_request_info(const struct poll_operation_id *id, const struct daemon_parms *parms, unsigned *finished)
+{
+ struct progress_info info;
+ int ret = 0;
+
+ *finished = 1;
+
+ if (!id->uuid) {
+ log_error(INTERNAL_ERROR "use of lvmpolld requires uuid being set");
+ return 0;
+ }
+
+ log_debug_lvmpolld("Asking lvmpolld for progress status of an operation on %s/%s.",
+ id->vg_name, id->lv_name);
+ info = _request_progress_info(id->uuid, parms->aborting);
+ *finished = info.finished;
+
+ if (info.error)
+ return_0;
+
+ if (info.finished) {
+ if (info.cmd_signal)
+ log_error("Command executed by lvmpolld got terminated by signal (%d).",
+ info.cmd_signal);
+ else if (info.cmd_retcode)
+ _explain_error_codes(info.cmd_retcode);
+ else {
+ log_verbose("Polling finished successfully.");
+ ret = 1;
+ }
+ } else
+ ret = 1;
+
+ return ret;
+}