mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
394 lines
9.9 KiB
C
394 lines
9.9 KiB
C
/*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "lvmpolld-common.h"
|
|
|
|
#include "config-util.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
|
|
static const char LVM_SYSTEM_DIR[] = "LVM_SYSTEM_DIR=";
|
|
|
|
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, "%s%s", LVM_SYSTEM_DIR, 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;
|
|
pdst->active_polling_count = 0;
|
|
|
|
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\t%s\"%s\"\n", LVM_SYSTEM_DIR,
|
|
(*pdlv->lvm_system_dir_env ? (pdlv->lvm_system_dir_env + (sizeof(LVM_SYSTEM_DIR) - 1)) : "<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->line_size = 0;
|
|
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);
|
|
}
|
|
|
|
/* may get reallocated in getline(). dm_free must not be used */
|
|
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)
|
|
(void) close(data->outpipe[0]);
|
|
|
|
if (data->outpipe[1] >= 0)
|
|
(void) close(data->outpipe[1]);
|
|
|
|
if (data->errpipe[0] >= 0)
|
|
(void) close(data->errpipe[0]);
|
|
|
|
if (data->errpipe[1] >= 0)
|
|
(void) close(data->errpipe[1]);
|
|
|
|
dm_free(data);
|
|
}
|