mirror of
git://sourceware.org/git/lvm2.git
synced 2024-10-06 22:19:30 +03:00
lvmpolld: Add standalone polldaemon.
See doc/lvmpolld_overview.txt
This commit is contained in:
parent
be23fae488
commit
e587b0677b
@ -1,5 +1,7 @@
|
||||
Version 2.02.120 -
|
||||
===============================
|
||||
Add lvpoll for cmdline communication with lvmpolld.
|
||||
Add lvmpolld acting as a free-standing version of polldaemon.
|
||||
Properly validate PV size for pvcreate --restorefile.
|
||||
Fix check if pvcreate wiped device (2.02.117).
|
||||
Fix storing of vgid when caching metadata (2.02.118).
|
||||
|
@ -938,6 +938,16 @@ global {
|
||||
# Comments starting with the character # are ignored.
|
||||
# This configuration option does not have a default value defined.
|
||||
# system_id_file = ""
|
||||
|
||||
# Indicates whether to use lvmpolld instead of classical polldaemon (a process
|
||||
# forked off an initiating lvm command) or not. When set to 1 and native systemd
|
||||
# service is installed in the system lvm starts using lvmpolld. lvmpolld gets
|
||||
# auto-activated by systemd when a message lands on the respective lvmpolld socket.
|
||||
# All commands that would require polling of in-progress operation are therefore
|
||||
# spawned in lvmpolld's service cgroup.
|
||||
#
|
||||
# When set to 0 lvm falls back to classical polling.
|
||||
use_lvmpolld = 0
|
||||
}
|
||||
|
||||
# Configuration section activation.
|
||||
|
49
configure
vendored
49
configure
vendored
@ -636,6 +636,7 @@ kerneldir
|
||||
interface
|
||||
CMIRRORD_PIDFILE
|
||||
CLVMD_PIDFILE
|
||||
LVMPOLLD_PIDFILE
|
||||
LVMETAD_PIDFILE
|
||||
DMEVENTD_PIDFILE
|
||||
WRITE_INSTALL
|
||||
@ -724,6 +725,7 @@ CLDWHOLEARCHIVE
|
||||
CLDNOWHOLEARCHIVE
|
||||
CLDFLAGS
|
||||
CACHE
|
||||
BUILD_LVMPOLLD
|
||||
BUILD_LVMETAD
|
||||
BUILD_DMEVENTD
|
||||
BUILD_CMIRRORD
|
||||
@ -914,6 +916,8 @@ enable_valgrind_pool
|
||||
enable_devmapper
|
||||
enable_lvmetad
|
||||
with_lvmetad_pidfile
|
||||
enable_lvmpolld
|
||||
with_lvmpolld_pidfile
|
||||
enable_blkid_wiping
|
||||
enable_udev_systemd_background_jobs
|
||||
enable_udev_sync
|
||||
@ -1627,6 +1631,7 @@ Optional Features:
|
||||
--enable-valgrind-pool enable valgrind awareness of pools
|
||||
--disable-devmapper disable LVM2 device-mapper interaction
|
||||
--enable-lvmetad enable the LVM Metadata Daemon
|
||||
--enable-lvmpolld enable the LVM Polling Daemon
|
||||
--disable-blkid_wiping disable libblkid detection of signatures when wiping
|
||||
and use native code instead
|
||||
--disable-udev-systemd-background-jobs
|
||||
@ -1718,6 +1723,8 @@ Optional Packages:
|
||||
--with-optimisation=OPT C optimisation flag [OPT=-O2]
|
||||
--with-lvmetad-pidfile=PATH
|
||||
lvmetad pidfile [PID_DIR/lvmetad.pid]
|
||||
--with-lvmpolld-pidfile=PATH
|
||||
lvmpolld pidfile [PID_DIR/lvmpolld.pid]
|
||||
--with-localedir=DIR translation files in DIR [PREFIX/share/locale]
|
||||
--with-confdir=DIR configuration files in DIR [/etc]
|
||||
--with-staticdir=DIR static binaries in DIR [EPREFIX/sbin]
|
||||
@ -3033,6 +3040,7 @@ case "$host_os" in
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
LVMETAD=no
|
||||
LVMPOLLD=no
|
||||
ODIRECT=yes
|
||||
DM_IOCTLS=yes
|
||||
SELINUX=yes
|
||||
@ -10855,6 +10863,39 @@ _ACEOF
|
||||
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build lvmpolld" >&5
|
||||
$as_echo_n "checking whether to build lvmpolld... " >&6; }
|
||||
# Check whether --enable-lvmpolld was given.
|
||||
if test "${enable_lvmpolld+set}" = set; then :
|
||||
enableval=$enable_lvmpolld; LVMPOLLD=$enableval
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LVMPOLLD" >&5
|
||||
$as_echo "$LVMPOLLD" >&6; }
|
||||
|
||||
BUILD_LVMPOLLD=$LVMPOLLD
|
||||
|
||||
if test "$BUILD_LVMPOLLD" = yes; then
|
||||
|
||||
$as_echo "#define LVMPOLLD_SUPPORT 1" >>confdefs.h
|
||||
|
||||
|
||||
|
||||
# Check whether --with-lvmpolld-pidfile was given.
|
||||
if test "${with_lvmpolld_pidfile+set}" = set; then :
|
||||
withval=$with_lvmpolld_pidfile; LVMPOLLD_PIDFILE=$withval
|
||||
else
|
||||
LVMPOLLD_PIDFILE="$DEFAULT_PID_DIR/lvmpolld.pid"
|
||||
fi
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define LVMPOLLD_PIDFILE "$LVMPOLLD_PIDFILE"
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable libblkid detection of signatures when wiping" >&5
|
||||
$as_echo_n "checking whether to enable libblkid detection of signatures when wiping... " >&6; }
|
||||
@ -13144,11 +13185,13 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile daemons/lvmpolld/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@ -13857,6 +13900,7 @@ do
|
||||
"daemons/dmeventd/plugins/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/snapshot/Makefile" ;;
|
||||
"daemons/dmeventd/plugins/thin/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/thin/Makefile" ;;
|
||||
"daemons/lvmetad/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmetad/Makefile" ;;
|
||||
"daemons/lvmpolld/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmpolld/Makefile" ;;
|
||||
"conf/Makefile") CONFIG_FILES="$CONFIG_FILES conf/Makefile" ;;
|
||||
"conf/example.conf") CONFIG_FILES="$CONFIG_FILES conf/example.conf" ;;
|
||||
"conf/lvmlocal.conf") CONFIG_FILES="$CONFIG_FILES conf/lvmlocal.conf" ;;
|
||||
@ -13900,6 +13944,9 @@ do
|
||||
"scripts/lvm2_lvmetad_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_init_red_hat" ;;
|
||||
"scripts/lvm2_lvmetad_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.service" ;;
|
||||
"scripts/lvm2_lvmetad_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.socket" ;;
|
||||
"scripts/lvm2_lvmpolld_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmpolld_init_red_hat" ;;
|
||||
"scripts/lvm2_lvmpolld_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmpolld_systemd_red_hat.service" ;;
|
||||
"scripts/lvm2_lvmpolld_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmpolld_systemd_red_hat.socket" ;;
|
||||
"scripts/lvm2_monitoring_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_init_red_hat" ;;
|
||||
"scripts/lvm2_monitoring_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_systemd_red_hat.service" ;;
|
||||
"scripts/lvm2_pvscan_systemd_red_hat@.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_pvscan_systemd_red_hat@.service" ;;
|
||||
|
30
configure.in
30
configure.in
@ -39,6 +39,7 @@ case "$host_os" in
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
LVMETAD=no
|
||||
LVMPOLLD=no
|
||||
ODIRECT=yes
|
||||
DM_IOCTLS=yes
|
||||
SELINUX=yes
|
||||
@ -1077,6 +1078,29 @@ if test "$BUILD_LVMETAD" = yes; then
|
||||
[Path to lvmetad pidfile.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmpolld
|
||||
AC_MSG_CHECKING(whether to build lvmpolld)
|
||||
AC_ARG_ENABLE(lvmpolld,
|
||||
AC_HELP_STRING([--enable-lvmpolld],
|
||||
[enable the LVM Polling Daemon]),
|
||||
LVMPOLLD=$enableval)
|
||||
AC_MSG_RESULT($LVMPOLLD)
|
||||
|
||||
BUILD_LVMPOLLD=$LVMPOLLD
|
||||
|
||||
if test "$BUILD_LVMPOLLD" = yes; then
|
||||
AC_DEFINE([LVMPOLLD_SUPPORT], 1, [Define to 1 to include code that uses lvmpolld.])
|
||||
|
||||
AC_ARG_WITH(lvmpolld-pidfile,
|
||||
AC_HELP_STRING([--with-lvmpolld-pidfile=PATH],
|
||||
[lvmpolld pidfile [PID_DIR/lvmpolld.pid]]),
|
||||
LVMPOLLD_PIDFILE=$withval,
|
||||
LVMPOLLD_PIDFILE="$DEFAULT_PID_DIR/lvmpolld.pid")
|
||||
AC_DEFINE_UNQUOTED(LVMPOLLD_PIDFILE, ["$LVMPOLLD_PIDFILE"],
|
||||
[Path to lvmpolld pidfile.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable blkid wiping functionality
|
||||
AC_MSG_CHECKING(whether to enable libblkid detection of signatures when wiping)
|
||||
@ -1687,6 +1711,7 @@ AC_SUBST(BLKID_WIPING)
|
||||
AC_SUBST(BUILD_CMIRRORD)
|
||||
AC_SUBST(BUILD_DMEVENTD)
|
||||
AC_SUBST(BUILD_LVMETAD)
|
||||
AC_SUBST(BUILD_LVMPOLLD)
|
||||
AC_SUBST(CACHE)
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CFLOW_CMD)
|
||||
@ -1805,6 +1830,7 @@ AC_SUBST(VALGRIND_POOL)
|
||||
AC_SUBST(WRITE_INSTALL)
|
||||
AC_SUBST(DMEVENTD_PIDFILE)
|
||||
AC_SUBST(LVMETAD_PIDFILE)
|
||||
AC_SUBST(LVMPOLLD_PIDFILE)
|
||||
AC_SUBST(CLVMD_PIDFILE)
|
||||
AC_SUBST(CMIRRORD_PIDFILE)
|
||||
AC_SUBST(interface)
|
||||
@ -1838,6 +1864,7 @@ daemons/dmeventd/plugins/mirror/Makefile
|
||||
daemons/dmeventd/plugins/snapshot/Makefile
|
||||
daemons/dmeventd/plugins/thin/Makefile
|
||||
daemons/lvmetad/Makefile
|
||||
daemons/lvmpolld/Makefile
|
||||
conf/Makefile
|
||||
conf/example.conf
|
||||
conf/lvmlocal.conf
|
||||
@ -1881,6 +1908,9 @@ scripts/lvm2_cmirrord_systemd_red_hat.service
|
||||
scripts/lvm2_lvmetad_init_red_hat
|
||||
scripts/lvm2_lvmetad_systemd_red_hat.service
|
||||
scripts/lvm2_lvmetad_systemd_red_hat.socket
|
||||
scripts/lvm2_lvmpolld_init_red_hat
|
||||
scripts/lvm2_lvmpolld_systemd_red_hat.service
|
||||
scripts/lvm2_lvmpolld_systemd_red_hat.socket
|
||||
scripts/lvm2_monitoring_init_red_hat
|
||||
scripts/lvm2_monitoring_systemd_red_hat.service
|
||||
scripts/lvm2_pvscan_systemd_red_hat@.service
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@ -15,7 +15,7 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
.PHONY: dmeventd clvmd cmirrord lvmetad
|
||||
.PHONY: dmeventd clvmd cmirrord lvmetad lvmpolld
|
||||
|
||||
ifneq ("@CLVMD@", "none")
|
||||
SUBDIRS += clvmd
|
||||
@ -36,8 +36,12 @@ ifeq ("@BUILD_LVMETAD@", "yes")
|
||||
SUBDIRS += lvmetad
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
|
||||
SUBDIRS += lvmpolld
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld
|
||||
endif
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
49
daemons/lvmpolld/Makefile.in
Normal file
49
daemons/lvmpolld/Makefile.in
Normal file
@ -0,0 +1,49 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES = lvmpolld-core.c lvmpolld-data-utils.c lvmpolld-cmd-utils.c
|
||||
|
||||
TARGETS = lvmpolld
|
||||
|
||||
.PHONY: install_lvmpolld
|
||||
|
||||
CFLOW_LIST = $(SOURCES)
|
||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
||||
CFLOW_TARGET = lvmpolld
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
||||
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
|
||||
|
||||
LIBS += $(PTHREAD_LIBS)
|
||||
|
||||
LDFLAGS += -L$(top_builddir)/libdaemon/server $(DAEMON_LDFLAGS)
|
||||
CLDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||
CFLAGS += $(DAEMON_CFLAGS)
|
||||
|
||||
lvmpolld: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
|
||||
$(DL_LIBS) $(LVMLIBS) $(LIBS)
|
||||
|
||||
install_lvmpolld: lvmpolld
|
||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||
|
||||
install_lvm2: install_lvmpolld
|
||||
|
||||
install: install_lvm2
|
140
daemons/lvmpolld/lvmpolld-cmd-utils.c
Normal file
140
daemons/lvmpolld/lvmpolld-cmd-utils.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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 "lvmpolld-common.h"
|
||||
|
||||
/* extract this info from autoconf/automake files */
|
||||
#define LVPOLL_CMD "lvpoll"
|
||||
|
||||
#define MIN_ARGV_SIZE 8
|
||||
|
||||
static const char *const const polling_ops[] = { [PVMOVE] = LVMPD_REQ_PVMOVE,
|
||||
[CONVERT] = LVMPD_REQ_CONVERT,
|
||||
[MERGE] = LVMPD_REQ_MERGE,
|
||||
[MERGE_THIN] = LVMPD_REQ_MERGE_THIN };
|
||||
|
||||
const char *polling_op(enum poll_type type)
|
||||
{
|
||||
return type < POLL_TYPE_MAX ? polling_ops[type] : "<undefined>";
|
||||
}
|
||||
|
||||
static int add_to_cmd_arr(const char ***cmdargv, const char *str, unsigned *ind)
|
||||
{
|
||||
const char **newargv = *cmdargv;
|
||||
|
||||
if (*ind && !(*ind % MIN_ARGV_SIZE)) {
|
||||
newargv = dm_realloc(*cmdargv, (*ind / MIN_ARGV_SIZE + 1) * MIN_ARGV_SIZE * sizeof(char *));
|
||||
if (!newargv)
|
||||
return 0;
|
||||
*cmdargv = newargv;
|
||||
}
|
||||
|
||||
*(*cmdargv + (*ind)++) = str;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char **cmdargv_ctr(const struct lvmpolld_lv *pdlv, const char *lvm_binary, unsigned abort_polling, unsigned handle_missing_pvs)
|
||||
{
|
||||
unsigned i = 0;
|
||||
const char **cmd_argv = dm_malloc(MIN_ARGV_SIZE * sizeof(char *));
|
||||
|
||||
if (!cmd_argv)
|
||||
return NULL;
|
||||
|
||||
/* path to lvm2 binary */
|
||||
if (!add_to_cmd_arr(&cmd_argv, lvm_binary, &i))
|
||||
goto err;
|
||||
|
||||
/* cmd to execute */
|
||||
if (!add_to_cmd_arr(&cmd_argv, LVPOLL_CMD, &i))
|
||||
goto err;
|
||||
|
||||
/* transfer internal polling interval */
|
||||
if (pdlv->sinterval &&
|
||||
(!add_to_cmd_arr(&cmd_argv, "--interval", &i) ||
|
||||
!add_to_cmd_arr(&cmd_argv, pdlv->sinterval, &i)))
|
||||
goto err;
|
||||
|
||||
/* pass abort param */
|
||||
if (abort_polling &&
|
||||
!add_to_cmd_arr(&cmd_argv, "--abort", &i))
|
||||
goto err;
|
||||
|
||||
/* pass handle-missing-pvs. used by mirror polling operation */
|
||||
if (handle_missing_pvs &&
|
||||
!add_to_cmd_arr(&cmd_argv, "--handlemissingpvs", &i))
|
||||
goto err;
|
||||
|
||||
/* one of: "convert", "pvmove", "merge", "merge_thin" */
|
||||
if (!add_to_cmd_arr(&cmd_argv, "--polloperation", &i) ||
|
||||
!add_to_cmd_arr(&cmd_argv, polling_ops[pdlv->type], &i))
|
||||
goto err;
|
||||
|
||||
/* vg/lv name */
|
||||
if (!add_to_cmd_arr(&cmd_argv, pdlv->lvname, &i))
|
||||
goto err;
|
||||
|
||||
/* terminating NULL */
|
||||
if (!add_to_cmd_arr(&cmd_argv, NULL, &i))
|
||||
goto err;
|
||||
|
||||
return cmd_argv;
|
||||
err:
|
||||
dm_free(cmd_argv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: in fact exclude should be va list */
|
||||
static int copy_env(const char ***cmd_envp, unsigned *i, const char *exclude)
|
||||
{
|
||||
const char * const* tmp = (const char * const*) environ;
|
||||
|
||||
if (!tmp)
|
||||
return 0;
|
||||
|
||||
while (*tmp) {
|
||||
if (strncmp(*tmp, exclude, strlen(exclude)) && !add_to_cmd_arr(cmd_envp, *tmp, i))
|
||||
return 0;
|
||||
tmp++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char **cmdenvp_ctr(const struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
unsigned i = 0;
|
||||
const char **cmd_envp = dm_malloc(MIN_ARGV_SIZE * sizeof(char *));
|
||||
|
||||
if (!cmd_envp)
|
||||
return NULL;
|
||||
|
||||
/* copy whole environment from lvmpolld, exclude LVM_SYSTEM_DIR if set */
|
||||
if (!copy_env(&cmd_envp, &i, "LVM_SYSTEM_DIR="))
|
||||
goto err;
|
||||
|
||||
/* Add per client LVM_SYSTEM_DIR variable if set */
|
||||
if (*pdlv->lvm_system_dir_env && !add_to_cmd_arr(&cmd_envp, pdlv->lvm_system_dir_env, &i))
|
||||
goto err;
|
||||
|
||||
/* terminating NULL */
|
||||
if (!add_to_cmd_arr(&cmd_envp, NULL, &i))
|
||||
goto err;
|
||||
|
||||
return cmd_envp;
|
||||
err:
|
||||
dm_free(cmd_envp);
|
||||
return NULL;
|
||||
}
|
25
daemons/lvmpolld/lvmpolld-cmd-utils.h
Normal file
25
daemons/lvmpolld/lvmpolld-cmd-utils.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _LVM_LVMPOLLD_CMD_UTILS_H
|
||||
#define _LVM_LVMPOLLD_CMD_UTILS_H
|
||||
|
||||
#include "lvmpolld-data-utils.h"
|
||||
|
||||
const char **cmdargv_ctr(const struct lvmpolld_lv *pdlv, const char *lvm_binary, unsigned abort, unsigned handle_missing_pvs);
|
||||
const char **cmdenvp_ctr(const struct lvmpolld_lv *pdlv);
|
||||
|
||||
const char *polling_op(enum poll_type);
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_CMD_UTILS_H */
|
36
daemons/lvmpolld/lvmpolld-common.h
Normal file
36
daemons/lvmpolld/lvmpolld-common.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file must be included first by every lvmpolld source file.
|
||||
*/
|
||||
#ifndef _LVM_LVMPOLLD_COMMON_H
|
||||
#define _LVM_LVMPOLLD_COMMON_H
|
||||
|
||||
#include "configure.h"
|
||||
|
||||
#define _REENTRANT
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "libdevmapper.h"
|
||||
|
||||
#include "lvmpolld-cmd-utils.h"
|
||||
#include "lvmpolld-protocol.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif /* _LVM_LVMPOLLD_COMMON_H */
|
817
daemons/lvmpolld/lvmpolld-core.c
Normal file
817
daemons/lvmpolld/lvmpolld-core.c
Normal file
@ -0,0 +1,817 @@
|
||||
/*
|
||||
* 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 "lvm-version.h"
|
||||
#include "daemon-server.h"
|
||||
#include "daemon-log.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <wait.h>
|
||||
|
||||
#define LVMPOLLD_SOCKET DEFAULT_RUN_DIR "/lvmpolld.socket"
|
||||
|
||||
#define PD_LOG_PREFIX "LVMPOLLD"
|
||||
#define LVM2_LOG_PREFIX "\tLVPOLL"
|
||||
|
||||
/* predefined reason for response = "failed" case */
|
||||
#define REASON_REQ_NOT_IMPLEMENTED "request not implemented"
|
||||
#define REASON_MISSING_LVID "request requires lvid set"
|
||||
#define REASON_MISSING_LVNAME "request requires lvname set"
|
||||
#define REASON_MISSING_VGNAME "request requires vgname set"
|
||||
#define REASON_POLLING_FAILED "polling of lvm command failed"
|
||||
#define REASON_ILLEGAL_ABORT_REQUEST "abort only supported with PVMOVE polling operation"
|
||||
#define REASON_DIFFERENT_OPERATION_IN_PROGRESS "Different operation on LV already in progress"
|
||||
#define REASON_INVALID_INTERVAL "request requires interval set"
|
||||
#define REASON_ENOMEM "not enough memory"
|
||||
|
||||
struct lvmpolld_state {
|
||||
daemon_idle *idle;
|
||||
log_state *log;
|
||||
const char *log_config;
|
||||
const char *lvm_binary;
|
||||
|
||||
struct lvmpolld_store *id_to_pdlv_abort;
|
||||
struct lvmpolld_store *id_to_pdlv_poll;
|
||||
};
|
||||
|
||||
static pthread_key_t key;
|
||||
|
||||
static const char *_strerror_r(int errnum, struct lvmpolld_thread_data *data)
|
||||
{
|
||||
#ifdef _GNU_SOURCE
|
||||
return strerror_r(errnum, data->buf, sizeof(data->buf)); /* never returns NULL */
|
||||
#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
|
||||
return strerror_r(errnum, data->buf, sizeof(data->buf)) ? "" : data->buf;
|
||||
#else
|
||||
# warning "Can't decide proper strerror_r implementation. lvmpolld will not issue specific system error messages"
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
static void usage(const char *prog, FILE *file)
|
||||
{
|
||||
fprintf(file, "Usage:\n"
|
||||
"%s [-V] [-h] [-f] [-l {all|wire|debug}] [-s path]\n\n"
|
||||
" -V Show version info\n"
|
||||
" -h Show this help information\n"
|
||||
" -f Don't fork, run in the foreground\n"
|
||||
" -l Logging message level (-l {all|wire|debug})\n"
|
||||
" -p Set path to the pidfile\n"
|
||||
" -s Set path to the socket to listen on\n"
|
||||
" -B Path to lvm2 binary\n"
|
||||
" -t Time to wait in seconds before shutdown on idle (missing or 0 = inifinite)\n\n", prog);
|
||||
}
|
||||
|
||||
static int init(struct daemon_state *s)
|
||||
{
|
||||
struct lvmpolld_state *ls = s->private;
|
||||
ls->log = s->log;
|
||||
|
||||
if (!daemon_log_parse(ls->log, DAEMON_LOG_OUTLET_STDERR, ls->log_config, 1))
|
||||
return 0;
|
||||
|
||||
if (pthread_key_create(&key, lvmpolld_thread_data_destroy)) {
|
||||
FATAL(ls, "%s: %s", PD_LOG_PREFIX, "Failed to create pthread key");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ls->id_to_pdlv_poll = pdst_init("polling");
|
||||
ls->id_to_pdlv_abort = pdst_init("abort");
|
||||
|
||||
if (!ls->id_to_pdlv_poll || !ls->id_to_pdlv_abort) {
|
||||
FATAL(ls, "%s: %s", PD_LOG_PREFIX, "Failed to allocate internal data structures");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ls->lvm_binary = ls->lvm_binary ?: LVM_PATH;
|
||||
|
||||
if (access(ls->lvm_binary, X_OK)) {
|
||||
FATAL(ls, "%s: %s %s", PD_LOG_PREFIX, "Execute access rights denied on", ls->lvm_binary);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ls->idle)
|
||||
ls->idle->is_idle = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void lvmpolld_stores_lock(struct lvmpolld_state *ls)
|
||||
{
|
||||
pdst_lock(ls->id_to_pdlv_poll);
|
||||
pdst_lock(ls->id_to_pdlv_abort);
|
||||
}
|
||||
|
||||
static void lvmpolld_stores_unlock(struct lvmpolld_state *ls)
|
||||
{
|
||||
pdst_unlock(ls->id_to_pdlv_abort);
|
||||
pdst_unlock(ls->id_to_pdlv_poll);
|
||||
}
|
||||
|
||||
static void lvmpolld_global_lock(struct lvmpolld_state *ls)
|
||||
{
|
||||
lvmpolld_stores_lock(ls);
|
||||
|
||||
pdst_locked_lock_all_pdlvs(ls->id_to_pdlv_poll);
|
||||
pdst_locked_lock_all_pdlvs(ls->id_to_pdlv_abort);
|
||||
}
|
||||
|
||||
static void lvmpolld_global_unlock(struct lvmpolld_state *ls)
|
||||
{
|
||||
pdst_locked_unlock_all_pdlvs(ls->id_to_pdlv_abort);
|
||||
pdst_locked_unlock_all_pdlvs(ls->id_to_pdlv_poll);
|
||||
|
||||
lvmpolld_stores_unlock(ls);
|
||||
}
|
||||
|
||||
static int fini(struct daemon_state *s)
|
||||
{
|
||||
int done;
|
||||
const struct timespec t = { .tv_nsec = 250000000 }; /* .25 sec */
|
||||
struct lvmpolld_state *ls = s->private;
|
||||
|
||||
DEBUGLOG(s, "fini");
|
||||
|
||||
DEBUGLOG(s, "sending cancel requests");
|
||||
|
||||
lvmpolld_global_lock(ls);
|
||||
pdst_locked_send_cancel(ls->id_to_pdlv_poll);
|
||||
pdst_locked_send_cancel(ls->id_to_pdlv_abort);
|
||||
lvmpolld_global_unlock(ls);
|
||||
|
||||
DEBUGLOG(s, "waiting for background threads to finish");
|
||||
|
||||
while(1) {
|
||||
lvmpolld_stores_lock(ls);
|
||||
done = !pdst_locked_get_active_count(ls->id_to_pdlv_poll) &&
|
||||
!pdst_locked_get_active_count(ls->id_to_pdlv_abort);
|
||||
lvmpolld_stores_unlock(ls);
|
||||
if (done)
|
||||
break;
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
DEBUGLOG(s, "destroying internal data structures");
|
||||
|
||||
lvmpolld_stores_lock(ls);
|
||||
pdst_locked_destroy_all_pdlvs(ls->id_to_pdlv_poll);
|
||||
pdst_locked_destroy_all_pdlvs(ls->id_to_pdlv_abort);
|
||||
lvmpolld_stores_unlock(ls);
|
||||
|
||||
pdst_destroy(ls->id_to_pdlv_poll);
|
||||
pdst_destroy(ls->id_to_pdlv_abort);
|
||||
|
||||
pthread_key_delete(key);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static response reply(const char *res, const char *reason)
|
||||
{
|
||||
return daemon_reply_simple(res, "reason = %s", reason, NULL);
|
||||
}
|
||||
|
||||
static int read_single_line(struct lvmpolld_thread_data *data, int err)
|
||||
{
|
||||
ssize_t r = getline(&data->line, &data->line_size, err ? data->ferr : data->fout);
|
||||
|
||||
if (r > 0 && *(data->line + r - 1) == '\n')
|
||||
*(data->line + r - 1) = '\0';
|
||||
|
||||
return (r > 0);
|
||||
}
|
||||
|
||||
static void update_idle_state(struct lvmpolld_state *ls)
|
||||
{
|
||||
if (!ls->idle)
|
||||
return;
|
||||
|
||||
lvmpolld_stores_lock(ls);
|
||||
|
||||
ls->idle->is_idle = !pdst_locked_get_active_count(ls->id_to_pdlv_poll) &&
|
||||
!pdst_locked_get_active_count(ls->id_to_pdlv_abort);
|
||||
|
||||
lvmpolld_stores_unlock(ls);
|
||||
|
||||
DEBUGLOG(ls, "%s: %s %s%s", PD_LOG_PREFIX, "daemon is", ls->idle->is_idle ? "" : "not ", "idle");
|
||||
}
|
||||
|
||||
/* make this configurable */
|
||||
#define MAX_TIMEOUT 2
|
||||
|
||||
static int poll_for_output(struct lvmpolld_lv *pdlv, struct lvmpolld_thread_data *data)
|
||||
{
|
||||
int ch_stat, r, err = 1, fds_count = 2, timeout = 0;
|
||||
pid_t pid;
|
||||
struct lvmpolld_cmd_stat cmd_state = { .retcode = -1, .signal = 0 };
|
||||
struct pollfd fds[] = { { .fd = data->outpipe[0], .events = POLLIN },
|
||||
{ .fd = data->errpipe[0], .events = POLLIN } };
|
||||
|
||||
if (!(data->fout = fdopen(data->outpipe[0], "r")) || !(data->ferr = fdopen(data->errpipe[0], "r"))) {
|
||||
ERROR(pdlv->ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "failed to open file stream",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
do {
|
||||
r = poll(fds, 2, pdlv_get_timeout(pdlv) * 1000);
|
||||
} while (r < 0 && errno == EINTR);
|
||||
|
||||
DEBUGLOG(pdlv->ls, "%s: %s %d", PD_LOG_PREFIX, "poll() returned", r);
|
||||
if (r < 0) {
|
||||
ERROR(pdlv->ls, "%s: %s (PID %d) failed: (%d) %s",
|
||||
PD_LOG_PREFIX, "poll() for LVM2 cmd", pdlv->cmd_pid,
|
||||
errno, _strerror_r(errno, data));
|
||||
goto out;
|
||||
} else if (!r) {
|
||||
timeout++;
|
||||
|
||||
WARN(pdlv->ls, "%s: %s (PID %d) %s", PD_LOG_PREFIX,
|
||||
"polling for output of the lvm cmd", pdlv->cmd_pid,
|
||||
"has timed out");
|
||||
|
||||
if (timeout > MAX_TIMEOUT) {
|
||||
ERROR(pdlv->ls, "%s: %s (PID %d) (no output for %d seconds)",
|
||||
PD_LOG_PREFIX,
|
||||
"LVM2 cmd is unresponsive too long",
|
||||
pdlv->cmd_pid,
|
||||
timeout * pdlv_get_timeout(pdlv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
continue; /* while(1) */
|
||||
}
|
||||
|
||||
timeout = 0;
|
||||
|
||||
/* handle the command's STDOUT */
|
||||
if (fds[0].revents & POLLIN) {
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught input data in STDOUT");
|
||||
|
||||
assert(read_single_line(data, 0)); /* may block indef. anyway */
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: '%s'", LVM2_LOG_PREFIX,
|
||||
pdlv->cmd_pid, "STDOUT", data->line);
|
||||
} else if (fds[0].revents) {
|
||||
if (fds[0].revents & POLLHUP)
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught POLLHUP");
|
||||
else
|
||||
WARN(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "poll for command's STDOUT failed");
|
||||
|
||||
fds[0].fd = -1;
|
||||
fds_count--;
|
||||
}
|
||||
|
||||
/* handle the command's STDERR */
|
||||
if (fds[1].revents & POLLIN) {
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX,
|
||||
"caught input data in STDERR");
|
||||
|
||||
assert(read_single_line(data, 1)); /* may block indef. anyway */
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: '%s'", LVM2_LOG_PREFIX,
|
||||
pdlv->cmd_pid, "STDERR", data->line);
|
||||
} else if (fds[1].revents) {
|
||||
if (fds[1].revents & POLLHUP)
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught err POLLHUP");
|
||||
else
|
||||
WARN(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "poll for command's STDOUT failed");
|
||||
|
||||
fds[1].fd = -1;
|
||||
fds_count--;
|
||||
}
|
||||
|
||||
do {
|
||||
/*
|
||||
* fds_count == 0 means polling reached EOF
|
||||
* or received error on both descriptors.
|
||||
* In such case, just wait for command to finish
|
||||
*/
|
||||
pid = waitpid(pdlv->cmd_pid, &ch_stat, fds_count ? WNOHANG : 0);
|
||||
} while (pid < 0 && errno == EINTR);
|
||||
|
||||
if (pid) {
|
||||
if (pid < 0) {
|
||||
ERROR(pdlv->ls, "%s: %s (PID %d) failed: (%d) %s",
|
||||
PD_LOG_PREFIX, "waitpid() for lvm2 cmd",
|
||||
pdlv->cmd_pid, errno,
|
||||
_strerror_r(errno, data));
|
||||
goto out;
|
||||
}
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "child exited");
|
||||
break;
|
||||
}
|
||||
} /* while(1) */
|
||||
|
||||
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "about to collect remaining lines");
|
||||
if (fds[0].fd >= 0)
|
||||
while (read_single_line(data, 0)) {
|
||||
assert(r > 0);
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: %s", LVM2_LOG_PREFIX, pdlv->cmd_pid, "STDOUT", data->line);
|
||||
}
|
||||
if (fds[1].fd >= 0)
|
||||
while (read_single_line(data, 1)) {
|
||||
assert(r > 0);
|
||||
INFO(pdlv->ls, "%s: PID %d: %s: %s", LVM2_LOG_PREFIX, pdlv->cmd_pid, "STDERR", data->line);
|
||||
}
|
||||
|
||||
if (WIFEXITED(ch_stat)) {
|
||||
INFO(pdlv->ls, "%s: %s (PID %d) %s (%d)", PD_LOG_PREFIX,
|
||||
"lvm2 cmd", pdlv->cmd_pid, "exited with", WEXITSTATUS(ch_stat));
|
||||
cmd_state.retcode = WEXITSTATUS(ch_stat);
|
||||
} else if (WIFSIGNALED(ch_stat)) {
|
||||
WARN(pdlv->ls, "%s: %s (PID %d) %s (%d)", PD_LOG_PREFIX,
|
||||
"lvm2 cmd", pdlv->cmd_pid, "got terminated by signal",
|
||||
WTERMSIG(ch_stat));
|
||||
cmd_state.signal = WTERMSIG(ch_stat);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
if (!err)
|
||||
pdlv_set_cmd_state(pdlv, &cmd_state);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void debug_print(struct lvmpolld_state *ls, const char * const* ptr)
|
||||
{
|
||||
const char * const* tmp = ptr;
|
||||
|
||||
if (!tmp)
|
||||
return;
|
||||
|
||||
while (*tmp) {
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, *tmp);
|
||||
tmp++;
|
||||
}
|
||||
}
|
||||
|
||||
static void *fork_and_poll(void *args)
|
||||
{
|
||||
int outfd, errfd, state;
|
||||
struct lvmpolld_thread_data *data;
|
||||
pid_t r;
|
||||
|
||||
int error = 1;
|
||||
struct lvmpolld_lv *pdlv = (struct lvmpolld_lv *) args;
|
||||
struct lvmpolld_state *ls = pdlv->ls;
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
||||
data = lvmpolld_thread_data_constructor(pdlv);
|
||||
pthread_setspecific(key, data);
|
||||
pthread_setcancelstate(state, &state);
|
||||
|
||||
if (!data) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "Failed to initialize per-thread data");
|
||||
goto err;
|
||||
}
|
||||
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "cmd line arguments:");
|
||||
debug_print(ls, pdlv->cmdargv);
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "---end---");
|
||||
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "cmd environment variables:");
|
||||
debug_print(ls, pdlv->cmdenvp);
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "---end---");
|
||||
|
||||
outfd = data->outpipe[1];
|
||||
errfd = data->errpipe[1];
|
||||
|
||||
r = fork();
|
||||
if (!r) {
|
||||
/* child */
|
||||
/* !!! Do not touch any posix thread primitives !!! */
|
||||
|
||||
if ((dup2(outfd, STDOUT_FILENO ) != STDOUT_FILENO) ||
|
||||
(dup2(errfd, STDERR_FILENO ) != STDERR_FILENO))
|
||||
_exit(100);
|
||||
|
||||
execve(*(pdlv->cmdargv), (char *const *)pdlv->cmdargv, (char *const *)pdlv->cmdenvp);
|
||||
|
||||
_exit(101);
|
||||
} else {
|
||||
/* parent */
|
||||
if (r == -1) {
|
||||
ERROR(ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "fork failed",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto err;
|
||||
}
|
||||
|
||||
INFO(ls, "%s: LVM2 cmd \"%s\" (PID: %d)", PD_LOG_PREFIX, *(pdlv->cmdargv), r);
|
||||
|
||||
pdlv->cmd_pid = r;
|
||||
|
||||
/* failure to close write end of any pipe will result in broken polling */
|
||||
if (close(data->outpipe[1])) {
|
||||
ERROR(ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "failed to close write end of pipe",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto err;
|
||||
}
|
||||
data->outpipe[1] = -1;
|
||||
|
||||
if (close(data->errpipe[1])) {
|
||||
ERROR(ls, "%s: %s: (%d) %s", PD_LOG_PREFIX, "failed to close write end of err pipe",
|
||||
errno, _strerror_r(errno, data));
|
||||
goto err;
|
||||
}
|
||||
data->errpipe[1] = -1;
|
||||
|
||||
error = poll_for_output(pdlv, data);
|
||||
DEBUGLOG(ls, "%s: %s", PD_LOG_PREFIX, "polling for lvpoll output has finished");
|
||||
}
|
||||
|
||||
err:
|
||||
r = 0;
|
||||
|
||||
pdst_lock(pdlv->pdst);
|
||||
|
||||
if (error) {
|
||||
/* last reader is responsible for pdlv cleanup */
|
||||
r = pdlv->cmd_pid;
|
||||
pdlv_set_error(pdlv, 1);
|
||||
}
|
||||
|
||||
pdlv_set_polling_finished(pdlv, 1);
|
||||
if (data)
|
||||
data->pdlv = NULL;
|
||||
|
||||
pdst_locked_dec(pdlv->pdst);
|
||||
|
||||
pdst_unlock(pdlv->pdst);
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
||||
lvmpolld_thread_data_destroy(data);
|
||||
pthread_setspecific(key, NULL);
|
||||
pthread_setcancelstate(state, &state);
|
||||
|
||||
update_idle_state(ls);
|
||||
|
||||
/*
|
||||
* This is unfortunate case where we
|
||||
* know nothing about state of lvm cmd and
|
||||
* (eventually) ongoing progress.
|
||||
*
|
||||
* harvest zombies
|
||||
*/
|
||||
if (r)
|
||||
while(waitpid(r, NULL, 0) < 0 && errno == EINTR);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static response progress_info(client_handle h, struct lvmpolld_state *ls, request req)
|
||||
{
|
||||
char *id;
|
||||
struct lvmpolld_lv *pdlv;
|
||||
struct lvmpolld_store *pdst;
|
||||
struct lvmpolld_lv_state st;
|
||||
response r;
|
||||
const char *lvid = daemon_request_str(req, LVMPD_PARM_LVID, NULL);
|
||||
const char *sysdir = daemon_request_str(req, LVMPD_PARM_SYSDIR, NULL);
|
||||
unsigned abort_polling = daemon_request_int(req, LVMPD_PARM_ABORT, 0);
|
||||
|
||||
if (!lvid)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_LVID);
|
||||
|
||||
id = construct_id(sysdir, lvid);
|
||||
if (!id) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "progress_info request failed to construct ID.");
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
|
||||
DEBUGLOG(ls, "%s: %s: %s", PD_LOG_PREFIX, "ID", id);
|
||||
|
||||
pdst = abort_polling ? ls->id_to_pdlv_abort : ls->id_to_pdlv_poll;
|
||||
|
||||
pdst_lock(pdst);
|
||||
|
||||
pdlv = pdst_locked_lookup(pdst, id);
|
||||
if (pdlv) {
|
||||
/*
|
||||
* with store lock held, I'm the only reader accessing the pdlv
|
||||
*/
|
||||
st = pdlv_get_status(pdlv);
|
||||
|
||||
if (st.error || st.polling_finished) {
|
||||
INFO(ls, "%s: %s %s", PD_LOG_PREFIX,
|
||||
"Polling finished. Removing related data structure for LV",
|
||||
lvid);
|
||||
pdst_locked_remove(pdst, id);
|
||||
pdlv_destroy(pdlv);
|
||||
}
|
||||
}
|
||||
/* pdlv must not be dereferenced from now on */
|
||||
|
||||
pdst_unlock(pdst);
|
||||
|
||||
dm_free(id);
|
||||
|
||||
if (pdlv) {
|
||||
if (st.error)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_POLLING_FAILED);
|
||||
|
||||
if (st.polling_finished)
|
||||
r = daemon_reply_simple(LVMPD_RESP_FINISHED,
|
||||
"reason = %s", st.cmd_state.signal ? LVMPD_REAS_SIGNAL : LVMPD_REAS_RETCODE,
|
||||
LVMPD_PARM_VALUE " = %d", st.cmd_state.signal ?: st.cmd_state.retcode,
|
||||
NULL);
|
||||
else
|
||||
r = daemon_reply_simple(LVMPD_RESP_IN_PROGRESS, NULL);
|
||||
}
|
||||
else
|
||||
r = daemon_reply_simple(LVMPD_RESP_NOT_FOUND, NULL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct lvmpolld_lv *construct_pdlv(request req, struct lvmpolld_state *ls,
|
||||
struct lvmpolld_store *pdst,
|
||||
const char *interval, const char *id,
|
||||
const char *vgname, const char *lvname,
|
||||
const char *sysdir, enum poll_type type,
|
||||
unsigned abort_polling, unsigned uinterval)
|
||||
{
|
||||
const char **cmdargv, **cmdenvp;
|
||||
struct lvmpolld_lv *pdlv;
|
||||
unsigned handle_missing_pvs = daemon_request_int(req, LVMPD_PARM_HANDLE_MISSING_PVS, 0);
|
||||
|
||||
pdlv = pdlv_create(ls, id, vgname, lvname, sysdir, type,
|
||||
interval, uinterval, pdst);
|
||||
|
||||
if (!pdlv) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to create internal LV data structure.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmdargv = cmdargv_ctr(pdlv, pdlv->ls->lvm_binary, abort_polling, handle_missing_pvs);
|
||||
if (!cmdargv) {
|
||||
pdlv_destroy(pdlv);
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to construct cmd arguments for lvpoll command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmdenvp = cmdenvp_ctr(pdlv);
|
||||
if (!cmdenvp) {
|
||||
pdlv_destroy(pdlv);
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to construct cmd environment for lvpoll command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pdlv->cmdargv = cmdargv;
|
||||
pdlv->cmdenvp = cmdenvp;
|
||||
|
||||
return pdlv;
|
||||
}
|
||||
|
||||
static int spawn_detached_thread(struct lvmpolld_lv *pdlv)
|
||||
{
|
||||
int r;
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
r = pthread_create(&pdlv->tid, &attr, fork_and_poll, (void *)pdlv);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return !r;
|
||||
}
|
||||
|
||||
static response poll_init(client_handle h, struct lvmpolld_state *ls, request req, enum poll_type type)
|
||||
{
|
||||
char *id;
|
||||
struct lvmpolld_lv *pdlv;
|
||||
struct lvmpolld_store *pdst;
|
||||
unsigned uinterval;
|
||||
|
||||
const char *interval = daemon_request_str(req, LVMPD_PARM_INTERVAL, NULL);
|
||||
const char *lvid = daemon_request_str(req, LVMPD_PARM_LVID, NULL);
|
||||
const char *lvname = daemon_request_str(req, LVMPD_PARM_LVNAME, NULL);
|
||||
const char *vgname = daemon_request_str(req, LVMPD_PARM_VGNAME, NULL);
|
||||
const char *sysdir = daemon_request_str(req, LVMPD_PARM_SYSDIR, NULL);
|
||||
unsigned abort_polling = daemon_request_int(req, LVMPD_PARM_ABORT, 0);
|
||||
|
||||
assert(type < POLL_TYPE_MAX);
|
||||
|
||||
if (abort_polling && type != PVMOVE)
|
||||
return reply(LVMPD_RESP_EINVAL, REASON_ILLEGAL_ABORT_REQUEST);
|
||||
|
||||
if (!interval || strpbrk(interval, "-") || sscanf(interval, "%u", &uinterval) != 1)
|
||||
return reply(LVMPD_RESP_EINVAL, REASON_INVALID_INTERVAL);
|
||||
|
||||
if (!lvname)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_LVNAME);
|
||||
|
||||
if (!lvid)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_LVID);
|
||||
|
||||
if (!vgname)
|
||||
return reply(LVMPD_RESP_FAILED, REASON_MISSING_VGNAME);
|
||||
|
||||
id = construct_id(sysdir, lvid);
|
||||
if (!id) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "poll_init request failed to construct ID.");
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
|
||||
DEBUGLOG(ls, "%s: %s=%s", PD_LOG_PREFIX, "ID", id);
|
||||
|
||||
pdst = abort_polling ? ls->id_to_pdlv_abort : ls->id_to_pdlv_poll;
|
||||
|
||||
pdst_lock(pdst);
|
||||
|
||||
pdlv = pdst_locked_lookup(pdst, id);
|
||||
if (pdlv && pdlv_get_polling_finished(pdlv)) {
|
||||
WARN(ls, "%s: %s %s", PD_LOG_PREFIX, "Force removal of uncollected info for LV",
|
||||
lvid);
|
||||
/*
|
||||
* lvmpolld has to remove uncollected results in this case.
|
||||
* otherwise it would have to refuse request for new polling
|
||||
* lv with same id.
|
||||
*/
|
||||
pdst_locked_remove(pdst, id);
|
||||
pdlv_destroy(pdlv);
|
||||
pdlv = NULL;
|
||||
}
|
||||
|
||||
if (pdlv) {
|
||||
if (!pdlv_is_type(pdlv, type)) {
|
||||
pdst_unlock(pdst);
|
||||
ERROR(ls, "%s: %s '%s': expected: %s, requested: %s",
|
||||
PD_LOG_PREFIX, "poll operation type mismatch on LV identified by",
|
||||
id,
|
||||
polling_op(pdlv_get_type(pdlv)), polling_op(type));
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_EINVAL,
|
||||
REASON_DIFFERENT_OPERATION_IN_PROGRESS);
|
||||
}
|
||||
pdlv->init_rq_count++; /* safe. protected by store lock */
|
||||
} else {
|
||||
pdlv = construct_pdlv(req, ls, pdst, interval, id, vgname,
|
||||
lvname, sysdir, type, abort_polling, 2 * uinterval);
|
||||
if (!pdlv) {
|
||||
pdst_unlock(pdst);
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
if (!pdst_locked_insert(pdst, id, pdlv)) {
|
||||
pdlv_destroy(pdlv);
|
||||
pdst_unlock(pdst);
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "couldn't store internal LV data structure");
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
if (!spawn_detached_thread(pdlv)) {
|
||||
ERROR(ls, "%s: %s", PD_LOG_PREFIX, "failed to spawn detached monitoring thread");
|
||||
pdst_locked_remove(pdst, id);
|
||||
pdlv_destroy(pdlv);
|
||||
pdst_unlock(pdst);
|
||||
dm_free(id);
|
||||
return reply(LVMPD_RESP_FAILED, REASON_ENOMEM);
|
||||
}
|
||||
|
||||
pdst_locked_inc(pdst);
|
||||
if (ls->idle)
|
||||
ls->idle->is_idle = 0;
|
||||
}
|
||||
|
||||
pdst_unlock(pdst);
|
||||
|
||||
dm_free(id);
|
||||
|
||||
return daemon_reply_simple(LVMPD_RESP_OK, NULL);
|
||||
}
|
||||
|
||||
static response dump_state(client_handle h, struct lvmpolld_state *ls, request r)
|
||||
{
|
||||
response res = { 0 };
|
||||
struct buffer *b = &res.buffer;
|
||||
|
||||
buffer_init(b);
|
||||
|
||||
lvmpolld_global_lock(ls);
|
||||
|
||||
buffer_append(b, "# Registered polling operations\n\n");
|
||||
buffer_append(b, "poll {\n");
|
||||
pdst_locked_dump(ls->id_to_pdlv_poll, b);
|
||||
buffer_append(b, "}\n\n");
|
||||
|
||||
buffer_append(b, "# Registered abort operations\n\n");
|
||||
buffer_append(b, "abort {\n");
|
||||
pdst_locked_dump(ls->id_to_pdlv_abort, b);
|
||||
buffer_append(b, "}\n\n");
|
||||
|
||||
lvmpolld_global_unlock(ls);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static response handler(struct daemon_state s, client_handle h, request r)
|
||||
{
|
||||
struct lvmpolld_state *ls = s.private;
|
||||
const char *rq = daemon_request_str(r, "request", "NONE");
|
||||
|
||||
if (!strcmp(rq, LVMPD_REQ_PVMOVE))
|
||||
return poll_init(h, ls, r, PVMOVE);
|
||||
else if (!strcmp(rq, LVMPD_REQ_CONVERT))
|
||||
return poll_init(h, ls, r, CONVERT);
|
||||
else if (!strcmp(rq, LVMPD_REQ_MERGE))
|
||||
return poll_init(h, ls, r, MERGE);
|
||||
else if (!strcmp(rq, LVMPD_REQ_MERGE_THIN))
|
||||
return poll_init(h, ls, r, MERGE_THIN);
|
||||
else if (!strcmp(rq, LVMPD_REQ_PROGRESS))
|
||||
return progress_info(h, ls, r);
|
||||
else if (!strcmp(rq, LVMPD_REQ_DUMP))
|
||||
return dump_state(h, ls, r);
|
||||
else
|
||||
return reply(LVMPD_RESP_EINVAL, REASON_REQ_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
static int process_timeout_arg(const char *str, unsigned *max_timeouts)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long l;
|
||||
|
||||
l = strtoul(str, &endptr, 10);
|
||||
if (errno || *endptr || l >= UINT_MAX)
|
||||
return 0;
|
||||
|
||||
*max_timeouts = (unsigned) l;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
signed char opt;
|
||||
struct timeval timeout;
|
||||
daemon_idle di = { .ptimeout = &timeout };
|
||||
struct lvmpolld_state ls = { .log_config = "" };
|
||||
daemon_state s = {
|
||||
.daemon_fini = fini,
|
||||
.daemon_init = init,
|
||||
.handler = handler,
|
||||
.name = "lvmpolld",
|
||||
.pidfile = getenv("LVM_LVMPOLLD_PIDFILE") ?: LVMPOLLD_PIDFILE,
|
||||
.private = &ls,
|
||||
.protocol = LVMPOLLD_PROTOCOL,
|
||||
.protocol_version = LVMPOLLD_PROTOCOL_VERSION,
|
||||
.socket_path = getenv("LVM_LVMPOLLD_SOCKET") ?: LVMPOLLD_SOCKET,
|
||||
};
|
||||
|
||||
while ((opt = getopt(argc, argv, "?fhVl:p:s:B:t:")) != EOF) {
|
||||
switch (opt) {
|
||||
case '?':
|
||||
usage(argv[0], stderr);
|
||||
exit(0);
|
||||
case 'B': /* --binary */
|
||||
ls.lvm_binary = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
printf("lvmpolld version: " LVM_VERSION "\n");
|
||||
exit(1);
|
||||
case 'f':
|
||||
s.foreground = 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0], stdout);
|
||||
exit(0);
|
||||
case 'l':
|
||||
ls.log_config = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
s.pidfile = optarg;
|
||||
break;
|
||||
case 's': /* --socket */
|
||||
s.socket_path = optarg;
|
||||
break;
|
||||
case 't': /* --timeout in seconds */
|
||||
if (!process_timeout_arg(optarg, &di.max_timeouts)) {
|
||||
fprintf(stderr, "Invalid value of timeout parameter");
|
||||
exit(1);
|
||||
}
|
||||
/* 0 equals to wait indefinitely */
|
||||
if (di.max_timeouts)
|
||||
s.idle = ls.idle = &di;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
daemon_start(s);
|
||||
|
||||
return 0;
|
||||
}
|
388
daemons/lvmpolld/lvmpolld-data-utils.c
Normal file
388
daemons/lvmpolld/lvmpolld-data-utils.c
Normal file
@ -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)
|
||||
{
|
||||