summaryrefslogtreecommitdiff
path: root/daemons/lvmpolld/lvmpolld-data-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/lvmpolld/lvmpolld-data-utils.c')
-rw-r--r--daemons/lvmpolld/lvmpolld-data-utils.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/daemons/lvmpolld/lvmpolld-data-utils.c b/daemons/lvmpolld/lvmpolld-data-utils.c
new file mode 100644
index 000000000..92147df0b
--- /dev/null
+++ b/daemons/lvmpolld/lvmpolld-data-utils.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2014-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 "lvmpolld-common.h"
+
+#include "config-util.h"
+
+#include <fcntl.h>
+#include <signal.h>
+
+static char *_construct_full_lvname(const char *vgname, const char *lvname)
+{
+ char *name;
+ size_t l;
+
+ l = strlen(vgname) + strlen(lvname) + 2; /* vg/lv and \0 */
+ name = (char *) dm_malloc(l * sizeof(char));
+ if (!name)
+ return NULL;
+
+ if (dm_snprintf(name, l, "%s/%s", vgname, lvname) < 0) {
+ dm_free(name);
+ name = NULL;
+ }
+
+ return name;
+}
+
+static char *_construct_lvm_system_dir_env(const char *sysdir)
+{
+ /*
+ * Store either "LVM_SYSTEM_DIR=/path/to..."
+ * - or -
+ * just single char to store NULL byte
+ */
+ size_t l = sysdir ? strlen(sysdir) + 16 : 1;
+ char *env = (char *) dm_malloc(l * sizeof(char));
+
+ if (!env)
+ return NULL;
+
+ *env = '\0';
+
+ if (sysdir && dm_snprintf(env, l, "LVM_SYSTEM_DIR=%s", sysdir) < 0) {
+ dm_free(env);
+ env = NULL;
+ }
+
+ return env;
+}
+
+static const char *_get_lvid(const char *lvmpolld_id, const char *sysdir)
+{
+ return lvmpolld_id ? (lvmpolld_id + (sysdir ? strlen(sysdir) : 0)) : NULL;
+}
+
+char *construct_id(const char *sysdir, const char *uuid)
+{
+ char *id;
+ int r;
+ size_t l;
+
+ l = strlen(uuid) + (sysdir ? strlen(sysdir) : 0) + 1;
+ id = (char *) dm_malloc(l * sizeof(char));
+ if (!id)
+ return NULL;
+
+ r = sysdir ? dm_snprintf(id, l, "%s%s", sysdir, uuid) :
+ dm_snprintf(id, l, "%s", uuid);
+
+ if (r < 0) {
+ dm_free(id);
+ id = NULL;
+ }
+
+ return id;
+}
+
+struct lvmpolld_lv *pdlv_create(struct lvmpolld_state *ls, const char *id,
+ const char *vgname, const char *lvname,
+ const char *sysdir, enum poll_type type,
+ const char *sinterval, unsigned pdtimeout,
+ struct lvmpolld_store *pdst)
+{
+ char *lvmpolld_id = dm_strdup(id), /* copy */
+ *full_lvname = _construct_full_lvname(vgname, lvname), /* copy */
+ *lvm_system_dir_env = _construct_lvm_system_dir_env(sysdir); /* copy */
+
+ struct lvmpolld_lv tmp = {
+ .ls = ls,
+ .type = type,
+ .lvmpolld_id = lvmpolld_id,
+ .lvid = _get_lvid(lvmpolld_id, sysdir),
+ .lvname = full_lvname,
+ .lvm_system_dir_env = lvm_system_dir_env,
+ .sinterval = dm_strdup(sinterval), /* copy */
+ .pdtimeout = pdtimeout < MIN_POLLING_TIMEOUT ? MIN_POLLING_TIMEOUT : pdtimeout,
+ .cmd_state = { .retcode = -1, .signal = 0 },
+ .pdst = pdst,
+ .init_rq_count = 1
+ }, *pdlv = (struct lvmpolld_lv *) dm_malloc(sizeof(struct lvmpolld_lv));
+
+ if (!pdlv || !tmp.lvid || !tmp.lvname || !tmp.lvm_system_dir_env || !tmp.sinterval)
+ goto err;
+
+ memcpy(pdlv, &tmp, sizeof(*pdlv));
+
+ if (pthread_mutex_init(&pdlv->lock, NULL))
+ goto err;
+
+ return pdlv;
+
+err:
+ dm_free((void *)full_lvname);
+ dm_free((void *)lvmpolld_id);
+ dm_free((void *)lvm_system_dir_env);
+ dm_free((void *)tmp.sinterval);
+ dm_free((void *)pdlv);
+
+ return NULL;
+}
+
+void pdlv_destroy(struct lvmpolld_lv *pdlv)
+{
+ dm_free((void *)pdlv->lvmpolld_id);
+ dm_free((void *)pdlv->lvname);
+ dm_free((void *)pdlv->sinterval);
+ dm_free((void *)pdlv->lvm_system_dir_env);
+ dm_free((void *)pdlv->cmdargv);
+ dm_free((void *)pdlv->cmdenvp);
+
+ pthread_mutex_destroy(&pdlv->lock);
+
+ dm_free((void *)pdlv);
+}
+
+unsigned pdlv_get_polling_finished(struct lvmpolld_lv *pdlv)
+{
+ unsigned ret;
+
+ pdlv_lock(pdlv);
+ ret = pdlv->polling_finished;
+ pdlv_unlock(pdlv);
+
+ return ret;
+}
+
+struct lvmpolld_lv_state pdlv_get_status(struct lvmpolld_lv *pdlv)
+{
+ struct lvmpolld_lv_state r;
+
+ pdlv_lock(pdlv);
+ r.error = pdlv_locked_error(pdlv);
+ r.polling_finished = pdlv_locked_polling_finished(pdlv);
+ r.cmd_state = pdlv_locked_cmd_state(pdlv);
+ pdlv_unlock(pdlv);
+
+ return r;
+}
+
+void pdlv_set_cmd_state(struct lvmpolld_lv *pdlv, const struct lvmpolld_cmd_stat *cmd_state)
+{
+ pdlv_lock(pdlv);
+ pdlv->cmd_state = *cmd_state;
+ pdlv_unlock(pdlv);
+}
+
+void pdlv_set_error(struct lvmpolld_lv *pdlv, unsigned error)
+{
+ pdlv_lock(pdlv);
+ pdlv->error = error;
+ pdlv_unlock(pdlv);
+}
+
+void pdlv_set_polling_finished(struct lvmpolld_lv *pdlv, unsigned finished)
+{
+ pdlv_lock(pdlv);
+ pdlv->polling_finished = finished;
+ pdlv_unlock(pdlv);
+}
+
+struct lvmpolld_store *pdst_init(const char *name)
+{
+ struct lvmpolld_store *pdst = (struct lvmpolld_store *) dm_malloc(sizeof(struct lvmpolld_store));
+ if (!pdst)
+ return NULL;
+
+ pdst->store = dm_hash_create(32);
+ if (!pdst->store)
+ goto err_hash;
+ if (pthread_mutex_init(&pdst->lock, NULL))
+ goto err_mutex;
+
+ pdst->name = name;
+
+ return pdst;
+
+err_mutex:
+ dm_hash_destroy(pdst->store);
+err_hash:
+ dm_free(pdst);
+ return NULL;
+}
+
+void pdst_destroy(struct lvmpolld_store *pdst)
+{
+ if (!pdst)
+ return;
+
+ dm_hash_destroy(pdst->store);
+ pthread_mutex_destroy(&pdst->lock);
+ dm_free(pdst);
+}
+
+void pdst_locked_lock_all_pdlvs(const struct lvmpolld_store *pdst)
+{
+ struct dm_hash_node *n;
+
+ dm_hash_iterate(n, pdst->store)
+ pdlv_lock(dm_hash_get_data(pdst->store, n));
+}
+
+void pdst_locked_unlock_all_pdlvs(const struct lvmpolld_store *pdst)
+{
+ struct dm_hash_node *n;
+
+ dm_hash_iterate(n, pdst->store)
+ pdlv_unlock(dm_hash_get_data(pdst->store, n));
+}
+
+static void _pdlv_locked_dump(struct buffer *buff, const struct lvmpolld_lv *pdlv)
+{
+ char tmp[1024];
+ const struct lvmpolld_cmd_stat *cmd_state = &pdlv->cmd_state;
+
+ /* pdlv-section { */
+ if (dm_snprintf(tmp, sizeof(tmp), "\t%s {\n", pdlv->lvmpolld_id) > 0)
+ buffer_append(buff, tmp);
+
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvid=\"%s\"\n", pdlv->lvid) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\ttype=\"%s\"\n", polling_op(pdlv->type)) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvname=\"%s\"\n", pdlv->lvname) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvmpolld_internal_timeout=%d\n", pdlv->pdtimeout) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_interval=\"%s\"\n", pdlv->sinterval ?: "<undefined>") > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tLVM_SYSTEM_DIR=\"%s\"\n",
+ (*pdlv->lvm_system_dir_env ? (pdlv->lvm_system_dir_env + strlen("LVM_SYSTEM_DIR=")) : "<undefined>")) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_pid=%d\n", pdlv->cmd_pid) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tpolling_finished=%d\n", pdlv->polling_finished) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\terror_occured=%d\n", pdlv->error) > 0)
+ buffer_append(buff, tmp);
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\tinit_requests_count=%d\n", pdlv->init_rq_count) > 0)
+ buffer_append(buff, tmp);
+
+ /* lvm_commmand-section { */
+ buffer_append(buff, "\t\tlvm_command {\n");
+ if (cmd_state->retcode == -1 && !cmd_state->signal)
+ buffer_append(buff, "\t\t\tstate=\"" LVMPD_RESP_IN_PROGRESS "\"\n");
+ else {
+ buffer_append(buff, "\t\t\tstate=\"" LVMPD_RESP_FINISHED "\"\n");
+ if (dm_snprintf(tmp, sizeof(tmp), "\t\t\treason=\"%s\"\n\t\t\tvalue=%d\n",
+ (cmd_state->signal ? LVMPD_REAS_SIGNAL : LVMPD_REAS_RETCODE),
+ (cmd_state->signal ?: cmd_state->retcode)) > 0)
+ buffer_append(buff, tmp);
+ }
+ buffer_append(buff, "\t\t}\n");
+ /* } lvm_commmand-section */
+
+ buffer_append(buff, "\t}\n");
+ /* } pdlv-section */
+}
+
+void pdst_locked_dump(const struct lvmpolld_store *pdst, struct buffer *buff)
+{
+ struct dm_hash_node *n;
+
+ dm_hash_iterate(n, pdst->store)
+ _pdlv_locked_dump(buff, dm_hash_get_data(pdst->store, n));
+}
+
+void pdst_locked_send_cancel(const struct lvmpolld_store *pdst)
+{
+ struct lvmpolld_lv *pdlv;
+ struct dm_hash_node *n;
+
+ dm_hash_iterate(n, pdst->store) {
+ pdlv = dm_hash_get_data(pdst->store, n);
+ if (!pdlv_locked_polling_finished(pdlv))
+ pthread_cancel(pdlv->tid);
+ }
+}
+
+void pdst_locked_destroy_all_pdlvs(const struct lvmpolld_store *pdst)
+{
+ struct dm_hash_node *n;
+
+ dm_hash_iterate(n, pdst->store)
+ pdlv_destroy(dm_hash_get_data(pdst->store, n));
+}
+
+struct lvmpolld_thread_data *lvmpolld_thread_data_constructor(struct lvmpolld_lv *pdlv)
+{
+ struct lvmpolld_thread_data *data = (struct lvmpolld_thread_data *) dm_malloc(sizeof(struct lvmpolld_thread_data));
+ if (!data)
+ return NULL;
+
+ data->pdlv = NULL;
+ data->line = NULL;
+ data->fout = data->ferr = NULL;
+ data->outpipe[0] = data->outpipe[1] = data->errpipe[0] = data->errpipe[1] = -1;
+
+ if (pipe(data->outpipe) || pipe(data->errpipe)) {
+ lvmpolld_thread_data_destroy(data);
+ return NULL;
+ }
+
+ if (fcntl(data->outpipe[0], F_SETFD, FD_CLOEXEC) ||
+ fcntl(data->outpipe[1], F_SETFD, FD_CLOEXEC) ||
+ fcntl(data->errpipe[0], F_SETFD, FD_CLOEXEC) ||
+ fcntl(data->errpipe[1], F_SETFD, FD_CLOEXEC)) {
+ lvmpolld_thread_data_destroy(data);
+ return NULL;
+ }
+
+ data->pdlv = pdlv;
+
+ return data;
+}
+
+void lvmpolld_thread_data_destroy(void *thread_private)
+{
+ struct lvmpolld_thread_data *data = (struct lvmpolld_thread_data *) thread_private;
+ if (!data)
+ return;
+
+ if (data->pdlv) {
+ pdst_lock(data->pdlv->pdst);
+ /*
+ * FIXME: skip this step if lvmpolld is activated
+ * by systemd.
+ */
+ if (!pdlv_get_polling_finished(data->pdlv))
+ kill(data->pdlv->cmd_pid, SIGTERM);
+ pdlv_set_polling_finished(data->pdlv, 1);
+ pdst_locked_dec(data->pdlv->pdst);
+ pdst_unlock(data->pdlv->pdst);
+ }
+
+ dm_free(data->line);
+
+ if (data->fout && !fclose(data->fout))
+ data->outpipe[0] = -1;
+
+ if (data->ferr && !fclose(data->ferr))
+ data->errpipe[0] = -1;
+
+ if (data->outpipe[0] >= 0)
+ close(data->outpipe[0]);
+
+ if (data->outpipe[1] >= 0)
+ close(data->outpipe[1]);
+
+ if (data->errpipe[0] >= 0)
+ close(data->errpipe[0]);
+
+ if (data->errpipe[1] >= 0)
+ close(data->errpipe[1]);
+
+ dm_free(data);
+}