1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-10-01 21:44:22 +03:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Alasdair Kergon
24581482d0 make -O2 optimisation flag configurable. 2004-06-29 13:29:25 +00:00
Alasdair Kergon
0571c3b453 Reduce severity of setlocale failure message (ie suppress during boot). 2004-06-29 13:28:57 +00:00
Alasdair Kergon
73e7f5a0b0 Add initrd-lvm to list of recognised argv[0]s. [pld-linux] 2004-06-29 13:27:19 +00:00
Alasdair Kergon
20c0adb961 Fix LD_FLAGS->LDFLAGS. LD_DEPS->LDDEPS.
Update configure script: add --disable-selinux & some missing messages.
2004-06-28 14:01:24 +00:00
Patrick Caulfield
a01e03562f Make sure errors reach syslog(). 2004-06-28 10:26:42 +00:00
Alasdair Kergon
d184ed0130 update version 2004-06-25 10:31:04 +00:00
Alasdair Kergon
089e1c2aee Fix vgchange (de)activation of (open) LVs. 2004-06-24 14:48:01 +00:00
Alasdair Kergon
ebab0e91ee Missing .exported_symbols 2004-06-24 08:16:09 +00:00
Alasdair Kergon
858a2b1b88 Add cluster support. 2004-06-24 08:02:38 +00:00
Alasdair Kergon
02bd59827c Remove pv segments line from backport. 2004-06-20 15:14:31 +00:00
Alasdair Kergon
4991428510 Fix targets string size calc in driver.
Fix a uuid free in libdm-iface. [Eric Taylor]
Update version.
2004-06-20 13:50:42 +00:00
Alasdair Kergon
3b245f5dc1 fsadm configurable (default disabled as untested)
version update
2004-06-20 13:38:54 +00:00
Alasdair Kergon
c9c81da901 Display all filtered devices, not just PVs, with pvs -a. 2004-06-19 19:27:00 +00:00
Alasdair Kergon
4919cdc3fb Fix sync_dir() when no / in filename. 2004-06-19 19:24:33 +00:00
Alasdair Kergon
e90e1f577d vgcfgbackup -f accepts template with %s for VG name. 2004-06-19 18:55:29 +00:00
Alasdair Kergon
afd4284403 Extend hash functions to handle non-null-terminated data. 2004-06-18 15:08:22 +00:00
Alasdair Kergon
150b350d31 Add local activation support. 2004-06-16 17:13:41 +00:00
Alasdair Kergon
2818520bd1 Add dmsetup -C for column-based output. 2004-06-16 16:44:12 +00:00
Alasdair Kergon
2819952292 fsadm 2004-06-15 17:29:20 +00:00
Alasdair Kergon
5af71af51c tidy relative paths in makefile includes 2004-06-15 17:25:07 +00:00
Alasdair Kergon
07a55b51df lvresize + fsadm support - needs testing 2004-06-15 17:23:49 +00:00
Alasdair Kergon
66dd68b49d Clear message buffer before use. 2004-06-10 16:14:16 +00:00
78 changed files with 9580 additions and 5235 deletions

View File

@@ -22,11 +22,13 @@ ifeq ("@INTL@", "yes")
SUBDIRS += po
endif
SUBDIRS += lib tools
SUBDIRS += lib tools daemons
ifeq ($(MAKECMDGOALS),distclean)
SUBDIRS += lib/format1 \
SUBDIRS += daemons/clvmd \
lib/format1 \
lib/format_pool \
lib/locking \
lib/mirror \
lib/snapshot \
po \
@@ -35,14 +37,16 @@ endif
include make.tmpl
daemons: lib
lib: include
tools: lib
po: lib tools
po: tools daemons
ifeq ("@INTL@", "yes")
lib.pofile: include.pofile
tools.pofile: lib.pofile
po.pofile: lib.pofile tools.pofile
daemons.pofile: lib.pofile
po.pofile: tools.pofile daemons.pofile
pofile: po.pofile
endif

View File

@@ -1 +1 @@
2.00.16-cvs (2004-05-24)
2.00.19-cvs (2004-06-29)

View File

@@ -1,5 +1,27 @@
Version 2.00.17 -
=============================
Version 2.00.19 - 29 June 2004
==============================
Reduce severity of setlocale failure message.
Recognise argv[0] "initrd-lvm" (pld-linux).
Make -O2 configurable.
Added --disable-selinux to configure script.
LD_FLAGS->LDFLAGS & LD_DEPS->LDDEPS in configure script.
Add init_debug to clvmd.
Version 2.00.18 - 24 June 2004
==============================
Fix vgchange activation.
Add cluster support.
Version 2.00.17 - 20 June 2004
==============================
configure --enable-fsadm to try out fsadm. fsadm is not tested yet.
Display all filtered devices, not just PVs, with pvs -a.
Fix sync_dir() when no / in filename
vgcfgbackup -f accepts template with %s for VG name.
Extend hash functions to handle non-null-terminated data.
Add local activation support.
Tidy relative paths in makefile includes.
fsadm support for fsck and resizing - needs testing.
Add read-only GFS pool support.
Add lvm2create_initrd script from http://poochiereds.net/svn/lvm2/
Fix rounding of large diplayed sizes.

View File

@@ -1,5 +1,8 @@
Version 1.00.18
Version 1.00.18 - 20 Jun 2004
=============================
Fix a uuid free in libdm-iface.
Fix a targets string size calc in driver.
Add -c to dmsetup for column-based output.
Add target message-passing ioctl.
Version 1.00.17 - 17 Apr 2004

