diff options
Diffstat (limited to 'daemons/lvmpolld/lvmpolld-data-utils.c')
-rw-r--r-- | daemons/lvmpolld/lvmpolld-data-utils.c | 388 |
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); +} |