6999
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -17,13 +17,51 @@
## simplifies distribution package building considerably.
################################################################################
dnl Process this file with autoconf to produce a configure script.
################################################################################
dnl -- Process this file with autoconf to produce a configure script.
AC_INIT(lib/device/dev-cache.h)
dnl setup the directory where autoconf has auxilary files
################################################################################
dnl -- setup the directory where autoconf has auxilary files
AC_CONFIG_AUX_DIR(autoconf)
dnl Checks for programs.
################################################################################
dnl -- Get system type
AC_CANONICAL_SYSTEM
case "$host_os" in
linux*)
CFLAGS="$CFLAGS"
COPTIMISE_FLAG="-O2"
CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
CLDWHOLEARCHIVE="-Wl,-whole-archive"
CLDNOWHOLEARCHIVE="-Wl,-no-whole-archive"
LDDEPS="$LDDEPS .export.sym"
LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
SOFLAG="-shared"
DEVMAPPER=yes
ODIRECT=yes
SELINUX=yes
CLUSTER=internal
FSADM=no ;;
darwin*)
CFLAGS="$CFLAGS -no-cpp-precomp -fno-common"
COPTIMISE_FLAG="-O2"
CLDFLAGS="$CLDFLAGS"
CLDWHOLEARCHIVE="-all_load"
CLDNOWHOLEARCHIVE=
LDDEPS="$LDDEPS"
LDFLAGS="$LDFLAGS"
SOFLAG="-dynamiclib"
DEVMAPPER=no
ODIRECT=no
SELINUX=no
CLUSTER=none
FSADM=no ;;
esac
################################################################################
dnl -- Checks for programs.
AC_PROG_AWK
AC_PROG_CC
AC_PROG_INSTALL
@@ -31,12 +69,14 @@ AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB
dnl Checks for header files.
################################################################################
dnl -- Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h malloc.h sys/ioctl.h unistd.h)
dnl Checks for typedefs, structures, and compiler characteristics.
################################################################################
dnl -- Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_OFF_T
@@ -45,70 +85,61 @@ AC_TYPE_SIZE_T
AC_STRUCT_ST_RDEV
AC_HEADER_TIME
dnl Get system type
AC_CANONICAL_SYSTEM
case "$host_os" in
linux*)
CFLAGS=
CLDFLAGS="-Wl,--version-script,.export.sym"
CLDWHOLEARCHIVE="-Wl,-whole-archive"
CLDNOWHOLEARCHIVE="-Wl,-no-whole-archive"
LD_DEPS=".export.sym"
LD_FLAGS="-Wl,--export-dynamic"
SOFLAG="-shared"
DEVMAPPER=yes
ODIRECT=yes ;;
darwin*)
CFLAGS="-no-cpp-precomp -fno-common"
CLDFLAGS=
CLDWHOLEARCHIVE="-all_load"
CLDNOWHOLEARCHIVE=
LD_DEPS=
LD_FLAGS=
SOFLAG="-dynamiclib"
DEVMAPPER=no
ODIRECT=no ;;
esac
dnl -- prefix is /usr by default, the exec_prefix default is setup later
################################################################################
dnl -- Prefix is /usr by default, the exec_prefix default is setup later
AC_PREFIX_DEFAULT(/usr)
OWNER="root"
GROUP="root"
################################################################################
dnl -- Parallel make jobs?
AC_ARG_ENABLE(jobs, [ --enable-jobs=NUM Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=-j2)
################################################################################
dnl -- Setup the ownership of the files
echo $ac_n "Setting file owner to""... $ac_c" 1>&6
OWNER="root"
dnl -- setup the ownership of the files
AC_ARG_WITH(user,
[ --with-user=USER Set the owner of installed files ],
[ OWNER="$withval" ])
echo "$ac_t""$OWNER" 1>&6
if test x$OWNER != x; then
OWNER="-o $OWNER"
fi
dnl -- setup the group ownership of the files
################################################################################
dnl -- Setup the group ownership of the files
echo $ac_n "Setting group owner to""... $ac_c" 1>&6
GROUP="root"
AC_ARG_WITH(group,
[ --with-group=GROUP Set the group owner of installed files ],
[ GROUP="$withval" ])
echo "$ac_t""$GROUP" 1>&6
if test x$GROUP != x; then
GROUP="-g $GROUP"
fi
################################################################################
dnl -- LVM1 tool fallback option
echo $ac_n "checking whether to enable lvm1 fallback""... $ac_c" 1>&6
AC_ARG_ENABLE(lvm1_fallback, [ --enable-lvm1_fallback Use this to fall back and use LVM1 binaries if
device-mapper is missing from the kernel], LVM1_FALLBACK=$enableval, LVM1_FALLBACK=no)
echo "$ac_t""$LVM1_FALLBACK" 1>&6
if test x$LVM1_FALLBACK = xyes; then
CFLAGS="$CFLAGS -DLVM1_FALLBACK"
fi
################################################################################
dnl -- format1 inclusion type
echo $ac_n "checking whether to include support for lvm1 metadata""... $ac_c" 1>&6
AC_ARG_WITH(lvm1,
[ --with-lvm1=TYPE LVM1 metadata support: internal/shared/none
[TYPE=internal] ],
[ LVM1="$withval" ],
[ LVM1="internal" ])
echo "$ac_t""$LVM1" 1>&6
if [[ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ]];
then AC_MSG_ERROR(
@@ -121,12 +152,15 @@ if test x$LVM1 = xinternal; then
CFLAGS="$CFLAGS -DLVM1_INTERNAL"
fi
################################################################################
dnl -- format_pool inclusion type
echo $ac_n "checking whether to include support for GFS pool metadata""... $ac_c" 1>&6
AC_ARG_WITH(pool,
[ --with-pool=TYPE GFS pool read-only support: internal/shared/none
[TYPE=internal] ],
[ POOL="$withval" ],
[ POOL="internal" ])
echo "$ac_t""$POOL" 1>&6
if [[ "x$POOL" != xnone -a "x$POOL" != xinternal -a "x$POOL" != xshared ]];
then AC_MSG_ERROR(
@@ -139,15 +173,35 @@ if test x$POOL = xinternal; then
CFLAGS="$CFLAGS -DPOOL_INTERNAL"
fi
################################################################################
dnl -- cluster_locking inclusion type
echo $ac_n "checking whether to include support for cluster locking""... $ac_c" 1>&6
AC_ARG_WITH(cluster,
[ --with-cluster=TYPE Cluster LVM locking support: internal/shared/none
[TYPE=internal] ],
[ CLUSTER="$withval" ])
echo "$ac_t""$CLUSTER" 1>&6
AC_ARG_ENABLE(jobs, [ --enable-jobs=NUM Number of jobs to run simultaneously], JOBS=-j$enableval, JOBS=-j2)
if [[ "x$CLUSTER" != xnone -a "x$CLUSTER" != xinternal -a "x$CLUSTER" != xshared ]];
then AC_MSG_ERROR(
--with-cluster parameter invalid
)
exit
fi;
if test x$CLUSTER = xinternal; then
CFLAGS="$CFLAGS -DCLUSTER_LOCKING_INTERNAL"
fi
################################################################################
dnl -- snapshots inclusion type
echo $ac_n "checking whether to include snapshots""... $ac_c" 1>&6
AC_ARG_WITH(snapshots,
[ --with-snapshots=TYPE Snapshot support: internal/shared/none
[TYPE=internal] ],
[ SNAPSHOTS="$withval" ],
[ SNAPSHOTS="internal" ])
echo "$ac_t""$SNAPSHOTS" 1>&6
if [[ "x$SNAPSHOTS" != xnone -a "x$SNAPSHOTS" != xinternal -a "x$SNAPSHOTS" != xshared ]];
then AC_MSG_ERROR(
@@ -160,12 +214,15 @@ if test x$SNAPSHOTS = xinternal; then
CFLAGS="$CFLAGS -DSNAPSHOT_INTERNAL"
fi
################################################################################
dnl -- mirrors inclusion type
echo $ac_n "checking whether to include mirrors""... $ac_c" 1>&6
AC_ARG_WITH(mirrors,
[ --with-mirrors=TYPE Mirror support: internal/shared/none
[TYPE=internal] ],
[ MIRRORS="$withval" ],
[ MIRRORS="internal" ])
echo "$ac_t""$MIRRORS" 1>&6
if [[ "x$MIRRORS" != xnone -a "x$MIRRORS" != xinternal -a "x$MIRRORS" != xshared ]];
then AC_MSG_ERROR(
@@ -178,26 +235,66 @@ if test x$MIRRORS = xinternal; then
CFLAGS="$CFLAGS -DMIRRORED_INTERNAL"
fi
dnl Enables staticly-linked tools
################################################################################
dnl -- Enables staticly-linked tools
echo $ac_n "checking whether to use static linking""... $ac_c" 1>&6
AC_ARG_ENABLE(static_link, [ --enable-static_link Use this to link the tools to their libraries
statically. Default is dynamic linking], STATIC_LINK=$enableval, STATIC_LINK=no)
echo "$ac_t""$STATIC_LINK" 1>&6
dnl Enable readline
################################################################################
dnl -- Enable readline
echo $ac_n "checking whether to enable readline""... $ac_c" 1>&6
AC_ARG_ENABLE(readline, [ --enable-readline Enable readline support], \
READLINE=$enableval, READLINE=no)
echo "$ac_t""$READLINE" 1>&6
if test x$READLINE = xyes; then
CFLAGS="$CFLAGS -DREADLINE_SUPPORT"
fi
################################################################################
dnl -- Disable selinux
echo $ac_n "checking whether to enable selinux support""... $ac_c" 1>&6
AC_ARG_ENABLE(selinux, [ --disable-selinux Disable selinux support], \
SELINUX=$enableval)
echo "$ac_t""$SELINUX" 1>&6
################################################################################
dnl -- Build cluster LVM daemon
echo $ac_n "checking whether to build cluster LVM daemon""... $ac_c" 1>&6
AC_ARG_WITH(clvmd, [ --with-clvmd Build cluster LVM Daemon], \
CLVMD=$withval, CLVMD=no)
echo "$ac_t""$CLVMD" 1>&6
dnl -- If clvmd enabled without cluster locking, automagically include it
if test x$CLVMD = xyes && test x$CLUSTER = xnone; then
CLUSTER=internal
fi
################################################################################
dnl -- Enable debugging
echo $ac_n "checking whether to enable debugging""... $ac_c" 1>&6
dnl Enable Debugging
AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging], \
DEBUG=yes, DEBUG=no)
echo "$ac_t""$DEBUG" 1>&6
echo $ac_n "checking whether to enable device-mapper""... $ac_c" 1>&6
dnl Disable devmapper
dnl -- Normally turn off optimisation for debug builds
if test x$DEBUG = xyes; then
COPTIMISE_FLAG=
fi
################################################################################
dnl -- Override optimisation
echo $ac_n "checking for C optimisation flag""... $ac_c" 1>&6
AC_ARG_WITH(optimisation,
[ --with-optimisation=OPT C optimisation flag [OPT=-O2] ],
[ COPTIMISE_FLAG="$withval" ])
echo "$ac_t""$COPTIMISE_FLAG" 1>&6
################################################################################
dnl -- Disable devmapper
echo $ac_n "checking whether to use device-mapper""... $ac_c" 1>&6
AC_ARG_ENABLE(devmapper, [ --disable-devmapper Disable device-mapper interaction], \
DEVMAPPER=no)
echo "$ac_t""$DEVMAPPER" 1>&6
@@ -206,8 +303,9 @@ if test x$DEVMAPPER = xyes; then
CFLAGS="$CFLAGS -DDEVMAPPER_SUPPORT"
fi
################################################################################
dnl -- Disable O_DIRECT
echo $ac_n "checking whether to enable O_DIRECT""... $ac_c" 1>&6
dnl Disable O_DIRECT
AC_ARG_ENABLE(o_direct, [ --disable-o_direct Disable O_DIRECT], \
ODIRECT=no)
echo "$ac_t""$ODIRECT" 1>&6
@@ -216,8 +314,9 @@ if test x$ODIRECT = xyes; then
CFLAGS="$CFLAGS -DO_DIRECT_SUPPORT"
fi
################################################################################
dnl -- Enable cmdlib
echo $ac_n "checking whether to compile liblvm2cmd.so""... $ac_c" 1>&6
dnl Enable cmdlib
AC_ARG_ENABLE(cmdlib, [ --enable-cmdlib Build shared command library], \
CMDLIB=yes, CMDLIB=no)
echo "$ac_t""$CMDLIB" 1>&6
@@ -226,18 +325,27 @@ if test x$CMDLIB = xyes; then
CFLAGS="$CFLAGS -DCMDLIB"
fi
dnl Mess with default exec_prefix
################################################################################
dnl -- Enable fsadm
echo $ac_n "checking whether to build fsadm""... $ac_c" 1>&6
AC_ARG_ENABLE(fsadm, [ --enable-fsadm Enable fsadm], FSADM=yes)
echo "$ac_t""$FSADM" 1>&6
################################################################################
dnl -- Mess with default exec_prefix
if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]];
then exec_prefix="";
fi;
dnl Checks for library functions.
################################################################################
dnl -- Checks for library functions.
AC_PROG_GCC_TRADITIONAL
AC_TYPE_SIGNAL
AC_FUNC_VPRINTF
AC_CHECK_FUNCS(mkdir rmdir uname)
dnl check for termcap (Shamelessly copied from parted 1.4.17)
################################################################################
dnl -- Check for termcap (Shamelessly copied from parted 1.4.17)
if test x$READLINE = xyes; then
AC_SEARCH_LIBS(tgetent, ncurses curses termcap termlib, ,
AC_MSG_ERROR(
@@ -254,7 +362,8 @@ Note: (n)curses also seems to work as a substitute for termcap. This was
)
fi
dnl Check for dlopen
################################################################################
dnl -- Check for dlopen
AC_CHECK_LIB(dl, dlopen, HAVE_LIBDL=yes, HAVE_LIBDL=no)
if [[ "x$HAVE_LIBDL" = xyes -a "x$STATIC_LINK" = xno ]]; then
@@ -264,9 +373,10 @@ else
HAVE_LIBDL=no
fi
dnl Check for shared/static conflicts
if [[ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o \
"x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \
################################################################################
dnl -- Check for shared/static conflicts
if [[ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \
-o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \
\) -a "x$STATIC_LINK" = xyes ]];
then AC_MSG_ERROR(
Features cannot be 'shared' when building statically
@@ -274,18 +384,25 @@ Features cannot be 'shared' when building statically
exit
fi
dnl Check for is_selinux_enabled
AC_CHECK_LIB(selinux, is_selinux_enabled, HAVE_SELINUX=yes, HAVE_SELINUX=no)
################################################################################
dnl -- Check for is_selinux_enabled
if test x$SELINUX = xyes; then
AC_CHECK_LIB(selinux, is_selinux_enabled, HAVE_SELINUX=yes, HAVE_SELINUX=no)
if test x$HAVE_SELINUX = xyes; then
CFLAGS="$CFLAGS -DHAVE_SELINUX"
LIBS="-lselinux $LIBS"
if test x$HAVE_SELINUX = xyes; then
CFLAGS="$CFLAGS -DHAVE_SELINUX"
LIBS="-lselinux $LIBS"
else
echo "Disabling selinux" 1>&6
fi
fi
dnl Check for getopt
################################################################################
dnl -- Check for getopt
AC_CHECK_HEADERS(getopt.h, CFLAGS="$CFLAGS -DHAVE_GETOPTLONG")
dnl Check for readline (Shamelessly copied from parted 1.4.17)
################################################################################
dnl -- Check for readline (Shamelessly copied from parted 1.4.17)
if test x$READLINE = xyes; then
AC_CHECK_LIB(readline, readline, ,
AC_MSG_ERROR(
@@ -302,8 +419,9 @@ package as well (which may be called readline-devel or something similar).
fi
################################################################################
dnl -- Internationalisation stuff
echo $ac_n "checking whether to enable internationalisation""... $ac_c" 1>&6
dnl Internationalisation stuff
AC_ARG_ENABLE(nls, [ --enable-nls Enable Native Language Support],\
INTL=yes, INTL=no)
echo "$ac_t""$INTL" 1>&6
@@ -324,6 +442,7 @@ if test x$INTL = xyes; then
[ LOCALEDIR='${prefix}/share/locale' ])
fi
################################################################################
AC_ARG_WITH(confdir,
[ --with-confdir=DIR Configuration files in DIR [/etc]],
[ CONFDIR="$withval" ],
@@ -335,12 +454,14 @@ AC_ARG_WITH(staticdir,
[ STATICDIR='${exec_prefix}/sbin' ])
################################################################################
if test "-f VERSION"; then
LVM_VERSION="\"`cat VERSION`\""
else
LVM_VERSION="Unknown"
fi
################################################################################
AC_SUBST(JOBS)
AC_SUBST(STATIC_LINK)
AC_SUBST(LVM1)
@@ -350,11 +471,12 @@ AC_SUBST(MIRRORS)
AC_SUBST(OWNER)
AC_SUBST(GROUP)
AC_SUBST(CFLAGS)
AC_SUBST(COPTIMISE_FLAG)
AC_SUBST(CLDFLAGS)
AC_SUBST(CLDWHOLEARCHIVE)
AC_SUBST(CLDNOWHOLEARCHIVE)
AC_SUBST(LD_DEPS)
AC_SUBST(LD_FLAGS)
AC_SUBST(LDDEPS)
AC_SUBST(LDFLAGS)
AC_SUBST(SOFLAG)
AC_SUBST(LIBS)
AC_SUBST(LVM_VERSION)
@@ -370,23 +492,31 @@ AC_SUBST(CONFDIR)
AC_SUBST(STATICDIR)
AC_SUBST(INTL_PACKAGE)
AC_SUBST(INTL)
AC_SUBST(CLVMD)
AC_SUBST(CLUSTER)
AC_SUBST(FSADM)
dnl First and last lines should not contain files to generate in order to
dnl keep utility scripts running properly
################################################################################
dnl -- First and last lines should not contain files to generate in order to
dnl -- keep utility scripts running properly
AC_OUTPUT( \
Makefile \
make.tmpl \
make.tmpl \
daemons/Makefile \
daemons/clvmd/Makefile \
doc/Makefile \
include/Makefile \
lib/Makefile \
lib/format1/Makefile \
lib/format_pool/Makefile \
lib/locking/Makefile \
lib/mirror/Makefile \
lib/snapshot/Makefile \
man/Makefile \
po/Makefile \
tools/Makefile \
tools/version.h \
tools/fsadm/Makefile \
test/mm/Makefile \
test/device/Makefile \
test/format1/Makefile \
@@ -400,3 +530,9 @@ if test x$ODIRECT != xyes; then
echo Use of pvmove may cause machine to lock up under low memory conditions.
echo
fi
if test x$FSADM == xyes; then
echo
echo Warning: fsadm support is untested.
echo
fi

23
daemons/Makefile.in Normal file
View File

@@ -0,0 +1,23 @@
#
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
#
# This file is part of the 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 General Public License v.2.
#
# You should have received a copy of the GNU 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@
VPATH = @srcdir@
ifeq ("@CLVMD@", "yes")
SUBDIRS = clvmd
endif
include $(top_srcdir)/make.tmpl

46
daemons/clvmd/Makefile.in Normal file
View File

@@ -0,0 +1,46 @@
#
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
#
# This file is part of the 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 General Public License v.2.
#
# You should have received a copy of the GNU 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@
VPATH = @srcdir@
SOURCES = \
clvmd-cman.c \
clvmd-command.c \
clvmd.c \
libclvm.c \
lvm-functions.c \
system-lv.c
TARGETS = \
clvmd
include $(top_srcdir)/make.tmpl
CFLAGS += -D_REENTRANT -fno-strict-aliasing
LIBS += -ldevmapper -ldlm -llvm -lpthread
INSTALL_TARGETS = \
install_clvmd
clvmd: $(OBJECTS) $(top_srcdir)/lib/liblvm.a
$(CC) -o clvmd $(OBJECTS) $(LDFLAGS) $(LVMLIBS) $(LIBS)
.PHONY: install_clvmd
install_clvmd: $(TARGETS)
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) clvmd \
$(sbindir)/clvmd
install: $(INSTALL_TARGETS)

65
daemons/clvmd/clvm.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/* Definitions for CLVMD server and clients */
/*
* The protocol spoken over the cluster and across the local socket.
*/
#ifndef _CLVM_H
#define _CLVM_H
struct clvm_header {
uint8_t cmd; /* See below */
uint8_t flags; /* See below */
uint16_t xid; /* Transaction ID */
uint32_t clientid; /* Only used in Daemon->Daemon comms */
int32_t status; /* For replies, whether request succeeded */
uint32_t arglen; /* Length of argument below.
If >1500 then it will be passed
around the cluster in the system LV */
char node[1]; /* Actually a NUL-terminated string, node name.
If this is empty then the command is
forwarded to all cluster nodes unless
FLAG_LOCAL is also set. */
char args[1]; /* Arguments for the command follow the
node name, This member is only
valid if the node name is empty */
} __attribute__ ((packed));
/* Flags */
#define CLVMD_FLAG_LOCAL 1 /* Only do this on the local node */
#define CLVMD_FLAG_SYSTEMLV 2 /* Data in system LV under my node name */
/* Name of the local socket to communicate between libclvm and clvmd */
//static const char CLVMD_SOCKNAME[]="/var/run/clvmd";
static const char CLVMD_SOCKNAME[] = "\0clvmd";
/* Internal commands & replies */
#define CLVMD_CMD_REPLY 1
#define CLVMD_CMD_VERSION 2 /* Send version around cluster when we start */
#define CLVMD_CMD_GOAWAY 3 /* Die if received this - we are running
an incompatible version */
#define CLVMD_CMD_TEST 4 /* Just for mucking about */
#define CLVMD_CMD_LOCK 30
#define CLVMD_CMD_UNLOCK 31
/* Lock/Unlock commands */
#define CLVMD_CMD_LOCK_LV 50
#define CLVMD_CMD_LOCK_VG 51
#endif

499
daemons/clvmd/clvmd-cman.c Normal file
View File

@@ -0,0 +1,499 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/*
* CMAN communication layer for clvmd.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <errno.h>
#include "clvmd-comms.h"
#include "clvm.h"
#include "libdlm.h"
#include "log.h"
#include "clvmd.h"
#include "lvm-functions.h"
#define LOCKSPACE_NAME "clvmd"
static int cluster_sock;
static int num_nodes;
static struct cl_cluster_node *nodes = NULL;
static int count_nodes; /* size of allocated nodes array */
static int max_updown_nodes = 50; /* Current size of the allocated array */
/* Node up/down status, indexed by nodeid */
static int *node_updown = NULL;
static dlm_lshandle_t *lockspace;
static void sigusr1_handler(int sig);
static void count_clvmds_running(void);
static void get_members(void);
static int nodeid_from_csid(char *csid);
static int name_from_nodeid(int nodeid, char *name);
struct lock_wait {
pthread_cond_t cond;
pthread_mutex_t mutex;
struct dlm_lksb lksb;
};
int init_cluster()
{
struct sockaddr_cl saddr;
int port = CLUSTER_PORT_CLVMD;
/* Open the cluster communication socket */
cluster_sock = socket(AF_CLUSTER, SOCK_DGRAM, CLPROTO_CLIENT);
if (cluster_sock == -1) {
perror("Can't open cluster socket");
return -1;
}
/* Bind to our port number on the cluster.
Writes to this will block if the cluster loses quorum */
saddr.scl_family = AF_CLUSTER;
saddr.scl_port = port;
if (bind
(cluster_sock, (struct sockaddr *) &saddr,
sizeof(struct sockaddr_cl))) {
log_error("Can't bind cluster socket: %m");
return -1;
}
/* Get the cluster members list */
get_members();
count_clvmds_running();
/* Create a lockspace for LV & VG locks to live in */
lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
if (!lockspace) {
log_error("Unable to create lockspace for CLVM\n");
return -1;
}
dlm_ls_pthread_init(lockspace);
return 0;
}
int get_main_cluster_fd()
{
return cluster_sock;
}
int get_num_nodes()
{
return num_nodes;
}
/* send_message with the fd check removed */
int cluster_send_message(void *buf, int msglen, char *csid, const char *errtext)
{
struct iovec iov[2];
struct msghdr msg;
struct sockaddr_cl saddr;
int len = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iovlen = 1;
msg.msg_iov = iov;
msg.msg_flags = 0;
iov[0].iov_len = msglen;
iov[0].iov_base = buf;
saddr.scl_family = AF_CLUSTER;
saddr.scl_port = CLUSTER_PORT_CLVMD;
if (csid) {
msg.msg_name = &saddr;
msg.msg_namelen = sizeof(saddr);
memcpy(&saddr.scl_nodeid, csid, MAX_CSID_LEN);
} else { /* Cluster broadcast */
msg.msg_name = NULL;
msg.msg_namelen = 0;
}
do {
len = sendmsg(cluster_sock, &msg, 0);
if (len < 0 && errno != EAGAIN)
log_error(errtext);
} while (len == -1 && errno == EAGAIN);
return len;
}
void get_our_csid(char *csid)
{
int i;
memset(csid, 0, MAX_CSID_LEN);
for (i = 0; i < num_nodes; i++) {
if (nodes[i].us)
memcpy(csid, &nodes[i].node_id, MAX_CSID_LEN);
}
}
/* Call a callback routine for each node that known (down mean not running a clvmd) */
int cluster_do_node_callback(struct local_client *client,
void (*callback) (struct local_client *, char *,
int))
{
int i;
int somedown = 0;
for (i = 0; i < get_num_nodes(); i++) {
callback(client, (char *)&nodes[i].node_id, node_updown[nodes[i].node_id]);
if (!node_updown[nodes[i].node_id])
somedown = -1;
}
return somedown;
}
/* Process OOB message from the cluster socket,
this currently just means that a node has stopped listening on our port */
static void process_oob_msg(char *buf, int len, int nodeid)
{
char namebuf[256];
switch (buf[0]) {
case CLUSTER_OOB_MSG_PORTCLOSED:
name_from_nodeid(nodeid, namebuf);
log_notice("clvmd on node %s has died\n", namebuf);
DEBUGLOG("Got OOB message, removing node %s\n", namebuf);
node_updown[nodeid] = 0;
break;
case CLUSTER_OOB_MSG_STATECHANGE:
DEBUGLOG("Got OOB message, Cluster state change\n");
get_members();
break;
default:
/* ERROR */
DEBUGLOG("Got unknown OOB message: %d\n", buf[0]);
}
}
int cluster_fd_callback(struct local_client *fd, char *buf, int len, char *csid,
struct local_client **new_client)
{
struct iovec iov[2];
struct msghdr msg;
struct sockaddr_cl saddr;
/* We never return a new client */
*new_client = NULL;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iovlen = 1;
msg.msg_iov = iov;
msg.msg_name = &saddr;
msg.msg_flags = 0;
msg.msg_namelen = sizeof(saddr);
iov[0].iov_len = len;
iov[0].iov_base = buf;
len = recvmsg(cluster_sock, &msg, MSG_OOB | O_NONBLOCK);
if (len < 0 && errno == EAGAIN)
return len;
DEBUGLOG("Read on cluster socket, len = %d\n", len);
/* A real error */
if (len < 0) {
log_error("read error on cluster socket: %m");
return 0;
}
/* EOF - we have left the cluster */
if (len == 0)
return 0;
/* Is it OOB? probably a node gone down */
if (msg.msg_flags & MSG_OOB) {
process_oob_msg(iov[0].iov_base, len, saddr.scl_nodeid);
/* Tell the upper layer to ignore this message */
len = -1;
errno = EAGAIN;
}
memcpy(csid, &saddr.scl_nodeid, sizeof(saddr.scl_nodeid));
return len;
}
void add_up_node(char *csid)
{
/* It's up ! */
int nodeid = nodeid_from_csid(csid);
if (nodeid >= max_updown_nodes) {
int *new_updown = realloc(node_updown, max_updown_nodes + 10);
if (new_updown) {
node_updown = new_updown;
max_updown_nodes += 10;
DEBUGLOG("realloced more space for nodes. now %d\n",
max_updown_nodes);
} else {
log_error
("Realloc failed. Node status for clvmd will be wrong\n");
return;
}
}
node_updown[nodeid] = 1;
DEBUGLOG("Added new node %d to updown list\n", nodeid);
}
void cluster_closedown()
{
unlock_all();
dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
close(cluster_sock);
}
static int is_listening(int nodeid)
{
struct cl_listen_request rq;
int status;
rq.port = CLUSTER_PORT_CLVMD;
rq.nodeid = nodeid;
do {
status = ioctl(cluster_sock, SIOCCLUSTER_ISLISTENING, &rq);
if (status < 0 && errno == EBUSY) { /* Don't busywait */
sleep(1);
errno = EBUSY; /* In case sleep trashes it */
}
}
while (status < 0 && errno == EBUSY);
return status;
}
/* Populate the list of CLVMDs running.
called only at startup time */
void count_clvmds_running(void)
{
int i;
for (i = 0; i < num_nodes; i++) {
node_updown[nodes[i].node_id] = is_listening(nodes[i].node_id);
}
}
/* Get a list of active cluster members */
static void get_members()
{
struct cl_cluster_nodelist nodelist;
num_nodes = ioctl(cluster_sock, SIOCCLUSTER_GETMEMBERS, 0);
if (num_nodes == -1) {
perror("get nodes");
} else {
/* Not enough room for new nodes list ? */
if (num_nodes > count_nodes && nodes) {
free(nodes);
nodes = NULL;
}
if (nodes == NULL) {
count_nodes = num_nodes + 10; /* Overallocate a little */
nodes = malloc(count_nodes * sizeof(struct cl_cluster_node));
if (!nodes) {
perror("Unable to allocate nodes array\n");
exit(5);
}
}
nodelist.max_members = count_nodes;
nodelist.nodes = nodes;
num_nodes = ioctl(cluster_sock, SIOCCLUSTER_GETMEMBERS, &nodelist);
if (num_nodes <= 0) {
perror("get node details");
exit(6);
}
/* Sanity check struct */
if (nodes[0].size != sizeof(struct cl_cluster_node)) {
log_error
("sizeof(cl_cluster_node) does not match size returned from the kernel: aborting\n");
exit(10);
}
if (node_updown == NULL) {
node_updown =
(int *) malloc(sizeof(int) *
max(num_nodes, max_updown_nodes));
memset(node_updown, 0,
sizeof(int) * max(num_nodes, max_updown_nodes));
}
}
}
/* Convert a node name to a CSID */
int csid_from_name(char *csid, char *name)
{
int i;
for (i = 0; i < num_nodes; i++) {
if (strcmp(name, nodes[i].name) == 0) {
memcpy(csid, &nodes[i].node_id, MAX_CSID_LEN);
return 0;
}
}
return -1;
}
/* Convert a CSID to a node name */
int name_from_csid(char *csid, char *name)
{
int i;
for (i = 0; i < num_nodes; i++) {
if (memcmp(csid, &nodes[i].node_id, MAX_CSID_LEN) == 0) {
strcpy(name, nodes[i].name);
return 0;
}
}
/* Who?? */
strcpy(name, "Unknown");
return -1;
}
/* Convert a node ID to a node name */
int name_from_nodeid(int nodeid, char *name)
{
int i;
for (i = 0; i < num_nodes; i++) {
if (nodeid == nodes[i].node_id) {
strcpy(name, nodes[i].name);
return 0;
}
}
/* Who?? */
strcpy(name, "Unknown");
return -1;
}
/* Convert a CSID to a node ID */
static int nodeid_from_csid(char *csid)
{
int nodeid;
memcpy(&nodeid, csid, MAX_CSID_LEN);
return nodeid;
}
int is_quorate()
{
return ioctl(cluster_sock, SIOCCLUSTER_ISQUORATE, 0);
}
static void sync_ast_routine(void *arg)
{
struct lock_wait *lwait = arg;
pthread_mutex_lock(&lwait->mutex);
pthread_cond_signal(&lwait->cond);
pthread_mutex_unlock(&lwait->mutex);
}
int sync_lock(const char *resource, int mode, int flags, int *lockid)
{
int status;
struct lock_wait lwait;
if (!lockid) {
errno = EINVAL;
return -1;
}
/* Conversions need the lockid in the LKSB */
if (flags & LKF_CONVERT)
lwait.lksb.sb_lkid = *lockid;
pthread_cond_init(&lwait.cond, NULL);
pthread_mutex_init(&lwait.mutex, NULL);
pthread_mutex_lock(&lwait.mutex);
status = dlm_ls_lock(lockspace,
mode,
&lwait.lksb,
flags,
resource,
strlen(resource),
0, sync_ast_routine, &lwait, NULL, NULL);
if (status)
return status;
/* Wait for it to complete */
pthread_cond_wait(&lwait.cond, &lwait.mutex);
pthread_mutex_unlock(&lwait.mutex);
*lockid = lwait.lksb.sb_lkid;
errno = lwait.lksb.sb_status;
if (lwait.lksb.sb_status)
return -1;
else
return 0;
}
int sync_unlock(const char *resource /* UNUSED */, int lockid)
{
int status;
struct lock_wait lwait;
pthread_cond_init(&lwait.cond, NULL);
pthread_mutex_init(&lwait.mutex, NULL);
pthread_mutex_lock(&lwait.mutex);
status = dlm_ls_unlock(lockspace, lockid, 0, &lwait.lksb, &lwait);
if (status)
return status;
/* Wait for it to complete */
pthread_cond_wait(&lwait.cond, &lwait.mutex);
pthread_mutex_unlock(&lwait.mutex);
errno = lwait.lksb.sb_status;
if (lwait.lksb.sb_status != EUNLOCK)
return -1;
else
return 0;
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/*
CLVMD Cluster LVM daemon command processor.
To add commands to the daemon simply add a processor in do_command and return
and messages back in buf and the length in *retlen. The initial value of
buflen is the maximum size of the buffer. if buf is not large enough then it
may be reallocated by the functions in here to a suitable size bearing in
mind that anything larger than the passed-in size will have to be returned
using the system LV and so performance will suffer.
The status return will be negated and passed back to the originating node.
pre- and post- command routines are called only on the local node. The
purpose is primarily to get and release locks, though the pre- routine should
also do any other local setups required by the command (if any) and can
return a failure code that prevents the command from being distributed around
the cluster
The pre- and post- routines are run in their own thread so can block as long
they like, do_command is run in the main clvmd thread so should not block for
too long. If the pre-command returns an error code (!=0) then the command
will not be propogated around the cluster but the post-command WILL be called
Also note that the pre and post routine are *always* called on the local
node, even if the command to be executed was only requested to run on a
remote node. It may peek inside the client structure to check the status of
the command.
The clients of the daemon must, naturally, understand the return messages and
codes.
Routines in here may only READ the values in the client structure passed in
apart from client->private which they are free to do what they like with.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include "list.h"
#include "locking.h"
#include "log.h"
#include "lvm-functions.h"
#include "clvmd-comms.h"
#include "clvm.h"
#include "clvmd.h"
#include "libdlm.h"
/* This is where all the real work happens:
NOTE: client will be NULL when this is executed on a remote node */
int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
char **buf, int buflen, int *retlen)
{
char *args = msg->node + strlen(msg->node) + 1;
int arglen = msglen - sizeof(struct clvm_header) - strlen(msg->node);
int status = 0;
char *lockname;
struct utsname nodeinfo;
unsigned char lock_cmd;
unsigned char lock_flags;
/* Do the command */
switch (msg->cmd) {
/* Just a test message */
case CLVMD_CMD_TEST:
if (arglen > buflen) {
buflen = arglen + 200;
*buf = realloc(*buf, buflen);
}
uname(&nodeinfo);
*retlen = 1 + snprintf(*buf, buflen, "TEST from %s: %s v%s",
nodeinfo.nodename, args,
nodeinfo.release);
break;
case CLVMD_CMD_LOCK_VG:
/* Check to see if the VG is in use by LVM1 */
status = do_check_lvm1(&args[2]);
break;
case CLVMD_CMD_LOCK_LV:
/* This is the biggie */
lock_cmd = args[0];
lock_flags = args[1];
lockname = &args[2];
status = do_lock_lv(lock_cmd, lock_flags, lockname);
/* Replace EIO with something less scary */
if (status == EIO) {
*retlen =
1 + snprintf(*buf, buflen,
"Internal lvm error, check syslog");
return EIO;
}
break;
default:
/* Won't get here because command is validated in pre_command */
break;
}
/* Check the status of the command and return the error text */
if (status) {
*retlen = 1 + snprintf(*buf, buflen, strerror(status));
}
return status;
}
/* Pre-command is a good place to get locks that are needed only for the duration
of the commands around the cluster (don't forget to free them in post-command),
and to sanity check the command arguments */
int do_pre_command(struct local_client *client)
{
struct clvm_header *header =
(struct clvm_header *) client->bits.localsock.cmd;
unsigned char lock_cmd;
unsigned char lock_flags;
char *args = header->node + strlen(header->node) + 1;
int lockid;
int status = 0;
char *lockname;
switch (header->cmd) {
case CLVMD_CMD_TEST:
status = sync_lock("CLVMD_TEST", LKM_EXMODE, 0, &lockid);
client->bits.localsock.private = (void *) lockid;
break;
case CLVMD_CMD_LOCK_VG:
lock_cmd = args[0];
lock_flags = args[1];
lockname = &args[2];
DEBUGLOG("doing PRE command LOCK_VG %s at %x\n", lockname,
lock_cmd);
if (lock_cmd == LCK_UNLOCK) {
hold_unlock(lockname);
} else {
status =
hold_lock(lockname, (int) lock_cmd,
(int) lock_flags);
if (status)
status = errno;
}
break;
case CLVMD_CMD_LOCK_LV:
lock_cmd = args[0];
lock_flags = args[1];
lockname = &args[2];
status = pre_lock_lv(lock_cmd, lock_flags, lockname);
break;
default:
log_error("Unknown command %d received\n", header->cmd);
status = EINVAL;
}
return status;
}
/* Note that the post-command routine is called even if the pre-command or the real command
failed */
int do_post_command(struct local_client *client)
{
struct clvm_header *header =
(struct clvm_header *) client->bits.localsock.cmd;
int status = 0;
unsigned char lock_cmd;
unsigned char lock_flags;
char *args = header->node + strlen(header->node) + 1;
char *lockname;
switch (header->cmd) {
case CLVMD_CMD_TEST:
status =
sync_unlock("CLVMD_TEST", (int) (long) client->bits.localsock.private);
break;
case CLVMD_CMD_LOCK_VG:
/* Nothing to do here */
break;
case CLVMD_CMD_LOCK_LV:
lock_cmd = args[0];
lock_flags = args[1];
lockname = &args[2];
status = post_lock_lv(lock_cmd, lock_flags, lockname);
break;
}
return status;
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/*
* Abstraction layer for clvmd cluster communications
*/
#ifndef _CLVMD_COMMS_H
#define _CLVMD_COMMS_H
struct local_client;
extern int cluster_send_message(void *buf, int msglen, char *csid,
const char *errtext);
extern int name_from_csid(char *csid, char *name);
extern int csid_from_name(char *csid, char *name);
extern int get_num_nodes(void);
extern int cluster_fd_callback(struct local_client *fd, char *buf, int len,
char *csid, struct local_client **new_client);
extern int init_cluster(void);
extern int get_main_cluster_fd(void); /* gets accept FD or cman cluster socket */
extern int cluster_do_node_callback(struct local_client *client,
void (*callback) (struct local_client *,
char *csid, int node_up));
extern int is_quorate(void);
extern void get_our_csid(char *csid);
extern void add_up_node(char *csid);
extern void cluster_closedown(void);
extern int sync_lock(const char *resource, int mode, int flags, int *lockid);
extern int sync_unlock(const char *resource, int lockid);
#ifdef USE_GULM
#include "tcp-comms.h"
#else
/* cman */
#include "cnxman-socket.h"
#define MAX_CSID_LEN 4
#endif
#endif

880
daemons/clvmd/clvmd-gulm.c Normal file
View File

@@ -0,0 +1,880 @@
/******************************************************************************
*******************************************************************************
**
** Copyright (C) Sistina Software, Inc. 2002-2003 All rights reserved.
**
*******************************************************************************
******************************************************************************/
/* This provides the interface between clvmd and gulm as the cluster
* and lock manager.
*
* It also provides the "liblm" functions too as it's hard (and pointless)
* to seperate them out when using gulm.
*
* What it does /not/ provide is the communications between clvmd daemons
* on the cluster nodes. That is done in tcp-comms.c
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <utmpx.h>
#include <syslog.h>
#include <assert.h>
#include "ccs.h"
#include "list.h"
#include "locking.h"
#include "log.h"
#include "clvm.h"
#include "clvmd-comms.h"
#include "clvmd.h"
#include "hash.h"
#include "clvmd-gulm.h"
#include "libgulm.h"
#include "hash.h"
/* Hash list of nodes in the cluster */
static struct hash_table *node_hash;
/* hash list of outstanding lock requests */
static struct hash_table *lock_hash;
/* Copy of the current core state */
static uint8_t current_corestate;
/* Number of active nodes */
static int num_nodes;
static char *cluster_name;
static pthread_mutex_t lock_start_mutex;
static volatile int lock_start_flag;
struct node_info
{
enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
char name[MAX_CLUSTER_MEMBER_NAME_LEN];
};
struct lock_wait
{
pthread_cond_t cond;
pthread_mutex_t mutex;
int status;
};
/* Forward */
static int read_from_core_sock(struct local_client *client, char *buf, int len, char *csid,
struct local_client **new_client);
static int read_from_lock_sock(struct local_client *client, char *buf, int len, char *csid,
struct local_client **new_client);
static int get_all_cluster_nodes(void);
/* In tcp-comms.c */
extern struct hash_table *sock_hash;
static int add_internal_client(int fd, fd_callback_t callback)
{
struct local_client *client;
DEBUGLOG("Add_internal_client, fd = %d\n", fd);
/* Add a GULM file descriptor it to the main loop */
client = malloc(sizeof(struct local_client));
if (!client)
{
DEBUGLOG("malloc failed\n");
return -1;
}
memset(client, 0, sizeof(struct local_client));
client->fd = fd;
client->type = CLUSTER_INTERNAL;
client->callback = callback;
add_client(client);
return 0;
}
/* Gulm library handle */
static gulm_interface_p gulm_if;
static lg_core_callbacks_t core_callbacks;
static lg_lockspace_callbacks_t lock_callbacks;
static void badsig_handler(int sig)
{
DEBUGLOG("got sig %d\n", sig);
cluster_closedown();
exit(0);
}
static void sighup_handler(int sig)
{
DEBUGLOG("got SIGHUP\n");
/* Re-read CCS node list */
get_all_cluster_nodes();
}
int init_cluster()
{
int status;
int ccs_h;
/* Get cluster name from CCS */
/* TODO: is this right? */
ccs_h = ccs_connect();
ccs_get(ccs_h, "//cluster/@name", &cluster_name);
ccs_disconnect(ccs_h);
/* Block locking until we are logged in */
pthread_mutex_init(&lock_start_mutex, NULL);
pthread_mutex_lock(&lock_start_mutex);
lock_start_flag = 1;
node_hash = hash_create(100);
lock_hash = hash_create(10);
/* Get all nodes from CCS */
get_all_cluster_nodes();
/* Initialise GULM library */
status = lg_initialize(&gulm_if, cluster_name, "clvmd");
if (status)
{
DEBUGLOG("lg_initialize failed: %d\n", status);
return status;
}
/* Connect to core - we are not "important" :-) */
status = lg_core_login(gulm_if, 0);
if (status)
{
DEBUGLOG("lg_core_login failed: %d\n", status);
return status;
}
/* Initialise the inter-node comms */
status = init_comms();
if (status)
return status;
/* Add core FD to the list */
status = add_internal_client(lg_core_selector(gulm_if), read_from_core_sock);
if (status)
{
DEBUGLOG("can't allocate client space\n");
return status;
}
/* Connect to the lock server */
if (lg_lock_login(gulm_if, "CLVM"))
{
syslog(LOG_ERR, "Cannot login in to LOCK server\n");
DEBUGLOG("Cannot login in to LOCK server\n");
exit(88);
}
/* Add lockspace FD to the list */
status = add_internal_client(lg_lock_selector(gulm_if), read_from_lock_sock);
if (status)
{
DEBUGLOG("can't allocate client space\n");
exit(status);
}
/* Request a list of nodes, we can;t really do anything until
this comes back */
status = lg_core_nodelist(gulm_if);
if (status)
{
DEBUGLOG("lg_core_nodelist failed: %d\n", status);
return status;
}
/* So I can kill it without taking GULM down too */
signal(SIGINT, badsig_handler);
signal(SIGTERM, badsig_handler);
/* Re-read the node list on SIGHUP */
signal(SIGHUP, sighup_handler);
return 0;
}
void cluster_closedown()
{
DEBUGLOG("cluster_closedown\n");
lg_lock_logout(gulm_if);
lg_core_logout(gulm_if);
lg_core_shutdown(gulm_if);
lg_release(gulm_if);
}
/* Expire locks for a named node, or us */
#define GIO_KEY_SIZE 46
static void drop_expired_locks(char *nodename)
{
struct utsname nodeinfo;
uint8_t mask[GIO_KEY_SIZE];
memset(mask, 0xff, GIO_KEY_SIZE);
if (!nodename)
{
uname(&nodeinfo);
nodename = nodeinfo.nodename;
}
if (lg_lock_drop_exp(gulm_if, nodename, mask, GIO_KEY_SIZE))
{
DEBUGLOG("Error calling lg_lock_drop_exp()\n");
}
}
static int read_from_core_sock(struct local_client *client, char *buf, int len, char *csid,
struct local_client **new_client)
{
int status;
*new_client = NULL;
status = lg_core_handle_messages(gulm_if, &core_callbacks, NULL);
return status<0 ? status : 1;
}
static int read_from_lock_sock(struct local_client *client, char *buf, int len, char *csid,
struct local_client **new_client)
{
int status;
*new_client = NULL;
status = lg_lock_handle_messages(gulm_if, &lock_callbacks, NULL);
return status<0 ? status : 1;
}
/* CORE callback routines */
static int core_login_reply(void *misc, uint64_t gen, uint32_t error, uint32_t rank, uint8_t corestate)
{
DEBUGLOG("CORE Got a Login reply. gen:%lld err:%d rank:%d corestate:%d\n",
gen, error, rank, corestate);
if (error)
exit(error);
current_corestate = corestate;
return 0;
}
static void set_node_state(struct node_info *ninfo, char *csid, uint8_t nodestate)
{
if (nodestate == lg_core_Logged_in)
{
/* Don't clobber NODE_CLVMD state */
if (ninfo->state != NODE_CLVMD)
{
if (ninfo->state == NODE_UNKNOWN ||
ninfo->state == NODE_DOWN)
num_nodes++;
ninfo->state = NODE_UP;
}
}
else
{
if (nodestate == lg_core_Expired ||
nodestate == lg_core_Fenced ||
nodestate == lg_core_Logged_out)
{
if (ninfo->state != NODE_DOWN)
num_nodes--;
ninfo->state = NODE_DOWN;
tcp_remove_client(csid);
}
}
DEBUGLOG("set_node_state, '%s' state = %d, num_nodes=%d\n",
ninfo->name, ninfo->state, num_nodes);
}
static struct node_info *add_or_set_node(char *name, uint32_t ip, uint8_t state)
{
struct node_info *ninfo;
ninfo = hash_lookup_binary(node_hash, (char *)&ip, MAX_CSID_LEN);
if (!ninfo)
{
/* If we can't find that node then re-read the config file in case it
was added after we were started */
DEBUGLOG("Node %s not found, re-reading config file\n", name);
get_all_cluster_nodes();
/* Now try again */
ninfo = hash_lookup_binary(node_hash, (char *)&ip, MAX_CSID_LEN);
if (!ninfo)
{
DEBUGLOG("Ignoring node %s, not part of the SAN cluster\n", name);
return NULL;
}
}
set_node_state(ninfo, (char *)&ip, state);
return ninfo;
}
static int core_nodelist(void *misc, lglcb_t type, char *name, uint32_t ip, uint8_t state)
{
DEBUGLOG("CORE nodelist\n");
if (type == lglcb_start)
{
DEBUGLOG("Got Nodelist, start\n");
}
else
{
if (type == lglcb_item)
{
DEBUGLOG("Got nodelist, item: %s, %#x, %#x\n", name, ip, state);
add_or_set_node(name, ip, state);
}
else
{
if (type == lglcb_stop)
{
char ourcsid[MAX_CSID_LEN];
DEBUGLOG("Got Nodelist, stop\n");
clvmd_cluster_init_completed();
/* Mark ourself as up */
get_our_csid(ourcsid);
add_up_node(ourcsid);
}
else
{
DEBUGLOG("Unknown lglcb_t %#x\n", type);
}
}
}
return 0;
}
static int core_statechange(void *misc, uint8_t corestate, uint32_t masterip, char *mastername)
{
DEBUGLOG("CORE Got statechange corestate:%#x masterip:%#x mastername:%s\n",
corestate, masterip, mastername);
current_corestate = corestate;
return 0;
}
static int core_nodechange(void *misc, char *nodename, uint32_t nodeip, uint8_t nodestate)
{
struct node_info *ninfo;
DEBUGLOG("CORE node change, name=%s, ip=%x, state = %d\n", nodename, nodeip, nodestate);
/* If we don't get nodeip here, try a lookup by name */
if (!nodeip)
csid_from_name((char *)&nodeip, nodename);
if (!nodeip)
return 0;
ninfo = add_or_set_node(nodename, nodeip, nodestate);
if (!ninfo)
return 0;
/* Check if we need to drop any expired locks */
if (ninfo->state == NODE_DOWN)
{
drop_expired_locks(nodename);
}
return 0;
}
static int core_error(void *misc, uint32_t err)
{
DEBUGLOG("CORE error: %d\n", err);
// Not sure what happens here
return 0;
}
/* LOCK callback routines */
static int lock_login_reply(void *misc, uint32_t error, uint8_t which)
{
DEBUGLOG("LOCK Got a Login reply. err:%d which:%d\n",
error, which);
if (error)
exit(error);
/* Drop any expired locks for us that might be hanging around */
drop_expired_locks(NULL);
/* Enable locking operations in other threads */
if (lock_start_flag)
{
lock_start_flag = 0;
pthread_mutex_unlock(&lock_start_mutex);
}
return 0;
}
static int lock_lock_state(void *misc, uint8_t *key, uint16_t keylen, uint8_t state, uint32_t flags, uint32_t error,
uint8_t *LVB, uint16_t LVBlen)
{
struct lock_wait *lwait;
DEBUGLOG("LOCK lock state: %s, error = %d\n", key, error);
lwait = hash_lookup(lock_hash, key);
if (!lwait)
{
DEBUGLOG("Can't find hash entry for resource %s\n", key);
return 0;
}
lwait->status = error;
pthread_mutex_lock(&lwait->mutex);
pthread_cond_signal(&lwait->cond);
pthread_mutex_unlock(&lwait->mutex);
return 0;
}
static int lock_error(void *misc, uint32_t err)
{
DEBUGLOG("LOCK error: %d\n", err);
// Not sure what happens here
return 0;
}
/* CORE callbacks */
static lg_core_callbacks_t core_callbacks = {
.login_reply = core_login_reply,
.nodelist = core_nodelist,
.statechange = core_statechange,
.nodechange = core_nodechange,
.error = core_error,
};
/* LOCK callbacks */
static lg_lockspace_callbacks_t lock_callbacks = {
.login_reply = lock_login_reply,
.lock_state = lock_lock_state,
.error = lock_error,
};
/* Allow tcp-comms to loop round the list of active nodes */
int get_next_node_csid(void **context, char *csid)
{
struct node_info *ninfo = NULL;
/* First node */
if (!*context)
{
*context = hash_get_first(node_hash);
}
else
{
*context = hash_get_next(node_hash, *context);
}
if (*context)
ninfo = hash_get_data(node_hash, *context);
/* Find a node that is UP */
while (*context && ninfo->state == NODE_DOWN)
{
*context = hash_get_next(node_hash, *context);
if (*context)
{
ninfo = hash_get_data(node_hash, *context);
}
}
if (!*context || ninfo->state == NODE_DOWN)
{
return 0;
}
memcpy(csid, hash_get_key(node_hash, *context), MAX_CSID_LEN);
return 1;
}
int name_from_csid(char *csid, char *name)
{
struct node_info *ninfo;
ninfo = hash_lookup_binary(node_hash, csid, MAX_CSID_LEN);
if (!ninfo)
{
sprintf(name, "UNKNOWN [%d.%d.%d.%d]",
csid[0], csid[1], csid[2], csid[3]);
return -1;
}
strcpy(name, ninfo->name);
return 0;
}
int csid_from_name(char *csid, char *name)
{
struct hash_node *hn;
struct node_info *ninfo;
hash_iterate(hn, node_hash)
{
ninfo = hash_get_data(node_hash, hn);
if (strcmp(ninfo->name, name) == 0)
{
memcpy(csid, hash_get_key(node_hash, hn), MAX_CSID_LEN);
return 0;
}
}
return -1;
}
int get_num_nodes()
{
DEBUGLOG("num_nodes = %d\n", num_nodes);
return num_nodes;
}
/* Node is now known to be running a clvmd */
void add_up_node(char *csid)
{
struct node_info *ninfo;
ninfo = hash_lookup_binary(node_hash, csid, MAX_CSID_LEN);
if (!ninfo)
return;
ninfo->state = NODE_CLVMD;
return;
}
/* Node is now known to be NOT running a clvmd */
void add_down_node(char *csid)
{
struct node_info *ninfo;
ninfo = hash_lookup_binary(node_hash, csid, MAX_CSID_LEN);
if (!ninfo)
return;
/* Only set it to UP if it was previously known to be
running clvmd - gulm may set it DOWN quite soon */
if (ninfo->state == NODE_CLVMD)
ninfo->state = NODE_UP;
return;
}
/* Call a callback for each node, so the caller knows whether it's up or down */
int cluster_do_node_callback(struct local_client *master_client,
void (*callback)(struct local_client *, char *csid, int node_up))
{
struct hash_node *hn;
struct node_info *ninfo;
hash_iterate(hn, node_hash)
{
char csid[MAX_CSID_LEN];
struct local_client *client;
ninfo = hash_get_data(node_hash, hn);
memcpy(csid, hash_get_key(node_hash, hn), MAX_CSID_LEN);
DEBUGLOG("down_callback. node %s, state = %d\n", ninfo->name, ninfo->state);
client = hash_lookup_binary(sock_hash, csid, MAX_CSID_LEN);
if (client)
callback(master_client, csid, ninfo->state == NODE_CLVMD);
}
return 0;
}
/* Convert gulm error codes to unix errno numbers */
static int gulm_to_errno(int gulm_ret)
{
switch (gulm_ret)
{
case lg_err_TryFailed:
errno = EAGAIN;
break;
case lg_err_AlreadyPend:
errno = EBUSY;
/* More?? */
default:
errno = EINVAL;
}
return gulm_ret ? -1 : 0;
}
/* Real locking */
static int _lock_resource(char *resource, int mode, int flags, int *lockid)
{
int status;
struct lock_wait lwait;
/* Wait until the lock module is ready */
if (lock_start_flag)
{
pthread_mutex_lock(&lock_start_mutex);
pthread_mutex_unlock(&lock_start_mutex);
}
pthread_cond_init(&lwait.cond, NULL);
pthread_mutex_init(&lwait.mutex, NULL);
pthread_mutex_lock(&lwait.mutex);
/* This needs to be converted from DLM/LVM2 value for GULM */
if (flags == LCK_NONBLOCK) flags = lg_lock_flag_Try;
hash_insert(lock_hash, resource, &lwait);
DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
status = lg_lock_state_req(gulm_if, resource, strlen(resource)+1,
mode, flags, NULL, 0);
if (status)
{
DEBUGLOG("lg_lock_state returned %d\n", status);
return status;
}
/* Wait for it to complete */
pthread_cond_wait(&lwait.cond, &lwait.mutex);
pthread_mutex_unlock(&lwait.mutex);
hash_remove(lock_hash, resource);
DEBUGLOG("lock-resource returning %d\n", lwait.status);
return gulm_to_errno(lwait.status);
}
static int _unlock_resource(char *resource, int lockid)
{
int status;
struct lock_wait lwait;
pthread_cond_init(&lwait.cond, NULL);
pthread_mutex_init(&lwait.mutex, NULL);
pthread_mutex_lock(&lwait.mutex);
hash_insert(lock_hash, resource, &lwait);
DEBUGLOG("unlock_resource %s\n", resource);
status = lg_lock_state_req(gulm_if, resource, strlen(resource)+1,
lg_lock_state_Unlock, 0, NULL, 0);
if (status)
{
DEBUGLOG("lg_lock_state(unlock) returned %d\n", status);
return status;
}
/* Wait for it to complete */
pthread_cond_wait(&lwait.cond, &lwait.mutex);
pthread_mutex_unlock(&lwait.mutex);
hash_remove(lock_hash, resource);
return gulm_to_errno(lwait.status);
}
/* These two locking functions MUST be called in a seperate thread from
the clvmd main loop because they expect to be woken up by it.
These are abstractions around the real locking functions (above)
as we need to emulate the DLM's EX/PW/CW interaction with GULM using
two locks.
To aid unlocking, we store the lock mode in the lockid (as GULM
doesn't use this).
*/
int sync_lock(const char *resource, int mode, int flags, int *lockid)
{
int status;
char lock1[strlen(resource)+3];
char lock2[strlen(resource)+3];
snprintf(lock1, sizeof(lock1), "%s-1", resource);
snprintf(lock2, sizeof(lock2), "%s-2", resource);
switch (mode)
{
case LCK_EXCL:
status = _lock_resource(lock1, lg_lock_state_Exclusive, flags, lockid);
if (status)
goto out;
/* If we can't get this lock then bail out */
status = _lock_resource(lock2, lg_lock_state_Exclusive, LCK_NONBLOCK, lockid);
if (status == lg_err_TryFailed)
{
_unlock_resource(lock1, *lockid);
status = -1;
errno = EAGAIN;
}
break;
case LCK_READ:
status = _lock_resource(lock1, lg_lock_state_Shared, flags, lockid);
break;
case LCK_WRITE:
status = _lock_resource(lock2, lg_lock_state_Exclusive, flags, lockid);
break;
default:
status = -1;
errno = EINVAL;
break;
}
out:
*lockid = mode;
return status;
}
int sync_unlock(const char *resource, int lockid)
{
int status = 0;
char lock1[strlen(resource)+3];
char lock2[strlen(resource)+3];
snprintf(lock1, sizeof(lock1), "%s-1", resource);
snprintf(lock2, sizeof(lock2), "%s-2", resource);
/* The held lock mode is in the lock id */
assert(lockid == LCK_EXCL ||
lockid == LCK_READ ||
lockid == LCK_WRITE);
switch (lockid)
{
case LCK_EXCL:
status = _unlock_resource(lock1, lockid);
if (status)
goto out;
status = _unlock_resource(lock2, lockid);
break;
case LCK_READ:
status = _unlock_resource(lock1, lockid);
break;
case LCK_WRITE:
status = _unlock_resource(lock2, lockid);
break;
}
out:
return status;
}
int is_quorate()
{
if (current_corestate == lg_core_Slave ||
current_corestate == lg_core_Master ||
current_corestate == lg_core_Client)
return 1;
else
return 0;
}
/* Get all the cluster node names & IPs from CCS and
add them to our node list so we know who to talk to.
Called when we start up and if we get sent SIGHUP.
*/
static int get_all_cluster_nodes()
{
int ctree;
char *nodename;
int error;
/* Open the config file */
ctree = ccs_connect();
if (ctree <= 0)
{
log_error("Error connecting to CCS");
return -1;
}
error = ccs_get(ctree, "//nodes/node/@name", &nodename);
while (nodename)
{
char nodeip[MAX_CSID_LEN];
char *clvmflag;
char key[256];
sprintf(key, "//nodes/node[@name=\"%s\"]/clvm", nodename);
ccs_get(ctree, key, &clvmflag);
if ((get_ip_address(nodename, nodeip) == 0) && atoi(clvmflag))
{
struct node_info *ninfo;
/* If it's not in the list, then add it */
ninfo = hash_lookup_binary(node_hash, nodeip, MAX_CSID_LEN);
if (!ninfo)
{
ninfo = malloc(sizeof(struct node_info));
if (!ninfo)
{
syslog(LOG_ERR, "Cannot alloc memory for node info\n");
ccs_disconnect(ctree);
return -1;
}
strcpy(ninfo->name, nodename);
ninfo->state = NODE_DOWN;
hash_insert_binary(node_hash, nodeip, MAX_CSID_LEN, ninfo);
}
}
else
{
DEBUGLOG("node %s has clvm disabled\n", nodename);
}
if (clvmflag) free(clvmflag);
free(nodename);
error = ccs_get(ctree, "//nodes/node/@name", &nodename);
}
/* Finished with config file */
ccs_disconnect(ctree);
return 0;
}
int gulm_fd(void)
{
return lg_core_selector(gulm_if);
}

View File

@@ -0,0 +1,9 @@
extern int get_next_node_csid(void **context, char *csid);
extern void add_down_node(char *csid);
extern int gulm_fd(void);
extern int get_ip_address(char *node, char *addr);
extern void tcp_remove_client(char *csid);
extern int alloc_client(int fd, char *csid, struct local_client **new_client);

1693
daemons/clvmd/clvmd.c Normal file

File diff suppressed because it is too large Load Diff

119
daemons/clvmd/clvmd.h Normal file
View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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 _CLVMD_H
#define _CLVMD_H
#define CLVMD_MAJOR_VERSION 0
#define CLVMD_MINOR_VERSION 2
#define CLVMD_PATCH_VERSION 1
/* Name of the cluster LVM admin lock */
#define ADMIN_LOCK_NAME "CLVMD_ADMIN"
/* Default time (in seconds) we will wait for all remote commands to execute
before declaring them dead */
#define DEFAULT_CMD_TIMEOUT 60
/* One of these for each reply we get from command execution on a node */
struct node_reply {
char node[MAX_CLUSTER_MEMBER_NAME_LEN];
char *replymsg;
int status;
struct node_reply *next;
};
/*
* These exist for the use of local sockets only when we are
* collecting responses from all cluster nodes
*/
struct localsock_bits {
struct node_reply *replies;
int num_replies;
int expected_replies;
time_t sent_time; /* So we can check for timeouts */
int in_progress; /* Only execute one cmd at a time per client */
int sent_out; /* Flag to indicate that a command was sent
to remote nodes */
void *private; /* Private area for command processor use */
void *cmd; /* Whole command as passed down local socket */
int cmd_len; /* Length of above */
int pipe; /* Pipe to send PRE completion status down */
int finished; /* Flag to tell subthread to exit */
int all_success; /* Set to 0 if any node (or the pre_command)
failed */
struct local_client *pipe_client;
pthread_t threadid;
enum { PRE_COMMAND, POST_COMMAND, QUIT } state;
pthread_mutex_t mutex; /* Main thread and worker synchronisation */
pthread_cond_t cond;
pthread_mutex_t reply_mutex; /* Protect reply structure */
};
/* Entries for PIPE clients */
struct pipe_bits {
struct local_client *client; /* Actual (localsock) client */
pthread_t threadid; /* Our own copy of the thread id */
};
/* Entries for Network socket clients */
struct netsock_bits {
void *private;
int flags;
};
typedef int (*fd_callback_t) (struct local_client * fd, char *buf, int len,
char *csid, struct local_client ** new_client);
/* One of these for each fd we are listening on */
struct local_client {
int fd;
enum { CLUSTER_MAIN_SOCK, CLUSTER_DATA_SOCK, LOCAL_RENDEZVOUS,
LOCAL_SOCK, THREAD_PIPE, CLUSTER_INTERNAL } type;
struct local_client *next;
unsigned short xid;
fd_callback_t callback;
union {
struct localsock_bits localsock;
struct pipe_bits pipe;
struct netsock_bits net;
} bits;
};
#ifdef DEBUG
#define DEBUGLOG(fmt, args...) fprintf(stderr, "CLVMD[%d]: %ld ", getpid(), time(NULL) ); fprintf(stderr, fmt, ## args)
#else
#define DEBUGLOG(fmt, args...)
#endif
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
/* The real command processor is in clvmd-command.c */
extern int do_command(struct local_client *client, struct clvm_header *msg,
int msglen, char **buf, int buflen, int *retlen);
/* Pre and post command routines are called only on the local node */
extern int do_pre_command(struct local_client *client);
extern int do_post_command(struct local_client *client);
extern int add_client(struct local_client *new_client);
extern void clvmd_cluster_init_completed(void);
#endif

View File

@@ -0,0 +1,226 @@
/******************************************************************************
*******************************************************************************
**
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
** Copyright (C) 2004 Red Hat, Inc. All rights reserved.
**
** 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 General Public License v.2.
**
*******************************************************************************
******************************************************************************/
/* CMAN socket interface header,
may be include by user or kernel code */
#ifndef __CNXMAN_SOCKET_H
#define __CNXMAN_SOCKET_H
/* Just made these up but the address family must be less than 32 (NPROTO) */
#define AF_CLUSTER 31
#define PF_CLUSTER AF_CLUSTER
/* Protocol(socket) types */
#define CLPROTO_MASTER 2
#define CLPROTO_CLIENT 3
/* Setsockopt -- maybe should be ioctls?? */
#define CLU_SET_MULTICAST 100
#define CLU_JOIN_CLUSTER 101
#define CLU_LEAVE_CLUSTER 102
#define CLU_SET_RCVONLY 103
#define CLU_SET_UNICAST 104
#define KCL_SET_MULTICAST 105
#define KCL_SET_RCVONLY 106
#define KCL_SET_UNICAST 107
#define KCL_SET_NODENAME 108
#define CLU_SET_NODENAME 109
/* ioctls -- should register these properly */
#define SIOCCLUSTER_NOTIFY _IOW('x', 0x01, int)
#define SIOCCLUSTER_REMOVENOTIFY _IO( 'x', 0x02)
#define SIOCCLUSTER_GETMEMBERS _IOR('x', 0x03, struct cl_cluster_nodelist)
#define SIOCCLUSTER_SETEXPECTED_VOTES _IOW('x', 0x04, int)
#define SIOCCLUSTER_ISQUORATE _IO( 'x', 0x05)
#define SIOCCLUSTER_ISLISTENING _IOW('x', 0x06, struct cl_listen_request)
#define SIOCCLUSTER_GETALLMEMBERS _IOR('x', 0x07, struct cl_cluster_nodelist)
#define SIOCCLUSTER_SET_VOTES _IOW('x', 0x08, int)
#define SIOCCLUSTER_GET_VERSION _IOR('x', 0x09, struct cl_version)
#define SIOCCLUSTER_SET_VERSION _IOW('x', 0x0a, struct cl_version)
#define SIOCCLUSTER_ISACTIVE _IO( 'x', 0x0b)
#define SIOCCLUSTER_KILLNODE _IOW('x', 0x0c, int)
#define SIOCCLUSTER_GET_JOINCOUNT _IO( 'x', 0x0d)
#define SIOCCLUSTER_SERVICE_REGISTER _IOW('x', 0x0e, char)
#define SIOCCLUSTER_SERVICE_UNREGISTER _IO('x', 0x0f)
#define SIOCCLUSTER_SERVICE_JOIN _IO( 'x', 0x10)
#define SIOCCLUSTER_SERVICE_LEAVE _IO( 'x', 0x20)
#define SIOCCLUSTER_SERVICE_SETSIGNAL _IOW('x', 0x30, int)
#define SIOCCLUSTER_SERVICE_STARTDONE _IOW('x', 0x40, unsigned int)
#define SIOCCLUSTER_SERVICE_GETEVENT _IOR('x', 0x50, struct cl_service_event)
#define SIOCCLUSTER_SERVICE_GETMEMBERS _IOR('x', 0x60, struct cl_cluster_node)
#define SIOCCLUSTER_SERVICE_GLOBALID _IOR('x', 0x70, uint32_t)
#define SIOCCLUSTER_SERVICE_SETLEVEL _IOR('x', 0x80, int)
#define SIOCCLUSTER_GETNODE _IOWR('x', 0x90, struct cl_cluster_node)
#define SIOCCLUSTER_BARRIER _IOW('x', 0x0a0, struct cl_barrier_info)
/* Maximum size of a cluster message */
#define MAX_CLUSTER_MESSAGE 1500
#define MAX_CLUSTER_MEMBER_NAME_LEN 255
#define MAX_BARRIER_NAME_LEN 33
#define MAX_SA_ADDR_LEN 12
#define MAX_CLUSTER_NAME_LEN 16
/* Well-known cluster port numbers */
#define CLUSTER_PORT_MEMBERSHIP 1 /* Mustn't block during cluster
* transitions! */
#define CLUSTER_PORT_SERVICES 2
#define CLUSTER_PORT_SYSMAN 10 /* Remote execution daemon */
#define CLUSTER_PORT_CLVMD 11 /* Cluster LVM daemon */
#define CLUSTER_PORT_SLM 12 /* LVM SLM (simple lock manager) */
/* Port numbers above this will be blocked when the cluster is inquorate or in
* transition */
#define HIGH_PROTECTED_PORT 9
/* Reasons for leaving the cluster */
#define CLUSTER_LEAVEFLAG_DOWN 0 /* Normal shutdown */
#define CLUSTER_LEAVEFLAG_KILLED 1
#define CLUSTER_LEAVEFLAG_PANIC 2
#define CLUSTER_LEAVEFLAG_REMOVED 3 /* This one can reduce quorum */
#define CLUSTER_LEAVEFLAG_REJECTED 4 /* Not allowed into the cluster in the
* first place */
#define CLUSTER_LEAVEFLAG_INCONSISTENT 5 /* Our view of the cluster is
* in a minority */
#define CLUSTER_LEAVEFLAG_DEAD 6 /* Discovered to be dead */
#define CLUSTER_LEAVEFLAG_FORCE 0x10 /* Forced by command-line */
/* OOB messages sent to a local socket */
#define CLUSTER_OOB_MSG_PORTCLOSED 1
#define CLUSTER_OOB_MSG_STATECHANGE 2
#define CLUSTER_OOB_MSG_SERVICEEVENT 3
/* Sendmsg flags, these are above the normal sendmsg flags so they don't
* interfere */
#define MSG_NOACK 0x010000 /* Don't need an ACK for this message */
#define MSG_QUEUE 0x020000 /* Queue the message for sending later */
#define MSG_MULTICAST 0x080000 /* Message was sent to all nodes in the cluster
*/
#define MSG_ALLINT 0x100000 /* Send out of all interfaces */
typedef enum { NODESTATE_REMOTEMEMBER, NODESTATE_JOINING, NODESTATE_MEMBER,
NODESTATE_DEAD } nodestate_t;
struct sockaddr_cl {
unsigned short scl_family;
unsigned char scl_flags;
unsigned char scl_port;
int scl_nodeid;
};
/* This is how we pass the multicast socket into kernel space. addr is the
* multicast address to use in the address family of the socket (eg for UDP it
* might be 255.255.255.0) */
struct cl_multicast_sock {
int fd; /* FD of master socket to do multicast on */
int number; /* Socket number, to match up recvonly & bcast
* sockets */
};
/* Cluster configuration info passed when we join the cluster */
struct cl_join_cluster_info {
unsigned char votes;
unsigned int expected_votes;
unsigned int two_node;
unsigned int config_version;
char cluster_name[17];
};
/* This is the structure, per node, returned from the membership ioctl */
struct cl_cluster_node {
unsigned int size;
unsigned int node_id;
unsigned int us;
unsigned int leave_reason;
unsigned int incarnation;
nodestate_t state;
char name[MAX_CLUSTER_MEMBER_NAME_LEN];
unsigned char votes;
};
/* The struct passed to the membership ioctls */
struct cl_cluster_nodelist {
uint32_t max_members;
struct cl_cluster_node *nodes;
};
/* Structure passed to SIOCCLUSTER_ISLISTENING */
struct cl_listen_request {
unsigned char port;
int nodeid;
};
/* A Cluster PORTCLOSED message - received by a local user as an OOB message */
struct cl_portclosed_oob {
unsigned char cmd; /* CLUSTER_OOB_MSG_PORTCLOSED */
unsigned char port;
};
/* Get all version numbers or set the config version */
struct cl_version {
unsigned int major;
unsigned int minor;
unsigned int patch;
unsigned int config;
};
/* structure passed to barrier ioctls */
struct cl_barrier_info {
char cmd;
char name[MAX_BARRIER_NAME_LEN];
unsigned int flags;
unsigned long arg;
};
typedef enum { SERVICE_EVENT_STOP, SERVICE_EVENT_START, SERVICE_EVENT_FINISH,
SERVICE_EVENT_LEAVEDONE } service_event_t;
typedef enum { SERVICE_START_FAILED, SERVICE_START_JOIN, SERVICE_START_LEAVE }
service_start_t;
struct cl_service_event {
service_event_t type;
service_start_t start_type;
unsigned int event_id;
unsigned int last_stop;
unsigned int last_start;
unsigned int last_finish;
unsigned int node_count;
};
/* Commands to the barrier ioctl */
#define BARRIER_IOCTL_REGISTER 1
#define BARRIER_IOCTL_CHANGE 2
#define BARRIER_IOCTL_DELETE 3
#define BARRIER_IOCTL_WAIT 4
/* Attributes of a barrier - bitmask */
#define BARRIER_ATTR_AUTODELETE 1
#define BARRIER_ATTR_MULTISTEP 2
#define BARRIER_ATTR_MANUAL 4
#define BARRIER_ATTR_ENABLED 8
#define BARRIER_ATTR_CALLBACK 16
/* Attribute setting commands */
#define BARRIER_SETATTR_AUTODELETE 1
#define BARRIER_SETATTR_MULTISTEP 2
#define BARRIER_SETATTR_ENABLED 3
#define BARRIER_SETATTR_NODES 4
#define BARRIER_SETATTR_CALLBACK 5
#define BARRIER_SETATTR_TIMEOUT 6
#endif

446
daemons/clvmd/libclvm.c Normal file
View File

@@ -0,0 +1,446 @@
/*
* Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/* library functions for Cluster LVM Daemon */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <search.h>
#include <errno.h>
#include "clvm.h"
#include "libclvm.h"
/* CLVM in hex! */
#define LVM_SIGNATURE 0x434C564D
#define MAX_CLUSTER_MEMBER_NAME_LEN 255
/* NOTE: the LVMD uses the socket FD as the client ID, this means
that any client that calls fork() will inherit the context of
it's parent. */
static int clvmd_sock = -1;
static int open_local_sock(void)
{
int local_socket;
struct sockaddr_un sockaddr;
/* Open local socket */
local_socket = socket(PF_UNIX, SOCK_STREAM, 0);
if (local_socket < 0) {
perror("Can't create local socket");
return -1;
}
fcntl(local_socket, F_SETFD, !FD_CLOEXEC);
strcpy(sockaddr.sun_path, CLVMD_SOCKNAME);
sockaddr.sun_family = AF_UNIX;
if (connect
(local_socket, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) {
int saved_errno = errno;
close(local_socket);
errno = saved_errno;
return -1;
}
return local_socket;
}
/* Send a request and return the status */
static int send_request(char *inbuf, int inlen, char **retbuf)
{
char outbuf[PIPE_BUF];
struct clvm_header *outheader = (struct clvm_header *) outbuf;
int len;
int off;
fd_set fds;
FD_ZERO(&fds);
FD_SET(clvmd_sock, &fds);
/* Send it to CLVMD */
if (write(clvmd_sock, inbuf, inlen) != inlen) {
perror("Error writing to CLVMD");
return -1;
}
/* Get the response */
if ((len = read(clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
perror("Error reading CLVMD");
return -1;
}
if (len == 0) {
fprintf(stderr, "EOF reading CLVMD");
errno = ENOTCONN;
return -1;
}
/* Allocate buffer */
*retbuf = malloc(len + outheader->arglen);
if (!*retbuf) {
errno = ENOMEM;
return -1;
}
/* Copy the header */
memcpy(*retbuf, outbuf, len);
outheader = (struct clvm_header *) *retbuf;
/* Read the returned values */
off = 1; /* we've already read the first byte */
while (off < outheader->arglen && len > 0) {
len = read(clvmd_sock, outheader->args + off, PIPE_BUF);
if (len > 0)
off += len;
}
/* Was it an error ? */
if (outheader->status < 0) {
errno = -outheader->status;
return -2;
}
return 0;
}
/* Build the structure header and parse-out wildcard node names */
static void build_header(struct clvm_header *head, int cmd, const char *node,
void *data, int len)
{
head->cmd = cmd;
head->status = 0;
head->flags = 0;
head->clientid = 0;
head->arglen = len;
if (node) {
/* Allow a couple of special node names:
"*" for all nodes,
"." for the local node only
*/
if (strcmp(node, "*") == 0) {
head->node[0] = '\0';
} else if (strcmp(node, ".") == 0) {
head->node[0] = '\0';
head->flags = CLVMD_FLAG_LOCAL;
} else {
strcpy(head->node, node);
}
} else {
head->node[0] = '\0';
}
}
/* Send a message to a(or all) node(s) in the cluster */
int lvm_cluster_write(char cmd, char *node, void *data, int len)
{
char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
char *retbuf = NULL;
int status;
struct clvm_header *head = (struct clvm_header *) outbuf;
if (clvmd_sock == -1)
clvmd_sock = open_local_sock();
if (clvmd_sock == -1)
return -1;
build_header(head, cmd, node, data, len);
memcpy(head->node + strlen(head->node) + 1, data, len);
status =
send_request(outbuf,
sizeof(struct clvm_header) + strlen(head->node) + len,
&retbuf);
if (retbuf)
free(retbuf);
return status;
}
/* API: Send a message to a(or all) node(s) in the cluster
and wait for replies */
int lvm_cluster_request(char cmd, const char *node, void *data, int len,
lvm_response_t ** response, int *num)
{
char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
int *outptr;
char *inptr;
char *retbuf = NULL;
int status;
int i;
int num_responses = 0;
struct clvm_header *head = (struct clvm_header *) outbuf;
lvm_response_t *rarray;
*num = 0;
if (clvmd_sock == -1)
clvmd_sock = open_local_sock();
if (clvmd_sock == -1)
return -1;
build_header(head, cmd, node, data, len);
memcpy(head->node + strlen(head->node) + 1, data, len);
status =
send_request(outbuf,
sizeof(struct clvm_header) + strlen(head->node) + len,
&retbuf);
if (status == 0 || status == -2) {
/* Count the number of responses we got */
head = (struct clvm_header *) retbuf;
inptr = head->args;
while (inptr[0]) {
num_responses++;
inptr += strlen(inptr) + 1;
inptr += sizeof(int);
inptr += strlen(inptr) + 1;
}
/* Allocate response array. With an extra pair of INTs on the front to sanity
check the pointer when we are given it back to free */
outptr =
malloc(sizeof(lvm_response_t) * num_responses +
sizeof(int) * 2);
if (!outptr) {
if (retbuf)
free(retbuf);
errno = ENOMEM;
return -1;
}
*response = (lvm_response_t *) (outptr + 2);
outptr[0] = LVM_SIGNATURE;
outptr[1] = num_responses;
rarray = *response;
/* Unpack the response into an lvm_response_t array */
inptr = head->args;
i = 0;
while (inptr[0]) {
strcpy(rarray[i].node, inptr);
inptr += strlen(inptr) + 1;
rarray[i].status = *(int *) inptr;
inptr += sizeof(int);
rarray[i].response = malloc(strlen(inptr) + 1);
if (rarray[i].response == NULL) {
/* Free up everything else and return error */
int j;
for (j = 0; j < i; j++)
free(rarray[i].response);
free(outptr);
errno = ENOMEM;
return -1;
}
strcpy(rarray[i].response, inptr);
rarray[i].len = strlen(inptr);
inptr += strlen(inptr) + 1;
i++;
}
*num = num_responses;
*response = rarray;
}
if (retbuf)
free(retbuf);
return status;
}
/* API: Free reply array */
int lvm_cluster_free_request(lvm_response_t * response)
{
int *ptr = (int *) response - 2;
int i;
int num;
/* Check it's ours to free */
if (response == NULL || *ptr != LVM_SIGNATURE) {
errno = EINVAL;
return -1;
}
num = ptr[1];
for (i = 0; i < num; i++) {
free(response[i].response);
}
free(ptr);
return 0;
}
/* These are a "higher-level" API providing black-box lock/unlock
functions for cluster LVM...maybe */
/* Set by lock(), used by unlock() */
static int num_responses;
static lvm_response_t *response;
int lvm_lock_for_cluster(char scope, char *name, int verbosity)
{
int status;
int i;
char *args;
int len;
if (name) {
len = strlen(name) + 2;
args = alloca(len);
strcpy(args + 1, name);
} else {
len = 2;
args = alloca(len);
args[1] = '\0';
}
args[0] = scope;
status = lvm_cluster_request(CLVMD_CMD_LOCK,
"", args, len, &response, &num_responses);
/* If any nodes were down then display them and return an error */
for (i = 0; i < num_responses; i++) {
if (response[i].status == -EHOSTDOWN) {
if (verbosity)
fprintf(stderr,
"clvmd not running on node %s\n",
response[i].node);
status = -1;
}
}
/* If there was an error then free the memory now as the caller won't
want to do the unlock */
if (status) {
int saved_errno = errno;
lvm_cluster_free_request(response);
num_responses = 0;
errno = saved_errno;
}
return status;
}
int lvm_unlock_for_cluster(char scope, char *name, int verbosity)
{
int status;
int i;
int len;
int failed;
int num_unlock_responses;
char *args;
lvm_response_t *unlock_response;
/* We failed - this should not have been called */
if (num_responses == 0)
return 0;
if (name) {
len = strlen(name) + 2;
args = alloca(len);
strcpy(args + 1, name);
} else {
len = 2;
args = alloca(len);
args[1] = '\0';
}
args[0] = scope;
/* See if it failed anywhere */
failed = 0;
for (i = 0; i < num_responses; i++) {
if (response[i].status != 0)
failed++;
}
/* If it failed on any nodes then we only unlock on
the nodes that succeeded */
if (failed) {
for (i = 0; i < num_responses; i++) {
/* Unlock the ones that succeeded */
if (response[i].status == 0) {
status = lvm_cluster_request(CLVMD_CMD_UNLOCK,
response[i].node,
args, len,
&unlock_response,
&num_unlock_responses);
if (status) {
if (verbosity)
fprintf(stderr,
"cluster command to node %s failed: %s\n",
response[i].node,
strerror(errno));
} else if (unlock_response[0].status != 0) {
if (verbosity > 1)
fprintf(stderr,
"unlock on node %s failed: %s\n",
response[i].node,
strerror(unlock_response
[0].status));
}
lvm_cluster_free_request(unlock_response);
} else {
if (verbosity)
fprintf(stderr,
"command on node %s failed: '%s' - will be left locked\n",
response[i].node,
strerror(response[i].status));
}
}
} else {
/* All OK, we can do a full cluster unlock */
status = lvm_cluster_request(CLVMD_CMD_UNLOCK,
"",
args, len,
&unlock_response,
&num_unlock_responses);
if (status) {
if (verbosity > 1)
fprintf(stderr, "cluster command failed: %s\n",
strerror(errno));
} else {
for (i = 0; i < num_unlock_responses; i++) {
if (unlock_response[i].status != 0) {
if (verbosity > 1)
fprintf(stderr,
"unlock on node %s failed: %s\n",
response[i].node,
strerror(unlock_response
[0].status));
}
}
}
lvm_cluster_free_request(unlock_response);
}
lvm_cluster_free_request(response);
return 0;
}

36
daemons/clvmd/libclvm.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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 _LIBCLVM_H
#define _LIBCLVM_H
typedef struct lvm_response {
char node[255];
char *response;
int status;
int len;
} lvm_response_t;
extern int lvm_cluster_request(char cmd, const char *node, void *data, int len,
lvm_response_t ** response, int *num);
extern int lvm_cluster_write(char cmd, char *node, void *data, int len);
extern int lvm_cluster_free_request(lvm_response_t * response);
/* The "high-level" API */
extern int lvm_lock_for_cluster(char scope, char *name, int verbosity);
extern int lvm_unlock_for_cluster(char scope, char *name, int verbosity);
#endif

View File

@@ -0,0 +1,447 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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 <pthread.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <assert.h>
#include "libdlm.h"
#include "clvm.h"
#include "clvmd-comms.h"
#include "clvmd.h"
#include "lvm-functions.h"
/* LVM2 headers */
#include "toolcontext.h"
#include "log.h"
#include "activate.h"
#include "hash.h"
#include "locking.h"
static struct cmd_context *cmd = NULL;
static struct hash_table *lv_hash = NULL;
struct lv_info {
int lock_id;
int lock_mode;
};
/* Return the mode a lock is currently held at (or -1 if not held) */
static int get_current_lock(char *resource)
{
struct lv_info *lvi;
lvi = hash_lookup(lv_hash, resource);
if (lvi) {
return lvi->lock_mode;
} else {
return -1;
}
}
/* Called at shutdown to tidy the lockspace */
void unlock_all()
{
struct hash_node *v;
hash_iterate(v, lv_hash) {
struct lv_info *lvi = hash_get_data(lv_hash, v);
sync_unlock(hash_get_key(lv_hash, v), lvi->lock_id);
}
}
/* Gets a real lock and keeps the info in the hash table */
int hold_lock(char *resource, int mode, int flags)
{
int status;
int saved_errno;
struct lv_info *lvi;
flags &= LKF_NOQUEUE; /* Only LKF_NOQUEUE is valid here */
lvi = hash_lookup(lv_hash, resource);
if (lvi) {
/* Already exists - convert it */
status =
sync_lock(resource, mode, LKF_CONVERT | flags,
&lvi->lock_id);
saved_errno = errno;
if (!status)
lvi->lock_mode = mode;
if (status) {
DEBUGLOG("hold_lock. convert to %d failed: %s\n", mode,
strerror(errno));
}
errno = saved_errno;
} else {
lvi = malloc(sizeof(struct lv_info));
if (!lvi)
return -1;
lvi->lock_mode = mode;
status = sync_lock(resource, mode, flags, &lvi->lock_id);
saved_errno = errno;
if (status) {
free(lvi);
DEBUGLOG("hold_lock. lock at %d failed: %s\n", mode,
strerror(errno));
} else {
hash_insert(lv_hash, resource, lvi);
}
errno = saved_errno;
}
return status;
}
/* Unlock and remove it from the hash table */
int hold_unlock(char *resource)
{
struct lv_info *lvi;
int status;
int saved_errno;
lvi = hash_lookup(lv_hash, resource);
if (!lvi) {
DEBUGLOG("hold_unlock, lock not already held\n");
return 0;
}
status = sync_unlock(resource, lvi->lock_id);
saved_errno = errno;
if (!status) {
hash_remove(lv_hash, resource);
free(lvi);
} else {
DEBUGLOG("hold_unlock. unlock failed(%d): %s\n", status,
strerror(errno));
}
errno = saved_errno;
return status;
}
/* Watch the return codes here.
liblvm API functions return 1(true) for success, 0(false) for failure and don't set errno.
libdlm API functions return 0 for success, -1 for failure and do set errno.
These functions here return 0 for success or >0 for failure (where the retcode is errno)
*/
/* Activate LV exclusive or non-exclusive */
static int do_activate_lv(char *resource, int mode)
{
int oldmode;
int status;
int activate_lv;
struct lvinfo lvi;
/* Is it already open ? */
oldmode = get_current_lock(resource);
if (oldmode == mode) {
return 0; /* Nothing to do */
}
/* Does the config file want us to activate this LV ? */
if (!lv_activation_filter(cmd, resource, &activate_lv))
return EIO;
if (!activate_lv)
return 0; /* Success, we did nothing! */
/* Do we need to activate exclusively? */
if (activate_lv == 2)
mode = LKM_EXMODE;
/* OK, try to get the lock */
status = hold_lock(resource, mode, LKF_NOQUEUE);
if (status)
return errno;
/* If it's suspended then resume it */
if (!lv_info_by_lvid(cmd, resource, &lvi))
return EIO;
if (lvi.suspended)
if (!lv_resume(cmd, resource))
return EIO;
/* Now activate it */
if (!lv_activate(cmd, resource))
return EIO;
return 0;
}
/* Resume the LV if it was active */
static int do_resume_lv(char *resource)
{
int oldmode;
/* Is it open ? */
oldmode = get_current_lock(resource);
if (oldmode == -1) {
DEBUGLOG("do_deactivate_lock, lock not already held\n");
return 0; /* We don't need to do anything */
}
if (!lv_resume_if_active(cmd, resource))
return EIO;
return 0;
}
/* Suspend the device if active */
static int do_suspend_lv(char *resource)
{
int oldmode;
struct lvinfo lvi;
/* Is it open ? */
oldmode = get_current_lock(resource);
if (oldmode == -1) {
DEBUGLOG("do_suspend_lv, lock held at %d\n", oldmode);
return 0; /* Not active, so it's OK */
}
/* Only suspend it if it exists */
if (!lv_info_by_lvid(cmd, resource, &lvi))
return EIO;
if (lvi.exists) {
if (!lv_suspend_if_active(cmd, resource)) {
return EIO;
}
}
return 0;
}
static int do_deactivate_lv(char *resource)
{
int oldmode;
int status;
/* Is it open ? */
oldmode = get_current_lock(resource);
if (oldmode == -1) {
DEBUGLOG("do_deactivate_lock, lock not already held\n");
return 0; /* We don't need to do anything */
}
if (!lv_deactivate(cmd, resource))
return EIO;
status = hold_unlock(resource);
if (status)
return errno;
return 0;
}
/* This is the LOCK_LV part that happens on all nodes in the cluster -
it is responsible for the interaction with device-mapper and LVM */
int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
{
int status = 0;
DEBUGLOG("do_lock_lv: resource '%s', cmd = 0x%x, flags = %d\n",
resource, command, lock_flags);
if (!cmd->config_valid || config_files_changed(cmd)) {
/* Reinitialise various settings inc. logging, filters */
if (!refresh_toolcontext(cmd)) {
log_error("Updated config file invalid. Aborting.");
return EINVAL;
}
}
switch (command) {
case LCK_LV_EXCLUSIVE:
status = do_activate_lv(resource, LKM_EXMODE);
break;
case LCK_LV_SUSPEND:
status = do_suspend_lv(resource);
break;
case LCK_UNLOCK:
case LCK_LV_RESUME: /* if active */
status = do_resume_lv(resource);
break;
case LCK_LV_ACTIVATE:
status = do_activate_lv(resource, LKM_CRMODE);
break;
case LCK_LV_DEACTIVATE:
status = do_deactivate_lv(resource);
break;
default:
DEBUGLOG("Invalid LV command 0x%x\n", command);
status = EINVAL;
break;
}
/* clean the pool for another command */
pool_empty(cmd->mem);
DEBUGLOG("Command return is %d\n", status);
return status;
}
/* Functions to do on the local node only BEFORE the cluster-wide stuff above happens */
int pre_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
{
/* Nearly all the stuff happens cluster-wide. Apart from SUSPEND. Here we get the
lock out on this node (because we are the node modifying the metadata)
before suspending cluster-wide.
*/
if (command == LCK_LV_SUSPEND) {
DEBUGLOG("pre_lock_lv: resource '%s', cmd = 0x%x, flags = %d\n",
resource, command, lock_flags);
if (hold_lock(resource, LKM_PWMODE, LKF_NOQUEUE))
return errno;
}
return 0;
}
/* Functions to do on the local node only AFTER the cluster-wide stuff above happens */
int post_lock_lv(unsigned char command, unsigned char lock_flags,
char *resource)
{
/* Opposite of above, done on resume after a metadata update */
if (command == LCK_LV_RESUME) {
int oldmode;
DEBUGLOG
("post_lock_lv: resource '%s', cmd = 0x%x, flags = %d\n",
resource, command, lock_flags);
/* If the lock state is PW then restore it to what it was */
oldmode = get_current_lock(resource);
if (oldmode == LKM_PWMODE) {
struct lvinfo lvi;
if (!lv_info_by_lvid(cmd, resource, &lvi))
return EIO;
if (lvi.exists) {
if (hold_lock(resource, LKM_CRMODE, 0))
return errno;
} else {
if (hold_unlock(resource))
return errno;
}
}
}
return 0;
}
/* Check if a VG is un use by LVM1 so we don't stomp on it */
int do_check_lvm1(char *vgname)
{
int status;
status = check_lvm1_vg_inactive(cmd, vgname);
return status == 1 ? 0 : EBUSY;
}
/*
* Ideally, clvmd should be started before any LVs are active
* but this may not be the case...
* I suppose this also comes in handy if clvmd crashes, not that it would!
*/
static void *get_initial_state()
{
char lv[64], vg[64], flags[25];
char uuid[65];
char line[255];
FILE *lvs =
popen
("/sbin/lvm lvs --nolocking --noheadings -o vg_uuid,lv_uuid,lv_attr",
"r");
if (!lvs)
return NULL;
while (fgets(line, sizeof(line), lvs)) {
if (sscanf(line, "%s %s %s\n", vg, lv, flags) == 3) {
/* States: s:suspended a:active S:dropped snapshot I:invalid snapshot */
if (flags[4] == 'a' || flags[4] == 's') { /* is it active or suspended? */
/* Convert hyphen-separated UUIDs into one */
memcpy(&uuid[0], &vg[0], 6);
memcpy(&uuid[6], &vg[7], 4);
memcpy(&uuid[10], &vg[12], 4);
memcpy(&uuid[14], &vg[17], 4);
memcpy(&uuid[18], &vg[22], 4);
memcpy(&uuid[22], &vg[27], 4);
memcpy(&uuid[26], &vg[32], 6);
memcpy(&uuid[32], &lv[0], 6);
memcpy(&uuid[38], &lv[7], 4);
memcpy(&uuid[42], &lv[12], 4);
memcpy(&uuid[46], &lv[17], 4);
memcpy(&uuid[50], &lv[22], 4);
memcpy(&uuid[54], &lv[27], 4);
memcpy(&uuid[58], &lv[32], 6);
uuid[64] = '\0';
DEBUGLOG("getting initial lock for %s\n", uuid);
hold_lock(uuid, LKM_CRMODE, LKF_NOQUEUE);
}
}
}
fclose(lvs);
return NULL;
}
void init_lvhash()
{
/* Create hash table for keeping LV locks & status */
lv_hash = hash_create(100);
}
/* Called to initialise the LVM context of the daemon */
int init_lvm(void)
{
if (!(cmd = create_toolcontext(NULL))) {
log_error("Failed to allocate command context");
return 0;
}
/* Use LOG_DAEMON for syslog messages instead of LOG_USER */
init_syslog(LOG_DAEMON);
init_debug(_LOG_ERR);
get_initial_state();
return 1;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/* Functions in lvm-functions.c */
#ifndef _LVM_FUNCTIONS_H
#define _LVM_FUNCTIONS_H
extern int pre_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
char *resource);
extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
char *resource);
extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
char *resource);
extern int do_check_lvm1(char *vgname);
extern int init_lvm(void);
extern void init_lvhash(void);
extern int hold_unlock(char *resource);
extern int hold_lock(char *resource, int mode, int flags);
extern void unlock_all(void);
#endif

369
daemons/clvmd/system-lv.c Normal file
View File

@@ -0,0 +1,369 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/* Routines dealing with the System LV */
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/utsname.h>
#include <syslog.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <mntent.h>
#include "libdlm.h"
#include "log.h"
#include "list.h"
#include "locking.h"
#include "system-lv.h"
#include "clvmd-comms.h"
#ifdef HAVE_CCS
#include "ccs.h"
#endif
#define SYSTEM_LV_FILESYSTEM "ext2"
#define SYSTEM_LV_MOUNTPOINT "/tmp/.clvmd-XXXXXX"
extern char *config_filename(void);
static char system_lv_name[PATH_MAX] = { '\0' };
static char mount_point[PATH_MAX] = { '\0' };
static int mounted = 0;
static int mounted_rw = 0;
static int lockid;
static const char *lock_name = "CLVM_SYSTEM_LV";
/* Look in /proc/mounts or (as a last resort) /etc/mtab to
see if the system-lv is mounted. If it is mounted and we
think it's not then abort because we don't have the right
lock status and we don't know what other processes are doing with it.
Returns 1 for mounted, 0 for not mounted so it matches the condition
of the "mounted" static variable above.
*/
static int is_really_mounted(void)
{
FILE *mountfile;
struct mntent *ment;
mountfile = setmntent("/proc/mounts", "r");
if (!mountfile) {
mountfile = setmntent("/etc/mtab", "r");
if (!mountfile) {
log_error("Unable to open /proc/mounts or /etc/mtab");
return -1;
}
}
/* Look for system LV name in the file */
do {
ment = getmntent(mountfile);
if (ment) {
if (strcmp(ment->mnt_fsname, system_lv_name) == 0) {
endmntent(mountfile);
return 1;
}
}
}
while (ment);
endmntent(mountfile);
return 0;
}
/* Get the system LV name from the config file */
static int find_system_lv(void)
{
if (system_lv_name[0] == '\0') {
#ifdef HAVE_CCS
int error;
ccs_node_t *ctree;
/* Read the cluster config file */
/* Open the config file */
error = open_ccs_file(&ctree, "clvm.ccs");
if (error) {
perror("reading config file");
return -1;
}
strcpy(system_lv_name, find_ccs_str(ctree,
"cluster/systemlv", '/',
"/dev/vg/system_lv"));
/* Finished with config file */
close_ccs_file(ctree);
#else
if (getenv("CLVMD_SYSTEM_LV"))
strcpy(system_lv_name, getenv("CLVMD_SYSTEM_LV"));
else
return -1;
#endif
}
/* See if it has been mounted outside our control */
if (is_really_mounted() != mounted) {
log_error
("The system LV state has been mounted/umounted outside the control of clvmd\n"
"it cannot not be used for cluster communications until this is fixed.\n");
return -1;
}
return 0;
}
/* No prizes */
int system_lv_umount(void)
{
if (!mounted)
return 0;
if (umount(mount_point) < 0) {
log_error("umount of system LV (%s) failed: %m\n",
system_lv_name);
return -1;
}
sync_unlock(lock_name, lockid);
mounted = 0;
/* Remove the mount point */
rmdir(mount_point);
return 0;
}
int system_lv_mount(int readwrite)
{
int status;
int saved_errno;
int fd;
if (find_system_lv()) {
errno = EBUSY;
return -1;
}
/* Is it already mounted suitably? */
if (mounted) {
if (!readwrite || (readwrite && mounted_rw)) {
return 0;
} else {
/* Mounted RO and we need RW */
if (system_lv_umount() < 0)
return -1;
}
}
/* Randomize the mount point */
strcpy(mount_point, SYSTEM_LV_MOUNTPOINT);
fd = mkstemp(mount_point);
if (fd < 0) {
log_error("mkstemp for system LV mount point failed: %m\n");
return -1;
}
/* Race condition here but there's no mkstemp for directories */
close(fd);
unlink(mount_point);
mkdir(mount_point, 0600);
/* Make sure we have a system-lv lock */
status =
sync_lock(lock_name, (readwrite) ? LKM_EXMODE : LKM_CRMODE, 0,
&lockid);
if (status < 0)
return -1;
/* Mount it */
if (mount(system_lv_name, mount_point, SYSTEM_LV_FILESYSTEM,
MS_MGC_VAL | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_SYNCHRONOUS
| (readwrite ? 0 : MS_RDONLY), NULL) < 0) {
/* mount(2) returns EINVAL if the volume has no FS on it. So, if we want to
write to it we try to make a filesystem in it and retry the mount */
if (errno == EINVAL && readwrite) {
char cmd[256];
log_error("Attempting mkfs on system LV device %s\n",
system_lv_name);
snprintf(cmd, sizeof(cmd), "/sbin/mkfs -t %s %s",
SYSTEM_LV_FILESYSTEM, system_lv_name);
system(cmd);
if (mount
(system_lv_name, mount_point, SYSTEM_LV_FILESYSTEM,
MS_MGC_VAL | MS_NOSUID | MS_NODEV | MS_NOEXEC |
MS_SYNCHRONOUS | (readwrite ? 0 : MS_RDONLY),
NULL) == 0)
goto mounted;
}
saved_errno = errno;
log_error("mount of system LV (%s, %s, %s) failed: %m\n",
system_lv_name, mount_point, SYSTEM_LV_FILESYSTEM);
sync_unlock(lock_name, lockid);
errno = saved_errno;
return -1;
}
mounted:
/* Set the internal flags */
mounted = 1;
mounted_rw = readwrite;
return 0;
}
/* Erase *all* files in the root directory of the system LV.
This *MUST* be called with an appropriate lock held!
The LV is left mounted RW because it is assumed that the
caller wants to write something here after clearing some space */
int system_lv_eraseall(void)
{
DIR *dir;
struct dirent *ent;
char fname[PATH_MAX];
/* Must be mounted R/W */
system_lv_mount(1);
dir = opendir(mount_point);
if (!dir)
return -1;
while ((ent = readdir(dir))) {
struct stat st;
snprintf(fname, sizeof(fname), "%s/%s", mount_point,
ent->d_name);
if (stat(fname, &st)) {
if (S_ISREG(st.st_mode))
unlink(fname);
}
}
closedir(dir);
return 0;
}
/* This is a "high-level" routine - it mounts the system LV, writes
the data into a file named after this node and then umounts the LV
again */
int system_lv_write_data(char *data, ssize_t len)
{
struct utsname nodeinfo;
char fname[PATH_MAX];
int outfile;
ssize_t thiswrite;
ssize_t written;
if (system_lv_mount(1))
return -1;
/* Build the file name we are goingto use. */
uname(&nodeinfo);
snprintf(fname, sizeof(fname), "%s/%s", mount_point, nodeinfo.nodename);
/* Open the file for output */
outfile = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (outfile < 0) {
int saved_errno = errno;
system_lv_umount();
errno = saved_errno;
return -1;
}
written = 0;
do {
thiswrite = write(outfile, data + written, len - written);
if (thiswrite > 0)
written += thiswrite;
} while (written < len && thiswrite > 0);
close(outfile);
system_lv_umount();
return (thiswrite < 0) ? -1 : 0;
}
/* This is a "high-level" routine - it mounts the system LV, reads
the data from a named file and then umounts the LV
again */
int system_lv_read_data(char *fname_base, char *data, ssize_t *len)
{
char fname[PATH_MAX];
int outfile;
struct stat st;
ssize_t filesize;
ssize_t thisread;
ssize_t readbytes;
if (system_lv_mount(0))
return -1;
/* Build the file name we are going to use. */
snprintf(fname, sizeof(fname), "%s/%s", mount_point, fname_base);
/* Get the file size and stuff. Actually we only need the file size but
this will also check that the file exists */
if (stat(fname, &st) < 0) {
int saved_errno = errno;
log_error("stat of file %s on system LV failed: %m\n", fname);
system_lv_umount();
errno = saved_errno;
return -1;
}
filesize = st.st_size;
outfile = open(fname, O_RDONLY);
if (outfile < 0) {
int saved_errno = errno;
log_error("open of file %s on system LV failed: %m\n", fname);
system_lv_umount();
errno = saved_errno;
return -1;
}
readbytes = 0;
do {
thisread =
read(outfile, data + readbytes, filesize - readbytes);
if (thisread > 0)
readbytes += thisread;
} while (readbytes < filesize && thisread > 0);
close(outfile);
system_lv_umount();
*len = readbytes;
return (thisread < 0) ? -1 : 0;
}

30
daemons/clvmd/system-lv.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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 _CLVM_SYSTEM_LV_H
#define _CLVM_SYSTEM_LV_H
/* Prototypes for System-LV functions */
/* "low-level" functions */
extern int system_lv_umount(void);
extern int system_lv_mount(int readwrite);
extern int system_lv_eraseall(void);
/* "high-level" functions */
extern int system_lv_write_data(char *data, ssize_t len);
extern int system_lv_read_data(char *fname_base, char *data, ssize_t *len);
#endif

480
daemons/clvmd/tcp-comms.c Normal file
View File

@@ -0,0 +1,480 @@
/******************************************************************************
*******************************************************************************
**
** Copyright (C) Sistina Software, Inc. 2002-2003 All rights reserved.
**
*******************************************************************************
******************************************************************************/
/* This provides the inter-clvmd communications for a system without CMAN.
There is a listening TCP socket which accepts new connections in the
normal way.
It can also make outgoing connnections to the other clvmd nodes.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <netdb.h>
#include <assert.h>
#include "ccs.h"
#include "clvm.h"
#include "clvmd-comms.h"
#include "clvmd.h"
#include "clvmd-gulm.h"
#include "hash.h"
#define DEFAULT_TCP_PORT 21064
static int listen_fd = -1;
static int tcp_port;
struct hash_table *sock_hash;
static int get_tcp_port(int default_port);
static int get_our_ip_address(char *addr, int *family);
static int read_from_tcpsock(struct local_client *fd, char *buf, int len, char *csid,
struct local_client **new_client);
/* Called by init_cluster() to open up the listening socket */
// TODO: IPv6 compat.
int init_comms()
{
struct sockaddr *addr = NULL;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
int addr_len;
int family;
char address[MAX_CSID_LEN];
sock_hash = hash_create(100);
tcp_port = get_tcp_port(DEFAULT_TCP_PORT);
/* Get IP address and IP type */
get_our_ip_address(address, &family);
if (family == AF_INET)
{
memcpy(&addr4.sin_addr, addr, sizeof(struct in_addr));
addr = (struct sockaddr *)&addr4;
addr4.sin_port = htons(tcp_port);
addr_len = sizeof(addr4);
}
else
{
memcpy(&addr6.sin6_addr, addr, sizeof(struct in6_addr));
addr = (struct sockaddr *)&addr6;
addr6.sin6_port = htons(tcp_port);
addr_len = sizeof(addr6);
}
listen_fd = socket(family, SOCK_STREAM, 0);
if (listen_fd < 0)
{
return -1;
}
else
{
int one = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
}
addr->sa_family = family;
if (bind(listen_fd, addr, addr_len) < 0)
{
DEBUGLOG("Can't bind to port\n");
syslog(LOG_ERR, "Can't bind to port %d, is clvmd already running ?", tcp_port);
close(listen_fd);
return -1;
}
listen(listen_fd, 5);
return 0;
}
void tcp_remove_client(char *csid)
{
struct local_client *client;
DEBUGLOG("tcp_remove_client\n");
/* Don't actually close the socket here - that's the
job of clvmd.c whch will do the job when it notices the
other end has gone. We just need to remove the client(s) from
the hash table so we don't try to use it for sending any more */
client = hash_lookup_binary(sock_hash, csid, MAX_CSID_LEN);
if (client)
{
hash_remove_binary(sock_hash, csid, MAX_CSID_LEN);
}
/* Look for a mangled one too */
csid[0] ^= 0x80;
client = hash_lookup_binary(sock_hash, csid, MAX_CSID_LEN);
if (client)
{
hash_remove_binary(sock_hash, csid, MAX_CSID_LEN);
}
/* Put it back as we found it */
csid[0] ^= 0x80;
}
int alloc_client(int fd, char *csid, struct local_client **new_client)
{
struct local_client *client;
DEBUGLOG("alloc_client %d csid = [%d.%d.%d.%d]\n", fd,csid[0],csid[1],csid[2],csid[3]);
/* Create a local_client and return it */
client = malloc(sizeof(struct local_client));
if (!client)
{
DEBUGLOG("malloc failed\n");
return -1;
}
memset(client, 0, sizeof(struct local_client));
client->fd = fd;
client->type = CLUSTER_DATA_SOCK;
client->callback = read_from_tcpsock;
if (new_client)
*new_client = client;
/* Add to our list of node sockets */
if (hash_lookup_binary(sock_hash, csid, MAX_CSID_LEN))
{
DEBUGLOG("alloc_client mangling CSID for second connection\n");
/* This is a duplicate connection but we can't close it because
the other end may already have started sending.
So, we mangle the IP address and keep it, all sending will
go out of the main FD
*/
csid[0] ^= 0x80;
client->bits.net.flags = 1; /* indicate mangled CSID */
/* If it still exists then kill the connection as we should only
ever have one incoming connection from each node */
if (hash_lookup_binary(sock_hash, csid, MAX_CSID_LEN))
{
DEBUGLOG("Multiple incoming connections from node\n");
syslog(LOG_ERR, " Bogus incoming connection from %d.%d.%d.%d\n", csid[0],csid[1],csid[2],csid[3]);
free(client);
errno = ECONNREFUSED;
return -1;
}
}
hash_insert_binary(sock_hash, csid, MAX_CSID_LEN, client);
return 0;
}
int get_main_cluster_fd()
{
return listen_fd;
}
/* Read on main comms (listen) socket, accept it */
int cluster_fd_callback(struct local_client *fd, char *buf, int len, char *csid,
struct local_client **new_client)
{
int newfd;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int status;
char name[MAX_CLUSTER_MEMBER_NAME_LEN];
DEBUGLOG("cluster_fd_callback\n");
*new_client = NULL;
newfd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
DEBUGLOG("cluster_fd_callback, newfd=%d (errno=%d)\n", newfd, errno);
if (!newfd)
{
syslog(LOG_ERR, "error in accept: %m");
errno = EAGAIN;
return -1; /* Don't return an error or clvmd will close the listening FD */
}
/* Check that the client is a member of the cluster
and reject if not.
// FIXME: IPv4 specific
*/
if (name_from_csid((char *)&addr.sin_addr.s_addr, name) < 0)
{
char *ip = (char *)&addr.sin_addr.s_addr;
syslog(LOG_ERR, "Got connect from non-cluster node %d.%d.%d.%d\n",
ip[0], ip[1], ip[2], ip[3]);
DEBUGLOG("Got connect from non-cluster node %d.%d.%d.%d\n",
ip[0], ip[1], ip[2], ip[3]);
close(newfd);
errno = EAGAIN;
return -1;
}
status = alloc_client(newfd, (char *)&addr.sin_addr.s_addr, new_client);
if (status)
{
DEBUGLOG("cluster_fd_callback, alloc_client failed, status = %d\n", status);
close(newfd);
/* See above... */
errno = EAGAIN;
return -1;
}
DEBUGLOG("cluster_fd_callback, returning %d, %p\n", newfd, *new_client);
return newfd;
}
static int read_from_tcpsock(struct local_client *client, char *buf, int len, char *csid,
struct local_client **new_client)
{
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
int status;
DEBUGLOG("read_from_tcpsock fd %d\n", client->fd);
*new_client = NULL;
/* Get "csid" */
getpeername(client->fd, (struct sockaddr *)&addr, &slen);
memcpy(csid, &addr.sin_addr.s_addr, MAX_CSID_LEN);
status = read(client->fd, buf, len);
DEBUGLOG("read_from_tcpsock, status = %d(errno = %d)\n", status, errno);
/* Remove it from the hash table if there's an error, clvmd will
remove the socket from its lists and free the client struct */
if (status == 0 ||
(status < 0 && errno != EAGAIN && errno != EINTR))
{
char remcsid[MAX_CSID_LEN];
memcpy(remcsid, csid, MAX_CSID_LEN);
close(client->fd);
/* If the csid was mangled, then make sure we remove the right entry */
if (client->bits.net.flags)
remcsid[0] ^= 0x80;
hash_remove_binary(sock_hash, remcsid, MAX_CSID_LEN);
/* Tell cluster manager layer */
add_down_node(remcsid);
}
return status;
}
static int connect_csid(char *csid, struct local_client **newclient)
{
int fd;
struct sockaddr_in addr;
int status;
DEBUGLOG("Connecting socket\n");
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
syslog(LOG_ERR, "Unable to create new socket: %m");
return -1;
}
addr.sin_family = AF_INET;
memcpy(&addr.sin_addr.s_addr, csid, MAX_CSID_LEN);
addr.sin_port = htons(tcp_port);
DEBUGLOG("Connecting socket %d\n", fd);
if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
{
syslog(LOG_ERR, "Unable to connect to remote node: %m");
DEBUGLOG("Unable to connect to remote node: %s\n", strerror(errno));
close(fd);
return -1;
}
status = alloc_client(fd, csid, newclient);
if (status)
close(fd);
else
add_client(*newclient);
/* If we can connect to it, it must be running a clvmd */
add_up_node(csid);
return status;
}
/* Send a message to a known CSID */
static int tcp_send_message(void *buf, int msglen, unsigned char *csid, const char *errtext)
{
int status;
struct local_client *client;
char ourcsid[MAX_CSID_LEN];
assert(csid);
DEBUGLOG("tcp_send_message, csid = [%d.%d.%d.%d], msglen = %d\n", csid[0],csid[1],csid[2],csid[3], msglen);
/* Don't connect to ourself */
get_our_csid(ourcsid);
if (memcmp(csid, ourcsid, MAX_CSID_LEN) == 0)
return msglen;
client = hash_lookup_binary(sock_hash, csid, MAX_CSID_LEN);
if (!client)
{
status = connect_csid(csid, &client);
if (status)
return -1;
}
DEBUGLOG("tcp_send_message, fd = %d\n", client->fd);
return write(client->fd, buf, msglen);
}
int cluster_send_message(void *buf, int msglen, char *csid, const char *errtext)
{
int status=0;
DEBUGLOG("cluster send message, csid = %p, msglen = %d\n", csid, msglen);
/* If csid is NULL then send to all known (not just connected) nodes */
if (!csid)
{
void *context = NULL;
char loop_csid[MAX_CSID_LEN];
/* Loop round all gulm-known nodes */
while (get_next_node_csid(&context, loop_csid))
{
status = tcp_send_message(buf, msglen, loop_csid, errtext);
if (status == 0 ||
(status < 0 && (errno == EAGAIN || errno == EINTR)))
break;
}
}
else
{
status = tcp_send_message(buf, msglen, csid, errtext);
}
return status;
}
static int get_tcp_port(int default_port)
{
int ccs_handle;
int port = default_port;
char *portstr;
ccs_handle = ccs_connect();
if (ccs_handle)
{
return port;
}
if (!ccs_get(ccs_handle, "//clvm/@port", &portstr))
{
port = atoi(portstr);
free(portstr);
if (port <= 0 && port >= 65536)
port = default_port;
}
ccs_disconnect(ccs_handle);
DEBUGLOG("Using port %d for communications\n", port);
return port;
}
/* To get our own IP address we get the locally bound address of the
socket that's talking to GULM in the assumption(eek) that it will
be on the "right" network in a multi-homed system */
static int get_our_ip_address(char *addr, int *family)
{
/* Use a sockaddr_in6 to make sure it's big enough */
struct sockaddr_in6 saddr;
int socklen = sizeof(saddr);
if (!getsockname(gulm_fd(), (struct sockaddr *)&saddr, &socklen))
{
if (saddr.sin6_family == AF_INET6)
{
memcpy(addr, &saddr.sin6_addr, sizeof(saddr.sin6_addr));
}
else
{
struct sockaddr_in *sin4 = (struct sockaddr_in *)&saddr;
memcpy(addr, &sin4->sin_addr, sizeof(sin4->sin_addr));
}
return 0;
}
return -1;
}
/* Public version of above for those that don't care what protocol
we're using */
void get_our_csid(char *csid)
{
static char our_csid[MAX_CSID_LEN];
static int got_csid = 0;
if (!got_csid)
{
int family;
memset(our_csid, 0, sizeof(our_csid));
if (get_our_ip_address(our_csid, &family))
{
got_csid = 1;
}
}
memcpy(csid, our_csid, MAX_CSID_LEN);
}
/* Get someone else's IP address from DNS */
int get_ip_address(char *node, char *addr)
{
struct hostent *he;
memset(addr, 0, MAX_CSID_LEN);
// TODO: what do we do about multi-homed hosts ???
// CCSs ip_interfaces solved this but some bugger removed it.
/* Try IPv6 first. The man page for gethostbyname implies that
it will lookup ip6 & ip4 names, but it seems not to */
he = gethostbyname2(node, AF_INET6);
if (!he)
he = gethostbyname2(node, AF_INET);
if (!he)
return -1;
/* For IPv4 address just use the lower 4 bytes */
memcpy(&addr, he->h_addr_list[0],
he->h_length);
return 0;
}

View File

@@ -0,0 +1,7 @@
#include <netinet/in.h>
#define MAX_CLUSTER_MESSAGE 1600
#define MAX_CSID_LEN sizeof(struct in6_addr)
#define MAX_CLUSTER_MEMBER_NAME_LEN 128
extern int init_comms(void);

View File

@@ -18,7 +18,7 @@ VPATH = @srcdir@
CONFSRC=example.conf
CONFDEST=lvm.conf
include ../make.tmpl
include $(top_srcdir)/make.tmpl
install:
@if [ ! -e $(confdir)/$(CONFDEST) ]; then \

View File

@@ -1,3 +1,4 @@
../daemons/clvmd/clvm.h
../lib/activate/activate.h
../lib/activate/targets.h
../lib/cache/lvmcache.h

View File

@@ -104,6 +104,14 @@ ifeq ("@POOL@", "internal")
format_pool/pool_label.c
endif
ifeq ("@CLUSTER@", "internal")
SOURCES += locking/cluster_locking.c
endif
ifeq ("@CLUSTER@", "shared")
SUBDIRS += locking
endif
ifeq ("@SNAPSHOTS@", "internal")
SOURCES += snapshot/snapshot.c
endif
@@ -136,5 +144,5 @@ LIB_STATIC = liblvm.a
$(SUBDIRS): $(LIB_STATIC)
include ../make.tmpl
include $(top_srcdir)/make.tmpl

View File

@@ -798,7 +798,7 @@ struct cmd_context *create_toolcontext(struct arg *the_args)
#endif
if (!setlocale(LC_ALL, ""))
log_error("setlocale failed");
log_very_verbose("setlocale failed");
#ifdef INTL_PACKAGE
bindtextdomain(INTL_PACKAGE, LOCALEDIR);

View File

@@ -19,7 +19,8 @@
struct hash_node {
struct hash_node *next;
void *data;
char key[1];
int keylen;
char key[0];
};
struct hash_table {
@@ -56,22 +57,23 @@ static unsigned char _nums[] = {
209
};
static struct hash_node *_create_node(const char *str)
static struct hash_node *_create_node(const char *str, int len)
{
/* remember sizeof(n) includes an extra char from key[1],
so not adding 1 to the strlen as you would expect */
struct hash_node *n = dbg_malloc(sizeof(*n) + strlen(str));
struct hash_node *n = dbg_malloc(sizeof(*n) + len);
if (n)
strcpy(n->key, str);
if (n) {
memcpy(n->key, str, len);
n->keylen = len;
}
return n;
}
static unsigned _hash(const char *str)
static unsigned _hash(const char *str, uint32_t len)
{
unsigned long h = 0, g;
while (*str) {
unsigned long h = 0, g, i;
for (i = 0; i < len; i++) {
h <<= 4;
h += _nums[(int) *str++];
g = h & ((unsigned long) 0xf << 16u);
@@ -80,6 +82,7 @@ static unsigned _hash(const char *str)
h ^= g >> 5u;
}
}
return h;
}
@@ -134,32 +137,35 @@ void hash_destroy(struct hash_table *t)
dbg_free(t);
}
static struct hash_node **_find(struct hash_table *t, const char *key)
static inline struct hash_node **_find(struct hash_table *t, const char *key,
uint32_t len)
{
unsigned h = _hash(key) & (t->num_slots - 1);
unsigned h = _hash(key, len) & (t->num_slots - 1);
struct hash_node **c;
for (c = &t->slots[h]; *c; c = &((*c)->next))
if (!strcmp(key, (*c)->key))
if (!memcmp(key, (*c)->key, len))
break;
return c;
}
void *hash_lookup(struct hash_table *t, const char *key)
void *hash_lookup_binary(struct hash_table *t, const char *key,
uint32_t len)
{
struct hash_node **c = _find(t, key);
struct hash_node **c = _find(t, key, len);
return *c ? (*c)->data : 0;
}
int hash_insert(struct hash_table *t, const char *key, void *data)
int hash_insert_binary(struct hash_table *t, const char *key,
uint32_t len, void *data)
{
struct hash_node **c = _find(t, key);
struct hash_node **c = _find(t, key, len);
if (*c)
(*c)->data = data;
else {
struct hash_node *n = _create_node(key);
struct hash_node *n = _create_node(key, len);
if (!n)
return 0;
@@ -173,9 +179,10 @@ int hash_insert(struct hash_table *t, const char *key, void *data)
return 1;
}
void hash_remove(struct hash_table *t, const char *key)
void hash_remove_binary(struct hash_table *t, const char *key,
uint32_t len)
{
struct hash_node **c = _find(t, key);
struct hash_node **c = _find(t, key, len);
if (*c) {
struct hash_node *old = *c;
@@ -185,6 +192,21 @@ void hash_remove(struct hash_table *t, const char *key)
}
}
void *hash_lookup(struct hash_table *t, const char *key)
{
return hash_lookup_binary(t, key, strlen(key) + 1);
}
int hash_insert(struct hash_table *t, const char *key, void *data)
{
return hash_insert_binary(t, key, strlen(key) + 1, data);
}
void hash_remove(struct hash_table *t, const char *key)
{
hash_remove_binary(t, key, strlen(key) + 1);
}
unsigned hash_get_num_entries(struct hash_table *t)
{
return t->num_nodes;
@@ -235,6 +257,6 @@ struct hash_node *hash_get_first(struct hash_table *t)
struct hash_node *hash_get_next(struct hash_table *t, struct hash_node *n)
{
unsigned h = _hash(n->key) & (t->num_slots - 1);
unsigned h = _hash(n->key, n->keylen) & (t->num_slots - 1);
return n->next ? n->next : _next_slot(t, h + 1);
}

View File

@@ -26,10 +26,14 @@ void hash_destroy(struct hash_table *t);
void hash_wipe(struct hash_table *t);
void *hash_lookup(struct hash_table *t, const char *key);
void *hash_lookup_fixed(struct hash_table *t, const char *key, uint32_t len);
int hash_insert(struct hash_table *t, const char *key, void *data);
void hash_remove(struct hash_table *t, const char *key);
void *hash_lookup_binary(struct hash_table *t, const char *key, uint32_t len);
int hash_insert_binary(struct hash_table *t, const char *key, uint32_t len,
void *data);
void hash_remove_binary(struct hash_table *t, const char *key, uint32_t len);
unsigned hash_get_num_entries(struct hash_table *t);
void hash_iter(struct hash_table *t, iterate_fn f);

View File

@@ -27,7 +27,7 @@ SOURCES =\
LIB_SHARED = liblvm2format1.so
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
.PHONY: install

View File

@@ -24,7 +24,7 @@ SOURCES =\
LIB_SHARED = liblvm2formatpool.so
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
.PHONY: install

View File

@@ -0,0 +1,4 @@
locking_init
locking_end
lock_resource
reset_locking

32
lib/locking/Makefile.in Normal file
View File

@@ -0,0 +1,32 @@
#
# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
#
# This file is part of the 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 General Public License v.2.
#
# You should have received a copy of the GNU 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@
VPATH = @srcdir@
SOURCES = cluster_locking.c
LIB_SHARED = liblvm2clusterlock.so
include $(top_srcdir)/make.tmpl
.PHONY: install
install: liblvm2clusterlock.so
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \
$(libdir)/liblvm2clusterlock.so.$(LIB_VERSION)
$(LN_S) -f liblvm2clusterlock.so.$(LIB_VERSION) \
$(libdir)/liblvm2clusterlock.so

View File

@@ -0,0 +1,462 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 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 General Public License v.2.
*
* You should have received a copy of the GNU 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
*/
/*
* Locking functions for LVM.
* The main purpose of this part of the library is to serialise LVM
* management operations across a cluster.
*/
#include "lib.h"
#include "clvm.h"
#include "lvm-string.h"
#include "locking.h"
#include "locking_types.h"
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#ifndef CLUSTER_LOCKING_INTERNAL
int lock_resource(struct cmd_context *cmd, const char *resource, int flags);
void locking_end(void);
int locking_init(int type, struct config_tree *cf, uint32_t *flags);
#endif
typedef struct lvm_response {
char node[255];
char *response;
int status;
int len;
} lvm_response_t;
/*
* This gets stuck at the start of memory we allocate so we
* can sanity-check it at deallocation time
*/
#define LVM_SIGNATURE 0x434C564D
/*
* NOTE: the LVMD uses the socket FD as the client ID, this means
* that any client that calls fork() will inherit the context of
* it's parent.
*/
static int _clvmd_sock = -1;
/* FIXME Install SIGPIPE handler? */
/* Open connection to the Cluster Manager daemon */
static int _open_local_sock(void)
{
int local_socket;
struct sockaddr_un sockaddr;
/* Open local socket */
if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
log_error("Local socket creation failed: %s", strerror(errno));
return -1;
}
memset(&sockaddr, 0, sizeof(sockaddr));
memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
sockaddr.sun_family = AF_UNIX;
if (connect(local_socket,(struct sockaddr *) &sockaddr,
sizeof(sockaddr))) {
int saved_errno = errno;
log_error("connect() failed on local socket: %s",
strerror(errno));
if (close(local_socket))
stack;
errno = saved_errno;
return -1;
}
return local_socket;
}
/* Send a request and return the status */
static int _send_request(char *inbuf, int inlen, char **retbuf)
{
char outbuf[PIPE_BUF];
struct clvm_header *outheader = (struct clvm_header *) outbuf;
int len;
int off;
int buflen;
int err;
/* Send it to CLVMD */
rewrite:
if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
if (err == -1 && errno == EINTR)
goto rewrite;
log_error("Error writing data to clvmd: %s", strerror(errno));
return 0;
}
/* Get the response */
reread:
if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
if (errno == EINTR)
goto reread;
log_error("Error reading data from clvmd: %s", strerror(errno));
return 0;
}
if (len == 0) {
log_error("EOF reading CLVMD");
errno = ENOTCONN;
return 0;
}
/* Allocate buffer */
buflen = len + outheader->arglen;
*retbuf = dbg_malloc(buflen);
if (!*retbuf) {
errno = ENOMEM;
return 0;
}
/* Copy the header */
memcpy(*retbuf, outbuf, len);
outheader = (struct clvm_header *) *retbuf;
/* Read the returned values */
off = 1; /* we've already read the first byte */
while (off < outheader->arglen && len > 0) {
len = read(_clvmd_sock, outheader->args + off,
buflen - off - offsetof(struct clvm_header, args));
if (len > 0)
off += len;
}
/* Was it an error ? */
if (outheader->status < 0) {
errno = -outheader->status;
log_error("cluster send request failed: %s", strerror(errno));
return 0;
}
return 1;
}
/* Build the structure header and parse-out wildcard node names */
static void _build_header(struct clvm_header *head, int cmd, const char *node,
int len)
{
head->cmd = cmd;
head->status = 0;
head->flags = 0;
head->clientid = 0;
head->arglen = len;
if (node) {
/*
* Allow a couple of special node names:
* "*" for all nodes,
* "." for the local node only
*/
if (strcmp(node, "*") == 0) {
head->node[0] = '\0';
} else if (strcmp(node, ".") == 0) {
head->node[0] = '\0';
head->flags = CLVMD_FLAG_LOCAL;
} else
strcpy(head->node, node);
} else
head->node[0] = '\0';
}
/*
* Send a message to a(or all) node(s) in the cluster and wait for replies
*/
static int _cluster_request(char cmd, const char *node, void *data, int len,
lvm_response_t ** response, int *num)
{
char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
int *outptr;
char *inptr;
char *retbuf = NULL;
int status;
int i;
int num_responses = 0;
struct clvm_header *head = (struct clvm_header *) outbuf;
lvm_response_t *rarray;
*num = 0;
if (_clvmd_sock == -1)
_clvmd_sock = _open_local_sock();
if (_clvmd_sock == -1)
return 0;
_build_header(head, cmd, node, len);
memcpy(head->node + strlen(head->node) + 1, data, len);
status = _send_request(outbuf, sizeof(struct clvm_header) +
strlen(head->node) + len, &retbuf);
if (!status)
goto out;
/* Count the number of responses we got */
head = (struct clvm_header *) retbuf;
inptr = head->args;
while (inptr[0]) {
num_responses++;
inptr += strlen(inptr) + 1;
inptr += sizeof(int);
inptr += strlen(inptr) + 1;
}
/*
* Allocate response array.
* With an extra pair of INTs on the front to sanity
* check the pointer when we are given it back to free
*/
outptr = dbg_malloc(sizeof(lvm_response_t) * num_responses +
sizeof(int) * 2);
if (!outptr) {
errno = ENOMEM;
status = 0;
goto out;
}
*response = (lvm_response_t *) (outptr + 2);
outptr[0] = LVM_SIGNATURE;
outptr[1] = num_responses;
rarray = *response;
/* Unpack the response into an lvm_response_t array */
inptr = head->args;
i = 0;
while (inptr[0]) {
strcpy(rarray[i].node, inptr);
inptr += strlen(inptr) + 1;
rarray[i].status = *(int *) inptr;
inptr += sizeof(int);
rarray[i].response = dbg_malloc(strlen(inptr) + 1);
if (rarray[i].response == NULL) {
/* Free up everything else and return error */
int j;
for (j = 0; j < i; j++)
dbg_free(rarray[i].response);
free(outptr);
errno = ENOMEM;
status = -1;
goto out;
}
strcpy(rarray[i].response, inptr);
rarray[i].len = strlen(inptr);
inptr += strlen(inptr) + 1;
i++;
}
*num = num_responses;
*response = rarray;
out:
if (retbuf)
dbg_free(retbuf);
return status;
}
/* Free reply array */
static int _cluster_free_request(lvm_response_t * response)
{
int *ptr = (int *) response - 2;
int i;
int num;
/* Check it's ours to free */
if (response == NULL || *ptr != LVM_SIGNATURE) {
errno = EINVAL;
return 0;
}
num = ptr[1];
for (i = 0; i < num; i++) {
dbg_free(response[i].response);
}
dbg_free(ptr);
return 1;
}
static int _lock_for_cluster(unsigned char cmd, unsigned int flags, char *name)
{
int status;
int i;
char *args;
const char *node = "";
int len;
int saved_errno = errno;
lvm_response_t *response = NULL;
int num_responses;
assert(name);
len = strlen(name) + 3;
args = alloca(len);
strcpy(args + 2, name);
args[0] = flags & 0xBF; /* Maskoff LOCAL flag */
args[1] = 0; /* Not used now */
/*
* VG locks are just that: locks, and have no side effects
* so we only need to do them on the local node because all
* locks are cluster-wide.
* Also, if the lock is exclusive it makes no sense to try to
* acquire it on all nodes, so just do that on the local node too.
*/
if (cmd == CLVMD_CMD_LOCK_VG ||
(flags & LCK_TYPE_MASK) == LCK_EXCL ||
(flags & LCK_LOCAL))
node = ".";
status = _cluster_request(cmd, node, args, len,
&response, &num_responses);
/* If any nodes were down then display them and return an error */
for (i = 0; i < num_responses; i++) {
if (response[i].status == -EHOSTDOWN) {
log_error("clvmd not running on node %s",
response[i].node);
status = 0;
} else if (response[i].status) {
log_error("Error locking on node %s: %s",
response[i].node,
response[i].response[0] ?
response[i].response :
strerror(response[i].status));
status = 0;
}
}
saved_errno = errno;
_cluster_free_request(response);
errno = saved_errno;
return status;
}
/* API entry point for LVM */
#ifdef CLUSTER_LOCKING_INTERNAL
static int _lock_resource(struct cmd_context *cmd, const char *resource,
int flags)
#else
int lock_resource(struct cmd_context *cmd, const char *resource, int flags)
#endif
{
char lockname[PATH_MAX];
int cluster_cmd = 0;
assert(strlen(resource) < sizeof(lockname));
switch (flags & LCK_SCOPE_MASK) {
case LCK_VG:
/* If the VG name is empty then lock the unused PVs */
if (!resource || !*resource)
lvm_snprintf(lockname, sizeof(lockname), "P_orphans");
else
lvm_snprintf(lockname, sizeof(lockname), "V_%s",
resource);
cluster_cmd = CLVMD_CMD_LOCK_VG;
flags &= LCK_TYPE_MASK;
break;
case LCK_LV:
cluster_cmd = CLVMD_CMD_LOCK_LV;
strcpy(lockname, resource);
flags &= 0xffdf; /* Mask off HOLD flag */
break;
default:
log_error("Unrecognised lock scope: %d",
flags & LCK_SCOPE_MASK);
return 0;
}
/* Send a message to the cluster manager */
log_very_verbose("Locking %s at 0x%x", lockname, flags);
return _lock_for_cluster(cluster_cmd, flags, lockname);
}
#ifdef CLUSTER_LOCKING_INTERNAL
static void _locking_end(void)
#else
void locking_end(void)
#endif
{
if (_clvmd_sock != -1 && close(_clvmd_sock))
stack;
_clvmd_sock = -1;
}
#ifdef CLUSTER_LOCKING_INTERNAL
static void _reset_locking(void)
#else
void reset_locking(void)
#endif
{
if (close(_clvmd_sock))
stack;
_clvmd_sock = _open_local_sock();
if (_clvmd_sock == -1)
stack;
}
#ifdef CLUSTER_LOCKING_INTERNAL
int init_cluster_locking(struct locking_type *locking, struct config_tree *cft)
{
locking->lock_resource = _lock_resource;
locking->fin_locking = _locking_end;
locking->reset_locking = _reset_locking;
locking->flags = LCK_PRE_MEMLOCK;
_clvmd_sock = _open_local_sock();
if (_clvmd_sock == -1)
return 0;
return 1;
}
#else
int locking_init(int type, struct config_tree *cf, uint32_t *flags)
{
_clvmd_sock = _open_local_sock();
if (_clvmd_sock == -1)
return 0;
/* Ask LVM to lock memory before calling us */
*flags |= LCK_PRE_MEMLOCK;
return 1;
}
#endif

View File

@@ -145,6 +145,14 @@ int init_locking(int type, struct config_tree *cft)
return 1;
#endif
#ifdef CLUSTER_LOCKING_INTERNAL
case 3:
if (!init_cluster_locking(&_locking, cft))
break;
log_very_verbose("Cluster locking enabled.");
return 1;
#endif
default:
log_error("Unknown locking type requested.");
return 0;

View File

@@ -63,6 +63,7 @@ int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
*/
#define LCK_NONBLOCK 0x00000010 /* Don't block waiting for lock? */
#define LCK_HOLD 0x00000020 /* Hold lock when lock_vol returns? */
#define LCK_LOCAL 0x00000040 /* Don't propagate to other nodes */
/*
* Common combinations
@@ -85,9 +86,14 @@ int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
#define activate_lv(cmd, vol) lock_vol(cmd, vol, LCK_LV_ACTIVATE | LCK_HOLD)
#define activate_lv_excl(cmd, vol) \
lock_vol(cmd, vol, LCK_LV_EXCLUSIVE | LCK_HOLD)
#define activate_lv_local(cmd, vol) \
lock_vol(cmd, vol, LCK_LV_ACTIVATE | LCK_HOLD | LCK_LOCAL)
#define deactivate_lv_local(cmd, vol) \
lock_vol(cmd, vol, LCK_LV_DEACTIVATE | LCK_LOCAL)
/* Process list of LVs */
int suspend_lvs(struct cmd_context *cmd, struct list *lvs);
int resume_lvs(struct cmd_context *cmd, struct list *lvs);
int activate_lvs_excl(struct cmd_context *cmd, struct list *lvs);

View File

@@ -40,3 +40,4 @@ int init_no_locking(struct locking_type *locking, struct config_tree *cf);
int init_file_locking(struct locking_type *locking, struct config_tree *cf);
int init_external_locking(struct locking_type *locking, struct config_tree *cf);
int init_cluster_locking(struct locking_type *locking, struct config_tree *cf);

View File

@@ -38,6 +38,7 @@ static int _indent = 1;
static int _log_cmd_name = 0;
static int _log_suppress = 0;
static int _ignorelockingfailure = 0;
static int _security_level = SECURITY_LEVEL;
static char _cmd_name[30] = "";
static char _msg_prefix[30] = " ";
static int _already_logging = 0;
@@ -147,6 +148,11 @@ void init_ignorelockingfailure(int level)
_ignorelockingfailure = level;
}
void init_security_level(int level)
{
_security_level = level;
}
void init_cmd_name(int status)
{
_log_cmd_name = status;
@@ -191,6 +197,11 @@ int ignorelockingfailure()
return _ignorelockingfailure;
}
int security_level()
{
return _security_level;
}
void init_debug(int level)
{
_debug_level = level;

View File

@@ -49,6 +49,7 @@
#define _LOG_FATAL 2
#define VERBOSE_BASE_LEVEL _LOG_WARN
#define SECURITY_LEVEL 0
void init_log_file(const char *log_file, int append);
void init_log_direct(const char *log_file, int append);
@@ -68,6 +69,7 @@ void init_cmd_name(int status);
void init_msg_prefix(const char *prefix);
void init_indent(int indent);
void init_ignorelockingfailure(int level);
void init_security_level(int level);
void set_cmd_name(const char *cmd_name);
@@ -76,6 +78,7 @@ int partial_mode(void);
int pvmove_mode(void);
int debug_level(void);
int ignorelockingfailure(void);
int security_level(void);
/* Suppress messages to stdout/stderr */
void log_suppress(int suppress);

View File

@@ -39,7 +39,7 @@ static int _add_pv_to_vg(struct format_instance *fid, struct volume_group *vg,
}
list_init(&mdas);
if (!(pv = pv_read(fid->fmt->cmd, pv_name, &mdas, NULL))) {
if (!(pv = pv_read(fid->fmt->cmd, pv_name, &mdas, NULL, 1))) {
log_error("%s not identified as an existing physical volume",
pv_name);
return 0;
@@ -426,7 +426,7 @@ struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
{
struct physical_volume *pv;
if (!(pv = pv_read(cmd, pv_name, NULL, NULL))) {
if (!(pv = pv_read(cmd, pv_name, NULL, NULL, 1))) {
log_error("Physical volume %s not found", pv_name);
return NULL;
}
@@ -605,7 +605,7 @@ static struct volume_group *_vg_read_orphans(struct cmd_context *cmd)
list_iterate(ih, &vginfo->infos) {
dev = list_item(ih, struct lvmcache_info)->dev;
if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL))) {
if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL, 1))) {
continue;
}
if (!(pvl = pool_zalloc(cmd->mem, sizeof(*pvl)))) {
@@ -814,7 +814,8 @@ struct logical_volume *lv_from_lvid(struct cmd_context *cmd, const char *lvid_s)
/* FIXME Use label functions instead of PV functions */
struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
struct list *mdas, uint64_t *label_sector)
struct list *mdas, uint64_t *label_sector,
int warnings)
{
struct physical_volume *pv;
struct label *label;
@@ -827,7 +828,9 @@ struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
}
if (!(label_read(dev, &label))) {
log_error("No physical volume label read from %s", pv_name);
if (warnings)
log_error("No physical volume label read from %s",
pv_name);
return 0;
}

View File

@@ -369,7 +369,8 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
int *consistent);
struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid);
struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
struct list *mdas, uint64_t *label_sector);
struct list *mdas, uint64_t *label_sector,
int warnings);
struct list *get_pvs(struct cmd_context *cmd);
/* Set full_scan to 1 to re-read every (filtered) device label */

View File

@@ -20,7 +20,7 @@ SOURCES = mirrored.c
LIB_SHARED = liblvm2mirror.so
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
.PHONY: install

View File

@@ -219,6 +219,9 @@ void sync_dir(const char *file)
while (*c != '/' && c > dir)
c--;
if (c == dir)
*c++ = '.';
*c = '\0';
}

View File

@@ -1063,6 +1063,7 @@ int report_object(void *handle, struct volume_group *vg,
struct row *row;
struct field *field;
void *data = NULL;
int skip;
if (lv && pv) {
log_error("report_object: One of *lv and *pv must be NULL!");
@@ -1090,6 +1091,8 @@ int report_object(void *handle, struct volume_group *vg,
list_iterate(fh, &rh->field_props) {
fp = list_item(fh, struct field_properties);
skip = 0;
if (!(field = pool_zalloc(rh->mem, sizeof(*field)))) {
log_error("struct field allocation failed");
return 0;
@@ -1101,6 +1104,10 @@ int report_object(void *handle, struct volume_group *vg,
data = (void *) lv + _fields[fp->field_num].offset;
break;
case VGS:
if (!vg) {
skip = 1;
break;
}
data = (void *) vg + _fields[fp->field_num].offset;
break;
case PVS:
@@ -1110,7 +1117,10 @@ int report_object(void *handle, struct volume_group *vg,
data = (void *) seg + _fields[fp->field_num].offset;
}
if (!_fields[fp->field_num].report_fn(rh, field, data)) {
if (skip) {
field->report_string = "";
field->sort_value = (const void *) field->report_string;
} else if (!_fields[fp->field_num].report_fn(rh, field, data)) {
log_error("report function failed for field %s",
_fields[fp->field_num].id);
return 0;

View File

@@ -20,7 +20,7 @@ SOURCES = snapshot.c
LIB_SHARED = liblvm2snapshot.so
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
.PHONY: install

View File

@@ -1121,8 +1121,8 @@ static int _create_and_load_v4(struct dm_task *dmt)
/* Use the original structure last so the info will be correct */
dmt->type = DM_DEVICE_RESUME;
dmt->uuid = NULL;
free(dmt->uuid);
dmt->uuid = NULL;
r = dm_task_run(dmt);

View File

@@ -28,8 +28,8 @@ CFLAGS += @CFLAGS@
CLDFLAGS += @CLDFLAGS@
CLDWHOLEARCHIVE += @CLDWHOLEARCHIVE@
CLDNOWHOLEARCHIVE += @CLDNOWHOLEARCHIVE@
LD_DEPS += @LD_DEPS@
LD_FLAGS += @LD_FLAGS@
LDDEPS += @LDDEPS@
LDFLAGS += @LDFLAGS@
SOFLAG += @SOFLAG@
# Setup directory variables
@@ -61,24 +61,24 @@ CFLAGS += -fPIC -Wall -Wundef -Wshadow -Wcast-align -Wwrite-strings -Wmissing-pr
#CFLAGS += -W -Wconversion -Wpointer-arith -Wredundant-decls -Wbad-function-cast -Wcast-qual -Wmissing-noreturn
CFLAGS += @COPTIMISE_FLAG@
ifeq ("@DEBUG@", "yes")
CFLAGS += -g -fno-omit-frame-pointer -DDEBUG
CFLAGS += -DDEBUG_MEM
else
CFLAGS += -O2
endif
ifeq ("@INTL@", "yes")
CFLAGS += -DINTL_PACKAGE=\"@INTL_PACKAGE@\" -DLOCALEDIR=\"@LOCALEDIR@\"
endif
LD_FLAGS += -L$(top_srcdir)/lib -L$(libdir)
LDFLAGS += -L$(top_srcdir)/lib -L$(libdir)
#CFLAGS += -DDEBUG_POOL
#CFLAGS += -DBOUNDS_CHECK
#CFLAGS += -pg
#LD_FLAGS += -pg
#LDFLAGS += -pg
STRIP=
#STRIP = -s
@@ -145,7 +145,7 @@ $(TARGETS): $(OBJECTS)
%.so: %.o
$(CC) -c $(INCLUDES) $(CFLAGS) $< -o $@
$(LIB_SHARED): $(OBJECTS) $(LD_DEPS)
$(LIB_SHARED): $(OBJECTS) $(LDDEPS)
$(CC) $(SOFLAG) -Wl,-soname,$(notdir $@).$(LIB_VERSION) \
$(CLDFLAGS) $(OBJECTS) -o $@
@@ -166,11 +166,11 @@ $(LIB_STATIC): $(OBJECTS)
clean: $(SUBDIRS.clean)
$(RM) $(OBJECTS) $(TARGETS) $(CLEAN_TARGETS) $(SOURCES:%.c=%.d) \
$(SOURCES:%.c=%.pot) $(LD_DEPS)
$(SOURCES:%.c=%.pot) $(LDDEPS)
distclean: $(SUBDIRS.distclean)
$(RM) $(OBJECTS) $(TARGETS) $(CLEAN_TARGETS) $(SOURCES:%.c=%.d) \
$(SOURCES:%.c=%.pot) $(LD_DEPS) \
$(SOURCES:%.c=%.pot) $(LDDEPS) \
config.cache config.log config.status \
Makefile make.tmpl core \
version.h lvm2.po

View File

@@ -27,7 +27,7 @@ MAN8=lvchange.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 lvmchange.8 \
MAN5DIR=${mandir}/man5
MAN8DIR=${mandir}/man8
include ../make.tmpl
include $(top_srcdir)/make.tmpl
install:
@echo "Installing $(MAN8) in $(MAN8DIR)"

View File

@@ -15,10 +15,18 @@ vgcfgbackup \- backup volume group descriptor area
allows you to backup the metadata
of your volume groups.
If you don't name any volume groups on the command line, all of them
will be backed up. This DOESN'T backup user/system data in logical
will be backed up.
.sp
In a default installation, each volume group gets backed up into a separate
file bearing the name of the volume group in the directory /etc/lvm/backup.
You can write the backup to an alternative file using -f. In this case
if you are backing up more than one volume group the filename is
treated as a template, and %s gets replaced by the volume group name.
.sp
NB. This DOESN'T backup user/system data in logical
volume(s)! Backup /etc/lvm regularly too.
.SH OPTIONS
See \fBlvm\fP for common options.
.SH SEE ALSO
.BR lvm (8),
.BR vgcreate (8)
.BR vgcfgrestore (8)

View File

@@ -21,7 +21,7 @@ SOURCES=\
TARGETS=dev_cache_t
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
dev_cache_t: dev_cache_t.o $(top_srcdir)/lib/liblvm.a
$(CC) -o dev_cache_t dev_cache_t.o -L$(top_srcdir)/lib -llvm

View File

@@ -24,7 +24,7 @@ TARGETS=\
rfilter_t \
pfilter_t
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
rfilter_t: rfilter_t.o $(top_srcdir)/lib/liblvm.a
$(CC) -o rfilter_t rfilter_t.o -L$(top_srcdir)/lib -llvm

View File

@@ -31,7 +31,7 @@ TARGETS=\
read_pv_t \
get_vgs_t
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
read_vg_t: read_vg_t.o pretty_print.o $(top_srcdir)/lib/liblvm.a
$(CC) -o read_vg_t read_vg_t.o pretty_print.o -L$(top_srcdir)/lib -llvm

View File

@@ -21,7 +21,7 @@ SOURCES=\
TARGETS=dbg_malloc_t
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
dbg_malloc_t: dbg_malloc_t.o
$(CC) $(CFLAGS) -o dbg_malloc_t dbg_malloc_t.o \

View File

@@ -24,7 +24,7 @@ TARGETS=\
parse_t \
matcher_t
include ../../make.tmpl
include $(top_srcdir)/make.tmpl
parse_t: parse_t.o $(top_srcdir)/lib/liblvm.a
$(CC) -o parse_t parse_t.o -L$(top_srcdir)/lib -llvm

View File

@@ -19,7 +19,7 @@ LANGS=de
TARGETS=$(LANGS:%=%.mo)
include ../make.tmpl
include $(top_srcdir)/make.tmpl
install: $(TARGETS)
@echo Installing translation files in $(localedir)

154
scripts/clvmd_fix_conf.sh Normal file
View File

@@ -0,0 +1,154 @@
#!/bin/sh
#
# Edit an lvm.conf file to enable cluster locking.
#
# $1 is the directory where the locking library is installed.
# $2 (optional) is the config file
# $3 (optional) is the locking library name
#
#
PREFIX=$1
LVMCONF=$2
LIB=$3
if [ -z "$PREFIX" ]
then
echo "usage: $0 <prefix> [<config file>] [<library>]"
echo ""
echo "<prefix> location of the cluster locking shared library. (no default)"
echo "<config file> name of the LVM config file (default: /etc/lvm/lvm.conf)"
echo "<library> name of the shared library (default: liblvm2clusterlock.so)"
echo ""
exit 0
fi
[ -z "$LVMCONF" ] && LVMCONF="/etc/lvm/lvm.conf"
[ -z "$LIB" ] && LIB="liblvm2clusterlock.so"
if [ "${PREFIX:0:1}" != "/" ]
then
echo "Prefix must be an absolute path name (starting with a /)"
exit 12
fi
if [ ! -f "$LVMCONF" ]
then
echo "$LVMCONF does not exist"
exit 10
fi
if [ ! -f "$PREFIX/$LIB" ]
then
echo "$PREFIX/$LIB does not exist, did you do a \"make install\" ?"
exit 11
fi
SCRIPTFILE=`mktemp -t lvmscript.XXXXXXXXXX`
TMPFILE=`mktemp -t lvmtmp.XXXXXXXXXX`
# Flags so we know which parts of the file we can replace and which need
# adding. These are return codes from grep, so zero means it IS present!
have_type=1
have_dir=1
have_library=1
have_global=1
grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $LVMCONF
have_type=$?
grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $LVMCONF
have_dir=$?
grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $LVMCONF
have_library=$?
# Those options are in section "global {" so we must have one if any are present.
if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
then
# See if we can find it...
grep -q '^[[:blank:]]*global[[:blank:]]*{' $LVMCONF
have_global=$?
if [ "$have_global" = "1" ]
then
echo "global keys but no 'global {' found, can't edit file"
exit 12
fi
fi
# So if we don't have "global {" we need to create one and
# populate it
if [ "$have_global" = "1" ]
then
cat $LVMCONF - <<EOF > $TMPFILE
global {
# Enable locking for cluster LVM
locking_type = 2
library_dir = "$PREFIX"
locking_library = "$LIB"
}
EOF
if [ $? != 0 ]
then
echo "failed to create temporary config file, $LVMCONF not updated"
exit 1
fi
else
#
# We have a "global {" section, so add or replace the
# locking entries as appropriate
#
if [ "$have_type" = "0" ]
then
SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = 2/g"
else
SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = 2"
fi
if [ "$have_dir" = "0" ]
then
SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$PREFIX\"'g"
else
SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$PREFIX\""
fi
if [ "$have_library" = "0" ]
then
SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LIB\"/g"
else
SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LIB\""
fi
echo -e $SEDCMD > $SCRIPTFILE
sed <$LVMCONF >$TMPFILE -f $SCRIPTFILE
if [ $? != 0 ]
then
echo "sed failed, $LVMCONF not updated"
exit 1
fi
fi
# Now we have a suitably editted config file in a temp place,
# backup the original and copy our new one into place.
cp $LVMCONF $LVMCONF.nocluster
if [ $? != 0 ]
then
echo "failed to backup old config file, $LVMCONF not updated"
exit 2
fi
cp $TMPFILE $LVMCONF
if [ $? != 0 ]
then
echo "failed to copy new config file into place, check $LVMCONF is still OK"
exit 3
fi
rm -f $SCRIPTFILE $TMPFILE

90
scripts/clvmd_init Executable file
View File

@@ -0,0 +1,90 @@
#!/bin/bash
#
# /etc/rc.d/init.d/clvmd
#
# Starts the clvm daemon
# NOTE: These startup levels may not be right yet - it depends on where
# the rest of the cluster startup goes.
#
# chkconfig: 345 72 5
# description: distributes LVM commands in a clustered environment. \
# a clvmd must be run on all nodes in a cluster for clustered LVM \
# operations to work.
# processname: clvmd
# Source function library.
. /etc/init.d/functions
BINARY=/usr/sbin/clvmd
LOCKFILE=/var/lock/subsys/clvmd
test -x "$BINARY" || exit 0
RETVAL=0
#
# See how we were called.
#
prog="clvmd"
start() {
# Check if clvmd is already running
if [ ! -f "$LOCKFILE" ]; then
echo -n $"Starting $prog: "
daemon $BINARY
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $LOCKFILE
echo
fi
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc $BINARY
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $LOCKFILE
echo
return $RETVAL
}
restart() {
stop
start
}
reload() {
restart
}
status_clvm() {
status $BINARY
}
case "$1" in
start)
start
;;
stop)
stop
;;
reload|restart)
restart
;;
condrestart)
if [ -f $LOCKFILE ]; then
restart
fi
;;
status)
status_clvm
;;
*)
echo $"Usage: $0 {start|stop|restart|condrestart|status}"
exit 1
esac
exit $?
exit $RETVAL

View File

@@ -16,6 +16,10 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
ifeq ("@FSADM@", "yes")
SUBDIRS += fsadm
endif
SOURCES =\
archiver.c \
dumpconfig.c \
@@ -84,20 +88,20 @@ ifeq ("@DEVMAPPER@", "yes")
LVMLIBS += -ldevmapper
endif
include ../make.tmpl
include $(top_srcdir)/make.tmpl
lvm: $(OBJECTS) lvm.o $(top_srcdir)/lib/liblvm.a
$(CC) -o $@ $(OBJECTS) lvm.o $(LD_FLAGS) $(LVMLIBS) $(LIBS) -rdynamic
$(CC) -o $@ $(OBJECTS) lvm.o $(LDFLAGS) $(LVMLIBS) $(LIBS) -rdynamic
lvm.static: $(OBJECTS) lvm.o $(top_srcdir)/lib/liblvm.a
$(CC) -o $@ $(OBJECTS) lvm.o -static $(LD_FLAGS) $(LVMLIBS) $(LIBS) \
$(CC) -o $@ $(OBJECTS) lvm.o -static $(LDFLAGS) $(LVMLIBS) $(LIBS) \
-rdynamic
liblvm2cmd.a: $(top_srcdir)/lib/liblvm.a $(OBJECTS)
cat $(top_srcdir)/lib/liblvm.a > $@
$(AR) rs $@ $(OBJECTS)
liblvm2cmd.so: liblvm2cmd.a $(LD_DEPS)
liblvm2cmd.so: liblvm2cmd.a $(LDDEPS)
$(CC) -o liblvm2cmd.so $(SOFLAG) $(CLDFLAGS) \
$(CLDWHOLEARCHIVE) liblvm2cmd.a $(CLDNOWHOLEARCHIVE)

View File

@@ -174,8 +174,6 @@ static int __backup(struct volume_group *vg)
return 0;
}
log_verbose("Creating volume group backup \"%s\"", name);
return backup_to_file(name, desc, vg);
}
@@ -332,6 +330,8 @@ int backup_to_file(const char *file, const char *desc, struct volume_group *vg)
cmd = vg->cmd;
log_verbose("Creating volume group backup \"%s\"", file);
if (!(context = create_text_context(cmd, file, desc)) ||
!(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
context))) {

View File

@@ -96,6 +96,7 @@ arg(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg)
arg(partial_ARG, 'P', "partial", NULL)
arg(physicalvolume_ARG, 'P', "physicalvolume", NULL)
arg(readahead_ARG, 'r', "readahead", int_arg)
arg(resizefs_ARG, 'r', "resizefs", NULL)
arg(reset_ARG, 'R', "reset", NULL)
arg(physicalextentsize_ARG, 's', "physicalextentsize", size_mb_arg)
arg(separator_ARG, 's', "separator", string_arg)

View File

@@ -169,14 +169,16 @@ xx(lvextend,
"\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
"\t{-l|--extents [+]LogicalExtentsNumber |\n"
"\t -L|--size [+]LogicalVolumeSize[kKmMgGtT]}\n"
"\t[-n|--nofsck]\n"
"\t[-r|--resizefs]\n"
"\t[-t|--test]\n"
"\t[--type VolumeType]\n"
"\t[-v|--verbose]\n"
"\t[--version]" "\n"
"\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
alloc_ARG, autobackup_ARG, extents_ARG, size_ARG, stripes_ARG,
stripesize_ARG, test_ARG, type_ARG)
alloc_ARG, autobackup_ARG, extents_ARG, nofsck_ARG, resizefs_ARG,
size_ARG, stripes_ARG, stripesize_ARG, test_ARG, type_ARG)
xx(lvmchange,
"With the device mapper, this is obsolete and does nothing.",
@@ -230,12 +232,14 @@ xx(lvreduce,
"\t[-h|--help]\n"
"\t{-l|--extents [-]LogicalExtentsNumber |\n"
"\t -L|--size [-]LogicalVolumeSize[kKmMgGtT]}\n"
"\t[-n|--nofsck]\n"
"\t[-r|--resizefs]\n"
"\t[-t|--test]\n"
"\t[-v|--verbose]\n"
"\t[--version]" "\n"
"\tLogicalVolume[Path]\n",
autobackup_ARG, force_ARG, extents_ARG,
autobackup_ARG, force_ARG, extents_ARG, nofsck_ARG, resizefs_ARG,
size_ARG, test_ARG, yes_ARG)
xx(lvremove,
@@ -276,14 +280,16 @@ xx(lvresize,
"\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
"\t{-l|--extents [+|-]LogicalExtentsNumber |\n"
"\t -L|--size [+|-]LogicalVolumeSize[kKmMgGtT]}\n"
"\t[-n|--nofsck]\n"
"\t[-r|--resizefs]\n"
"\t[-t|--test]\n"
"\t[--type VolumeType]\n"
"\t[-v|--verbose]\n"
"\t[--version]" "\n"
"\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
alloc_ARG, autobackup_ARG, extents_ARG, size_ARG, stripes_ARG,
stripesize_ARG, test_ARG, type_ARG)
alloc_ARG, autobackup_ARG, extents_ARG, nofsck_ARG, resizefs_ARG,
size_ARG, stripes_ARG, stripesize_ARG, test_ARG, type_ARG)
xx(lvs,
"Display information about logical volumes",
@@ -473,6 +479,7 @@ xx(pvs,
"Display information about physical volumes",
"pvs" "\n"
"\t[--aligned]\n"
"\t[-a|--all]\n"
"\t[-d|--debug]" "\n"
"\t[-h|-?|--help] " "\n"
"\t[--noheadings]\n"
@@ -487,9 +494,9 @@ xx(pvs,
"\t[--version]\n"
"\t[PhysicalVolume [PhysicalVolume...]]\n",
aligned_ARG, ignorelockingfailure_ARG, noheadings_ARG, nolocking_ARG,
nosuffix_ARG, options_ARG, separator_ARG, sort_ARG, unbuffered_ARG,
units_ARG)
aligned_ARG, all_ARG, ignorelockingfailure_ARG, noheadings_ARG,
nolocking_ARG, nosuffix_ARG, options_ARG, separator_ARG, sort_ARG,
unbuffered_ARG, units_ARG)
xx(pvscan,
"List all physical volumes",

View File

@@ -54,6 +54,7 @@ extern char *optarg;
*/
enum {
READ_ONLY = 0,
COLS_ARG,
MAJOR_ARG,
MINOR_ARG,
NOTABLE_ARG,
@@ -124,15 +125,42 @@ static int _parse_file(struct dm_task *dmt, const char *file)
return r;
}
static void _display_info(struct dm_task *dmt)
static void _display_info_cols(struct dm_task *dmt, struct dm_info *info)
{
struct dm_info info;
static int _headings = 0;
const char *uuid;
if (!dm_task_get_info(dmt, &info))
if (!info->exists) {
printf("Device does not exist.\n");
return;
}
if (!info.exists) {
if (!_headings) {
printf("Name Maj Min Stat Open Targ Event UUID\n");
_headings = 1;
}
printf("%-16s ", dm_task_get_name(dmt));
printf("%3d %3d %s%s%s%s %4d %4d %6" PRIu32 " ",
info->major, info->minor,
info->live_table ? "L" : "-",
info->inactive_table ? "I" : "-",
info->suspended ? "s" : "-",
info->read_only ? "r" : "w",
info->open_count, info->target_count, info->event_nr);
if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
printf("%s", uuid);
printf("\n");
}
static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
{
const char *uuid;
if (!info->exists) {
printf("Device does not exist.\n");
return;
}
@@ -140,25 +168,25 @@ static void _display_info(struct dm_task *dmt)
printf("Name: %s\n", dm_task_get_name(dmt));
printf("State: %s%s\n",
info.suspended ? "SUSPENDED" : "ACTIVE",
info.read_only ? " (READ-ONLY)" : "");
info->suspended ? "SUSPENDED" : "ACTIVE",
info->read_only ? " (READ-ONLY)" : "");
if (!info.live_table && !info.inactive_table)
if (!info->live_table && !info->inactive_table)
printf("Tables present: None\n");
else
printf("Tables present: %s%s%s\n",
info.live_table ? "LIVE" : "",
info.live_table && info.inactive_table ? " & " : "",
info.inactive_table ? "INACTIVE" : "");
info->live_table ? "LIVE" : "",
info->live_table && info->inactive_table ? " & " : "",
info->inactive_table ? "INACTIVE" : "");
if (info.open_count != -1)
printf("Open count: %d\n", info.open_count);
if (info->open_count != -1)
printf("Open count: %d\n", info->open_count);
printf("Event number: %" PRIu32 "\n", info.event_nr);
printf("Major, minor: %d, %d\n", info.major, info.minor);
printf("Event number: %" PRIu32 "\n", info->event_nr);
printf("Major, minor: %d, %d\n", info->major, info->minor);
if (info.target_count != -1)
printf("Number of targets: %d\n", info.target_count);
if (info->target_count != -1)
printf("Number of targets: %d\n", info->target_count);
if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
printf("UUID: %s\n", uuid);
@@ -166,6 +194,19 @@ static void _display_info(struct dm_task *dmt)
printf("\n");
}
static void _display_info(struct dm_task *dmt)
{
struct dm_info info;
if (!dm_task_get_info(dmt, &info))
return;
if (_switches[COLS_ARG])
_display_info_cols(dmt, &info);
else
_display_info_long(dmt, &info);
}
static int _load(int task, const char *name, const char *file, const char *uuid)
{
int r = 0;
@@ -270,6 +311,7 @@ static int _message(int argc, char **argv, void *data)
sz += strlen(argv[i]) + 1;
str = malloc(sz);
memset(str, 0, sz);
for (i = 0; i < argc; i++) {
if (i)
@@ -713,6 +755,7 @@ static int _process_switches(int *argc, char ***argv)
#ifdef HAVE_GETOPTLONG
static struct option long_options[] = {
{"columns", 0, NULL, COLS_ARG},
{"readonly", 0, NULL, READ_ONLY},
{"major", 1, NULL, MAJOR_ARG},
{"minor", 1, NULL, MINOR_ARG},
@@ -734,8 +777,10 @@ static int _process_switches(int *argc, char ***argv)
optarg = 0;
optind = OPTIND_INIT;
while ((c = GETOPTLONG_FN(*argc, *argv, "j:m:nru:v",
while ((c = GETOPTLONG_FN(*argc, *argv, "cCj:m:nru:v",
long_options, &ind)) != -1) {
if (c == 'c' || c == 'C' || ind == COLS_ARG)
_switches[COLS_ARG]++;
if (c == 'r' || ind == READ_ONLY)
_switches[READ_ONLY]++;
if (c == 'j' || ind == MAJOR_ARG) {

31
tools/fsadm/Makefile.in Normal file
View File

@@ -0,0 +1,31 @@
#
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
#
# This file is part of the 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 General Public License v.2.
#
# You should have received a copy of the GNU 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@
VPATH = @srcdir@
SOURCES = fsadm.c
TARGETS = fsadm
include $(top_srcdir)/make.tmpl
fsadm: $(OBJECTS)
$(CC) -o $@ $(OBJECTS) -rdynamic
install: fsadm
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) fsadm \
$(sbindir)/fsadm

338
tools/fsadm/fsadm.c Normal file
View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2004 Red Hat GmbH. All rights reserved.
*
* LVM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*/
/*
* FIXME: pass smart resizer arguments through from lvresize
* (eg, for xfs_growfs)
*/
/* FIXME All funcs to return 0 on success or an error from errno.h on failure */
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#define MAX_ARGS 8
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#include <fstab.h>
#include <sys/mount.h>
#include <sys/vfs.h>
#define log_error(str, x...) fprintf(stderr, "%s(%u): " str "\n", __FILE__, __LINE__, x)
/* Filesystem related information */
struct fsinfo {
struct fstab *fsent;
struct statfs statfs;
uint64_t new_size;
const char *cmd;
};
static void _usage(const char *cmd)
{
log_error("Usage: %s [check <filesystem> | resize <filesystem> <size>]",
basename(cmd));
}
/* FIXME Make this more robust - /proc, multiple mounts, TMPDIR + security etc. */
/* FIXME Ensure filesystem is not mounted anywhere before running fsck/resize */
/* Gather filesystem information (VFS type, special file and block size) */
static int _get_fsinfo(const char *file, struct fsinfo *fsinfo)
{
char *dir, template[] = "/tmp/fscmd_XXXXXX";
struct stat info;
int ret = 0;
if (stat(file, &info)) {
log_error("%s: stat failed: %s", file, strerror(errno));
return errno;
}
/* FIXME: are we limited to /etc/fstab entries ? */
if (!(fsinfo->fsent = info.st_rdev ? getfsspec(file) : getfsfile(file))) {
log_error("%s: getfsspec/getfsfile failed: "
"Missing from /etc/fstab?", file);
return EINVAL;
}
/* FIXME: any other way to retrieve fs blocksize avoiding mounting ? */
if (!(dir = (mkdtemp(template)))) {
log_error("%s: mkdtemp failed: %s", template, strerror(errno));
return errno;
}
if (mount(fsinfo->fsent->fs_spec, dir, fsinfo->fsent->fs_vfstype,
MS_RDONLY, NULL)) {
log_error("%s: mount %s failed: %s", fsinfo->fsent->fs_spec,
dir, strerror(errno));
ret = errno;
goto out;
}
if (statfs(dir, &fsinfo->statfs)) {
log_error("%s: statfs failed: %s", dir, strerror(errno));
ret = errno;
goto out1;
}
out1:
if (umount(dir))
log_error("%s: umount failed: %s", dir, strerror(errno));
out:
if (rmdir(dir))
log_error("%s: rmdir failed: %s", dir, strerror(errno));
return ret;
}
enum {
BLOCKS,
KILOBYTES
};
#define LEN 32 /* Length of temporary string */
/* Create size string in units of blocks or kilobytes
* (size expected in kilobytes)
*/
static char *_size_str(struct fsinfo *fsinfo, int unit)
{
uint64_t new_size = fsinfo->new_size;
static char s[LEN];
/* Avoid switch() as long as there's only 2 cases */
snprintf(s, LEN, "%" PRIu64,
unit == BLOCKS ? new_size / (fsinfo->statfs.f_bsize >> 10) :
new_size);
return s;
}
/* Allocate memory and store string updating string pointer */
static int _store(char **arg, char **str)
{
size_t len = 0;
char *s = *str;
while (*s && *s != '%' && *(s++) != ' ')
len++;
if ((*arg = (char *) malloc(len + 1))) {
strncpy(*arg, *str, len);
(*arg)[len] = '\0';
*str = s - 1;
return 0;
}
return 1;
}
/* Construct a new argument vector for the real external command */
static int _new_argv(char **new_argv, struct fsinfo *fsinfo)
{
int i = 0, b = 0, d = 0, k = 0, m = 0;
char *s1;
char *s = (char *) fsinfo->cmd;
if (!s || !strlen(s))
return 1;
do {
switch (*s) {
case ' ':
break;
case '%':
s++;
switch (tolower(*s)) {
case 'b':
s1 = _size_str(fsinfo, BLOCKS);
if (b++ + k || _store(&new_argv[i++], &s1))
goto error;
break;
case 'k':
s1 = _size_str(fsinfo, KILOBYTES);
if (b + k++ || _store(&new_argv[i++], &s1))
goto error;
break;
case 'd':
s1 = fsinfo->fsent->fs_spec;
if (d++ + m || _store(&new_argv[i++], &s1))
goto error;
break;
case 'm':
s1 = fsinfo->fsent->fs_file;
if (d + m++ || _store(&new_argv[i++], &s1))
goto error;
break;
default:
goto error;
}
break;
default:
if (_store(&new_argv[i++], &s))
goto error;
}
} while (*++s);
new_argv[i] = NULL;
return 0;
error:
new_argv[i] = NULL;
log_error("Failed constructing arguments for %s", s);
return EINVAL;
}
/*
* Get filesystem command arguments derived from a command definition string
*
* Command definition syntax: 'cmd [-option]{0,} [(option)argument]{0,}'
*
* (option)argument can be: '%{bdkm}'
*
* Command definition is parsed into argument strings of
* an argument vector with:
*
* %b replaced by the size in filesystem blocks
* %k replaced by the size in kilobytes
* %d replaced by the name of the device special of the LV
* %m replaced by the mountpoint of the filesystem
*
*/
static int _get_cmd(char *command, struct fsinfo *fsinfo)
{
const char *vfstype = fsinfo->fsent->fs_vfstype;
struct fscmd {
const char *vfstype;
const char *fsck;
const char *fsresize;
} fscmds[] = {
{ "ext2", "fsck -fy %d", "ext2resize %d %b"},
{"ext3", "fsck -fy %d", "ext2resize %d %b"},
{"reiserfs", "", "resize_reiserfs -s%k %d"},
{"xfs", "", "xfs_growfs -D %b %m"}, /* simple xfs grow */
{NULL, NULL, NULL},
}, *p = &fscmds[0];
for (; p->vfstype; p++) {
if (!strcmp(vfstype, p->vfstype)) {
if (!strcmp(command, "resize"))
fsinfo->cmd = p->fsresize;
else if (!strcmp(command, "check"))
fsinfo->cmd = p->fsck;
else {
log_error("Unrecognised command: %s", command);
return EINVAL;
}
break;
}
}
if (!fsinfo->cmd) {
log_error("%s: Unrecognised filesystem type", vfstype);
return EINVAL;
}
return 0;
}
/* Collapse multiple slashes */
static char *_collapse_slashes(char *path)
{
char *s = path;
/* Slight overhead but short ;) */
while ((s = strchr(s, '/')) && *(s + 1))
*(s + 1) == '/' ? memmove(s, s + 1, strlen(s)) : s++;
return path;
}
/* Free the argument array */
static void _free_argv(char **new_argv)
{
int i;
for (i = 0; new_argv[i]; i++)
free(new_argv[i]);
}
/*
* check/resize a filesystem
*/
int main(int argc, char **argv)
{
int ret = 0;
struct fsinfo fsinfo;
char *new_argv[MAX_ARGS];
char *command, *path;
if (argc < 3)
goto error;
command = argv[1];
path = _collapse_slashes(argv[2]);
if (!strcmp(command, "resize")) {
if (argc != 4)
goto error;
/* FIXME sanity checks */
fsinfo.new_size = strtoul(argv[3], NULL, 10);
} else if (argc != 3)
goto error;
/* Retrieve filesystem information (block size...) */
if ((ret = _get_fsinfo(path, &fsinfo))) {
log_error("Can't get filesystem information from %s", path);
return ret;
}
/* Get filesystem command info */
if ((ret = _get_cmd(command, &fsinfo))) {
log_error("Can't get filesystem command for %s", command);
return ret;
}
if (_new_argv(new_argv, &fsinfo))
return EINVAL;
if (!new_argv[0])
return 0;
execvp(new_argv[0], new_argv);
ret = errno;
log_error("%s: execvp %s failed", new_argv[0], strerror(errno));
_free_argv(new_argv);
return ret;
error:
_usage(argv[0]);
return EINVAL;
}

View File

@@ -80,28 +80,49 @@ static int lvchange_availability(struct cmd_context *cmd,
activate = arg_uint_value(cmd, available_ARG, 0);
if (activate) {
log_verbose("Activating logical volume \"%s\"", lv->name);
if (lv_is_origin(lv) || (activate == 2)) {
if (!activate_lv_excl(cmd, lv->lvid.s)) {
stack;
return 0;
}
} else if (!activate_lv(cmd, lv->lvid.s)) {
if (activate == CHANGE_ALN) {
log_verbose("Deactivating logical volume \"%s\" locally",
lv->name);
if (!deactivate_lv_local(cmd, lv->lvid.s)) {
stack;
return 0;
}
if ((lv->status & LOCKED) && (pvname = get_pvmove_pvname_from_lv(lv))) {
log_verbose("Spawning background pvmove process for %s",
pvname);
pvmove_poll(cmd, pvname, 1);
}
} else {
} else if (activate == CHANGE_AN) {
log_verbose("Deactivating logical volume \"%s\"", lv->name);
if (!deactivate_lv(cmd, lv->lvid.s)) {
stack;
return 0;
}
} else {
if (lv_is_origin(lv) || (activate == CHANGE_AE)) {
log_verbose("Activating logical volume \"%s\" "
"exclusively", lv->name);
if (!activate_lv_excl(cmd, lv->lvid.s)) {
stack;
return 0;
}
} else if (activate == CHANGE_ALY) {
log_verbose("Activating logical volume \"%s\" locally",
lv->name);
if (!activate_lv_local(cmd, lv->lvid.s)) {
stack;
return 0;
}
} else {
log_verbose("Activating logical volume \"%s\"",
lv->name);
if (!activate_lv(cmd, lv->lvid.s)) {
stack;
return 0;
}
}
if ((lv->status & LOCKED) &&
(pvname = get_pvmove_pvname_from_lv(lv))) {
log_verbose("Spawning background pvmove process for %s",
pvname);
pvmove_poll(cmd, pvname, 1);
}
}
return 1;

View File

@@ -90,18 +90,28 @@ int yes_no_excl_arg(struct cmd_context *cmd, struct arg *a)
a->sign = SIGN_NONE;
if (!strcmp(a->value, "e")) {
a->i_value = 2;
a->ui_value = 2;
a->i_value = CHANGE_AE;
a->ui_value = CHANGE_AE;
}
else if (!strcmp(a->value, "y")) {
a->i_value = 1;
a->ui_value = 1;
a->i_value = CHANGE_AY;
a->ui_value = CHANGE_AY;
}
else if (!strcmp(a->value, "n")) {
a->i_value = 0;
a->ui_value = 0;
a->i_value = CHANGE_AN;
a->ui_value = CHANGE_AN;
}
else if (!strcmp(a->value, "ln") || !strcmp(a->value, "nl")) {
a->i_value = CHANGE_ALN;
a->ui_value = CHANGE_ALN;
}
else if (!strcmp(a->value, "ly") || !strcmp(a->value, "yl")) {
a->i_value = CHANGE_ALY;
a->ui_value = CHANGE_ALY;
}
else
@@ -1384,7 +1394,8 @@ int lvm2_main(int argc, char **argv)
base = basename(namebase);
while (*base == '/')
base++;
if (strcmp(base, "lvm") && strcmp(base, "lvm.static"))
if (strcmp(base, "lvm") && strcmp(base, "lvm.static") &&
strcmp(base, "initrd-lvm"))
alias = 1;
free(namebase);

View File

@@ -15,6 +15,8 @@
#include "tools.h"
#define SIZE_BUF 128
struct lvresize_params {
const char *vg_name;
const char *lv_name;
@@ -35,6 +37,9 @@ struct lvresize_params {
LV_EXTEND = 2
} resize;
int resizefs;
int nofsck;
int argc;
char **argv;
};
@@ -80,6 +85,9 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
return 0;
}
lp->resizefs = arg_count(cmd, resizefs_ARG) ? 1 : 0;
lp->nofsck = arg_count(cmd, nofsck_ARG) ? 1 : 0;
if (!argc) {
log_error("Please provide the logical volume name");
return 0;
@@ -121,6 +129,8 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
uint32_t seg_extents;
uint32_t sz, str;
struct list *pvh = NULL;
char size_buf[SIZE_BUF];
char lv_path[PATH_MAX];
if (!(vg = vg_read(cmd, lp->vg_name, &consistent))) {
log_error("Volume group %s doesn't exist", lp->vg_name);
@@ -340,7 +350,13 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
if (lp->resize == LV_REDUCE) {
if (lp->argc)
log_print("Ignoring PVs on command line when reducing");
} else if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
lp->argv) : &vg->pvs)) {
stack;
return ECMD_FAILED;
}
if (lp->resize == LV_REDUCE || lp->resizefs) {
memset(&info, 0, sizeof(info));
if (!lv_info(lv, &info) && driver_version(NULL, 0)) {
@@ -348,7 +364,13 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
return ECMD_FAILED;
}
if (info.exists) {
if (lp->resizefs && !info.exists) {
log_error("Logical volume %s must be activated "
"before resizing filesystem", lp->lv_name);
return ECMD_FAILED;
}
if (info.exists && !lp->resizefs && (lp->resize == LV_REDUCE)) {
log_print("WARNING: Reducing active%s logical volume "
"to %s", info.open_count ? " and open" : "",
display_size(cmd, (uint64_t) lp->extents *
@@ -357,50 +379,71 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
log_print("THIS MAY DESTROY YOUR DATA "
"(filesystem etc.)");
if (!arg_count(cmd, force_ARG)) {
if (yes_no_prompt("Do you really want to "
"reduce %s? [y/n]: ",
lp->lv_name) == 'n') {
log_print("Logical volume %s NOT "
"reduced", lp->lv_name);
return ECMD_FAILED;
}
}
}
}
if (lp->resizefs) {
if (lvm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir,
lp->vg_name, lp->lv_name) < 0) {
log_error("Couldn't create LV path for %s",
lp->lv_name);
return ECMD_FAILED;
}
if (!arg_count(cmd, force_ARG)) {
if (yes_no_prompt("Do you really want to reduce %s?"
" [y/n]: ", lp->lv_name) == 'n') {
log_print("Logical volume %s NOT reduced",
lp->lv_name);
if (lvm_snprintf(size_buf, SIZE_BUF, "%" PRIu64,
(uint64_t) lp->extents * vg->extent_size / 2)
< 0) {
log_error("Couldn't generate new LV size string");
return ECMD_FAILED;
}
if (!lp->nofsck) {
if (!exec_cmd("fsadm", "check", lv_path, NULL)) {
stack;
return ECMD_FAILED;
}
}
if (!archive(vg)) {
stack;
return ECMD_FAILED;
if (lp->resize == LV_REDUCE) {
if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
stack;
return ECMD_FAILED;
}
}
if (!lv_reduce(vg->fid, lv, lv->le_count - lp->extents))
return ECMD_FAILED;
}
if (lp->resize == LV_EXTEND) {
if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
lp->argv) : &vg->pvs)) {
if (!archive(vg)) {
stack;
return ECMD_FAILED;
}
log_print("%sing logical volume %s to %s",
(lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
lp->lv_name,
display_size(cmd, (uint64_t) lp->extents * vg->extent_size,
SIZE_SHORT));
if (lp->resize == LV_REDUCE) {
if (!lv_reduce(vg->fid, lv, lv->le_count - lp->extents)) {
stack;
return ECMD_FAILED;
}
if (!archive(vg)) {
stack;
return ECMD_FAILED;
}
log_print("Extending logical volume %s to %s", lp->lv_name,
display_size(cmd, (uint64_t)
lp->extents * vg->extent_size,
SIZE_SHORT));
if (!lv_extend(vg->fid, lv, lp->segtype, lp->stripes,
} else if (!lv_extend(vg->fid, lv, lp->segtype, lp->stripes,
lp->stripe_size, 0u,
lp->extents - lv->le_count,
NULL, 0u, 0u, pvh, alloc)) {
stack;
return ECMD_FAILED;
}
stack;
return ECMD_FAILED;
}
/* store vg on disk(s) */
@@ -436,6 +479,13 @@ static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
log_print("Logical volume %s successfully resized", lp->lv_name);
if (lp->resizefs && (lp->resize == LV_EXTEND)) {
if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
stack;
return ECMD_FAILED;
}
}
return ECMD_PROCESSED;
}

View File

@@ -107,7 +107,7 @@ static int _pvchange_single(struct cmd_context *cmd, struct physical_volume *pv,
return 0;
}
if (!(pv = pv_read(cmd, pv_name, &mdas, &sector))) {
if (!(pv = pv_read(cmd, pv_name, &mdas, &sector, 1))) {
unlock_vg(cmd, ORPHAN);
log_error("Unable to read PV \"%s\"", pv_name);
return 0;
@@ -236,10 +236,9 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv)
for (; opt < argc; opt++) {
pv_name = argv[opt];
/* FIXME Read VG instead - pv_read will fail */
if (!(pv = pv_read(cmd, pv_name, &mdas, NULL))) {
log_error
("Failed to read physical volume \"%s\"",
pv_name);
if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1))) {
log_error("Failed to read physical volume %s",
pv_name);
continue;
}
total++;

View File

@@ -35,7 +35,7 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name)
/* is there a pv here already */
/* FIXME Use partial mode here? */
if (!(pv = pv_read(cmd, name, NULL, NULL)))
if (!(pv = pv_read(cmd, name, NULL, NULL, 0)))
return 1;
/* orphan ? */

View File

@@ -34,7 +34,7 @@ static int pvremove_check(struct cmd_context *cmd, const char *name)
}
/* is there a pv here already */
if (!(pv = pv_read(cmd, name, NULL, NULL)))
if (!(pv = pv_read(cmd, name, NULL, NULL, 1)))
return 1;
/* orphan ? */

View File

@@ -60,21 +60,25 @@ static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg,
int consistent = 0;
int ret = ECMD_PROCESSED;
if (!lock_vol(cmd, pv->vg_name, LCK_VG_READ)) {
log_error("Can't lock %s: skipping", pv->vg_name);
return ECMD_FAILED;
}
if (pv->vg_name) {
if (!lock_vol(cmd, pv->vg_name, LCK_VG_READ)) {
log_error("Can't lock %s: skipping", pv->vg_name);
return ECMD_FAILED;
}
if (!(vg = vg_read(cmd, pv->vg_name, &consistent))) {
log_error("Can't read %s: skipping", pv->vg_name);
unlock_vg(cmd, pv->vg_name);
return ECMD_FAILED;
if (!(vg = vg_read(cmd, pv->vg_name, &consistent))) {
log_error("Can't read %s: skipping", pv->vg_name);
unlock_vg(cmd, pv->vg_name);
return ECMD_FAILED;
}
}
if (!report_object(handle, vg, NULL, pv, NULL))
ret = ECMD_FAILED;
unlock_vg(cmd, pv->vg_name);
if (pv->vg_name)
unlock_vg(cmd, pv->vg_name);
return ret;
}

View File

@@ -16,6 +16,7 @@
#include "tools.h"
#include <sys/stat.h>
#include <sys/wait.h>
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct list *arg_lvnames, struct list *tags,
@@ -121,7 +122,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
int consistent;
struct list *slh, *tags_arg;
struct list *vgnames; /* VGs to process */
struct list *vgnames; /* VGs to process */
struct str_list *sll;
struct volume_group *vg;
struct list tags, lvnames;
@@ -221,8 +222,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
log_error("vg/lv string alloc failed");
return ECMD_FAILED;
}
if (!str_list_add(cmd->mem, &arg_lvnames,
vglv)) {
if (!str_list_add(cmd->mem, &arg_lvnames, vglv)) {
log_error("strlist allocation failed");
return ECMD_FAILED;
}
@@ -463,6 +463,43 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
return ret_max;
}
static int _process_all_devs(struct cmd_context *cmd, void *handle,
int (*process_single) (struct cmd_context * cmd,
struct volume_group * vg,
struct physical_volume * pv,
void *handle))
{
struct physical_volume *pv;
struct physical_volume pv_dummy;
struct dev_iter *iter;
struct device *dev;
int ret_max = 0;
int ret = 0;
if (!(iter = dev_iter_create(cmd->filter))) {
log_error("dev_iter creation failed");
return ECMD_FAILED;
}
while ((dev = dev_iter_get(iter))) {
if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL, 0))) {
memset(&pv_dummy, 0, sizeof(pv_dummy));
list_init(&pv_dummy.tags);
pv_dummy.dev = dev;
pv_dummy.fmt = NULL;
pv = &pv_dummy;
}
ret = process_single(cmd, NULL, pv, handle);
if (ret > ret_max)
ret_max = ret;
}
dev_iter_destroy(iter);
return ret_max;
}
int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
struct volume_group *vg, void *handle,
int (*process_single) (struct cmd_context * cmd,
@@ -516,7 +553,8 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
}
pv = pvl->pv;
} else {
if (!(pv = pv_read(cmd, argv[opt], NULL, NULL))) {
if (!(pv = pv_read(cmd, argv[opt], NULL,
NULL, 1))) {
log_error("Failed to read physical "
"volume \"%s\"", argv[opt]);
ret_max = ECMD_FAILED;
@@ -549,6 +587,10 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
process_single);
if (ret > ret_max)
ret_max = ret;
} else if (arg_count(cmd, all_ARG)) {
ret = _process_all_devs(cmd, handle, process_single);
if (ret > ret_max)
ret_max = ret;
} else {
log_verbose("Scanning for physical volume names");
if (!(pvslist = get_pvs(cmd)))
@@ -883,3 +925,44 @@ struct list *clone_pv_list(struct pool *mem, struct list *pvsl)
return r;
}
int exec_cmd(const char *command, const char *fscmd, const char *lv_path,
const char *size)
{
pid_t pid;
int status;
log_verbose("Executing: %s %s %s %s", command, fscmd, lv_path, size);
if ((pid = fork()) == -1) {
log_error("fork failed: %s", strerror(errno));
return 0;
}
if (!pid) {
/* Child */
/* FIXME Use execve directly */
execlp(command, command, fscmd, lv_path, size, NULL);
log_sys_error("execlp", command);
exit(errno);
}
/* Parent */
if (wait4(pid, &status, 0, NULL) != pid) {
log_error("wait4 child process %u failed: %s", pid,
strerror(errno));
return 0;
}
if (!WIFEXITED(status)) {
log_error("Child %u exited abnormally", pid);
return 0;
}
if (WEXITSTATUS(status)) {
log_error("%s failed: %u", command, WEXITSTATUS(status));
return 0;
}
return 1;
}

View File

@@ -79,4 +79,7 @@ struct list *create_pv_list(struct pool *mem,
struct list *clone_pv_list(struct pool *mem, struct list *pvs);
int exec_cmd(const char *command, const char *fscmd, const char *lv_path,
const char *size);
#endif

View File

@@ -78,6 +78,14 @@ typedef enum {
SIGN_MINUS = 2
} sign_t;
enum {
CHANGE_AY = 0,
CHANGE_AN = 1,
CHANGE_AE = 2,
CHANGE_ALY = 3,
CHANGE_ALN = 4
};
/* a global table of possible arguments */
struct arg {
const char short_arg;

View File

@@ -15,10 +15,42 @@
#include "tools.h"
static char *_expand_filename(const char *template, const char *vg_name,
char **last_filename)
{
char *filename;
if (security_level())
return dbg_strdup(template);
filename = dbg_malloc(PATH_MAX);
if (snprintf(filename, PATH_MAX, template, vg_name) < 0) {
log_error("Error processing filename template %s",
template);
dbg_free(filename);
return NULL;
}
if (*last_filename && !strncmp(*last_filename, filename,
strlen(template))) {
log_error("VGs must be backed up into different files. "
"Use %%s in filename for VG name.");
dbg_free(filename);
return NULL;
}
dbg_free(*last_filename);
*last_filename = filename;
return filename;
}
static int vg_backup_single(struct cmd_context *cmd, const char *vg_name,
struct volume_group *vg, int consistent,
void *handle)
{
char **last_filename = (char **)handle;
char *filename;
if (!vg) {
log_error("Volume group \"%s\" not found", vg_name);
return ECMD_FAILED;
@@ -28,8 +60,13 @@ static int vg_backup_single(struct cmd_context *cmd, const char *vg_name,
log_error("Warning: Volume group \"%s\" inconsistent", vg_name);
if (arg_count(cmd, file_ARG)) {
backup_to_file(arg_value(cmd, file_ARG), vg->cmd->cmd_line, vg);
if (!(filename = _expand_filename(arg_value(cmd, file_ARG),
vg->name, last_filename))) {
stack;
return ECMD_FAILED;
}
backup_to_file(filename, vg->cmd->cmd_line, vg);
} else {
if (!consistent) {
log_error("No backup taken: specify filename with -f "
@@ -53,13 +90,16 @@ static int vg_backup_single(struct cmd_context *cmd, const char *vg_name,
int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv)
{
int ret;
char *last_filename = NULL;
if (partial_mode())
init_pvmove(1);
ret = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, NULL,
ret = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, &last_filename,
&vg_backup_single);
dbg_free(last_filename);
init_pvmove(0);
return ret;

View File

@@ -31,15 +31,23 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd,
continue;
/* Can't deactive a pvmove LV */
if (!activate && (lv->status & PVMOVE))
/* FIXME There needs to be a controlled way of doing this */
if (((activate == CHANGE_AN) || (activate == CHANGE_ALN)) &&
(lv->status & PVMOVE))
continue;
if (!activate) {
if (activate == CHANGE_AN) {
if (!deactivate_lv(cmd, lv->lvid.s))
continue;
} else if (lv_is_origin(lv) || (activate == 2)) {
} else if (activate == CHANGE_ALN) {
if (!deactivate_lv_local(cmd, lv->lvid.s))
continue;
} else if (lv_is_origin(lv) || (activate == CHANGE_AE)) {
if (!activate_lv_excl(cmd, lv->lvid.s))
continue;
} else if (activate == CHANGE_ALY) {
if (!activate_lv_local(cmd, lv->lvid.s))
continue;
} else if (!activate_lv(cmd, lv->lvid.s))
continue;
@@ -61,25 +69,29 @@ static int _vgchange_available(struct cmd_context *cmd, struct volume_group *vg)
{
int lv_open, active;
int available;
int activate = 1;
available = arg_uint_value(cmd, available_ARG, 0);
if ((available == CHANGE_AN) || (available == CHANGE_ALN))
activate = 0;
/* FIXME: Force argument to deactivate them? */
if (!available && (lv_open = lvs_in_vg_opened(vg))) {
if (!activate && (lv_open = lvs_in_vg_opened(vg))) {
log_error("Can't deactivate volume group \"%s\" with %d open "
"logical volume(s)", vg->name, lv_open);
return ECMD_FAILED;
}
if (available && (active = lvs_in_vg_activated(vg)))
if (activate && (active = lvs_in_vg_activated(vg)))
log_verbose("%d logical volume(s) in volume group \"%s\" "
"already active", active, vg->name);
if (available && _activate_lvs_in_vg(cmd, vg, available))
if (activate && _activate_lvs_in_vg(cmd, vg, available))
log_verbose("Activated logical volumes in "
"volume group \"%s\"", vg->name);
if (!available && _activate_lvs_in_vg(cmd, vg, 0))
if (!activate && _activate_lvs_in_vg(cmd, vg, available))
log_verbose("Deactivated logical volumes in "
"volume group \"%s\"", vg->name);
@@ -96,7 +108,7 @@ static int _vgchange_alloc(struct cmd_context *cmd, struct volume_group *vg)
if (alloc == ALLOC_INHERIT) {
log_error("Volume Group allocation policy cannot inherit "
"from anything");
"from anything");
return EINVALID_CMD_LINE;
}
@@ -121,7 +133,6 @@ static int _vgchange_alloc(struct cmd_context *cmd, struct volume_group *vg)
return ECMD_PROCESSED;
}
static int _vgchange_resizeable(struct cmd_context *cmd,
struct volume_group *vg)
{