From a8b03479af1cd23baddd373a4d52f366b49c2398 Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Thu, 27 Feb 2014 08:23:33 +0000 Subject: glusterd: op-version check for brickops. cluster op-version must be atleast 4 for add/remove brick to proceed. This change is required for the new afr-changelog xattr changes that will be done for glusterFS 3.6 (http://review.gluster.org/#/c/7155/). In add-brick, the check is done only when replica count is increased because only that will affect the AFR xattrs. In remove-brick, the check is unconditional failing which there will be inconsistencies in the client xlator names amongst the volfiles of different peers. Change-Id: If981da2f33899aed585ab70bb11c09a093c9d8e6 BUG: 1066778 Signed-off-by: Ravishankar N Reviewed-on: http://review.gluster.org/7122 Reviewed-by: Kaushal M Reviewed-by: Pranith Kumar Karampuri Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- libglusterfs/src/globals.h | 1 + xlators/mgmt/glusterd/src/glusterd-brick-ops.c | 31 ++++++++++++++++++++++---- xlators/mgmt/glusterd/src/glusterd-utils.c | 21 +++++++++++++++++ xlators/mgmt/glusterd/src/glusterd-utils.h | 3 +++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/libglusterfs/src/globals.h b/libglusterfs/src/globals.h index 16ab96268..2e520c10e 100644 --- a/libglusterfs/src/globals.h +++ b/libglusterfs/src/globals.h @@ -33,6 +33,7 @@ should keep changing with introduction of newer versions */ #define GD_OP_VERSION_4 4 /* Op-Version 4 */ +#define GD_OP_VER_PERSISTENT_AFR_XATTRS GD_OP_VERSION_4 #include "xlator.h" diff --git a/xlators/mgmt/glusterd/src/glusterd-brick-ops.c b/xlators/mgmt/glusterd/src/glusterd-brick-ops.c index f15ec7b18..1804dd02e 100644 --- a/xlators/mgmt/glusterd/src/glusterd-brick-ops.c +++ b/xlators/mgmt/glusterd/src/glusterd-brick-ops.c @@ -1207,6 +1207,7 @@ glusterd_op_stage_add_brick (dict_t *dict, char **op_errstr) int ret = 0; char *volname = NULL; int count = 0; + int replica_count = 0; int i = 0; char *bricks = NULL; char *brick_list = NULL; @@ -1215,17 +1216,31 @@ glusterd_op_stage_add_brick (dict_t *dict, char **op_errstr) char *brick = NULL; glusterd_brickinfo_t *brickinfo = NULL; glusterd_volinfo_t *volinfo = NULL; - glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; char msg[2048] = {0,}; gf_boolean_t brick_alloc = _gf_false; char *all_bricks = NULL; char *str_ret = NULL; gf_boolean_t is_force = _gf_false; - priv = THIS->private; - if (!priv) - goto out; + this = THIS; + GF_ASSERT (this); + + ret = dict_get_int32 (dict, "replica-count", &replica_count); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, + "Unable to get replica count"); + } + if (replica_count > 0) { + ret = op_version_check (this, GD_OP_VER_PERSISTENT_AFR_XATTRS, + msg, sizeof(msg)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "%s", msg); + *op_errstr = gf_strdup (msg); + goto out; + } + } ret = dict_get_str (dict, "volname", &volname); if (ret) { gf_log (THIS->name, GF_LOG_ERROR, @@ -1376,6 +1391,14 @@ glusterd_op_stage_remove_brick (dict_t *dict, char **op_errstr) this = THIS; GF_ASSERT (this); + ret = op_version_check (this, GD_OP_VER_PERSISTENT_AFR_XATTRS, + msg, sizeof(msg)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "%s", msg); + *op_errstr = gf_strdup (msg); + goto out; + } + ret = dict_get_str (dict, "volname", &volname); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to get volume name"); diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.c b/xlators/mgmt/glusterd/src/glusterd-utils.c index 0bec8c06b..fdfdcc281 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-utils.c @@ -9042,6 +9042,27 @@ gd_update_volume_op_versions (glusterd_volinfo_t *volinfo) return; } +int +op_version_check (xlator_t *this, int min_op_version, char *msg, int msglen) +{ + int ret = 0; + glusterd_conf_t *priv = NULL; + + GF_ASSERT (this); + GF_ASSERT (msg); + + priv = this->private; + if (priv->op_version < min_op_version) { + snprintf (msg, msglen, "One or more nodes do not support " + "the required op-version. Cluster op-version must " + "atleast be %d.", min_op_version); + gf_log (this->name, GF_LOG_ERROR, "%s", msg); + ret = -1; + } + return ret; +} + + /* A task is committed/completed once the task-id for it is cleared */ gf_boolean_t gd_is_remove_brick_committed (glusterd_volinfo_t *volinfo) diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.h b/xlators/mgmt/glusterd/src/glusterd-utils.h index aebf5fcef..ec59d9143 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.h +++ b/xlators/mgmt/glusterd/src/glusterd-utils.h @@ -582,6 +582,9 @@ glusterd_is_same_address (char *name1, char *name2); void gd_update_volume_op_versions (glusterd_volinfo_t *volinfo); +int +op_version_check (xlator_t *this, int min_op_version, char *msg, int msglen); + char* gd_peer_uuid_str (glusterd_peerinfo_t *peerinfo); -- cgit From f7a815a2d0e7e9d7ed1ec2da587790bd3ddda9e5 Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Fri, 21 Mar 2014 20:13:16 +0000 Subject: features/glupy: Rename Glupy python module to avoid namespace conflict * Rename gluster.py to glupy.py to avoid namespace conflict (#1018619) * Move the main Glupy files into glusterfs-extra-xlators rpm * Move the Glupy Translator examples into glusterfs-devel rpm * Add Glupy entry to the MAINTAINERS file BUG: 1018619 Change-Id: I48de598ba5ae8eec0e7e276bbcca1abb0e549cef Signed-off-by: Justin Clift Reviewed-on: http://review.gluster.org/6979 Reviewed-by: Kaleb KEITHLEY Reviewed-by: Jeff Darcy --- .gitignore | 4 +- MAINTAINERS | 5 + api/examples/Makefile.am | 6 +- api/examples/__init__.py.in | 1 + api/examples/setup.py.in | 5 - configure.ac | 3 + glusterfs.spec.in | 61 +- xlators/features/glupy/Makefile.am | 2 +- xlators/features/glupy/examples/Makefile.am | 5 + xlators/features/glupy/examples/debug-trace.py | 775 +++++++++++++++++++++++ xlators/features/glupy/examples/helloworld.py | 19 + xlators/features/glupy/examples/negative.py | 91 +++ xlators/features/glupy/src/Makefile.am | 13 +- xlators/features/glupy/src/debug-trace.py | 774 ----------------------- xlators/features/glupy/src/glupy.c | 5 +- xlators/features/glupy/src/glupy.py | 841 +++++++++++++++++++++++++ xlators/features/glupy/src/gluster.py | 841 ------------------------- xlators/features/glupy/src/helloworld.py | 19 - xlators/features/glupy/src/negative.py | 92 --- xlators/features/glupy/src/setup.py.in | 24 + 20 files changed, 1843 insertions(+), 1743 deletions(-) create mode 100644 api/examples/__init__.py.in create mode 100644 xlators/features/glupy/examples/Makefile.am create mode 100644 xlators/features/glupy/examples/debug-trace.py create mode 100644 xlators/features/glupy/examples/helloworld.py create mode 100644 xlators/features/glupy/examples/negative.py delete mode 100644 xlators/features/glupy/src/debug-trace.py create mode 100644 xlators/features/glupy/src/glupy.py delete mode 100644 xlators/features/glupy/src/gluster.py delete mode 100644 xlators/features/glupy/src/helloworld.py delete mode 100644 xlators/features/glupy/src/negative.py create mode 100644 xlators/features/glupy/src/setup.py.in diff --git a/.gitignore b/.gitignore index e8d0012aa..08bd0dae2 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,8 @@ Makefile stamp-h1 # Generated files -api/examples/__init__.py* +api/examples/__init__.py +api/examples/__init__.py? api/examples/setup.py argp-standalone/libargp.a contrib/uuid/uuid_types.h @@ -48,5 +49,6 @@ libtool run-tests.sh xlators/mount/fuse/utils/mount.glusterfs xlators/mount/fuse/utils/mount_glusterfs +xlators/features/glupy/src/setup.py geo-replication/src/peer_add_secret_pub geo-replication/src/peer_gsec_create diff --git a/MAINTAINERS b/MAINTAINERS index 57f7deb59..49c7c3431 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -119,6 +119,11 @@ M: Venky Shankar S: Maintained F: geo-replication/ +Glupy +M: Justin Clift +S: Maintained +F: xlators/features/glupy/ + libgfapi M: Anand Avati S: Maintained diff --git a/api/examples/Makefile.am b/api/examples/Makefile.am index 05f40ff53..f1a8d12b2 100644 --- a/api/examples/Makefile.am +++ b/api/examples/Makefile.am @@ -1,6 +1,10 @@ +# The bits needed for glfsxmp EXTRA_PROGRAMS = glfsxmp glfsxmp_SOURCES = glfsxmp.c glfsxmp_CFLAGS = $(GLFS_CFLAGS) -Wall glfsxmp_LDADD = $(GLFS_LIBS) -lrt -EXTRA_DIST = gfapi.py +# Install __init__.py (a generated file), and gfapi.py into +# the Python site-packages area +pygfapidir = $(pythondir)/gluster +pygfapi_PYTHON = __init__.py gfapi.py diff --git a/api/examples/__init__.py.in b/api/examples/__init__.py.in new file mode 100644 index 000000000..280beb2dc --- /dev/null +++ b/api/examples/__init__.py.in @@ -0,0 +1 @@ +__version__ = "@PACKAGE_VERSION@" diff --git a/api/examples/setup.py.in b/api/examples/setup.py.in index 44b738094..f22fa1f30 100644 --- a/api/examples/setup.py.in +++ b/api/examples/setup.py.in @@ -1,10 +1,5 @@ from distutils.core import setup -# generate a __init__.py for the package namespace -fo = open('__init__.py', 'w') -fo.write('__version__ = "@PACKAGE_VERSION@"\n') -fo.close() - DESC = """GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file system. diff --git a/configure.ac b/configure.ac index d3f5c1b18..3a3d8712b 100644 --- a/configure.ac +++ b/configure.ac @@ -104,7 +104,9 @@ AC_CONFIG_FILES([Makefile xlators/features/changelog/lib/Makefile xlators/features/changelog/lib/src/Makefile xlators/features/glupy/Makefile + xlators/features/glupy/examples/Makefile xlators/features/glupy/src/Makefile + xlators/features/glupy/src/setup.py xlators/features/locks/Makefile xlators/features/locks/src/Makefile xlators/features/quota/Makefile @@ -178,6 +180,7 @@ AC_CONFIG_FILES([Makefile api/Makefile api/src/Makefile api/examples/Makefile + api/examples/__init__.py api/examples/setup.py geo-replication/Makefile geo-replication/src/Makefile diff --git a/glusterfs.spec.in b/glusterfs.spec.in index f3038eaa4..5c4f74af4 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -347,6 +347,25 @@ is in user space and easily manageable. This package provides the glusterfs libgfapi library. +%package extra-xlators +Summary: Extra Gluster filesystem Translators +Group: Applications/File +# We need -api rpm for its __init__.py in Python site-packages area +Requires: %{name}-api = %{version}-%{release} +Requires: python python-ctypes + +%description extra-xlators +GlusterFS is a clustered file-system capable of scaling to several +petabytes. It aggregates various storage bricks over Infiniband RDMA +or TCP/IP interconnect into one large parallel network file +system. GlusterFS is one of the most sophisticated file systems in +terms of features and extensibility. It borrows a powerful concept +called Translators from GNU Hurd kernel. Much of the code in GlusterFS +is in user space and easily manageable. + +This package provides extra filesystem Translators, such as Glupy, +for GlusterFS. + %if ( 0%{!?_without_ocf:1} ) %package resource-agents Summary: OCF Resource Agents for GlusterFS @@ -384,6 +403,8 @@ like Pacemaker. Summary: Development Libraries Group: Development/Libraries Requires: %{name} = %{version}-%{release} +# Needed for the Glupy examples to work +Requires: %{name}-extra-xlators = %{version}-%{release} %description devel GlusterFS is a clustered file-system capable of scaling to several @@ -451,6 +472,12 @@ sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|' libtool make %{?_smp_mflags} +# Build Glupy +pushd xlators/features/glupy/src +FLAGS="$RPM_OPT_FLAGS" python setup.py build +popd + +# Build the Python libgfapi examples pushd api/examples FLAGS="$RPM_OPT_FLAGS" python setup.py build popd @@ -458,6 +485,10 @@ popd %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} +# install the Glupy Python library in /usr/lib/python*/site-packages +pushd xlators/features/glupy/src +python setup.py install --skip-build --verbose --root %{buildroot} +popd # install the gfapi Python library in /usr/lib/python*/site-packages pushd api/examples python setup.py install --skip-build --verbose --root %{buildroot} @@ -669,6 +700,8 @@ rm -rf %{buildroot} %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/protocol/server* %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mgmt* %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/nfs* +# Glupy files are in the -extra-xlators package +%exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy* # sample xlators not generally used or usable %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/encryption/rot-13* %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/mac-compat* @@ -806,9 +839,26 @@ fi %files api %exclude %{_libdir}/*.so +# Shared Python-GlusterFS files +%{python_sitelib}/gluster/__init__.* +# Libgfapi files %{_libdir}/libgfapi.* %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mount/api* -%{python_sitelib}/* +%{python_sitelib}/gluster/gfapi.* +# Don't expect a .egg-info file on EL5 +%if ( 0%{?rhel} && 0%{?rhel} > 5 ) || ( 0%{?fedora} ) +%{python_sitelib}/glusterfs_api*.egg-info +%endif + +%files extra-xlators +# Glupy C shared library +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy.so +# Glupy Python files +%{python_sitelib}/gluster/glupy.* +# Don't expect a .egg-info file on EL5 +%if ( 0%{?rhel} && 0%{?rhel} > 5 ) || ( 0%{?fedora} ) +%{python_sitelib}/glusterfs_glupy*.egg-info +%endif %if ( 0%{!?_without_ocf:1} ) %files resource-agents @@ -822,6 +872,10 @@ fi %exclude %{_includedir}/glusterfs/api %exclude %{_libdir}/libgfapi.so %{_libdir}/*.so +# Glupy Translator examples +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/debug-trace.* +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/helloworld.* +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/negative.* %files api-devel %{_libdir}/pkgconfig/glusterfs-api.pc @@ -899,6 +953,11 @@ fi * Wed Feb 26 2014 Niels de Vos - Drop glusterfs-devel dependency from glusterfs-api (#1065750) +* Wed Feb 19 2014 Justin Clift +- Rename gluster.py to glupy.py to avoid namespace conflict (#1018619) +- Move the main Glupy files into glusterfs-extra-xlators rpm +- Move the Glupy Translator examples into glusterfs-devel rpm + * Thu Feb 06 2014 Aravinda VK - Include geo-replication upgrade scripts and hook scripts. diff --git a/xlators/features/glupy/Makefile.am b/xlators/features/glupy/Makefile.am index a985f42a8..060429ecf 100644 --- a/xlators/features/glupy/Makefile.am +++ b/xlators/features/glupy/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = src +SUBDIRS = src examples CLEANFILES = diff --git a/xlators/features/glupy/examples/Makefile.am b/xlators/features/glupy/examples/Makefile.am new file mode 100644 index 000000000..c26abeaaf --- /dev/null +++ b/xlators/features/glupy/examples/Makefile.am @@ -0,0 +1,5 @@ +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +glupyexamplesdir = $(xlatordir)/glupy + +glupyexamples_PYTHON = negative.py helloworld.py debug-trace.py diff --git a/xlators/features/glupy/examples/debug-trace.py b/xlators/features/glupy/examples/debug-trace.py new file mode 100644 index 000000000..6eef1b58b --- /dev/null +++ b/xlators/features/glupy/examples/debug-trace.py @@ -0,0 +1,775 @@ +import sys +import stat +from uuid import UUID +from time import strftime, localtime +from gluster.glupy import * + +# This translator was written primarily to test the fop entry point definitions +# and structure definitions in 'glupy.py'. + +# It is similar to the C language debug-trace translator, which logs the +# arguments passed to the fops and their corresponding cbk functions. + +dl.get_id.restype = c_long +dl.get_id.argtypes = [ POINTER(call_frame_t) ] + +dl.get_rootunique.restype = c_uint64 +dl.get_rootunique.argtypes = [ POINTER(call_frame_t) ] + +def uuid2str (gfid): + return str(UUID(''.join(map("{0:02x}".format, gfid)))) + + +def st_mode_from_ia (prot, filetype): + st_mode = 0 + type_bit = 0 + prot_bit = 0 + + if filetype == IA_IFREG: + type_bit = stat.S_IFREG + elif filetype == IA_IFDIR: + type_bit = stat.S_IFDIR + elif filetype == IA_IFLNK: + type_bit = stat.S_IFLNK + elif filetype == IA_IFBLK: + type_bit = stat.S_IFBLK + elif filetype == IA_IFCHR: + type_bit = stat.S_IFCHR + elif filetype == IA_IFIFO: + type_bit = stat.S_IFIFO + elif filetype == IA_IFSOCK: + type_bit = stat.S_IFSOCK + elif filetype == IA_INVAL: + pass + + + if prot.suid: + prot_bit |= stat.S_ISUID + if prot.sgid: + prot_bit |= stat.S_ISGID + if prot.sticky: + prot_bit |= stat.S_ISVTX + + if prot.owner.read: + prot_bit |= stat.S_IRUSR + if prot.owner.write: + prot_bit |= stat.S_IWUSR + if prot.owner.execn: + prot_bit |= stat.S_IXUSR + + if prot.group.read: + prot_bit |= stat.S_IRGRP + if prot.group.write: + prot_bit |= stat.S_IWGRP + if prot.group.execn: + prot_bit |= stat.S_IXGRP + + if prot.other.read: + prot_bit |= stat.S_IROTH + if prot.other.write: + prot_bit |= stat.S_IWOTH + if prot.other.execn: + prot_bit |= stat.S_IXOTH + + st_mode = (type_bit | prot_bit) + + return st_mode + + +def trace_stat2str (buf): + gfid = uuid2str(buf.contents.ia_gfid) + mode = st_mode_from_ia(buf.contents.ia_prot, buf.contents.ia_type) + atime_buf = strftime("[%b %d %H:%M:%S]", + localtime(buf.contents.ia_atime)) + mtime_buf = strftime("[%b %d %H:%M:%S]", + localtime(buf.contents.ia_mtime)) + ctime_buf = strftime("[%b %d %H:%M:%S]", + localtime(buf.contents.ia_ctime)) + return ("(gfid={0:s}, ino={1:d}, mode={2:o}, nlink={3:d}, uid ={4:d}, "+ + "gid ={5:d}, size={6:d}, blocks={7:d}, atime={8:s}, mtime={9:s}, "+ + "ctime={10:s})").format(gfid, buf.contents.ia_no, mode, + buf.contents.ia_nlink, + buf.contents.ia_uid, + buf.contents.ia_gid, + buf.contents.ia_size, + buf.contents.ia_blocks, + atime_buf, mtime_buf, + ctime_buf) + +class xlator(Translator): + + def __init__(self, c_this): + Translator.__init__(self, c_this) + self.gfids = {} + + def lookup_fop(self, frame, this, loc, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.gfid) + print("GLUPY TRACE LOOKUP FOP- {0:d}: gfid={1:s}; " + + "path={2:s}").format(unique, gfid, loc.contents.path) + self.gfids[key] = gfid + dl.wind_lookup(frame, POINTER(xlator_t)(), loc, xdata) + return 0 + + def lookup_cbk(self, frame, cookie, this, op_ret, op_errno, + inode, buf, xdata, postparent): + unique =dl.get_rootunique(frame) + key =dl.get_id(frame) + if op_ret == 0: + gfid = uuid2str(buf.contents.ia_gfid) + statstr = trace_stat2str(buf) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE LOOKUP CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; *buf={3:s}; " + + "*postparent={4:s}").format(unique, gfid, + op_ret, statstr, + postparentstr) + else: + gfid = self.gfids[key] + print("GLUPY TRACE LOOKUP CBK - {0:d}: gfid={1:s};" + + " op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_lookup(frame, cookie, this, op_ret, op_errno, + inode, buf, xdata, postparent) + return 0 + + def create_fop(self, frame, this, loc, flags, mode, umask, fd, + xdata): + unique = dl.get_rootunique(frame) + gfid = uuid2str(loc.contents.gfid) + print("GLUPY TRACE CREATE FOP- {0:d}: gfid={1:s}; path={2:s}; " + + "fd={3:s}; flags=0{4:o}; mode=0{5:o}; " + + "umask=0{6:o}").format(unique, gfid, loc.contents.path, + fd, flags, mode, umask) + dl.wind_create(frame, POINTER(xlator_t)(), loc, flags,mode, + umask, fd, xdata) + return 0 + + def create_cbk(self, frame, cookie, this, op_ret, op_errno, fd, + inode, buf, preparent, postparent, xdata): + unique = dl.get_rootunique(frame) + if op_ret >= 0: + gfid = uuid2str(inode.contents.gfid) + statstr = trace_stat2str(buf) + preparentstr = trace_stat2str(preparent) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE CREATE CBK- {0:d}: gfid={1:s};" + + " op_ret={2:d}; fd={3:s}; *stbuf={4:s}; " + + "*preparent={5:s};" + + " *postparent={6:s}").format(unique, gfid, op_ret, + fd, statstr, + preparentstr, + postparentstr) + else: + print ("GLUPY TRACE CREATE CBK- {0:d}: op_ret={1:d}; " + + "op_errno={2:d}").format(unique, op_ret, op_errno) + dl.unwind_create(frame, cookie, this, op_ret, op_errno, fd, + inode, buf, preparent, postparent, xdata) + return 0 + + def open_fop(self, frame, this, loc, flags, fd, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE OPEN FOP- {0:d}: gfid={1:s}; path={2:s}; "+ + "flags={3:d}; fd={4:s}").format(unique, gfid, + loc.contents.path, flags, + fd) + self.gfids[key] = gfid + dl.wind_open(frame, POINTER(xlator_t)(), loc, flags, fd, xdata) + return 0 + + def open_cbk(self, frame, cookie, this, op_ret, op_errno, fd, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE OPEN CBK- {0:d}: gfid={1:s}; op_ret={2:d}; " + "op_errno={3:d}; *fd={4:s}").format(unique, gfid, + op_ret, op_errno, fd) + del self.gfids[key] + dl.unwind_open(frame, cookie, this, op_ret, op_errno, fd, + xdata) + return 0 + + def readv_fop(self, frame, this, fd, size, offset, flags, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE READV FOP- {0:d}: gfid={1:s}; "+ + "fd={2:s}; size ={3:d}; offset={4:d}; " + + "flags=0{5:x}").format(unique, gfid, fd, size, offset, + flags) + self.gfids[key] = gfid + dl.wind_readv (frame, POINTER(xlator_t)(), fd, size, offset, + flags, xdata) + return 0 + + def readv_cbk(self, frame, cookie, this, op_ret, op_errno, vector, + count, buf, iobref, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret >= 0: + statstr = trace_stat2str(buf) + print("GLUPY TRACE READV CBK- {0:d}: gfid={1:s}, "+ + "op_ret={2:d}; *buf={3:s};").format(unique, gfid, + op_ret, + statstr) + + else: + print("GLUPY TRACE READV CBK- {0:d}: gfid={1:s}, "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_readv (frame, cookie, this, op_ret, op_errno, + vector, count, buf, iobref, xdata) + return 0 + + def writev_fop(self, frame, this, fd, vector, count, offset, flags, + iobref, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE WRITEV FOP- {0:d}: gfid={1:s}; " + + "fd={2:s}; count={3:d}; offset={4:d}; " + + "flags=0{5:x}").format(unique, gfid, fd, count, offset, + flags) + self.gfids[key] = gfid + dl.wind_writev(frame, POINTER(xlator_t)(), fd, vector, count, + offset, flags, iobref, xdata) + return 0 + + def writev_cbk(self, frame, cookie, this, op_ret, op_errno, prebuf, + postbuf, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + if op_ret >= 0: + preopstr = trace_stat2str(prebuf) + postopstr = trace_stat2str(postbuf) + print("GLUPY TRACE WRITEV CBK- {0:d}: op_ret={1:d}; " + + "*prebuf={2:s}; " + + "*postbuf={3:s}").format(unique, op_ret, preopstr, + postopstr) + else: + gfid = self.gfids[key] + print("GLUPY TRACE WRITEV CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_writev (frame, cookie, this, op_ret, op_errno, + prebuf, postbuf, xdata) + return 0 + + def opendir_fop(self, frame, this, loc, fd, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE OPENDIR FOP- {0:d}: gfid={1:s}; path={2:s}; "+ + "fd={3:s}").format(unique, gfid, loc.contents.path, fd) + self.gfids[key] = gfid + dl.wind_opendir(frame, POINTER(xlator_t)(), loc, fd, xdata) + return 0 + + def opendir_cbk(self, frame, cookie, this, op_ret, op_errno, fd, + xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE OPENDIR CBK- {0:d}: gfid={1:s}; op_ret={2:d};"+ + " op_errno={3:d}; fd={4:s}").format(unique, gfid, op_ret, + op_errno, fd) + del self.gfids[key] + dl.unwind_opendir(frame, cookie, this, op_ret, op_errno, + fd, xdata) + return 0 + + def readdir_fop(self, frame, this, fd, size, offset, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE READDIR FOP- {0:d}: gfid={1:s}; fd={2:s}; " + + "size={3:d}; offset={4:d}").format(unique, gfid, fd, size, + offset) + self.gfids[key] = gfid + dl.wind_readdir(frame, POINTER(xlator_t)(), fd, size, offset, + xdata) + return 0 + + def readdir_cbk(self, frame, cookie, this, op_ret, op_errno, buf, + xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE READDIR CBK- {0:d}: gfid={1:s}; op_ret={2:d};"+ + " op_errno={3:d}").format(unique, gfid, op_ret, op_errno) + del self.gfids[key] + dl.unwind_readdir(frame, cookie, this, op_ret, op_errno, buf, + xdata) + return 0 + + def readdirp_fop(self, frame, this, fd, size, offset, dictionary): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE READDIRP FOP- {0:d}: gfid={1:s}; fd={2:s}; "+ + " size={3:d}; offset={4:d}").format(unique, gfid, fd, size, + offset) + self.gfids[key] = gfid + dl.wind_readdirp(frame, POINTER(xlator_t)(), fd, size, offset, + dictionary) + return 0 + + def readdirp_cbk(self, frame, cookie, this, op_ret, op_errno, buf, + xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE READDIRP CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, gfid, + op_ret, op_errno) + del self.gfids[key] + dl.unwind_readdirp(frame, cookie, this, op_ret, op_errno, buf, + xdata) + return 0 + + def mkdir_fop(self, frame, this, loc, mode, umask, xdata): + unique = dl.get_rootunique(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE MKDIR FOP- {0:d}: gfid={1:s}; path={2:s}; " + + "mode={3:d}; umask=0{4:o}").format(unique, gfid, + loc.contents.path, mode, + umask) + dl.wind_mkdir(frame, POINTER(xlator_t)(), loc, mode, umask, + xdata) + return 0 + + def mkdir_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, + preparent, postparent, xdata): + unique = dl.get_rootunique(frame) + if op_ret == 0: + gfid = uuid2str(inode.contents.gfid) + statstr = trace_stat2str(buf) + preparentstr = trace_stat2str(preparent) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE MKDIR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; *stbuf={3:s}; *prebuf={4:s}; "+ + "*postbuf={5:s} ").format(unique, gfid, op_ret, + statstr, + preparentstr, + postparentstr) + else: + print("GLUPY TRACE MKDIR CBK- {0:d}: op_ret={1:d}; "+ + "op_errno={2:d}").format(unique, op_ret, op_errno) + dl.unwind_mkdir(frame, cookie, this, op_ret, op_errno, inode, + buf, preparent, postparent, xdata) + return 0 + + def rmdir_fop(self, frame, this, loc, flags, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE RMDIR FOP- {0:d}: gfid={1:s}; path={2:s}; "+ + "flags={3:d}").format(unique, gfid, loc.contents.path, + flags) + self.gfids[key] = gfid + dl.wind_rmdir(frame, POINTER(xlator_t)(), loc, flags, xdata) + return 0 + + def rmdir_cbk(self, frame, cookie, this, op_ret, op_errno, preparent, + postparent, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + preparentstr = trace_stat2str(preparent) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE RMDIR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; *prebuf={3:s}; "+ + "*postbuf={4:s}").format(unique, gfid, op_ret, + preparentstr, + postparentstr) + else: + print("GLUPY TRACE RMDIR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_rmdir(frame, cookie, this, op_ret, op_errno, + preparent, postparent, xdata) + return 0 + + def stat_fop(self, frame, this, loc, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE STAT FOP- {0:d}: gfid={1:s}; " + + " path={2:s}").format(unique, gfid, loc.contents.path) + self.gfids[key] = gfid + dl.wind_stat(frame, POINTER(xlator_t)(), loc, xdata) + return 0 + + def stat_cbk(self, frame, cookie, this, op_ret, op_errno, buf, + xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + statstr = trace_stat2str(buf) + print("GLUPY TRACE STAT CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; *buf={3:s};").format(unique, + gfid, + op_ret, + statstr) + else: + print("GLUPY TRACE STAT CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_stat(frame, cookie, this, op_ret, op_errno, + buf, xdata) + return 0 + + def fstat_fop(self, frame, this, fd, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE FSTAT FOP- {0:d}: gfid={1:s}; " + + "fd={2:s}").format(unique, gfid, fd) + self.gfids[key] = gfid + dl.wind_fstat(frame, POINTER(xlator_t)(), fd, xdata) + return 0 + + def fstat_cbk(self, frame, cookie, this, op_ret, op_errno, buf, + xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + statstr = trace_stat2str(buf) + print("GLUPY TRACE FSTAT CBK- {0:d}: gfid={1:s} "+ + " op_ret={2:d}; *buf={3:s}").format(unique, + gfid, + op_ret, + statstr) + else: + print("GLUPY TRACE FSTAT CBK- {0:d}: gfid={1:s} "+ + "op_ret={2:d}; op_errno={3:d}").format(unique. + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_fstat(frame, cookie, this, op_ret, op_errno, + buf, xdata) + return 0 + + def statfs_fop(self, frame, this, loc, xdata): + unique = dl.get_rootunique(frame) + if loc.contents.inode: + gfid = uuid2str(loc.contents.inode.contents.gfid) + else: + gfid = "0" + print("GLUPY TRACE STATFS FOP- {0:d}: gfid={1:s}; "+ + "path={2:s}").format(unique, gfid, loc.contents.path) + dl.wind_statfs(frame, POINTER(xlator_t)(), loc, xdata) + return 0 + + def statfs_cbk(self, frame, cookie, this, op_ret, op_errno, buf, + xdata): + unique = dl.get_rootunique(frame) + if op_ret == 0: + #TBD: print buf (pointer to an iovec type object) + print("GLUPY TRACE STATFS CBK {0:d}: "+ + "op_ret={1:d}").format(unique, op_ret) + else: + print("GLUPY TRACE STATFS CBK- {0:d}"+ + "op_ret={1:d}; op_errno={2:d}").format(unique, + op_ret, + op_errno) + dl.unwind_statfs(frame, cookie, this, op_ret, op_errno, + buf, xdata) + return 0 + + def getxattr_fop(self, frame, this, loc, name, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE GETXATTR FOP- {0:d}: gfid={1:s}; path={2:s};"+ + " name={3:s}").format(unique, gfid, loc.contents.path, + name) + self.gfids[key]=gfid + dl.wind_getxattr(frame, POINTER(xlator_t)(), loc, name, xdata) + return 0 + + def getxattr_cbk(self, frame, cookie, this, op_ret, op_errno, + dictionary, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE GETXATTR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}; "+ + " dictionary={4:s}").format(unique, gfid, op_ret, op_errno, + dictionary) + del self.gfids[key] + dl.unwind_getxattr(frame, cookie, this, op_ret, op_errno, + dictionary, xdata) + return 0 + + def fgetxattr_fop(self, frame, this, fd, name, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE FGETXATTR FOP- {0:d}: gfid={1:s}; fd={2:s}; "+ + "name={3:s}").format(unique, gfid, fd, name) + self.gfids[key] = gfid + dl.wind_fgetxattr(frame, POINTER(xlator_t)(), fd, name, xdata) + return 0 + + def fgetxattr_cbk(self, frame, cookie, this, op_ret, op_errno, + dictionary, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE FGETXATTR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d};"+ + " dictionary={4:s}").format(unique, gfid, op_ret, + op_errno, dictionary) + del self.gfids[key] + dl.unwind_fgetxattr(frame, cookie, this, op_ret, op_errno, + dictionary, xdata) + return 0 + + def setxattr_fop(self, frame, this, loc, dictionary, flags, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE SETXATTR FOP- {0:d}: gfid={1:s}; path={2:s};"+ + " flags={3:d}").format(unique, gfid, loc.contents.path, + flags) + self.gfids[key] = gfid + dl.wind_setxattr(frame, POINTER(xlator_t)(), loc, dictionary, + flags, xdata) + return 0 + + def setxattr_cbk(self, frame, cookie, this, op_ret, op_errno, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE SETXATTR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, gfid, + op_ret, op_errno) + del self.gfids[key] + dl.unwind_setxattr(frame, cookie, this, op_ret, op_errno, + xdata) + return 0 + + def fsetxattr_fop(self, frame, this, fd, dictionary, flags, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(fd.contents.inode.contents.gfid) + print("GLUPY TRACE FSETXATTR FOP- {0:d}: gfid={1:s}; fd={2:p}; "+ + "flags={3:d}").format(unique, gfid, fd, flags) + self.gfids[key] = gfid + dl.wind_fsetxattr(frame, POINTER(xlator_t)(), fd, dictionary, + flags, xdata) + return 0 + + def fsetxattr_cbk(self, frame, cookie, this, op_ret, op_errno, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE FSETXATTR CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, gfid, + op_ret, op_errno) + del self.gfids[key] + dl.unwind_fsetxattr(frame, cookie, this, op_ret, op_errno, + xdata) + return 0 + + def removexattr_fop(self, frame, this, loc, name, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE REMOVEXATTR FOP- {0:d}: gfid={1:s}; "+ + "path={2:s}; name={3:s}").format(unique, gfid, + loc.contents.path, + name) + self.gfids[key] = gfid + dl.wind_removexattr(frame, POINTER(xlator_t)(), loc, name, + xdata) + return 0 + + def removexattr_cbk(self, frame, cookie, this, op_ret, op_errno, + xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + print("GLUPY TRACE REMOVEXATTR CBK- {0:d}: gfid={1:s} "+ + " op_ret={2:d}; op_errno={3:d}").format(unique, gfid, + op_ret, op_errno) + del self.gfids[key] + dl.unwind_removexattr(frame, cookie, this, op_ret, op_errno, + xdata) + return 0 + + def link_fop(self, frame, this, oldloc, newloc, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + if (newloc.contents.inode): + newgfid = uuid2str(newloc.contents.inode.contents.gfid) + else: + newgfid = "0" + oldgfid = uuid2str(oldloc.contents.inode.contents.gfid) + print("GLUPY TRACE LINK FOP-{0:d}: oldgfid={1:s}; oldpath={2:s};"+ + "newgfid={3:s};"+ + "newpath={4:s}").format(unique, oldgfid, + oldloc.contents.path, + newgfid, + newloc.contents.path) + self.gfids[key] = oldgfid + dl.wind_link(frame, POINTER(xlator_t)(), oldloc, newloc, + xdata) + return 0 + + def link_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, + preparent, postparent, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + statstr = trace_stat2str(buf) + preparentstr = trace_stat2str(preparent) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE LINK CBK- {0:d}: op_ret={1:d} "+ + "*stbuf={2:s}; *prebuf={3:s}; "+ + "*postbuf={4:s} ").format(unique, op_ret, statstr, + preparentstr, + postparentstr) + else: + print("GLUPY TRACE LINK CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; "+ + "op_errno={3:d}").format(unique, gfid, + op_ret, op_errno) + del self.gfids[key] + dl.unwind_link(frame, cookie, this, op_ret, op_errno, inode, + buf, preparent, postparent, xdata) + return 0 + + def unlink_fop(self, frame, this, loc, xflag, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE UNLINK FOP- {0:d}; gfid={1:s}; path={2:s}; "+ + "flag={3:d}").format(unique, gfid, loc.contents.path, + xflag) + self.gfids[key] = gfid + dl.wind_unlink(frame, POINTER(xlator_t)(), loc, xflag, + xdata) + return 0 + + def unlink_cbk(self, frame, cookie, this, op_ret, op_errno, + preparent, postparent, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + preparentstr = trace_stat2str(preparent) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE UNLINK CBK- {0:d}: gfid ={1:s}; "+ + "op_ret={2:d}; *prebuf={3:s}; "+ + "*postbuf={4:s} ").format(unique, gfid, op_ret, + preparentstr, + postparentstr) + else: + print("GLUPY TRACE UNLINK CBK: {0:d}: gfid ={1:s}; "+ + "op_ret={2:d}; "+ + "op_errno={3:d}").format(unique, gfid, op_ret, + op_errno) + del self.gfids[key] + dl.unwind_unlink(frame, cookie, this, op_ret, op_errno, + preparent, postparent, xdata) + return 0 + + def readlink_fop(self, frame, this, loc, size, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE READLINK FOP- {0:d}: gfid={1:s}; path={2:s};"+ + " size={3:d}").format(unique, gfid, loc.contents.path, + size) + self.gfids[key] = gfid + dl.wind_readlink(frame, POINTER(xlator_t)(), loc, size, + xdata) + return 0 + + def readlink_cbk(self, frame, cookie, this, op_ret, op_errno, + buf, stbuf, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + statstr = trace_stat2str(stbuf) + print("GLUPY TRACE READLINK CBK- {0:d}: gfid={1:s} "+ + " op_ret={2:d}; op_errno={3:d}; *prebuf={4:s}; "+ + "*postbuf={5:s} ").format(unique, gfid, + op_ret, op_errno, + buf, statstr) + else: + print("GLUPY TRACE READLINK CBK- {0:d}: gfid={1:s} "+ + " op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_readlink(frame, cookie, this, op_ret, op_errno, buf, + stbuf, xdata) + return 0 + + def symlink_fop(self, frame, this, linkpath, loc, umask, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = uuid2str(loc.contents.inode.contents.gfid) + print("GLUPY TRACE SYMLINK FOP- {0:d}: gfid={1:s}; "+ + "linkpath={2:s}; path={3:s};"+ + "umask=0{4:o}").format(unique, gfid, linkpath, + loc.contents.path, umask) + self.gfids[key] = gfid + dl.wind_symlink(frame, POINTER(xlator_t)(), linkpath, loc, + umask, xdata) + return 0 + + def symlink_cbk(self, frame, cookie, this, op_ret, op_errno, + inode, buf, preparent, postparent, xdata): + unique = dl.get_rootunique(frame) + key = dl.get_id(frame) + gfid = self.gfids[key] + if op_ret == 0: + statstr = trace_stat2str(buf) + preparentstr = trace_stat2str(preparent) + postparentstr = trace_stat2str(postparent) + print("GLUPY TRACE SYMLINK CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; *stbuf={3:s}; *preparent={4:s}; "+ + "*postparent={5:s}").format(unique, gfid, + op_ret, statstr, + preparentstr, + postparentstr) + else: + print("GLUPY TRACE SYMLINK CBK- {0:d}: gfid={1:s}; "+ + "op_ret={2:d}; op_errno={3:d}").format(unique, + gfid, + op_ret, + op_errno) + del self.gfids[key] + dl.unwind_symlink(frame, cookie, this, op_ret, op_errno, + inode, buf, preparent, postparent, xdata) + return 0 diff --git a/xlators/features/glupy/examples/helloworld.py b/xlators/features/glupy/examples/helloworld.py new file mode 100644 index 000000000..b565a4e5b --- /dev/null +++ b/xlators/features/glupy/examples/helloworld.py @@ -0,0 +1,19 @@ +import sys +from gluster.glupy import * + +class xlator (Translator): + + def __init__(self, c_this): + Translator.__init__(self, c_this) + + def lookup_fop(self, frame, this, loc, xdata): + print "Python xlator: Hello!" + dl.wind_lookup(frame, POINTER(xlator_t)(), loc, xdata) + return 0 + + def lookup_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, + xdata, postparent): + print "Python xlator: Hello again!" + dl.unwind_lookup(frame, cookie, this, op_ret, op_errno, inode, buf, + xdata, postparent) + return 0 diff --git a/xlators/features/glupy/examples/negative.py b/xlators/features/glupy/examples/negative.py new file mode 100644 index 000000000..e7a4fc07c --- /dev/null +++ b/xlators/features/glupy/examples/negative.py @@ -0,0 +1,91 @@ +import sys +from uuid import UUID +from gluster.glupy import * + +# Negative-lookup-caching example. If a file wasn't there the last time we +# looked, it's probably still not there. This translator keeps track of +# those failed lookups for us, and returns ENOENT without needing to pass the +# call any further for repeated requests. + +# If we were doing this for real, we'd need separate caches for each xlator +# instance. The easiest way to do this would be to have xlator.__init__ +# "register" each instance in a module-global dict, with the key as the C +# translator address and the value as the xlator object itself. For testing +# and teaching, it's sufficient just to have one cache. The keys are parent +# GFIDs, and the entries are lists of names within that parent that we know +# don't exist. +cache = {} + +# TBD: we need a better way of handling per-request data (frame->local in C). +dl.get_id.restype = c_long +dl.get_id.argtypes = [ POINTER(call_frame_t) ] + +def uuid2str (gfid): + return str(UUID(''.join(map("{0:02x}".format, gfid)))) + +class xlator (Translator): + + def __init__ (self, c_this): + self.requests = {} + Translator.__init__(self,c_this) + + def lookup_fop (self, frame, this, loc, xdata): + pargfid = uuid2str(loc.contents.pargfid) + print "lookup FOP: %s:%s" % (pargfid, loc.contents.name) + # Check the cache. + if cache.has_key(pargfid): + if loc.contents.name in cache[pargfid]: + print "short-circuiting for %s:%s" % (pargfid, + loc.contents.name) + dl.unwind_lookup(frame,0,this,-1,2,None,None,None,None) + return 0 + key = dl.get_id(frame) + self.requests[key] = (pargfid, loc.contents.name[:]) + # TBD: get real child xl from init, pass it here + dl.wind_lookup(frame,POINTER(xlator_t)(),loc,xdata) + return 0 + + def lookup_cbk (self, frame, cookie, this, op_ret, op_errno, inode, buf, + xdata, postparent): + print "lookup CBK: %d (%d)" % (op_ret, op_errno) + key = dl.get_id(frame) + pargfid, name = self.requests[key] + # Update the cache. + if op_ret == 0: + print "found %s, removing from cache" % name + if cache.has_key(pargfid): + cache[pargfid].discard(name) + elif op_errno == 2: # ENOENT + print "failed to find %s, adding to cache" % name + if cache.has_key(pargfid): + cache[pargfid].add(name) + else: + cache[pargfid] = set([name]) + del self.requests[key] + dl.unwind_lookup(frame,cookie,this,op_ret,op_errno, + inode,buf,xdata,postparent) + return 0 + + def create_fop (self, frame, this, loc, flags, mode, umask, fd, xdata): + pargfid = uuid2str(loc.contents.pargfid) + print "create FOP: %s:%s" % (pargfid, loc.contents.name) + key = dl.get_id(frame) + self.requests[key] = (pargfid, loc.contents.name[:]) + # TBD: get real child xl from init, pass it here + dl.wind_create(frame,POINTER(xlator_t)(),loc,flags,mode,umask,fd,xdata) + return 0 + + def create_cbk (self, frame, cookie, this, op_ret, op_errno, fd, inode, + buf, preparent, postparent, xdata): + print "create CBK: %d (%d)" % (op_ret, op_errno) + key = dl.get_id(frame) + pargfid, name = self.requests[key] + # Update the cache. + if op_ret == 0: + print "created %s, removing from cache" % name + if cache.has_key(pargfid): + cache[pargfid].discard(name) + del self.requests[key] + dl.unwind_create(frame,cookie,this,op_ret,op_errno,fd,inode,buf, + preparent,postparent,xdata) + return 0 diff --git a/xlators/features/glupy/src/Makefile.am b/xlators/features/glupy/src/Makefile.am index 21b91a164..ae7b6d14d 100644 --- a/xlators/features/glupy/src/Makefile.am +++ b/xlators/features/glupy/src/Makefile.am @@ -1,11 +1,12 @@ xlator_LTLIBRARIES = glupy.la +# Ensure GLUSTER_PYTHON_PATH is passed to glupy.so xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features - glupydir = $(xlatordir)/glupy +AM_CPPFLAGS = $(PYTHONDEV_CPPFLAGS) $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src -isystem $(BUILD_PYTHON_INC) +AM_CFLAGS = $(PYTHONDEV_CFLAGS) -Wall -fno-strict-aliasing -DGLUSTER_PYTHON_PATH=\"$(glupydir)\" $(GF_CFLAGS) -glupy_PYTHON = gluster.py negative.py helloworld.py debug-trace.py - +# Flags to build glupy.so with glupy_la_LDFLAGS = $(PYTHONDEV_LDFLAGS) -module -avoid-version -shared -nostartfiles glupy_la_SOURCES = glupy.c glupy_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ @@ -13,8 +14,8 @@ glupy_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ noinst_HEADERS = glupy.h -AM_CPPFLAGS = $(PYTHONDEV_CPPFLAGS) $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src -isystem $(BUILD_PYTHON_INC) - -AM_CFLAGS = $(PYTHONDEV_CFLAGS) -Wall -fno-strict-aliasing -DGLUSTER_PYTHON_PATH=\"$(glupydir)\" $(GF_CFLAGS) +# Install glupy.py into the Python site-packages area +pyglupydir = $(pythondir)/gluster +pyglupy_PYTHON = glupy.py CLEANFILES = diff --git a/xlators/features/glupy/src/debug-trace.py b/xlators/features/glupy/src/debug-trace.py deleted file mode 100644 index 53e76546b..000000000 --- a/xlators/features/glupy/src/debug-trace.py +++ /dev/null @@ -1,774 +0,0 @@ -import sys -import stat -from uuid import UUID -from time import strftime, localtime -from gluster import * -# This translator was written primarily to test the fop entry point definitions -# and structure definitions in 'gluster.py'. -# It is similar to the debug-trace translator, one of the already available -# translator types written in C, that logs the arguments passed to the fops and -# their corresponding cbk functions. - -dl.get_id.restype = c_long -dl.get_id.argtypes = [ POINTER(call_frame_t) ] - -dl.get_rootunique.restype = c_uint64 -dl.get_rootunique.argtypes = [ POINTER(call_frame_t) ] - -def uuid2str (gfid): - return str(UUID(''.join(map("{0:02x}".format, gfid)))) - - -def st_mode_from_ia (prot, filetype): - st_mode = 0 - type_bit = 0 - prot_bit = 0 - - if filetype == IA_IFREG: - type_bit = stat.S_IFREG - elif filetype == IA_IFDIR: - type_bit = stat.S_IFDIR - elif filetype == IA_IFLNK: - type_bit = stat.S_IFLNK - elif filetype == IA_IFBLK: - type_bit = stat.S_IFBLK - elif filetype == IA_IFCHR: - type_bit = stat.S_IFCHR - elif filetype == IA_IFIFO: - type_bit = stat.S_IFIFO - elif filetype == IA_IFSOCK: - type_bit = stat.S_IFSOCK - elif filetype == IA_INVAL: - pass - - - if prot.suid: - prot_bit |= stat.S_ISUID - if prot.sgid: - prot_bit |= stat.S_ISGID - if prot.sticky: - prot_bit |= stat.S_ISVTX - - if prot.owner.read: - prot_bit |= stat.S_IRUSR - if prot.owner.write: - prot_bit |= stat.S_IWUSR - if prot.owner.execn: - prot_bit |= stat.S_IXUSR - - if prot.group.read: - prot_bit |= stat.S_IRGRP - if prot.group.write: - prot_bit |= stat.S_IWGRP - if prot.group.execn: - prot_bit |= stat.S_IXGRP - - if prot.other.read: - prot_bit |= stat.S_IROTH - if prot.other.write: - prot_bit |= stat.S_IWOTH - if prot.other.execn: - prot_bit |= stat.S_IXOTH - - st_mode = (type_bit | prot_bit) - - return st_mode - - -def trace_stat2str (buf): - gfid = uuid2str(buf.contents.ia_gfid) - mode = st_mode_from_ia(buf.contents.ia_prot, buf.contents.ia_type) - atime_buf = strftime("[%b %d %H:%M:%S]", - localtime(buf.contents.ia_atime)) - mtime_buf = strftime("[%b %d %H:%M:%S]", - localtime(buf.contents.ia_mtime)) - ctime_buf = strftime("[%b %d %H:%M:%S]", - localtime(buf.contents.ia_ctime)) - return ("(gfid={0:s}, ino={1:d}, mode={2:o}, nlink={3:d}, uid ={4:d}, "+ - "gid ={5:d}, size={6:d}, blocks={7:d}, atime={8:s}, mtime={9:s}, "+ - "ctime={10:s})").format(gfid, buf.contents.ia_no, mode, - buf.contents.ia_nlink, - buf.contents.ia_uid, - buf.contents.ia_gid, - buf.contents.ia_size, - buf.contents.ia_blocks, - atime_buf, mtime_buf, - ctime_buf) - -class xlator(Translator): - - def __init__(self, c_this): - Translator.__init__(self, c_this) - self.gfids = {} - - def lookup_fop(self, frame, this, loc, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.gfid) - print("GLUPY TRACE LOOKUP FOP- {0:d}: gfid={1:s}; " + - "path={2:s}").format(unique, gfid, loc.contents.path) - self.gfids[key] = gfid - dl.wind_lookup(frame, POINTER(xlator_t)(), loc, xdata) - return 0 - - def lookup_cbk(self, frame, cookie, this, op_ret, op_errno, - inode, buf, xdata, postparent): - unique =dl.get_rootunique(frame) - key =dl.get_id(frame) - if op_ret == 0: - gfid = uuid2str(buf.contents.ia_gfid) - statstr = trace_stat2str(buf) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE LOOKUP CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; *buf={3:s}; " + - "*postparent={4:s}").format(unique, gfid, - op_ret, statstr, - postparentstr) - else: - gfid = self.gfids[key] - print("GLUPY TRACE LOOKUP CBK - {0:d}: gfid={1:s};" + - " op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_lookup(frame, cookie, this, op_ret, op_errno, - inode, buf, xdata, postparent) - return 0 - - def create_fop(self, frame, this, loc, flags, mode, umask, fd, - xdata): - unique = dl.get_rootunique(frame) - gfid = uuid2str(loc.contents.gfid) - print("GLUPY TRACE CREATE FOP- {0:d}: gfid={1:s}; path={2:s}; " + - "fd={3:s}; flags=0{4:o}; mode=0{5:o}; " + - "umask=0{6:o}").format(unique, gfid, loc.contents.path, - fd, flags, mode, umask) - dl.wind_create(frame, POINTER(xlator_t)(), loc, flags,mode, - umask, fd, xdata) - return 0 - - def create_cbk(self, frame, cookie, this, op_ret, op_errno, fd, - inode, buf, preparent, postparent, xdata): - unique = dl.get_rootunique(frame) - if op_ret >= 0: - gfid = uuid2str(inode.contents.gfid) - statstr = trace_stat2str(buf) - preparentstr = trace_stat2str(preparent) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE CREATE CBK- {0:d}: gfid={1:s};" + - " op_ret={2:d}; fd={3:s}; *stbuf={4:s}; " + - "*preparent={5:s};" + - " *postparent={6:s}").format(unique, gfid, op_ret, - fd, statstr, - preparentstr, - postparentstr) - else: - print ("GLUPY TRACE CREATE CBK- {0:d}: op_ret={1:d}; " + - "op_errno={2:d}").format(unique, op_ret, op_errno) - dl.unwind_create(frame, cookie, this, op_ret, op_errno, fd, - inode, buf, preparent, postparent, xdata) - return 0 - - def open_fop(self, frame, this, loc, flags, fd, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE OPEN FOP- {0:d}: gfid={1:s}; path={2:s}; "+ - "flags={3:d}; fd={4:s}").format(unique, gfid, - loc.contents.path, flags, - fd) - self.gfids[key] = gfid - dl.wind_open(frame, POINTER(xlator_t)(), loc, flags, fd, xdata) - return 0 - - def open_cbk(self, frame, cookie, this, op_ret, op_errno, fd, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE OPEN CBK- {0:d}: gfid={1:s}; op_ret={2:d}; " - "op_errno={3:d}; *fd={4:s}").format(unique, gfid, - op_ret, op_errno, fd) - del self.gfids[key] - dl.unwind_open(frame, cookie, this, op_ret, op_errno, fd, - xdata) - return 0 - - def readv_fop(self, frame, this, fd, size, offset, flags, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE READV FOP- {0:d}: gfid={1:s}; "+ - "fd={2:s}; size ={3:d}; offset={4:d}; " + - "flags=0{5:x}").format(unique, gfid, fd, size, offset, - flags) - self.gfids[key] = gfid - dl.wind_readv (frame, POINTER(xlator_t)(), fd, size, offset, - flags, xdata) - return 0 - - def readv_cbk(self, frame, cookie, this, op_ret, op_errno, vector, - count, buf, iobref, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret >= 0: - statstr = trace_stat2str(buf) - print("GLUPY TRACE READV CBK- {0:d}: gfid={1:s}, "+ - "op_ret={2:d}; *buf={3:s};").format(unique, gfid, - op_ret, - statstr) - - else: - print("GLUPY TRACE READV CBK- {0:d}: gfid={1:s}, "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_readv (frame, cookie, this, op_ret, op_errno, - vector, count, buf, iobref, xdata) - return 0 - - def writev_fop(self, frame, this, fd, vector, count, offset, flags, - iobref, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE WRITEV FOP- {0:d}: gfid={1:s}; " + - "fd={2:s}; count={3:d}; offset={4:d}; " + - "flags=0{5:x}").format(unique, gfid, fd, count, offset, - flags) - self.gfids[key] = gfid - dl.wind_writev(frame, POINTER(xlator_t)(), fd, vector, count, - offset, flags, iobref, xdata) - return 0 - - def writev_cbk(self, frame, cookie, this, op_ret, op_errno, prebuf, - postbuf, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - if op_ret >= 0: - preopstr = trace_stat2str(prebuf) - postopstr = trace_stat2str(postbuf) - print("GLUPY TRACE WRITEV CBK- {0:d}: op_ret={1:d}; " + - "*prebuf={2:s}; " + - "*postbuf={3:s}").format(unique, op_ret, preopstr, - postopstr) - else: - gfid = self.gfids[key] - print("GLUPY TRACE WRITEV CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_writev (frame, cookie, this, op_ret, op_errno, - prebuf, postbuf, xdata) - return 0 - - def opendir_fop(self, frame, this, loc, fd, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE OPENDIR FOP- {0:d}: gfid={1:s}; path={2:s}; "+ - "fd={3:s}").format(unique, gfid, loc.contents.path, fd) - self.gfids[key] = gfid - dl.wind_opendir(frame, POINTER(xlator_t)(), loc, fd, xdata) - return 0 - - def opendir_cbk(self, frame, cookie, this, op_ret, op_errno, fd, - xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE OPENDIR CBK- {0:d}: gfid={1:s}; op_ret={2:d};"+ - " op_errno={3:d}; fd={4:s}").format(unique, gfid, op_ret, - op_errno, fd) - del self.gfids[key] - dl.unwind_opendir(frame, cookie, this, op_ret, op_errno, - fd, xdata) - return 0 - - def readdir_fop(self, frame, this, fd, size, offset, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE READDIR FOP- {0:d}: gfid={1:s}; fd={2:s}; " + - "size={3:d}; offset={4:d}").format(unique, gfid, fd, size, - offset) - self.gfids[key] = gfid - dl.wind_readdir(frame, POINTER(xlator_t)(), fd, size, offset, - xdata) - return 0 - - def readdir_cbk(self, frame, cookie, this, op_ret, op_errno, buf, - xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE READDIR CBK- {0:d}: gfid={1:s}; op_ret={2:d};"+ - " op_errno={3:d}").format(unique, gfid, op_ret, op_errno) - del self.gfids[key] - dl.unwind_readdir(frame, cookie, this, op_ret, op_errno, buf, - xdata) - return 0 - - def readdirp_fop(self, frame, this, fd, size, offset, dictionary): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE READDIRP FOP- {0:d}: gfid={1:s}; fd={2:s}; "+ - " size={3:d}; offset={4:d}").format(unique, gfid, fd, size, - offset) - self.gfids[key] = gfid - dl.wind_readdirp(frame, POINTER(xlator_t)(), fd, size, offset, - dictionary) - return 0 - - def readdirp_cbk(self, frame, cookie, this, op_ret, op_errno, buf, - xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE READDIRP CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, gfid, - op_ret, op_errno) - del self.gfids[key] - dl.unwind_readdirp(frame, cookie, this, op_ret, op_errno, buf, - xdata) - return 0 - - def mkdir_fop(self, frame, this, loc, mode, umask, xdata): - unique = dl.get_rootunique(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE MKDIR FOP- {0:d}: gfid={1:s}; path={2:s}; " + - "mode={3:d}; umask=0{4:o}").format(unique, gfid, - loc.contents.path, mode, - umask) - dl.wind_mkdir(frame, POINTER(xlator_t)(), loc, mode, umask, - xdata) - return 0 - - def mkdir_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, - preparent, postparent, xdata): - unique = dl.get_rootunique(frame) - if op_ret == 0: - gfid = uuid2str(inode.contents.gfid) - statstr = trace_stat2str(buf) - preparentstr = trace_stat2str(preparent) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE MKDIR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; *stbuf={3:s}; *prebuf={4:s}; "+ - "*postbuf={5:s} ").format(unique, gfid, op_ret, - statstr, - preparentstr, - postparentstr) - else: - print("GLUPY TRACE MKDIR CBK- {0:d}: op_ret={1:d}; "+ - "op_errno={2:d}").format(unique, op_ret, op_errno) - dl.unwind_mkdir(frame, cookie, this, op_ret, op_errno, inode, - buf, preparent, postparent, xdata) - return 0 - - def rmdir_fop(self, frame, this, loc, flags, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE RMDIR FOP- {0:d}: gfid={1:s}; path={2:s}; "+ - "flags={3:d}").format(unique, gfid, loc.contents.path, - flags) - self.gfids[key] = gfid - dl.wind_rmdir(frame, POINTER(xlator_t)(), loc, flags, xdata) - return 0 - - def rmdir_cbk(self, frame, cookie, this, op_ret, op_errno, preparent, - postparent, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - preparentstr = trace_stat2str(preparent) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE RMDIR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; *prebuf={3:s}; "+ - "*postbuf={4:s}").format(unique, gfid, op_ret, - preparentstr, - postparentstr) - else: - print("GLUPY TRACE RMDIR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_rmdir(frame, cookie, this, op_ret, op_errno, - preparent, postparent, xdata) - return 0 - - def stat_fop(self, frame, this, loc, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE STAT FOP- {0:d}: gfid={1:s}; " + - " path={2:s}").format(unique, gfid, loc.contents.path) - self.gfids[key] = gfid - dl.wind_stat(frame, POINTER(xlator_t)(), loc, xdata) - return 0 - - def stat_cbk(self, frame, cookie, this, op_ret, op_errno, buf, - xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - statstr = trace_stat2str(buf) - print("GLUPY TRACE STAT CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; *buf={3:s};").format(unique, - gfid, - op_ret, - statstr) - else: - print("GLUPY TRACE STAT CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_stat(frame, cookie, this, op_ret, op_errno, - buf, xdata) - return 0 - - def fstat_fop(self, frame, this, fd, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE FSTAT FOP- {0:d}: gfid={1:s}; " + - "fd={2:s}").format(unique, gfid, fd) - self.gfids[key] = gfid - dl.wind_fstat(frame, POINTER(xlator_t)(), fd, xdata) - return 0 - - def fstat_cbk(self, frame, cookie, this, op_ret, op_errno, buf, - xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - statstr = trace_stat2str(buf) - print("GLUPY TRACE FSTAT CBK- {0:d}: gfid={1:s} "+ - " op_ret={2:d}; *buf={3:s}").format(unique, - gfid, - op_ret, - statstr) - else: - print("GLUPY TRACE FSTAT CBK- {0:d}: gfid={1:s} "+ - "op_ret={2:d}; op_errno={3:d}").format(unique. - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_fstat(frame, cookie, this, op_ret, op_errno, - buf, xdata) - return 0 - - def statfs_fop(self, frame, this, loc, xdata): - unique = dl.get_rootunique(frame) - if loc.contents.inode: - gfid = uuid2str(loc.contents.inode.contents.gfid) - else: - gfid = "0" - print("GLUPY TRACE STATFS FOP- {0:d}: gfid={1:s}; "+ - "path={2:s}").format(unique, gfid, loc.contents.path) - dl.wind_statfs(frame, POINTER(xlator_t)(), loc, xdata) - return 0 - - def statfs_cbk(self, frame, cookie, this, op_ret, op_errno, buf, - xdata): - unique = dl.get_rootunique(frame) - if op_ret == 0: - #TBD: print buf (pointer to an iovec type object) - print("GLUPY TRACE STATFS CBK {0:d}: "+ - "op_ret={1:d}").format(unique, op_ret) - else: - print("GLUPY TRACE STATFS CBK- {0:d}"+ - "op_ret={1:d}; op_errno={2:d}").format(unique, - op_ret, - op_errno) - dl.unwind_statfs(frame, cookie, this, op_ret, op_errno, - buf, xdata) - return 0 - - def getxattr_fop(self, frame, this, loc, name, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE GETXATTR FOP- {0:d}: gfid={1:s}; path={2:s};"+ - " name={3:s}").format(unique, gfid, loc.contents.path, - name) - self.gfids[key]=gfid - dl.wind_getxattr(frame, POINTER(xlator_t)(), loc, name, xdata) - return 0 - - def getxattr_cbk(self, frame, cookie, this, op_ret, op_errno, - dictionary, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE GETXATTR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}; "+ - " dictionary={4:s}").format(unique, gfid, op_ret, op_errno, - dictionary) - del self.gfids[key] - dl.unwind_getxattr(frame, cookie, this, op_ret, op_errno, - dictionary, xdata) - return 0 - - def fgetxattr_fop(self, frame, this, fd, name, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE FGETXATTR FOP- {0:d}: gfid={1:s}; fd={2:s}; "+ - "name={3:s}").format(unique, gfid, fd, name) - self.gfids[key] = gfid - dl.wind_fgetxattr(frame, POINTER(xlator_t)(), fd, name, xdata) - return 0 - - def fgetxattr_cbk(self, frame, cookie, this, op_ret, op_errno, - dictionary, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE FGETXATTR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d};"+ - " dictionary={4:s}").format(unique, gfid, op_ret, - op_errno, dictionary) - del self.gfids[key] - dl.unwind_fgetxattr(frame, cookie, this, op_ret, op_errno, - dictionary, xdata) - return 0 - - def setxattr_fop(self, frame, this, loc, dictionary, flags, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE SETXATTR FOP- {0:d}: gfid={1:s}; path={2:s};"+ - " flags={3:d}").format(unique, gfid, loc.contents.path, - flags) - self.gfids[key] = gfid - dl.wind_setxattr(frame, POINTER(xlator_t)(), loc, dictionary, - flags, xdata) - return 0 - - def setxattr_cbk(self, frame, cookie, this, op_ret, op_errno, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE SETXATTR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, gfid, - op_ret, op_errno) - del self.gfids[key] - dl.unwind_setxattr(frame, cookie, this, op_ret, op_errno, - xdata) - return 0 - - def fsetxattr_fop(self, frame, this, fd, dictionary, flags, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(fd.contents.inode.contents.gfid) - print("GLUPY TRACE FSETXATTR FOP- {0:d}: gfid={1:s}; fd={2:p}; "+ - "flags={3:d}").format(unique, gfid, fd, flags) - self.gfids[key] = gfid - dl.wind_fsetxattr(frame, POINTER(xlator_t)(), fd, dictionary, - flags, xdata) - return 0 - - def fsetxattr_cbk(self, frame, cookie, this, op_ret, op_errno, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE FSETXATTR CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, gfid, - op_ret, op_errno) - del self.gfids[key] - dl.unwind_fsetxattr(frame, cookie, this, op_ret, op_errno, - xdata) - return 0 - - def removexattr_fop(self, frame, this, loc, name, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE REMOVEXATTR FOP- {0:d}: gfid={1:s}; "+ - "path={2:s}; name={3:s}").format(unique, gfid, - loc.contents.path, - name) - self.gfids[key] = gfid - dl.wind_removexattr(frame, POINTER(xlator_t)(), loc, name, - xdata) - return 0 - - def removexattr_cbk(self, frame, cookie, this, op_ret, op_errno, - xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - print("GLUPY TRACE REMOVEXATTR CBK- {0:d}: gfid={1:s} "+ - " op_ret={2:d}; op_errno={3:d}").format(unique, gfid, - op_ret, op_errno) - del self.gfids[key] - dl.unwind_removexattr(frame, cookie, this, op_ret, op_errno, - xdata) - return 0 - - def link_fop(self, frame, this, oldloc, newloc, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - if (newloc.contents.inode): - newgfid = uuid2str(newloc.contents.inode.contents.gfid) - else: - newgfid = "0" - oldgfid = uuid2str(oldloc.contents.inode.contents.gfid) - print("GLUPY TRACE LINK FOP-{0:d}: oldgfid={1:s}; oldpath={2:s};"+ - "newgfid={3:s};"+ - "newpath={4:s}").format(unique, oldgfid, - oldloc.contents.path, - newgfid, - newloc.contents.path) - self.gfids[key] = oldgfid - dl.wind_link(frame, POINTER(xlator_t)(), oldloc, newloc, - xdata) - return 0 - - def link_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, - preparent, postparent, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - statstr = trace_stat2str(buf) - preparentstr = trace_stat2str(preparent) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE LINK CBK- {0:d}: op_ret={1:d} "+ - "*stbuf={2:s}; *prebuf={3:s}; "+ - "*postbuf={4:s} ").format(unique, op_ret, statstr, - preparentstr, - postparentstr) - else: - print("GLUPY TRACE LINK CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; "+ - "op_errno={3:d}").format(unique, gfid, - op_ret, op_errno) - del self.gfids[key] - dl.unwind_link(frame, cookie, this, op_ret, op_errno, inode, - buf, preparent, postparent, xdata) - return 0 - - def unlink_fop(self, frame, this, loc, xflag, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE UNLINK FOP- {0:d}; gfid={1:s}; path={2:s}; "+ - "flag={3:d}").format(unique, gfid, loc.contents.path, - xflag) - self.gfids[key] = gfid - dl.wind_unlink(frame, POINTER(xlator_t)(), loc, xflag, - xdata) - return 0 - - def unlink_cbk(self, frame, cookie, this, op_ret, op_errno, - preparent, postparent, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - preparentstr = trace_stat2str(preparent) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE UNLINK CBK- {0:d}: gfid ={1:s}; "+ - "op_ret={2:d}; *prebuf={3:s}; "+ - "*postbuf={4:s} ").format(unique, gfid, op_ret, - preparentstr, - postparentstr) - else: - print("GLUPY TRACE UNLINK CBK: {0:d}: gfid ={1:s}; "+ - "op_ret={2:d}; "+ - "op_errno={3:d}").format(unique, gfid, op_ret, - op_errno) - del self.gfids[key] - dl.unwind_unlink(frame, cookie, this, op_ret, op_errno, - preparent, postparent, xdata) - return 0 - - def readlink_fop(self, frame, this, loc, size, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE READLINK FOP- {0:d}: gfid={1:s}; path={2:s};"+ - " size={3:d}").format(unique, gfid, loc.contents.path, - size) - self.gfids[key] = gfid - dl.wind_readlink(frame, POINTER(xlator_t)(), loc, size, - xdata) - return 0 - - def readlink_cbk(self, frame, cookie, this, op_ret, op_errno, - buf, stbuf, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - statstr = trace_stat2str(stbuf) - print("GLUPY TRACE READLINK CBK- {0:d}: gfid={1:s} "+ - " op_ret={2:d}; op_errno={3:d}; *prebuf={4:s}; "+ - "*postbuf={5:s} ").format(unique, gfid, - op_ret, op_errno, - buf, statstr) - else: - print("GLUPY TRACE READLINK CBK- {0:d}: gfid={1:s} "+ - " op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_readlink(frame, cookie, this, op_ret, op_errno, buf, - stbuf, xdata) - return 0 - - def symlink_fop(self, frame, this, linkpath, loc, umask, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = uuid2str(loc.contents.inode.contents.gfid) - print("GLUPY TRACE SYMLINK FOP- {0:d}: gfid={1:s}; "+ - "linkpath={2:s}; path={3:s};"+ - "umask=0{4:o}").format(unique, gfid, linkpath, - loc.contents.path, umask) - self.gfids[key] = gfid - dl.wind_symlink(frame, POINTER(xlator_t)(), linkpath, loc, - umask, xdata) - return 0 - - def symlink_cbk(self, frame, cookie, this, op_ret, op_errno, - inode, buf, preparent, postparent, xdata): - unique = dl.get_rootunique(frame) - key = dl.get_id(frame) - gfid = self.gfids[key] - if op_ret == 0: - statstr = trace_stat2str(buf) - preparentstr = trace_stat2str(preparent) - postparentstr = trace_stat2str(postparent) - print("GLUPY TRACE SYMLINK CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; *stbuf={3:s}; *preparent={4:s}; "+ - "*postparent={5:s}").format(unique, gfid, - op_ret, statstr, - preparentstr, - postparentstr) - else: - print("GLUPY TRACE SYMLINK CBK- {0:d}: gfid={1:s}; "+ - "op_ret={2:d}; op_errno={3:d}").format(unique, - gfid, - op_ret, - op_errno) - del self.gfids[key] - dl.unwind_symlink(frame, cookie, this, op_ret, op_errno, - inode, buf, preparent, postparent, xdata) - return 0 diff --git a/xlators/features/glupy/src/glupy.c b/xlators/features/glupy/src/glupy.c index dc86c0071..948b66f8d 100644 --- a/xlators/features/glupy/src/glupy.c +++ b/xlators/features/glupy/src/glupy.c @@ -2365,7 +2365,7 @@ init (xlator_t *this) goto *err_cleanup; } - gf_log (this->name, GF_LOG_ERROR, "py_mod_name = %s", module_name); + gf_log (this->name, GF_LOG_DEBUG, "py_mod_name = %s", module_name); priv->py_module = PyImport_Import(py_mod_name); Py_DECREF(py_mod_name); if (!priv->py_module) { @@ -2375,6 +2375,7 @@ init (xlator_t *this) } goto *err_cleanup; } + gf_log (this->name, GF_LOG_INFO, "Import of %s succeeded", module_name); err_cleanup = &&err_deref_module; py_init_func = PyObject_GetAttrString(priv->py_module, "xlator"); @@ -2407,7 +2408,7 @@ init (xlator_t *this) } goto *err_cleanup; } - gf_log (this->name, GF_LOG_INFO, "init returned %p", priv->py_xlator); + gf_log (this->name, GF_LOG_DEBUG, "init returned %p", priv->py_xlator); return 0; diff --git a/xlators/features/glupy/src/glupy.py b/xlators/features/glupy/src/glupy.py new file mode 100644 index 000000000..a5daa77d3 --- /dev/null +++ b/xlators/features/glupy/src/glupy.py @@ -0,0 +1,841 @@ +import sys +from ctypes import * + +dl = CDLL("",RTLD_GLOBAL) + + +class call_frame_t (Structure): + pass + +class dev_t (Structure): + pass + + +class dict_t (Structure): + pass + + +class gf_dirent_t (Structure): + pass + + +class iobref_t (Structure): + pass + + +class iovec_t (Structure): + pass + + +class list_head (Structure): + pass + +list_head._fields_ = [ + ("next", POINTER(list_head)), + ("prev", POINTER(list_head)) + ] + + +class rwxperm_t (Structure): + _fields_ = [ + ("read", c_uint8, 1), + ("write", c_uint8, 1), + ("execn", c_uint8, 1) + ] + + +class statvfs_t (Structure): + pass + + +class xlator_t (Structure): + pass + + +class ia_prot_t (Structure): + _fields_ = [ + ("suid", c_uint8, 1), + ("sgid", c_uint8, 1), + ("sticky", c_uint8, 1), + ("owner", rwxperm_t), + ("group", rwxperm_t), + ("other", rwxperm_t) + ] + +# For checking file type. +(IA_INVAL, IA_IFREG, IA_IFDIR, IA_IFLNK, IA_IFBLK, IA_IFCHR, IA_IFIFO, + IA_IFSOCK) = xrange(8) + + +class iatt_t (Structure): + _fields_ = [ + ("ia_no", c_uint64), + ("ia_gfid", c_ubyte * 16), + ("ia_dev", c_uint64), + ("ia_type", c_uint), + ("ia_prot", ia_prot_t), + ("ia_nlink", c_uint32), + ("ia_uid", c_uint32), + ("ia_gid", c_uint32), + ("ia_rdev", c_uint64), + ("ia_size", c_uint64), + ("ia_blksize", c_uint32), + ("ia_blocks", c_uint64), + ("ia_atime", c_uint32 ), + ("ia_atime_nsec", c_uint32), + ("ia_mtime", c_uint32), + ("ia_mtime_nsec", c_uint32), + ("ia_ctime", c_uint32), + ("ia_ctime_nsec", c_uint32) + ] + + +class mem_pool (Structure): + _fields_ = [ + ("list", list_head), + ("hot_count", c_int), + ("cold_count", c_int), + ("lock", c_void_p), + ("padded_sizeof_type", c_ulong), + ("pool", c_void_p), + ("pool_end", c_void_p), + ("real_sizeof_type", c_int), + ("alloc_count", c_uint64), + ("pool_misses", c_uint64), + ("max_alloc", c_int), + ("curr_stdalloc", c_int), + ("max_stdalloc", c_int), + ("name", c_char_p), + ("global_list", list_head) + ] + + +class U_ctx_key_inode (Union): + _fields_ = [ + ("key", c_uint64), + ("xl_key", POINTER(xlator_t)) + ] + + +class U_ctx_value1 (Union): + _fields_ = [ + ("value1", c_uint64), + ("ptr1", c_void_p) + ] + + +class U_ctx_value2 (Union): + _fields_ = [ + ("value2", c_uint64), + ("ptr2", c_void_p) + ] + +class inode_ctx (Structure): + _anonymous_ = ("u_key","u_value1","u_value2",) + _fields_ = [ + ("u_key", U_ctx_key_inode), + ("u_value1", U_ctx_value1), + ("u_value2", U_ctx_value2) + ] + +class inode_t (Structure): + pass + +class inode_table_t (Structure): + _fields_ = [ + ("lock", c_void_p), + ("hashsize", c_size_t), + ("name", c_char_p), + ("root", POINTER(inode_t)), + ("xl", POINTER(xlator_t)), + ("lru_limit", c_uint32), + ("inode_hash", POINTER(list_head)), + ("name_hash", POINTER(list_head)), + ("active", list_head), + ("active_size", c_uint32), + ("lru", list_head), + ("lru_size", c_uint32), + ("purge", list_head), + ("purge_size", c_uint32), + ("inode_pool", POINTER(mem_pool)), + ("dentry_pool", POINTER(mem_pool)), + ("fd_mem_pool", POINTER(mem_pool)) + ] + +inode_t._fields_ = [ + ("table", POINTER(inode_table_t)), + ("gfid", c_ubyte * 16), + ("lock", c_void_p), + ("nlookup", c_uint64), + ("fd_count", c_uint32), + ("ref", c_uint32), + ("ia_type", c_uint), + ("fd_list", list_head), + ("dentry_list", list_head), + ("hashv", list_head), + ("listv", list_head), + ("ctx", POINTER(inode_ctx)) + ] + + + +class U_ctx_key_fd (Union): + _fields_ = [ + ("key", c_uint64), + ("xl_key", c_void_p) + ] + +class fd_lk_ctx (Structure): + _fields_ = [ + ("lk_list", list_head), + ("ref", c_int), + ("lock", c_void_p) + ] + +class fd_ctx (Structure): + _anonymous_ = ("u_key","u_value1") + _fields_ = [ + ("u_key", U_ctx_key_fd), + ("u_value1", U_ctx_value1) + ] + +class fd_t (Structure): + _fields_ = [ + ("pid", c_uint64), + ("flags", c_int32), + ("refcount", c_int32), + ("inode_list", list_head), + ("inode", POINTER(inode_t)), + ("lock", c_void_p), + ("ctx", POINTER(fd_ctx)), + ("xl_count", c_int), + ("lk_ctx", POINTER(fd_lk_ctx)), + ("anonymous", c_uint) + ] + +class loc_t (Structure): + _fields_ = [ + ("path", c_char_p), + ("name", c_char_p), + ("inode", POINTER(inode_t)), + ("parent", POINTER(inode_t)), + ("gfid", c_ubyte * 16), + ("pargfid", c_ubyte * 16), + ] + + + +def _init_op (a_class, fop, cbk, wind, unwind): + # Decorators, used by translators. We could pass the signatures as + # parameters, but it's actually kind of nice to keep them around for + # inspection. + a_class.fop_type = apply(CFUNCTYPE,a_class.fop_sig) + a_class.cbk_type = apply(CFUNCTYPE,a_class.cbk_sig) + # Dispatch-function registration. + fop.restype = None + fop.argtypes = [ c_long, a_class.fop_type ] + # Callback-function registration. + cbk.restype = None + cbk.argtypes = [ c_long, a_class.cbk_type ] + # STACK_WIND function. + wind.restype = None + wind.argtypes = list(a_class.fop_sig[1:]) + # STACK_UNWIND function. + unwind.restype = None + unwind.argtypes = list(a_class.cbk_sig[1:]) + +class OpLookup: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(inode_t), POINTER(iatt_t), + POINTER(dict_t), POINTER(iatt_t)) +_init_op (OpLookup, dl.set_lookup_fop, dl.set_lookup_cbk, + dl.wind_lookup, dl.unwind_lookup) + +class OpCreate: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_int, c_uint, c_uint, POINTER(fd_t), + POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(fd_t), POINTER(inode_t), + POINTER(iatt_t), POINTER(iatt_t), POINTER(iatt_t), + POINTER(dict_t)) +_init_op (OpCreate, dl.set_create_fop, dl.set_create_cbk, + dl.wind_create, dl.unwind_create) + +class OpOpen: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_int, POINTER(fd_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(fd_t), POINTER(dict_t)) +_init_op (OpOpen, dl.set_open_fop, dl.set_open_cbk, + dl.wind_open, dl.unwind_open) + +class OpReadv: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), c_size_t, c_long, c_uint32, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(iovec_t), c_int, POINTER(iatt_t), + POINTER(iobref_t), POINTER(dict_t)) +_init_op (OpReadv, dl.set_readv_fop, dl.set_readv_cbk, + dl.wind_readv, dl.unwind_readv) +class OpWritev: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), POINTER(iovec_t), c_int, c_long, c_uint32, + POINTER(iobref_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(iatt_t), POINTER(iatt_t), + POINTER(dict_t)) +_init_op (OpWritev, dl.set_writev_fop, dl.set_writev_cbk, + dl.wind_writev, dl.unwind_writev) + +class OpOpendir: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), POINTER(fd_t) ,POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(fd_t), POINTER(dict_t)) +_init_op (OpOpendir, dl.set_opendir_fop, dl.set_opendir_cbk, + dl.wind_opendir, dl.unwind_opendir) + +class OpReaddir: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), c_size_t, c_long, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(gf_dirent_t), POINTER(dict_t)) +_init_op (OpReaddir, dl.set_readdir_fop, dl.set_readdir_cbk, + dl.wind_readdir, dl.unwind_readdir) + +class OpReaddirp: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), c_size_t, c_long, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(gf_dirent_t), POINTER(dict_t)) +_init_op (OpReaddirp, dl.set_readdirp_fop, dl.set_readdirp_cbk, + dl.wind_readdirp, dl.unwind_readdirp) + +class OpStat: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(iatt_t), POINTER(dict_t)) +_init_op (OpStat, dl.set_stat_fop, dl.set_stat_cbk, + dl.wind_stat, dl.unwind_stat) + +class OpFstat: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(iatt_t), POINTER(dict_t)) +_init_op (OpFstat, dl.set_fstat_fop, dl.set_fstat_cbk, + dl.wind_fstat, dl.unwind_fstat) + +class OpStatfs: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(statvfs_t), POINTER(dict_t)) +_init_op (OpStatfs, dl.set_statfs_fop, dl.set_statfs_cbk, + dl.wind_statfs, dl.unwind_statfs) + + +class OpSetxattr: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), POINTER(dict_t), c_int32, + POINTER (dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(dict_t)) +_init_op (OpSetxattr, dl.set_setxattr_fop, dl.set_setxattr_cbk, + dl.wind_setxattr, dl.unwind_setxattr) + +class OpGetxattr: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_char_p, POINTER (dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(dict_t), POINTER(dict_t)) +_init_op (OpGetxattr, dl.set_getxattr_fop, dl.set_getxattr_cbk, + dl.wind_getxattr, dl.unwind_getxattr) + +class OpFsetxattr: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), POINTER(dict_t), c_int32, + POINTER (dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(dict_t)) +_init_op (OpFsetxattr, dl.set_fsetxattr_fop, dl.set_fsetxattr_cbk, + dl.wind_fsetxattr, dl.unwind_fsetxattr) + +class OpFgetxattr: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), c_char_p, POINTER (dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(dict_t), POINTER(dict_t)) +_init_op (OpFgetxattr, dl.set_fgetxattr_fop, dl.set_fgetxattr_cbk, + dl.wind_fgetxattr, dl.unwind_fgetxattr) + +class OpRemovexattr: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_char_p, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(dict_t)) +_init_op (OpRemovexattr, dl.set_removexattr_fop, dl.set_removexattr_cbk, + dl.wind_removexattr, dl.unwind_removexattr) + + +class OpFremovexattr: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(fd_t), c_char_p, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(dict_t)) +_init_op (OpFremovexattr, dl.set_fremovexattr_fop, dl.set_fremovexattr_cbk, + dl.wind_fremovexattr, dl.unwind_fremovexattr) + +class OpLink: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), POINTER(loc_t), POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(inode_t), POINTER(iatt_t), + POINTER(iatt_t), POINTER(iatt_t), POINTER(dict_t)) +_init_op (OpLink, dl.set_link_fop, dl.set_link_cbk, + dl.wind_link, dl.unwind_link) + +class OpSymlink: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + c_char_p, POINTER(loc_t), c_uint, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(inode_t), POINTER(iatt_t), + POINTER(iatt_t), POINTER(iatt_t), POINTER(dict_t)) +_init_op (OpSymlink, dl.set_symlink_fop, dl.set_symlink_cbk, + dl.wind_symlink, dl.unwind_symlink) + +class OpUnlink: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_int, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(iatt_t), POINTER(iatt_t), + POINTER(dict_t)) +_init_op (OpUnlink, dl.set_unlink_fop, dl.set_unlink_cbk, + dl.wind_unlink, dl.unwind_unlink) + +class OpReadlink: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_size_t, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, c_char_p, POINTER(iatt_t), POINTER(dict_t)) +_init_op (OpReadlink, dl.set_readlink_fop, dl.set_readlink_cbk, + dl.wind_readlink, dl.unwind_readlink) + +class OpMkdir: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_uint, c_uint, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(inode_t), POINTER(iatt_t), + POINTER(iatt_t), POINTER(iatt_t), POINTER(dict_t)) +_init_op (OpMkdir, dl.set_mkdir_fop, dl.set_mkdir_cbk, + dl.wind_mkdir, dl.unwind_mkdir) + +class OpRmdir: + fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), + POINTER(loc_t), c_int, POINTER(dict_t)) + cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), + c_int, c_int, POINTER(iatt_t), POINTER(iatt_t), + POINTER(dict_t)) +_init_op (OpRmdir, dl.set_rmdir_fop, dl.set_rmdir_cbk, + dl.wind_rmdir, dl.unwind_rmdir) + + +class Translator: + def __init__ (self, c_this): + # This is only here to keep references to the stubs we create, + # because ctypes doesn't and glupy.so can't because it doesn't + # get a pointer to the actual Python object. It's a dictionary + # instead of a list in case we ever allow changing fops/cbks + # after initialization and need to look them up. + self.stub_refs = {} + funcs = dir(self.__class__) + if "lookup_fop" in funcs: + @OpLookup.fop_type + def stub (frame, this, loc, xdata, s=self): + return s.lookup_fop (frame, this, loc, xdata) + self.stub_refs["lookup_fop"] = stub + dl.set_lookup_fop(c_this,stub) + if "lookup_cbk" in funcs: + @OpLookup.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, inode, + buf, xdata, postparent, s=self): + return s.lookup_cbk(frame, cookie, this, op_ret, + op_errno, inode, buf, xdata, + postparent) + self.stub_refs["lookup_cbk"] = stub + dl.set_lookup_cbk(c_this,stub) + if "create_fop" in funcs: + @OpCreate.fop_type + def stub (frame, this, loc, flags, mode, umask, fd, + xdata, s=self): + return s.create_fop (frame, this, loc, flags, + mode, umask, fd, xdata) + self.stub_refs["create_fop"] = stub + dl.set_create_fop(c_this,stub) + if "create_cbk" in funcs: + @OpCreate.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, fd, + inode, buf, preparent, postparent, xdata, + s=self): + return s.create_cbk (frame, cookie, this, + op_ret, op_errno, fd, + inode, buf, preparent, + postparent, xdata) + self.stub_refs["create_cbk"] = stub + dl.set_create_cbk(c_this,stub) + if "open_fop" in funcs: + @OpOpen.fop_type + def stub (frame, this, loc, flags, fd, + xdata, s=self): + return s.open_fop (frame, this, loc, flags, + fd, xdata) + self.stub_refs["open_fop"] = stub + dl.set_open_fop(c_this,stub) + if "open_cbk" in funcs: + @OpOpen.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, fd, + xdata, s=self): + return s.open_cbk (frame, cookie, this, + op_ret, op_errno, fd, + xdata) + self.stub_refs["open_cbk"] = stub + dl.set_open_cbk(c_this,stub) + if "readv_fop" in funcs: + @OpReadv.fop_type + def stub (frame, this, fd, size, offset, flags, + xdata, s=self): + return s.readv_fop (frame, this, fd, size, + offset, flags, xdata) + self.stub_refs["readv_fop"] = stub + dl.set_readv_fop(c_this,stub) + if "readv_cbk" in funcs: + @OpReadv.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + vector, count, stbuf, iobref, xdata, + s=self): + return s.readv_cbk (frame, cookie, this, + op_ret, op_errno, vector, + count, stbuf, iobref, + xdata) + self.stub_refs["readv_cbk"] = stub + dl.set_readv_cbk(c_this,stub) + if "writev_fop" in funcs: + @OpWritev.fop_type + def stub (frame, this, fd, vector, count, + offset, flags, iobref, xdata, s=self): + return s.writev_fop (frame, this, fd, vector, + count, offset, flags, + iobref, xdata) + self.stub_refs["writev_fop"] = stub + dl.set_writev_fop(c_this,stub) + if "writev_cbk" in funcs: + @OpWritev.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + prebuf, postbuf, xdata, s=self): + return s.writev_cbk (frame, cookie, this, + op_ret, op_errno, prebuf, + postbuf, xdata) + self.stub_refs["writev_cbk"] = stub + dl.set_writev_cbk(c_this,stub) + if "opendir_fop" in funcs: + @OpOpendir.fop_type + def stub (frame, this, loc, fd, xdata, s=self): + return s.opendir_fop (frame, this, loc, fd, + xdata) + self.stub_refs["opendir_fop"] = stub + dl.set_opendir_fop(c_this,stub) + if "opendir_cbk" in funcs: + @OpOpendir.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, fd, + xdata, s=self): + return s.opendir_cbk(frame, cookie, this, + op_ret, op_errno, fd, + xdata) + self.stub_refs["opendir_cbk"] = stub + dl.set_opendir_cbk(c_this,stub) + if "readdir_fop" in funcs: + @OpReaddir.fop_type + def stub (frame, this, fd, size, offset, xdata, s=self): + return s.readdir_fop (frame, this, fd, size, + offset, xdata) + self.stub_refs["readdir_fop"] = stub + dl.set_readdir_fop(c_this,stub) + if "readdir_cbk" in funcs: + @OpReaddir.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + entries, xdata, s=self): + return s.readdir_cbk(frame, cookie, this, + op_ret, op_errno, entries, + xdata) + self.stub_refs["readdir_cbk"] = stub + dl.set_readdir_cbk(c_this,stub) + if "readdirp_fop" in funcs: + @OpReaddirp.fop_type + def stub (frame, this, fd, size, offset, xdata, s=self): + return s.readdirp_fop (frame, this, fd, size, + offset, xdata) + self.stub_refs["readdirp_fop"] = stub + dl.set_readdirp_fop(c_this,stub) + if "readdirp_cbk" in funcs: + @OpReaddirp.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + entries, xdata, s=self): + return s.readdirp_cbk (frame, cookie, this, + op_ret, op_errno, + entries, xdata) + self.stub_refs["readdirp_cbk"] = stub + dl.set_readdirp_cbk(c_this,stub) + if "stat_fop" in funcs: + @OpStat.fop_type + def stub (frame, this, loc, xdata, s=self): + return s.stat_fop (frame, this, loc, xdata) + self.stub_refs["stat_fop"] = stub + dl.set_stat_fop(c_this,stub) + if "stat_cbk" in funcs: + @OpStat.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, buf, + xdata, s=self): + return s.stat_cbk(frame, cookie, this, op_ret, + op_errno, buf, xdata) + self.stub_refs["stat_cbk"] = stub + dl.set_stat_cbk(c_this,stub) + if "fstat_fop" in funcs: + @OpFstat.fop_type + def stub (frame, this, fd, xdata, s=self): + return s.fstat_fop (frame, this, fd, xdata) + self.stub_refs["fstat_fop"] = stub + dl.set_fstat_fop(c_this,stub) + if "fstat_cbk" in funcs: + @OpFstat.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, buf, + xdata, s=self): + return s.fstat_cbk(frame, cookie, this, op_ret, + op_errno, buf, xdata) + self.stub_refs["fstat_cbk"] = stub + dl.set_fstat_cbk(c_this,stub) + if "statfs_fop" in funcs: + @OpStatfs.fop_type + def stub (frame, this, loc, xdata, s=self): + return s.statfs_fop (frame, this, loc, xdata) + self.stub_refs["statfs_fop"] = stub + dl.set_statfs_fop(c_this,stub) + if "statfs_cbk" in funcs: + @OpStatfs.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, buf, + xdata, s=self): + return s.statfs_cbk (frame, cookie, this, + op_ret, op_errno, buf, + xdata) + self.stub_refs["statfs_cbk"] = stub + dl.set_statfs_cbk(c_this,stub) + if "setxattr_fop" in funcs: + @OpSetxattr.fop_type + def stub (frame, this, loc, dictionary, flags, xdata, + s=self): + return s.setxattr_fop (frame, this, loc, + dictionary, flags, + xdata) + self.stub_refs["setxattr_fop"] = stub + dl.set_setxattr_fop(c_this,stub) + if "setxattr_cbk" in funcs: + @OpSetxattr.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, xdata, + s=self): + return s.setxattr_cbk(frame, cookie, this, + op_ret, op_errno, xdata) + self.stub_refs["setxattr_cbk"] = stub + dl.set_setxattr_cbk(c_this,stub) + if "getxattr_fop" in funcs: + @OpGetxattr.fop_type + def stub (frame, this, loc, name, xdata, s=self): + return s.getxattr_fop (frame, this, loc, name, + xdata) + self.stub_refs["getxattr_fop"] = stub + dl.set_getxattr_fop(c_this,stub) + if "getxattr_cbk" in funcs: + @OpGetxattr.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + dictionary, xdata, s=self): + return s.getxattr_cbk(frame, cookie, this, + op_ret, op_errno, + dictionary, xdata) + self.stub_refs["getxattr_cbk"] = stub + dl.set_getxattr_cbk(c_this,stub) + if "fsetxattr_fop" in funcs: + @OpFsetxattr.fop_type + def stub (frame, this, fd, dictionary, flags, xdata, + s=self): + return s.fsetxattr_fop (frame, this, fd, + dictionary, flags, + xdata) + self.stub_refs["fsetxattr_fop"] = stub + dl.set_fsetxattr_fop(c_this,stub) + if "fsetxattr_cbk" in funcs: + @OpFsetxattr.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, xdata, + s=self): + return s.fsetxattr_cbk(frame, cookie, this, + op_ret, op_errno, xdata) + self.stub_refs["fsetxattr_cbk"] = stub + dl.set_fsetxattr_cbk(c_this,stub) + if "fgetxattr_fop" in funcs: + @OpFgetxattr.fop_type + def stub (frame, this, fd, name, xdata, s=self): + return s.fgetxattr_fop (frame, this, fd, name, + xdata) + self.stub_refs["fgetxattr_fop"] = stub + dl.set_fgetxattr_fop(c_this,stub) + if "fgetxattr_cbk" in funcs: + @OpFgetxattr.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + dictionary, xdata, s=self): + return s.fgetxattr_cbk(frame, cookie, this, + op_ret, op_errno, + dictionary, xdata) + self.stub_refs["fgetxattr_cbk"] = stub + dl.set_fgetxattr_cbk(c_this,stub) + if "removexattr_fop" in funcs: + @OpRemovexattr.fop_type + def stub (frame, this, loc, name, xdata, s=self): + return s.removexattr_fop (frame, this, loc, + name, xdata) + self.stub_refs["removexattr_fop"] = stub + dl.set_removexattr_fop(c_this,stub) + if "removexattr_cbk" in funcs: + @OpRemovexattr.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + xdata, s=self): + return s.removexattr_cbk(frame, cookie, this, + op_ret, op_errno, + xdata) + self.stub_refs["removexattr_cbk"] = stub + dl.set_removexattr_cbk(c_this,stub) + if "fremovexattr_fop" in funcs: + @OpFremovexattr.fop_type + def stub (frame, this, fd, name, xdata, s=self): + return s.fremovexattr_fop (frame, this, fd, + name, xdata) + self.stub_refs["fremovexattr_fop"] = stub + dl.set_fremovexattr_fop(c_this,stub) + if "fremovexattr_cbk" in funcs: + @OpFremovexattr.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + xdata, s=self): + return s.fremovexattr_cbk(frame, cookie, this, + op_ret, op_errno, + xdata) + self.stub_refs["fremovexattr_cbk"] = stub + dl.set_fremovexattr_cbk(c_this,stub) + if "link_fop" in funcs: + @OpLink.fop_type + def stub (frame, this, oldloc, newloc, + xdata, s=self): + return s.link_fop (frame, this, oldloc, + newloc, xdata) + self.stub_refs["link_fop"] = stub + dl.set_link_fop(c_this,stub) + if "link_cbk" in funcs: + @OpLink.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + inode, buf, preparent, postparent, xdata, + s=self): + return s.link_cbk (frame, cookie, this, + op_ret, op_errno, inode, + buf, preparent, + postparent, xdata) + self.stub_refs["link_cbk"] = stub + dl.set_link_cbk(c_this,stub) + if "symlink_fop" in funcs: + @OpSymlink.fop_type + def stub (frame, this, linkname, loc, + umask, xdata, s=self): + return s.symlink_fop (frame, this, linkname, + loc, umask, xdata) + self.stub_refs["symlink_fop"] = stub + dl.set_symlink_fop(c_this,stub) + if "symlink_cbk" in funcs: + @OpSymlink.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + inode, buf, preparent, postparent, xdata, + s=self): + return s.symlink_cbk (frame, cookie, this, + op_ret, op_errno, inode, + buf, preparent, + postparent, xdata) + self.stub_refs["symlink_cbk"] = stub + dl.set_symlink_cbk(c_this,stub) + if "unlink_fop" in funcs: + @OpUnlink.fop_type + def stub (frame, this, loc, xflags, + xdata, s=self): + return s.unlink_fop (frame, this, loc, + xflags, xdata) + self.stub_refs["unlink_fop"] = stub + dl.set_unlink_fop(c_this,stub) + if "unlink_cbk" in funcs: + @OpUnlink.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + preparent, postparent, xdata, s=self): + return s.unlink_cbk (frame, cookie, this, + op_ret, op_errno, + preparent, postparent, + xdata) + self.stub_refs["unlink_cbk"] = stub + dl.set_unlink_cbk(c_this,stub) + if "readlink_fop" in funcs: + @OpReadlink.fop_type + def stub (frame, this, loc, size, + xdata, s=self): + return s.readlink_fop (frame, this, loc, + size, xdata) + self.stub_refs["readlink_fop"] = stub + dl.set_readlink_fop(c_this,stub) + if "readlink_cbk" in funcs: + @OpReadlink.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + path, buf, xdata, s=self): + return s.readlink_cbk (frame, cookie, this, + op_ret, op_errno, + path, buf, xdata) + self.stub_refs["readlink_cbk"] = stub + dl.set_readlink_cbk(c_this,stub) + if "mkdir_fop" in funcs: + @OpMkdir.fop_type + def stub (frame, this, loc, mode, umask, xdata, + s=self): + return s.mkdir_fop (frame, this, loc, mode, + umask, xdata) + self.stub_refs["mkdir_fop"] = stub + dl.set_mkdir_fop(c_this,stub) + if "mkdir_cbk" in funcs: + @OpMkdir.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, inode, + buf, preparent, postparent, xdata, s=self): + return s.mkdir_cbk (frame, cookie, this, + op_ret, op_errno, inode, + buf, preparent, + postparent, xdata) + self.stub_refs["mkdir_cbk"] = stub + dl.set_mkdir_cbk(c_this,stub) + if "rmdir_fop" in funcs: + @OpRmdir.fop_type + def stub (frame, this, loc, xflags, + xdata, s=self): + return s.rmdir_fop (frame, this, loc, + xflags, xdata) + self.stub_refs["rmdir_fop"] = stub + dl.set_rmdir_fop(c_this,stub) + if "rmdir_cbk" in funcs: + @OpRmdir.cbk_type + def stub (frame, cookie, this, op_ret, op_errno, + preparent, postparent, xdata, s=self): + return s.rmdir_cbk (frame, cookie, this, + op_ret, op_errno, + preparent, postparent, + xdata) + self.stub_refs["rmdir_cbk"] = stub + dl.set_rmdir_cbk(c_this,stub) diff --git a/xlators/features/glupy/src/gluster.py b/xlators/features/glupy/src/gluster.py deleted file mode 100644 index a5daa77d3..000000000 --- a/xlators/features/glupy/src/gluster.py +++ /dev/null @@ -1,841 +0,0 @@ -import sys -from ctypes import * - -dl = CDLL("",RTLD_GLOBAL) - - -class call_frame_t (Structure): - pass - -class dev_t (Structure): - pass - - -class dict_t (Structure): - pass - - -class gf_dirent_t (Structure): - pass - - -class iobref_t (Structure): - pass - - -class iovec_t (Structure): - pass - - -class list_head (Structure): - pass - -list_head._fields_ = [ - ("next", POINTER(list_head)), - ("prev", POINTER(list_head)) - ] - - -class rwxperm_t (Structure): - _fields_ = [ - ("read", c_uint8, 1), - ("write", c_uint8, 1), - ("execn", c_uint8, 1) - ] - - -class statvfs_t (Structure): - pass - - -class xlator_t (Structure): - pass - - -class ia_prot_t (Structure): - _fields_ = [ - ("suid", c_uint8, 1), - ("sgid", c_uint8, 1), - ("sticky", c_uint8, 1), - ("owner", rwxperm_t), - ("group", rwxperm_t), - ("other", rwxperm_t) - ] - -# For checking file type. -(IA_INVAL, IA_IFREG, IA_IFDIR, IA_IFLNK, IA_IFBLK, IA_IFCHR, IA_IFIFO, - IA_IFSOCK) = xrange(8) - - -class iatt_t (Structure): - _fields_ = [ - ("ia_no", c_uint64), - ("ia_gfid", c_ubyte * 16), - ("ia_dev", c_uint64), - ("ia_type", c_uint), - ("ia_prot", ia_prot_t), - ("ia_nlink", c_uint32), - ("ia_uid", c_uint32), - ("ia_gid", c_uint32), - ("ia_rdev", c_uint64), - ("ia_size", c_uint64), - ("ia_blksize", c_uint32), - ("ia_blocks", c_uint64), - ("ia_atime", c_uint32 ), - ("ia_atime_nsec", c_uint32), - ("ia_mtime", c_uint32), - ("ia_mtime_nsec", c_uint32), - ("ia_ctime", c_uint32), - ("ia_ctime_nsec", c_uint32) - ] - - -class mem_pool (Structure): - _fields_ = [ - ("list", list_head), - ("hot_count", c_int), - ("cold_count", c_int), - ("lock", c_void_p), - ("padded_sizeof_type", c_ulong), - ("pool", c_void_p), - ("pool_end", c_void_p), - ("real_sizeof_type", c_int), - ("alloc_count", c_uint64), - ("pool_misses", c_uint64), - ("max_alloc", c_int), - ("curr_stdalloc", c_int), - ("max_stdalloc", c_int), - ("name", c_char_p), - ("global_list", list_head) - ] - - -class U_ctx_key_inode (Union): - _fields_ = [ - ("key", c_uint64), - ("xl_key", POINTER(xlator_t)) - ] - - -class U_ctx_value1 (Union): - _fields_ = [ - ("value1", c_uint64), - ("ptr1", c_void_p) - ] - - -class U_ctx_value2 (Union): - _fields_ = [ - ("value2", c_uint64), - ("ptr2", c_void_p) - ] - -class inode_ctx (Structure): - _anonymous_ = ("u_key","u_value1","u_value2",) - _fields_ = [ - ("u_key", U_ctx_key_inode), - ("u_value1", U_ctx_value1), - ("u_value2", U_ctx_value2) - ] - -class inode_t (Structure): - pass - -class inode_table_t (Structure): - _fields_ = [ - ("lock", c_void_p), - ("hashsize", c_size_t), - ("name", c_char_p), - ("root", POINTER(inode_t)), - ("xl", POINTER(xlator_t)), - ("lru_limit", c_uint32), - ("inode_hash", POINTER(list_head)), - ("name_hash", POINTER(list_head)), - ("active", list_head), - ("active_size", c_uint32), - ("lru", list_head), - ("lru_size", c_uint32), - ("purge", list_head), - ("purge_size", c_uint32), - ("inode_pool", POINTER(mem_pool)), - ("dentry_pool", POINTER(mem_pool)), - ("fd_mem_pool", POINTER(mem_pool)) - ] - -inode_t._fields_ = [ - ("table", POINTER(inode_table_t)), - ("gfid", c_ubyte * 16), - ("lock", c_void_p), - ("nlookup", c_uint64), - ("fd_count", c_uint32), - ("ref", c_uint32), - ("ia_type", c_uint), - ("fd_list", list_head), - ("dentry_list", list_head), - ("hashv", list_head), - ("listv", list_head), - ("ctx", POINTER(inode_ctx)) - ] - - - -class U_ctx_key_fd (Union): - _fields_ = [ - ("key", c_uint64), - ("xl_key", c_void_p) - ] - -class fd_lk_ctx (Structure): - _fields_ = [ - ("lk_list", list_head), - ("ref", c_int), - ("lock", c_void_p) - ] - -class fd_ctx (Structure): - _anonymous_ = ("u_key","u_value1") - _fields_ = [ - ("u_key", U_ctx_key_fd), - ("u_value1", U_ctx_value1) - ] - -class fd_t (Structure): - _fields_ = [ - ("pid", c_uint64), - ("flags", c_int32), - ("refcount", c_int32), - ("inode_list", list_head), - ("inode", POINTER(inode_t)), - ("lock", c_void_p), - ("ctx", POINTER(fd_ctx)), - ("xl_count", c_int), - ("lk_ctx", POINTER(fd_lk_ctx)), - ("anonymous", c_uint) - ] - -class loc_t (Structure): - _fields_ = [ - ("path", c_char_p), - ("name", c_char_p), - ("inode", POINTER(inode_t)), - ("parent", POINTER(inode_t)), - ("gfid", c_ubyte * 16), - ("pargfid", c_ubyte * 16), - ] - - - -def _init_op (a_class, fop, cbk, wind, unwind): - # Decorators, used by translators. We could pass the signatures as - # parameters, but it's actually kind of nice to keep them around for - # inspection. - a_class.fop_type = apply(CFUNCTYPE,a_class.fop_sig) - a_class.cbk_type = apply(CFUNCTYPE,a_class.cbk_sig) - # Dispatch-function registration. - fop.restype = None - fop.argtypes = [ c_long, a_class.fop_type ] - # Callback-function registration. - cbk.restype = None - cbk.argtypes = [ c_long, a_class.cbk_type ] - # STACK_WIND function. - wind.restype = None - wind.argtypes = list(a_class.fop_sig[1:]) - # STACK_UNWIND function. - unwind.restype = None - unwind.argtypes = list(a_class.cbk_sig[1:]) - -class OpLookup: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(inode_t), POINTER(iatt_t), - POINTER(dict_t), POINTER(iatt_t)) -_init_op (OpLookup, dl.set_lookup_fop, dl.set_lookup_cbk, - dl.wind_lookup, dl.unwind_lookup) - -class OpCreate: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_int, c_uint, c_uint, POINTER(fd_t), - POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(fd_t), POINTER(inode_t), - POINTER(iatt_t), POINTER(iatt_t), POINTER(iatt_t), - POINTER(dict_t)) -_init_op (OpCreate, dl.set_create_fop, dl.set_create_cbk, - dl.wind_create, dl.unwind_create) - -class OpOpen: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_int, POINTER(fd_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(fd_t), POINTER(dict_t)) -_init_op (OpOpen, dl.set_open_fop, dl.set_open_cbk, - dl.wind_open, dl.unwind_open) - -class OpReadv: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), c_size_t, c_long, c_uint32, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(iovec_t), c_int, POINTER(iatt_t), - POINTER(iobref_t), POINTER(dict_t)) -_init_op (OpReadv, dl.set_readv_fop, dl.set_readv_cbk, - dl.wind_readv, dl.unwind_readv) -class OpWritev: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), POINTER(iovec_t), c_int, c_long, c_uint32, - POINTER(iobref_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(iatt_t), POINTER(iatt_t), - POINTER(dict_t)) -_init_op (OpWritev, dl.set_writev_fop, dl.set_writev_cbk, - dl.wind_writev, dl.unwind_writev) - -class OpOpendir: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), POINTER(fd_t) ,POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(fd_t), POINTER(dict_t)) -_init_op (OpOpendir, dl.set_opendir_fop, dl.set_opendir_cbk, - dl.wind_opendir, dl.unwind_opendir) - -class OpReaddir: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), c_size_t, c_long, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(gf_dirent_t), POINTER(dict_t)) -_init_op (OpReaddir, dl.set_readdir_fop, dl.set_readdir_cbk, - dl.wind_readdir, dl.unwind_readdir) - -class OpReaddirp: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), c_size_t, c_long, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(gf_dirent_t), POINTER(dict_t)) -_init_op (OpReaddirp, dl.set_readdirp_fop, dl.set_readdirp_cbk, - dl.wind_readdirp, dl.unwind_readdirp) - -class OpStat: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(iatt_t), POINTER(dict_t)) -_init_op (OpStat, dl.set_stat_fop, dl.set_stat_cbk, - dl.wind_stat, dl.unwind_stat) - -class OpFstat: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(iatt_t), POINTER(dict_t)) -_init_op (OpFstat, dl.set_fstat_fop, dl.set_fstat_cbk, - dl.wind_fstat, dl.unwind_fstat) - -class OpStatfs: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(statvfs_t), POINTER(dict_t)) -_init_op (OpStatfs, dl.set_statfs_fop, dl.set_statfs_cbk, - dl.wind_statfs, dl.unwind_statfs) - - -class OpSetxattr: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), POINTER(dict_t), c_int32, - POINTER (dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(dict_t)) -_init_op (OpSetxattr, dl.set_setxattr_fop, dl.set_setxattr_cbk, - dl.wind_setxattr, dl.unwind_setxattr) - -class OpGetxattr: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_char_p, POINTER (dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(dict_t), POINTER(dict_t)) -_init_op (OpGetxattr, dl.set_getxattr_fop, dl.set_getxattr_cbk, - dl.wind_getxattr, dl.unwind_getxattr) - -class OpFsetxattr: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), POINTER(dict_t), c_int32, - POINTER (dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(dict_t)) -_init_op (OpFsetxattr, dl.set_fsetxattr_fop, dl.set_fsetxattr_cbk, - dl.wind_fsetxattr, dl.unwind_fsetxattr) - -class OpFgetxattr: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), c_char_p, POINTER (dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(dict_t), POINTER(dict_t)) -_init_op (OpFgetxattr, dl.set_fgetxattr_fop, dl.set_fgetxattr_cbk, - dl.wind_fgetxattr, dl.unwind_fgetxattr) - -class OpRemovexattr: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_char_p, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(dict_t)) -_init_op (OpRemovexattr, dl.set_removexattr_fop, dl.set_removexattr_cbk, - dl.wind_removexattr, dl.unwind_removexattr) - - -class OpFremovexattr: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(fd_t), c_char_p, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(dict_t)) -_init_op (OpFremovexattr, dl.set_fremovexattr_fop, dl.set_fremovexattr_cbk, - dl.wind_fremovexattr, dl.unwind_fremovexattr) - -class OpLink: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), POINTER(loc_t), POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(inode_t), POINTER(iatt_t), - POINTER(iatt_t), POINTER(iatt_t), POINTER(dict_t)) -_init_op (OpLink, dl.set_link_fop, dl.set_link_cbk, - dl.wind_link, dl.unwind_link) - -class OpSymlink: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - c_char_p, POINTER(loc_t), c_uint, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(inode_t), POINTER(iatt_t), - POINTER(iatt_t), POINTER(iatt_t), POINTER(dict_t)) -_init_op (OpSymlink, dl.set_symlink_fop, dl.set_symlink_cbk, - dl.wind_symlink, dl.unwind_symlink) - -class OpUnlink: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_int, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(iatt_t), POINTER(iatt_t), - POINTER(dict_t)) -_init_op (OpUnlink, dl.set_unlink_fop, dl.set_unlink_cbk, - dl.wind_unlink, dl.unwind_unlink) - -class OpReadlink: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_size_t, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, c_char_p, POINTER(iatt_t), POINTER(dict_t)) -_init_op (OpReadlink, dl.set_readlink_fop, dl.set_readlink_cbk, - dl.wind_readlink, dl.unwind_readlink) - -class OpMkdir: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_uint, c_uint, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(inode_t), POINTER(iatt_t), - POINTER(iatt_t), POINTER(iatt_t), POINTER(dict_t)) -_init_op (OpMkdir, dl.set_mkdir_fop, dl.set_mkdir_cbk, - dl.wind_mkdir, dl.unwind_mkdir) - -class OpRmdir: - fop_sig = (c_int, POINTER(call_frame_t), POINTER(xlator_t), - POINTER(loc_t), c_int, POINTER(dict_t)) - cbk_sig = (c_int, POINTER(call_frame_t), c_long, POINTER(xlator_t), - c_int, c_int, POINTER(iatt_t), POINTER(iatt_t), - POINTER(dict_t)) -_init_op (OpRmdir, dl.set_rmdir_fop, dl.set_rmdir_cbk, - dl.wind_rmdir, dl.unwind_rmdir) - - -class Translator: - def __init__ (self, c_this): - # This is only here to keep references to the stubs we create, - # because ctypes doesn't and glupy.so can't because it doesn't - # get a pointer to the actual Python object. It's a dictionary - # instead of a list in case we ever allow changing fops/cbks - # after initialization and need to look them up. - self.stub_refs = {} - funcs = dir(self.__class__) - if "lookup_fop" in funcs: - @OpLookup.fop_type - def stub (frame, this, loc, xdata, s=self): - return s.lookup_fop (frame, this, loc, xdata) - self.stub_refs["lookup_fop"] = stub - dl.set_lookup_fop(c_this,stub) - if "lookup_cbk" in funcs: - @OpLookup.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, inode, - buf, xdata, postparent, s=self): - return s.lookup_cbk(frame, cookie, this, op_ret, - op_errno, inode, buf, xdata, - postparent) - self.stub_refs["lookup_cbk"] = stub - dl.set_lookup_cbk(c_this,stub) - if "create_fop" in funcs: - @OpCreate.fop_type - def stub (frame, this, loc, flags, mode, umask, fd, - xdata, s=self): - return s.create_fop (frame, this, loc, flags, - mode, umask, fd, xdata) - self.stub_refs["create_fop"] = stub - dl.set_create_fop(c_this,stub) - if "create_cbk" in funcs: - @OpCreate.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, fd, - inode, buf, preparent, postparent, xdata, - s=self): - return s.create_cbk (frame, cookie, this, - op_ret, op_errno, fd, - inode, buf, preparent, - postparent, xdata) - self.stub_refs["create_cbk"] = stub - dl.set_create_cbk(c_this,stub) - if "open_fop" in funcs: - @OpOpen.fop_type - def stub (frame, this, loc, flags, fd, - xdata, s=self): - return s.open_fop (frame, this, loc, flags, - fd, xdata) - self.stub_refs["open_fop"] = stub - dl.set_open_fop(c_this,stub) - if "open_cbk" in funcs: - @OpOpen.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, fd, - xdata, s=self): - return s.open_cbk (frame, cookie, this, - op_ret, op_errno, fd, - xdata) - self.stub_refs["open_cbk"] = stub - dl.set_open_cbk(c_this,stub) - if "readv_fop" in funcs: - @OpReadv.fop_type - def stub (frame, this, fd, size, offset, flags, - xdata, s=self): - return s.readv_fop (frame, this, fd, size, - offset, flags, xdata) - self.stub_refs["readv_fop"] = stub - dl.set_readv_fop(c_this,stub) - if "readv_cbk" in funcs: - @OpReadv.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - vector, count, stbuf, iobref, xdata, - s=self): - return s.readv_cbk (frame, cookie, this, - op_ret, op_errno, vector, - count, stbuf, iobref, - xdata) - self.stub_refs["readv_cbk"] = stub - dl.set_readv_cbk(c_this,stub) - if "writev_fop" in funcs: - @OpWritev.fop_type - def stub (frame, this, fd, vector, count, - offset, flags, iobref, xdata, s=self): - return s.writev_fop (frame, this, fd, vector, - count, offset, flags, - iobref, xdata) - self.stub_refs["writev_fop"] = stub - dl.set_writev_fop(c_this,stub) - if "writev_cbk" in funcs: - @OpWritev.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - prebuf, postbuf, xdata, s=self): - return s.writev_cbk (frame, cookie, this, - op_ret, op_errno, prebuf, - postbuf, xdata) - self.stub_refs["writev_cbk"] = stub - dl.set_writev_cbk(c_this,stub) - if "opendir_fop" in funcs: - @OpOpendir.fop_type - def stub (frame, this, loc, fd, xdata, s=self): - return s.opendir_fop (frame, this, loc, fd, - xdata) - self.stub_refs["opendir_fop"] = stub - dl.set_opendir_fop(c_this,stub) - if "opendir_cbk" in funcs: - @OpOpendir.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, fd, - xdata, s=self): - return s.opendir_cbk(frame, cookie, this, - op_ret, op_errno, fd, - xdata) - self.stub_refs["opendir_cbk"] = stub - dl.set_opendir_cbk(c_this,stub) - if "readdir_fop" in funcs: - @OpReaddir.fop_type - def stub (frame, this, fd, size, offset, xdata, s=self): - return s.readdir_fop (frame, this, fd, size, - offset, xdata) - self.stub_refs["readdir_fop"] = stub - dl.set_readdir_fop(c_this,stub) - if "readdir_cbk" in funcs: - @OpReaddir.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - entries, xdata, s=self): - return s.readdir_cbk(frame, cookie, this, - op_ret, op_errno, entries, - xdata) - self.stub_refs["readdir_cbk"] = stub - dl.set_readdir_cbk(c_this,stub) - if "readdirp_fop" in funcs: - @OpReaddirp.fop_type - def stub (frame, this, fd, size, offset, xdata, s=self): - return s.readdirp_fop (frame, this, fd, size, - offset, xdata) - self.stub_refs["readdirp_fop"] = stub - dl.set_readdirp_fop(c_this,stub) - if "readdirp_cbk" in funcs: - @OpReaddirp.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - entries, xdata, s=self): - return s.readdirp_cbk (frame, cookie, this, - op_ret, op_errno, - entries, xdata) - self.stub_refs["readdirp_cbk"] = stub - dl.set_readdirp_cbk(c_this,stub) - if "stat_fop" in funcs: - @OpStat.fop_type - def stub (frame, this, loc, xdata, s=self): - return s.stat_fop (frame, this, loc, xdata) - self.stub_refs["stat_fop"] = stub - dl.set_stat_fop(c_this,stub) - if "stat_cbk" in funcs: - @OpStat.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, buf, - xdata, s=self): - return s.stat_cbk(frame, cookie, this, op_ret, - op_errno, buf, xdata) - self.stub_refs["stat_cbk"] = stub - dl.set_stat_cbk(c_this,stub) - if "fstat_fop" in funcs: - @OpFstat.fop_type - def stub (frame, this, fd, xdata, s=self): - return s.fstat_fop (frame, this, fd, xdata) - self.stub_refs["fstat_fop"] = stub - dl.set_fstat_fop(c_this,stub) - if "fstat_cbk" in funcs: - @OpFstat.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, buf, - xdata, s=self): - return s.fstat_cbk(frame, cookie, this, op_ret, - op_errno, buf, xdata) - self.stub_refs["fstat_cbk"] = stub - dl.set_fstat_cbk(c_this,stub) - if "statfs_fop" in funcs: - @OpStatfs.fop_type - def stub (frame, this, loc, xdata, s=self): - return s.statfs_fop (frame, this, loc, xdata) - self.stub_refs["statfs_fop"] = stub - dl.set_statfs_fop(c_this,stub) - if "statfs_cbk" in funcs: - @OpStatfs.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, buf, - xdata, s=self): - return s.statfs_cbk (frame, cookie, this, - op_ret, op_errno, buf, - xdata) - self.stub_refs["statfs_cbk"] = stub - dl.set_statfs_cbk(c_this,stub) - if "setxattr_fop" in funcs: - @OpSetxattr.fop_type - def stub (frame, this, loc, dictionary, flags, xdata, - s=self): - return s.setxattr_fop (frame, this, loc, - dictionary, flags, - xdata) - self.stub_refs["setxattr_fop"] = stub - dl.set_setxattr_fop(c_this,stub) - if "setxattr_cbk" in funcs: - @OpSetxattr.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, xdata, - s=self): - return s.setxattr_cbk(frame, cookie, this, - op_ret, op_errno, xdata) - self.stub_refs["setxattr_cbk"] = stub - dl.set_setxattr_cbk(c_this,stub) - if "getxattr_fop" in funcs: - @OpGetxattr.fop_type - def stub (frame, this, loc, name, xdata, s=self): - return s.getxattr_fop (frame, this, loc, name, - xdata) - self.stub_refs["getxattr_fop"] = stub - dl.set_getxattr_fop(c_this,stub) - if "getxattr_cbk" in funcs: - @OpGetxattr.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - dictionary, xdata, s=self): - return s.getxattr_cbk(frame, cookie, this, - op_ret, op_errno, - dictionary, xdata) - self.stub_refs["getxattr_cbk"] = stub - dl.set_getxattr_cbk(c_this,stub) - if "fsetxattr_fop" in funcs: - @OpFsetxattr.fop_type - def stub (frame, this, fd, dictionary, flags, xdata, - s=self): - return s.fsetxattr_fop (frame, this, fd, - dictionary, flags, - xdata) - self.stub_refs["fsetxattr_fop"] = stub - dl.set_fsetxattr_fop(c_this,stub) - if "fsetxattr_cbk" in funcs: - @OpFsetxattr.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, xdata, - s=self): - return s.fsetxattr_cbk(frame, cookie, this, - op_ret, op_errno, xdata) - self.stub_refs["fsetxattr_cbk"] = stub - dl.set_fsetxattr_cbk(c_this,stub) - if "fgetxattr_fop" in funcs: - @OpFgetxattr.fop_type - def stub (frame, this, fd, name, xdata, s=self): - return s.fgetxattr_fop (frame, this, fd, name, - xdata) - self.stub_refs["fgetxattr_fop"] = stub - dl.set_fgetxattr_fop(c_this,stub) - if "fgetxattr_cbk" in funcs: - @OpFgetxattr.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - dictionary, xdata, s=self): - return s.fgetxattr_cbk(frame, cookie, this, - op_ret, op_errno, - dictionary, xdata) - self.stub_refs["fgetxattr_cbk"] = stub - dl.set_fgetxattr_cbk(c_this,stub) - if "removexattr_fop" in funcs: - @OpRemovexattr.fop_type - def stub (frame, this, loc, name, xdata, s=self): - return s.removexattr_fop (frame, this, loc, - name, xdata) - self.stub_refs["removexattr_fop"] = stub - dl.set_removexattr_fop(c_this,stub) - if "removexattr_cbk" in funcs: - @OpRemovexattr.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - xdata, s=self): - return s.removexattr_cbk(frame, cookie, this, - op_ret, op_errno, - xdata) - self.stub_refs["removexattr_cbk"] = stub - dl.set_removexattr_cbk(c_this,stub) - if "fremovexattr_fop" in funcs: - @OpFremovexattr.fop_type - def stub (frame, this, fd, name, xdata, s=self): - return s.fremovexattr_fop (frame, this, fd, - name, xdata) - self.stub_refs["fremovexattr_fop"] = stub - dl.set_fremovexattr_fop(c_this,stub) - if "fremovexattr_cbk" in funcs: - @OpFremovexattr.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - xdata, s=self): - return s.fremovexattr_cbk(frame, cookie, this, - op_ret, op_errno, - xdata) - self.stub_refs["fremovexattr_cbk"] = stub - dl.set_fremovexattr_cbk(c_this,stub) - if "link_fop" in funcs: - @OpLink.fop_type - def stub (frame, this, oldloc, newloc, - xdata, s=self): - return s.link_fop (frame, this, oldloc, - newloc, xdata) - self.stub_refs["link_fop"] = stub - dl.set_link_fop(c_this,stub) - if "link_cbk" in funcs: - @OpLink.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - inode, buf, preparent, postparent, xdata, - s=self): - return s.link_cbk (frame, cookie, this, - op_ret, op_errno, inode, - buf, preparent, - postparent, xdata) - self.stub_refs["link_cbk"] = stub - dl.set_link_cbk(c_this,stub) - if "symlink_fop" in funcs: - @OpSymlink.fop_type - def stub (frame, this, linkname, loc, - umask, xdata, s=self): - return s.symlink_fop (frame, this, linkname, - loc, umask, xdata) - self.stub_refs["symlink_fop"] = stub - dl.set_symlink_fop(c_this,stub) - if "symlink_cbk" in funcs: - @OpSymlink.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - inode, buf, preparent, postparent, xdata, - s=self): - return s.symlink_cbk (frame, cookie, this, - op_ret, op_errno, inode, - buf, preparent, - postparent, xdata) - self.stub_refs["symlink_cbk"] = stub - dl.set_symlink_cbk(c_this,stub) - if "unlink_fop" in funcs: - @OpUnlink.fop_type - def stub (frame, this, loc, xflags, - xdata, s=self): - return s.unlink_fop (frame, this, loc, - xflags, xdata) - self.stub_refs["unlink_fop"] = stub - dl.set_unlink_fop(c_this,stub) - if "unlink_cbk" in funcs: - @OpUnlink.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - preparent, postparent, xdata, s=self): - return s.unlink_cbk (frame, cookie, this, - op_ret, op_errno, - preparent, postparent, - xdata) - self.stub_refs["unlink_cbk"] = stub - dl.set_unlink_cbk(c_this,stub) - if "readlink_fop" in funcs: - @OpReadlink.fop_type - def stub (frame, this, loc, size, - xdata, s=self): - return s.readlink_fop (frame, this, loc, - size, xdata) - self.stub_refs["readlink_fop"] = stub - dl.set_readlink_fop(c_this,stub) - if "readlink_cbk" in funcs: - @OpReadlink.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - path, buf, xdata, s=self): - return s.readlink_cbk (frame, cookie, this, - op_ret, op_errno, - path, buf, xdata) - self.stub_refs["readlink_cbk"] = stub - dl.set_readlink_cbk(c_this,stub) - if "mkdir_fop" in funcs: - @OpMkdir.fop_type - def stub (frame, this, loc, mode, umask, xdata, - s=self): - return s.mkdir_fop (frame, this, loc, mode, - umask, xdata) - self.stub_refs["mkdir_fop"] = stub - dl.set_mkdir_fop(c_this,stub) - if "mkdir_cbk" in funcs: - @OpMkdir.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, inode, - buf, preparent, postparent, xdata, s=self): - return s.mkdir_cbk (frame, cookie, this, - op_ret, op_errno, inode, - buf, preparent, - postparent, xdata) - self.stub_refs["mkdir_cbk"] = stub - dl.set_mkdir_cbk(c_this,stub) - if "rmdir_fop" in funcs: - @OpRmdir.fop_type - def stub (frame, this, loc, xflags, - xdata, s=self): - return s.rmdir_fop (frame, this, loc, - xflags, xdata) - self.stub_refs["rmdir_fop"] = stub - dl.set_rmdir_fop(c_this,stub) - if "rmdir_cbk" in funcs: - @OpRmdir.cbk_type - def stub (frame, cookie, this, op_ret, op_errno, - preparent, postparent, xdata, s=self): - return s.rmdir_cbk (frame, cookie, this, - op_ret, op_errno, - preparent, postparent, - xdata) - self.stub_refs["rmdir_cbk"] = stub - dl.set_rmdir_cbk(c_this,stub) diff --git a/xlators/features/glupy/src/helloworld.py b/xlators/features/glupy/src/helloworld.py deleted file mode 100644 index 8fe403711..000000000 --- a/xlators/features/glupy/src/helloworld.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys -from gluster import * - -class xlator (Translator): - - def __init__(self, c_this): - Translator.__init__(self, c_this) - - def lookup_fop(self, frame, this, loc, xdata): - print "Python xlator: Hello!" - dl.wind_lookup(frame, POINTER(xlator_t)(), loc, xdata) - return 0 - - def lookup_cbk(self, frame, cookie, this, op_ret, op_errno, inode, buf, - xdata, postparent): - print "Python xlator: Hello again!" - dl.unwind_lookup(frame, cookie, this, op_ret, op_errno, inode, buf, - xdata, postparent) - return 0 diff --git a/xlators/features/glupy/src/negative.py b/xlators/features/glupy/src/negative.py deleted file mode 100644 index 1023602b9..000000000 --- a/xlators/features/glupy/src/negative.py +++ /dev/null @@ -1,92 +0,0 @@ -import sys -from uuid import UUID -from gluster import * - -# Negative-lookup-caching example. If a file wasn't there the last time we -# looked, it's probably still not there. This translator keeps track of -# those failed lookups for us, and returns ENOENT without needing to pass the -# call any further for repeated requests. - -# If we were doing this for real, we'd need separate caches for each xlator -# instance. The easiest way to do this would be to have xlator.__init__ -# "register" each instance in a module-global dict, with the key as the C -# translator address and the value as the xlator object itself. For testing -# and teaching, it's sufficient just to have one cache. The keys are parent -# GFIDs, and the entries are lists of names within that parent that we know -# don't exist. -cache = {} - -# TBD: we need a better way of handling per-request data (frame->local in C). -dl.get_id.restype = c_long -dl.get_id.argtypes = [ POINTER(call_frame_t) ] - -def uuid2str (gfid): - return str(UUID(''.join(map("{0:02x}".format, gfid)))) - -class xlator (Translator): - - def __init__ (self, c_this): - self.requests = {} - Translator.__init__(self,c_this) - - def lookup_fop (self, frame, this, loc, xdata): - pargfid = uuid2str(loc.contents.pargfid) - print "lookup FOP: %s:%s" % (pargfid, loc.contents.name) - # Check the cache. - if cache.has_key(pargfid): - if loc.contents.name in cache[pargfid]: - print "short-circuiting for %s:%s" % (pargfid, - loc.contents.name) - dl.unwind_lookup(frame,0,this,-1,2,None,None,None,None) - return 0 - key = dl.get_id(frame) - self.requests[key] = (pargfid, loc.contents.name[:]) - # TBD: get real child xl from init, pass it here - dl.wind_lookup(frame,POINTER(xlator_t)(),loc,xdata) - return 0 - - def lookup_cbk (self, frame, cookie, this, op_ret, op_errno, inode, buf, - xdata, postparent): - print "lookup CBK: %d (%d)" % (op_ret, op_errno) - key = dl.get_id(frame) - pargfid, name = self.requests[key] - # Update the cache. - if op_ret == 0: - print "found %s, removing from cache" % name - if cache.has_key(pargfid): - cache[pargfid].discard(name) - elif op_errno == 2: # ENOENT - print "failed to find %s, adding to cache" % name - if cache.has_key(pargfid): - cache[pargfid].add(name) - else: - cache[pargfid] = set([name]) - del self.requests[key] - dl.unwind_lookup(frame,cookie,this,op_ret,op_errno, - inode,buf,xdata,postparent) - return 0 - - def create_fop (self, frame, this, loc, flags, mode, umask, fd, xdata): - pargfid = uuid2str(loc.contents.pargfid) - print "create FOP: %s:%s" % (pargfid, loc.contents.name) - key = dl.get_id(frame) - self.requests[key] = (pargfid, loc.contents.name[:]) - # TBD: get real child xl from init, pass it here - dl.wind_create(frame,POINTER(xlator_t)(),loc,flags,mode,umask,fd,xdata) - return 0 - - def create_cbk (self, frame, cookie, this, op_ret, op_errno, fd, inode, - buf, preparent, postparent, xdata): - print "create CBK: %d (%d)" % (op_ret, op_errno) - key = dl.get_id(frame) - pargfid, name = self.requests[key] - # Update the cache. - if op_ret == 0: - print "created %s, removing from cache" % name - if cache.has_key(pargfid): - cache[pargfid].discard(name) - del self.requests[key] - dl.unwind_create(frame,cookie,this,op_ret,op_errno,fd,inode,buf, - preparent,postparent,xdata) - return 0 - diff --git a/xlators/features/glupy/src/setup.py.in b/xlators/features/glupy/src/setup.py.in new file mode 100644 index 000000000..1aea9875f --- /dev/null +++ b/xlators/features/glupy/src/setup.py.in @@ -0,0 +1,24 @@ +from distutils.core import setup + +DESC = """GlusterFS is a clustered file-system capable of scaling to +several petabytes. It aggregates various storage bricks over Infiniband +RDMA or TCP/IP interconnect into one large parallel network file system. +GlusterFS is one of the most sophisticated file systems in terms of +features and extensibility. It borrows a powerful concept called +Translators from GNU Hurd kernel. Much of the code in GlusterFS is in +user space and easily manageable. + +This package contains Glupy, the Python translator interface for GlusterFS.""" + +setup( + name='glusterfs-glupy', + version='@PACKAGE_VERSION@', + description='Glupy is the Python translator interface for GlusterFS', + long_description=DESC, + author='Gluster Community', + author_email='gluster-devel@nongnu.org', + license='LGPLv3', + url='http://gluster.org/', + package_dir={'gluster':''}, + packages=['gluster'] +) -- cgit From 00802b3a484499267af2e4474d75d3f75887ad07 Mon Sep 17 00:00:00 2001 From: Pranith Kumar K Date: Sun, 23 Mar 2014 08:01:15 +0530 Subject: features/compress: Add mem accounting support for compress Change-Id: I89a7a4cd64ef65ad3bab180d66797a62b4e1e195 BUG: 923540 Signed-off-by: Pranith Kumar K Reviewed-on: http://review.gluster.org/7320 Tested-by: Gluster Build System Reviewed-by: Prashanth Pai Reviewed-by: Vijay Bellur --- xlators/features/compress/src/cdc-mem-types.h | 1 + xlators/features/compress/src/cdc.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/xlators/features/compress/src/cdc-mem-types.h b/xlators/features/compress/src/cdc-mem-types.h index efa008059..ead2c70ba 100644 --- a/xlators/features/compress/src/cdc-mem-types.h +++ b/xlators/features/compress/src/cdc-mem-types.h @@ -17,6 +17,7 @@ enum gf_cdc_mem_types { gf_cdc_mt_priv_t = gf_common_mt_end + 1, gf_cdc_mt_vec_t = gf_common_mt_end + 2, gf_cdc_mt_gzip_trailer_t = gf_common_mt_end + 3, + gf_cdc_mt_end = gf_common_mt_end + 4, }; #endif diff --git a/xlators/features/compress/src/cdc.c b/xlators/features/compress/src/cdc.c index a334c7e06..67fc52505 100644 --- a/xlators/features/compress/src/cdc.c +++ b/xlators/features/compress/src/cdc.c @@ -189,6 +189,25 @@ cdc_writev (call_frame_t *frame, return 0; } +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + if (!this) + return ret; + + ret = xlator_mem_acct_init (this, gf_cdc_mt_end); + + if (ret != 0) { + gf_log(this->name, GF_LOG_ERROR, "Memory accounting init" + "failed"); + return ret; + } + + return ret; +} + int32_t init (xlator_t *this) { -- cgit From 21c282ef311d3d7385bba37ddb0a26fb12178409 Mon Sep 17 00:00:00 2001 From: Pranith Kumar K Date: Tue, 25 Mar 2014 11:07:31 +0530 Subject: cluster/afr: Sparse file self-heal canges - Fix boundary condition for offset - Honour data-self-heal-algorithm option - Added tests for sparse file self-healing Change-Id: I14bb1c9d04118a3df4072f962fc8f2f197391d95 BUG: 1080707 Signed-off-by: Pranith Kumar K Reviewed-on: http://review.gluster.org/7339 Tested-by: Gluster Build System Reviewed-by: Anand Avati --- tests/basic/afr/sparse-file-self-heal.t | 121 +++++++++++++++++++++++++++ tests/volume.rc | 10 ++- xlators/cluster/afr/src/afr-self-heal-data.c | 37 ++++++-- 3 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 tests/basic/afr/sparse-file-self-heal.t diff --git a/tests/basic/afr/sparse-file-self-heal.t b/tests/basic/afr/sparse-file-self-heal.t new file mode 100644 index 000000000..9b795c331 --- /dev/null +++ b/tests/basic/afr/sparse-file-self-heal.t @@ -0,0 +1,121 @@ +#!/bin/bash + +#This file checks if self-heal of files with holes is working properly or not +#bigger is 2M, big is 1M, small is anything less +. $(dirname $0)/../../include.rc +. $(dirname $0)/../../volume.rc + +cleanup; + +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/${V0}{0,1} +TEST $CLI volume set $V0 data-self-heal-algorithm full +TEST $CLI volume start $V0 + +TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 $M0 --attribute-timeout=0 --entry-timeout=0 +TEST dd if=/dev/urandom of=$M0/small count=1 bs=1M +TEST dd if=/dev/urandom of=$M0/bigger2big count=1 bs=2M +TEST dd if=/dev/urandom of=$M0/big2bigger count=1 bs=1M + +TEST kill_brick $V0 $H0 $B0/${V0}0 + +#File with >128k size hole +TEST truncate -s 1M $M0/big +big_md5sum=$(md5sum $M0/big | awk '{print $1}') + +#File with <128k hole +TEST truncate -s 0 $M0/small +TEST truncate -s 64k $M0/small +small_md5sum=$(md5sum $M0/small | awk '{print $1}') + +#Bigger file truncated to big size hole. +TEST truncate -s 0 $M0/bigger2big +TEST truncate -s 1M $M0/bigger2big +bigger2big_md5sum=$(md5sum $M0/bigger2big | awk '{print $1}') + +#Big file truncated to Bigger size hole +TEST truncate -s 2M $M0/big2bigger +big2bigger_md5sum=$(md5sum $M0/big2bigger | awk '{print $1}') + +$CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST gluster volume heal $V0 full +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +big_md5sum_0=$(md5sum $B0/${V0}0/big | awk '{print $1}') +small_md5sum_0=$(md5sum $B0/${V0}0/small | awk '{print $1}') +bigger2big_md5sum_0=$(md5sum $B0/${V0}0/bigger2big | awk '{print $1}') +big2bigger_md5sum_0=$(md5sum $B0/${V0}0/big2bigger | awk '{print $1}') + +EXPECT $big_md5sum echo $big_md5sum_0 +EXPECT $small_md5sum echo $small_md5sum_0 +EXPECT $big2bigger_md5sum echo $big2bigger_md5sum_0 +EXPECT $bigger2big_md5sum echo $bigger2big_md5sum_0 + + +EXPECT "1" has_holes $B0/${V0}0/big +#Because self-heal writes the final chunk hole should not be there for +#files < 128K +EXPECT "0" has_holes $B0/${V0}0/small +# Since source is smaller than sink, self-heal does blind copy so no holes will +# be present +EXPECT "0" has_holes $B0/${V0}0/bigger2big +EXPECT "1" has_holes $B0/${V0}0/big2bigger + +TEST rm -f $M0/* + +#check the same tests with diff self-heal +TEST $CLI volume set $V0 data-self-heal-algorithm diff + +TEST dd if=/dev/urandom of=$M0/small count=1 bs=1M +TEST dd if=/dev/urandom of=$M0/big2bigger count=1 bs=1M +TEST dd if=/dev/urandom of=$M0/bigger2big count=1 bs=2M + +TEST kill_brick $V0 $H0 $B0/${V0}0 + +#File with >128k size hole +TEST truncate -s 1M $M0/big +big_md5sum=$(md5sum $M0/big | awk '{print $1}') + +#File with <128k hole +TEST truncate -s 0 $M0/small +TEST truncate -s 64k $M0/small +small_md5sum=$(md5sum $M0/small | awk '{print $1}') + +#Bigger file truncated to big size hole +TEST truncate -s 0 $M0/bigger2big +TEST truncate -s 1M $M0/bigger2big +bigger2big_md5sum=$(md5sum $M0/bigger2big | awk '{print $1}') + +#Big file truncated to Bigger size hole +TEST truncate -s 2M $M0/big2bigger +big2bigger_md5sum=$(md5sum $M0/big2bigger | awk '{print $1}') + +$CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST gluster volume heal $V0 full +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +big_md5sum_0=$(md5sum $B0/${V0}0/big | awk '{print $1}') +small_md5sum_0=$(md5sum $B0/${V0}0/small | awk '{print $1}') +bigger2big_md5sum_0=$(md5sum $B0/${V0}0/bigger2big | awk '{print $1}') +big2bigger_md5sum_0=$(md5sum $B0/${V0}0/big2bigger | awk '{print $1}') + +EXPECT $big_md5sum echo $big_md5sum_0 +EXPECT $small_md5sum echo $small_md5sum_0 +EXPECT $big2bigger_md5sum echo $big2bigger_md5sum_0 +EXPECT $bigger2big_md5sum echo $bigger2big_md5sum_0 + +EXPECT "1" has_holes $B0/${V0}0/big +EXPECT "1" has_holes $B0/${V0}0/big2bigger +EXPECT "0" has_holes $B0/${V0}0/bigger2big +EXPECT "0" has_holes $B0/${V0}0/small + +cleanup diff --git a/tests/volume.rc b/tests/volume.rc index 9a06687cd..9e4843e06 100644 --- a/tests/volume.rc +++ b/tests/volume.rc @@ -300,5 +300,11 @@ function data_written_count { echo "$1" | grep "Data Written:$2bytes" | wc -l } - - +function has_holes { + if [ $((`stat -c '%b*%B-%s' -- $1`)) -lt 0 ]; + then + echo "1" + else + echo "0" + fi +} diff --git a/xlators/cluster/afr/src/afr-self-heal-data.c b/xlators/cluster/afr/src/afr-self-heal-data.c index c0385153f..c0548d995 100644 --- a/xlators/cluster/afr/src/afr-self-heal-data.c +++ b/xlators/cluster/afr/src/afr-self-heal-data.c @@ -159,7 +159,7 @@ __afr_selfheal_data_read_write (call_frame_t *frame, xlator_t *this, fd_t *fd, */ #define is_last_block(o,b,s) ((s >= o) && (s <= (o + b))) if (HAS_HOLES ((&replies[source].poststat)) && - offset > replies[i].poststat.ia_size && + offset >= replies[i].poststat.ia_size && !is_last_block (offset, size, replies[source].poststat.ia_size) && (iov_0filled (iovec, count) == 0)) @@ -267,6 +267,31 @@ afr_selfheal_data_restore_time (call_frame_t *frame, xlator_t *this, return 0; } +static int +afr_data_self_heal_type_get (afr_private_t *priv, unsigned char *healed_sinks, + int source, struct afr_reply *replies) +{ + int type = AFR_SELFHEAL_DATA_FULL; + int i = 0; + + if (priv->data_self_heal_algorithm == NULL) { + type = AFR_SELFHEAL_DATA_FULL; + for (i = 0; i < priv->child_count; i++) { + if (!healed_sinks[i] && i != source) + continue; + if (replies[i].poststat.ia_size) { + type = AFR_SELFHEAL_DATA_DIFF; + break; + } + } + } else if (strcmp (priv->data_self_heal_algorithm, "full") == 0) { + type = AFR_SELFHEAL_DATA_FULL; + } else if (strcmp (priv->data_self_heal_algorithm, "diff") == 0) { + type = AFR_SELFHEAL_DATA_DIFF; + } + return type; +} + static int afr_selfheal_data_do (call_frame_t *frame, xlator_t *this, fd_t *fd, int source, unsigned char *healed_sinks, @@ -296,14 +321,8 @@ afr_selfheal_data_do (call_frame_t *frame, xlator_t *this, fd_t *fd, "source=%d sinks=%s", uuid_utoa (fd->inode->gfid), source, sinks_str); - for (i = 0; i < priv->child_count; i++) { - if (!healed_sinks[i] && i != source) - continue; - if (replies[i].poststat.ia_size) { - type = AFR_SELFHEAL_DATA_DIFF; - break; - } - } + type = afr_data_self_heal_type_get (priv, healed_sinks, source, + replies); iter_frame = afr_copy_frame (frame); if (!iter_frame) -- cgit From 468a14b8b10639f0983be57e985cc9db98c6550c Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Mon, 10 Mar 2014 06:55:11 +0000 Subject: glusterd: suppress spurious error message during startup From glusterd log: ---------- E [glusterd-store.c:1981:glusterd_store_retrieve_volume] 0-: Unknown key: brick-0 ---------- The message is emitted from glusterd_store_retrieve_volume() when it reads the volinfo file because it doesn't do anything with the key-value pair. Suppress the error. The key is needed by glusterd_store_retrieve_bricks() which anyway re-reads it. Also change the log level to WARNING since we do not error out if an unknown key is got while parsing the volinfo file. Change-Id: Icd7962d9e16e0f90e6a37ee053dcafe97d2cab94 BUG: 1079279 Reviewed-on: http://review.gluster.org/7314 Tested-by: Gluster Build System Reviewed-by: Kaushal M Reviewed-by: Anand Avati --- xlators/mgmt/glusterd/src/glusterd-store.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/xlators/mgmt/glusterd/src/glusterd-store.c b/xlators/mgmt/glusterd/src/glusterd-store.c index 37cf98894..d5bfeda3f 100644 --- a/xlators/mgmt/glusterd/src/glusterd-store.c +++ b/xlators/mgmt/glusterd/src/glusterd-store.c @@ -1994,8 +1994,11 @@ glusterd_store_retrieve_volume (char *volname) goto out; case 0: - gf_log ("", GF_LOG_ERROR, "Unknown key: %s", - key); + /*Ignore GLUSTERD_STORE_KEY_VOL_BRICK since + glusterd_store_retrieve_bricks gets it later*/ + if (!strstr (key, GLUSTERD_STORE_KEY_VOL_BRICK)) + gf_log ("", GF_LOG_WARNING, + "Unknown key: %s", key); break; case 1: -- cgit From 326b77695f15444f79cea9822e35361e6bd167d5 Mon Sep 17 00:00:00 2001 From: Poornima G Date: Wed, 26 Mar 2014 17:30:32 +0530 Subject: rpm: Include the hook scripts of add-brick, volume start, stop and set. Change-Id: I9485da3d9a54a52cc524daff8f4f4caca3a6065d BUG: 1080970 Signed-off-by: Poornima G Reviewed-on: http://review.gluster.org/7346 Reviewed-by: Vijay Bellur Tested-by: Gluster Build System --- glusterfs.spec.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 5c4f74af4..5d29ad39e 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -651,6 +651,16 @@ touch %{buildroot}%{_sharedstatedir}/glusterd/nfs/run/nfs.pid install -p -m 0744 extras/hook-scripts/S56glusterd-geo-rep-create-post.sh \ %{buildroot}%{_sharedstatedir}/glusterd/hooks/1/gsync-create/post %endif +%{__install} -p -m 0744 extras/hook-scripts/start/post/*.sh \ + %{buildroot}%{_sharedstatedir}/glusterd/hooks/1/start/post +%{__install} -p -m 0744 extras/hook-scripts/stop/pre/*.sh \ + %{buildroot}%{_sharedstatedir}/glusterd/hooks/1/stop/pre +%{__install} -p -m 0744 extras/hook-scripts/set/post/*.sh \ + %{buildroot}%{_sharedstatedir}/glusterd/hooks/1/set/post +%{__install} -p -m 0744 extras/hook-scripts/add-brick/post/*.sh \ + %{buildroot}%{_sharedstatedir}/glusterd/hooks/1/add-brick/post +%{__install} -p -m 0744 extras/hook-scripts/add-brick/pre/*.sh \ + %{buildroot}%{_sharedstatedir}/glusterd/hooks/1/add-brick/pre find ./tests ./run-tests.sh -type f | cpio -pd %{buildroot}%{_prefix}/share/glusterfs @@ -795,6 +805,7 @@ fi %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/protocol/server* %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mgmt* %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/nfs* +%{_sharedstatedir}/glusterd %ghost %attr(0644,-,-) %config(noreplace) %{_sharedstatedir}/glusterd/glusterd.info %ghost %attr(0600,-,-) %{_sharedstatedir}/glusterd/options # This is really ugly, but I have no idea how to mark these directories in @@ -950,6 +961,9 @@ if [ $1 -ge 1 ]; then fi %changelog +* Wed Mar 26 2014 Poornima G +- Include the hook scripts of add-brick, volume start, stop and set + * Wed Feb 26 2014 Niels de Vos - Drop glusterfs-devel dependency from glusterfs-api (#1065750) -- cgit From 31e34cfd72712c76c127509d14d50eb008743fd5 Mon Sep 17 00:00:00 2001 From: ShyamsundarR Date: Fri, 20 Dec 2013 13:19:00 +0530 Subject: log: enhance gluster log format with message ID and standardize errno reporting Currently there are quite a slew of logs in Gluster that do not lend themselves to trivial analysis by various tools that help collect and monitor logs, due to the textual nature of the logs. This FEAT is to make this better by giving logs message IDs so that the tools do not have to do complex log parsing to break it down to problem areas and suggest troubleshooting options. With this patch, a new set of logging APIs are introduced that take additionally a message ID and an error number, so as to print the message ID and the descriptive string for the error. New APIs: - gf_msg, gf_msg_debug/trace, gf_msg_nomem, gf_msg_callingfn These APIs follow the functionality of the previous gf_log* counterparts, and hence are 1:1 replacements, with the delta that, gf_msg, gf_msg_callingfn take additional parameters as specified above. Defining the log messages: Each invocation of gf_msg/gf_msg_callingfn, should provide an ID and an errnum (if available). Towards this, a common message id file is provided, which contains defines to various messages and their respective strings. As other messages are changed to the new infrastructure APIs, it is intended that this file is edited to add these messages as well. Framework enhanced: The logging framework is also enhanced to be able to support different logging backends in the future. Hence new configuration options for logging framework and logging formats are introduced. Backward compatibility: Currently the framework supports logging in the traditional format, with the inclusion of an error string based on the errnum passed in. Hence the shift to these new APIs would retain the log file names, locations, and format with the exception of an additional error string where applicable. Testing done: Tested the new APIs with different messages in normal code paths Tested with configurations set to gluster logs (syslog pending) Tested nomem variants, inducing the message in normal code paths Tested ident generation for normal code paths (other paths pending) Tested with sample gfapi program for gfapi messages Test code is stripped from the commit Pending work (not to be addressed in this patch (future)): - Logging framework should be configurable - Logging format should be configurable - Once all messages move to the new APIs deprecate/delete older APIs to prevent misuse/abuse using the same - Repeated log messages should be suppressed (as a configurable option) - Logging framework assumes that only one init is possible, but there is no protection around the same (in existing code) - gf_log_fini is not invoked anywhere and does very little cleanup (in existing code) - DOxygen comments to message id headers for each message Change-Id: Ia043fda99a1c6cf7817517ef9e279bfcf35dcc24 BUG: 1075611 Signed-off-by: ShyamsundarR Reviewed-on: http://review.gluster.org/6547 Reviewed-by: Krutika Dhananjay Tested-by: Gluster Build System Reviewed-by: Raghavendra G Reviewed-by: Pranith Kumar Karampuri Reviewed-by: Vijay Bellur --- api/src/glfs.c | 11 +- glusterfsd/src/Makefile.am | 2 +- glusterfsd/src/glusterfsd-messages.h | 111 +++ glusterfsd/src/glusterfsd.c | 275 +++--- libglusterfs/src/Makefile.am | 3 +- libglusterfs/src/common-utils.c | 241 +++--- libglusterfs/src/common-utils.h | 1 + libglusterfs/src/glfs-message-id.h | 48 ++ libglusterfs/src/glusterfs.h | 1 + libglusterfs/src/logging.c | 1098 ++++++++++++++++++++---- libglusterfs/src/logging.h | 129 ++- libglusterfs/src/mem-pool.c | 6 +- libglusterfs/src/mem-pool.h | 6 +- libglusterfs/src/template-component-messages.h | 55 ++ libglusterfs/src/unittest/log_mock.c | 7 + rpc/rpc-transport/rdma/src/rdma.c | 2 +- tests/basic/logchecks-messages.h | 84 ++ tests/basic/logchecks.c | 208 +++++ 18 files changed, 1774 insertions(+), 514 deletions(-) create mode 100644 glusterfsd/src/glusterfsd-messages.h create mode 100644 libglusterfs/src/glfs-message-id.h create mode 100644 libglusterfs/src/template-component-messages.h create mode 100644 tests/basic/logchecks-messages.h create mode 100644 tests/basic/logchecks.c diff --git a/api/src/glfs.c b/api/src/glfs.c index 9e9a55ccb..d09c737f6 100644 --- a/api/src/glfs.c +++ b/api/src/glfs.c @@ -486,7 +486,7 @@ glfs_set_volfile_server (struct glfs *fs, const char *transport, int glfs_set_logging (struct glfs *fs, const char *logfile, int loglevel) { - int ret = 0; + int ret = 0; char *tmplog = NULL; if (!logfile) { @@ -498,15 +498,16 @@ glfs_set_logging (struct glfs *fs, const char *logfile, int loglevel) tmplog = (char *)logfile; } + /* finish log set parameters before init */ + if (loglevel >= 0) + gf_log_set_loglevel (loglevel); + ret = gf_log_init (fs->ctx, tmplog, NULL); if (ret) goto out; - if (loglevel >= 0) - gf_log_set_loglevel (loglevel); - out: - return ret; + return ret; } diff --git a/glusterfsd/src/Makefile.am b/glusterfsd/src/Makefile.am index 05a10dee3..1797c32be 100644 --- a/glusterfsd/src/Makefile.am +++ b/glusterfsd/src/Makefile.am @@ -6,7 +6,7 @@ glusterfsd_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ $(top_builddir)/rpc/xdr/src/libgfxdr.la \ $(GF_LDADD) $(GF_GLUSTERFS_LIBS) glusterfsd_LDFLAGS = $(GF_LDFLAGS) -noinst_HEADERS = glusterfsd.h glusterfsd-mem-types.h +noinst_HEADERS = glusterfsd.h glusterfsd-mem-types.h glusterfsd-messages.h AM_CPPFLAGS = $(GF_CPPFLAGS) \ -I$(top_srcdir)/libglusterfs/src -DDATADIR=\"$(localstatedir)\" \ diff --git a/glusterfsd/src/glusterfsd-messages.h b/glusterfsd/src/glusterfsd-messages.h new file mode 100644 index 000000000..48a0b17f6 --- /dev/null +++ b/glusterfsd/src/glusterfsd-messages.h @@ -0,0 +1,111 @@ +/* + Copyright (c) 2013 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _GLUSTERFSD_MESSAGES_H_ +#define _GLUSTERFSD_MESSAGES_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glfs-message-id.h" + +/* NOTE: Rules for message additions + * 1) Each instance of a message is _better_ left with a unique message ID, even + * if the message format is the same. Reasoning is that, if the message + * format needs to change in one instance, the other instances are not + * impacted or the new change does not change the ID of the instance being + * modified. + * 2) Addition of a message, + * - Should increment the GLFS_NUM_MESSAGES + * - Append to the list of messages defined, towards the end + * - Retain macro naming as glfs_msg_X (for redability across developers) + * NOTE: Rules for message format modifications + * 3) Check acorss the code if the message ID macro in question is reused + * anywhere. If reused then then the modifications should ensure correctness + * everywhere, or needs a new message ID as (1) above was not adhered to. If + * not used anywhere, proceed with the required modification. + * NOTE: Rules for message deletion + * 4) Check (3) and if used anywhere else, then cannot be deleted. If not used + * anywhere, then can be deleted, but will leave a hole by design, as + * addition rules specify modification to the end of the list and not filling + * holes. + */ + +#define GLFS_COMP_BASE GLFS_MSGID_COMP_GLUSTERFSD +#define GLFS_NUM_MESSAGES 33 +#define GLFS_MSGID_END (GLFS_COMP_BASE + GLFS_NUM_MESSAGES + 1) +/* Messaged with message IDs */ +#define glfs_msg_start_x GLFS_COMP_BASE, "Invalid: Start of messages" +/*------------*/ +#define glusterfsd_msg_1 (GLFS_COMP_BASE + 1), "Could not create absolute" \ + " mountpoint path" +#define glusterfsd_msg_2 (GLFS_COMP_BASE + 2), "Could not get current " \ + "working directory" +#define glusterfsd_msg_3 (GLFS_COMP_BASE + 3), "failed to set mount-point" \ + " to options dictionary" +#define glusterfsd_msg_4 (GLFS_COMP_BASE + 4), "failed to set dict value" \ + " for key %s" +#define glusterfsd_msg_5 (GLFS_COMP_BASE + 5), "failed to set 'disable'" \ + " for key %s" +#define glusterfsd_msg_6 (GLFS_COMP_BASE + 6), "failed to set 'enable'" \ + " for key %s" +#define glusterfsd_msg_7 (GLFS_COMP_BASE + 7), "Not a client process, not" \ + " performing mount operation" +#define glusterfsd_msg_8 (GLFS_COMP_BASE + 8), "MOUNT-POINT %s" \ + " initialization failed" +#define glusterfsd_msg_9 (GLFS_COMP_BASE + 9), "loading volume file %s" \ + " failed" +#define glusterfsd_msg_10 (GLFS_COMP_BASE + 10), "xlator option %s is" \ + " invalid" +#define glusterfsd_msg_11 (GLFS_COMP_BASE + 11), "Fetching the volume" \ + " file from server..." +#define glusterfsd_msg_12 (GLFS_COMP_BASE + 12), "volume initialization" \ + " failed." +#define glusterfsd_msg_13 (GLFS_COMP_BASE + 13), "ERROR: glusterfs uuid" \ + " generation failed" +#define glusterfsd_msg_14 (GLFS_COMP_BASE + 14), "ERROR: glusterfs %s" \ + " pool creation failed" +#define glusterfsd_msg_15 (GLFS_COMP_BASE + 15), "ERROR: '--volfile-id' is" \ + " mandatory if '-s' OR '--volfile-server'" \ + " option is given" +#define glusterfsd_msg_16 (GLFS_COMP_BASE + 16), "ERROR: parsing the" \ + " volfile failed" +#define glusterfsd_msg_17 (GLFS_COMP_BASE + 17), "pidfile %s open failed" +#define glusterfsd_msg_18 (GLFS_COMP_BASE + 18), "pidfile %s lock failed" +#define glusterfsd_msg_19 (GLFS_COMP_BASE + 19), "pidfile %s unlock failed" +#define glusterfsd_msg_20 (GLFS_COMP_BASE + 20), "pidfile %s truncation" \ + " failed" +#define glusterfsd_msg_21 (GLFS_COMP_BASE + 21), "pidfile %s write failed" +#define glusterfsd_msg_22 (GLFS_COMP_BASE + 22), "failed to execute" \ + " pthread_sigmask" +#define glusterfsd_msg_23 (GLFS_COMP_BASE + 23), "failed to create pthread" +#define glusterfsd_msg_24 (GLFS_COMP_BASE + 24), "daemonization failed" +#define glusterfsd_msg_25 (GLFS_COMP_BASE + 25), "mount failed" +#define glusterfsd_msg_26 (GLFS_COMP_BASE + 26), "failed to construct" \ + " the graph" +#define glusterfsd_msg_27 (GLFS_COMP_BASE + 27), "fuse xlator cannot be" \ + " specified in volume file" +#define glusterfsd_msg_28 (GLFS_COMP_BASE + 28), "Cannot reach volume" \ + " specification file" +#define glusterfsd_msg_29 (GLFS_COMP_BASE + 29), "ERROR: glusterfs context" \ + " not initialized" +#define glusterfsd_msg_30 (GLFS_COMP_BASE + 30), "Started running %s" \ + " version %s (args: %s)" +#define glusterfsd_msg_31 (GLFS_COMP_BASE + 31), "Could not create new" \ + " sync-environment" +#define glusterfsd_msg_32 (GLFS_COMP_BASE + 32), "received signum (%d)," \ + " shutting down" +/*------------*/ +#define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages" + + +#endif /* !_GLUSTERFSD_MESSAGES_H_ */ diff --git a/glusterfsd/src/glusterfsd.c b/glusterfsd/src/glusterfsd.c index 6a4655fc6..449fadda6 100644 --- a/glusterfsd/src/glusterfsd.c +++ b/glusterfsd/src/glusterfsd.c @@ -50,6 +50,7 @@ #include "glusterfs.h" #include "compat.h" #include "logging.h" +#include "glusterfsd-messages.h" #include "dict.h" #include "list.h" #include "timer.h" @@ -240,14 +241,13 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = gf_asprintf (&mount_point, "%s/%s", cwd, cmd_args->mount_point); if (ret == -1) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "Could not create absolute mountpoint " - "path"); + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, + glusterfsd_msg_1); goto err; } } else { - gf_log ("glusterfsd", GF_LOG_ERROR, - "Could not get current working directory"); + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, + glusterfsd_msg_2); goto err; } } else @@ -255,8 +255,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_dynstr (options, ZR_MOUNTPOINT_OPT, mount_point); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set mount-point to options dictionary"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_3); goto err; } @@ -265,9 +264,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) cmd_args->fuse_attribute_timeout); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", - ZR_ATTR_TIMEOUT_OPT); + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, + glusterfsd_msg_4, ZR_ATTR_TIMEOUT_OPT); goto err; } } @@ -276,8 +274,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_double (options, ZR_ENTRY_TIMEOUT_OPT, cmd_args->fuse_entry_timeout); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, ZR_ENTRY_TIMEOUT_OPT); goto err; } @@ -287,8 +284,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_double (options, ZR_NEGATIVE_TIMEOUT_OPT, cmd_args->fuse_negative_timeout); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, ZR_NEGATIVE_TIMEOUT_OPT); goto err; } @@ -298,8 +294,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_int32 (options, "client-pid", cmd_args->client_pid); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, "client-pid"); goto err; } @@ -309,8 +304,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_int32 (options, "uid-map-root", cmd_args->uid_map_root); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, "uid-map-root"); goto err; } @@ -320,8 +314,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_int32 (options, ZR_STRICT_VOLFILE_CHECK, cmd_args->volfile_check); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, ZR_STRICT_VOLFILE_CHECK); goto err; } @@ -331,8 +324,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, ZR_DUMP_FUSE, cmd_args->dump_fuse); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, ZR_DUMP_FUSE); goto err; } @@ -341,8 +333,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) if (cmd_args->acl) { ret = dict_set_static_ptr (options, "acl", "on"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key acl"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "acl"); goto err; } } @@ -350,8 +342,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) if (cmd_args->selinux) { ret = dict_set_static_ptr (options, "selinux", "on"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key selinux"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "selinux"); goto err; } } @@ -360,8 +352,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, "virtual-gfid-access", "on"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key " + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, "aux-gfid-mount"); goto err; } @@ -370,8 +361,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) if (cmd_args->enable_ino32) { ret = dict_set_static_ptr (options, "enable-ino32", "on"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key enable-ino32"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "enable-ino32"); goto err; } } @@ -379,8 +370,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) if (cmd_args->read_only) { ret = dict_set_static_ptr (options, "read-only", "on"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key read-only"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "read-only"); goto err; } } @@ -390,8 +381,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr(options, "fopen-keep-cache", "on"); if (ret < 0) { - gf_log("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key " + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, "fopen-keep-cache"); goto err; } @@ -400,17 +390,15 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr(options, "fopen-keep-cache", "off"); if (ret < 0) { - gf_log("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key " + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, "fopen-keep-cache"); goto err; } break; case GF_OPTION_DEFERRED: /* default */ default: - gf_log ("glusterfsd", GF_LOG_DEBUG, - "fopen-keep-cache mode %d", - cmd_args->fopen_keep_cache); + gf_msg_debug ("glusterfsd", 0, "fopen-keep-cache mode %d", + cmd_args->fopen_keep_cache); break; } @@ -418,8 +406,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_int32(options, "gid-timeout", cmd_args->gid_timeout); if (ret < 0) { - gf_log("glusterfsd", GF_LOG_ERROR, "failed to set dict " - "value for key gid-timeout"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "gid-timeout"); goto err; } } @@ -427,8 +415,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_int32 (options, "background-qlen", cmd_args->background_qlen); if (ret < 0) { - gf_log("glusterfsd", GF_LOG_ERROR, "failed to set dict " - "value for key background-qlen"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "background-qlen"); goto err; } } @@ -436,8 +424,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_int32 (options, "congestion-threshold", cmd_args->congestion_threshold); if (ret < 0) { - gf_log("glusterfsd", GF_LOG_ERROR, "failed to set dict " - "value for key congestion-threshold"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "congestion-threshold"); goto err; } } @@ -447,8 +435,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, ZR_DIRECT_IO_OPT, "disable"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set 'disable' for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_5, ZR_DIRECT_IO_OPT); goto err; } @@ -457,16 +444,15 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, ZR_DIRECT_IO_OPT, "enable"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set 'enable' for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_6, ZR_DIRECT_IO_OPT); goto err; } break; case GF_OPTION_DEFERRED: /* default */ default: - gf_log ("", GF_LOG_DEBUG, "fuse direct io type %d", - cmd_args->fuse_direct_io_mode); + gf_msg_debug ("glusterfsd", 0, "fuse direct io type %d", + cmd_args->fuse_direct_io_mode); break; } @@ -500,8 +486,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, "sync-to-mount", "enable"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key sync-mtab"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "sync-mtab"); goto err; } } @@ -510,8 +496,8 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_str (options, "use-readdirp", cmd_args->use_readdirp); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, "failed to set dict" - " value for key use-readdirp"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, + "use-readdirp"); goto err; } } @@ -530,14 +516,13 @@ create_fuse_mount (glusterfs_ctx_t *ctx) cmd_args = &ctx->cmd_args; if (!cmd_args->mount_point) { - gf_log ("", GF_LOG_TRACE, - "mount point not found, not a client process"); + gf_msg_trace ("glusterfsd", 0, + "mount point not found, not a client process"); return 0; } if (ctx->process_mode != GF_CLIENT_PROCESS) { - gf_log("glusterfsd", GF_LOG_ERROR, - "Not a client process, not performing mount operation"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_7); return -1; } @@ -551,8 +536,7 @@ create_fuse_mount (glusterfs_ctx_t *ctx) goto err; if (xlator_set_type (master, "mount/fuse") == -1) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "MOUNT-POINT %s initialization failed", + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_8, cmd_args->mount_point); goto err; } @@ -570,8 +554,7 @@ create_fuse_mount (glusterfs_ctx_t *ctx) ret = dict_set_static_ptr (master->options, ZR_FUSE_MOUNTOPTS, cmd_args->fuse_mountopts); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set dict value for key %s", + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4, ZR_FUSE_MOUNTOPTS); goto err; } @@ -579,7 +562,8 @@ create_fuse_mount (glusterfs_ctx_t *ctx) ret = xlator_init (master); if (ret) { - gf_log ("", GF_LOG_DEBUG, "failed to initialize fuse translator"); + gf_msg_debug ("glusterfsd", 0, + "failed to initialize fuse translator"); goto err; } @@ -608,21 +592,19 @@ get_volfp (glusterfs_ctx_t *ctx) ret = sys_lstat (cmd_args->volfile, &statbuf); if (ret == -1) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "%s: %s", cmd_args->volfile, strerror (errno)); + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_9, + cmd_args->volfile); return NULL; } if ((specfp = fopen (cmd_args->volfile, "r")) == NULL) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "volume file %s: %s", - cmd_args->volfile, - strerror (errno)); + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_9, + cmd_args->volfile); return NULL; } - gf_log ("glusterfsd", GF_LOG_DEBUG, - "loading volume file %s", cmd_args->volfile); + gf_msg_debug ("glusterfsd", 0, "loading volume file %s", + cmd_args->volfile); return specfp; } @@ -658,8 +640,8 @@ gf_remember_backup_volfile_server (char *arg) } if (!server->volfile_server) { - gf_log ("", GF_LOG_WARNING, - "xlator option %s is invalid", arg); + gf_msg ("glusterfsd", GF_LOG_WARNING, 0, glusterfsd_msg_10, + arg); goto out; } @@ -700,8 +682,7 @@ gf_remember_xlator_option (char *arg) dot = strchr (arg, '.'); if (!dot) { - gf_log ("", GF_LOG_WARNING, - "xlator option %s is invalid", arg); + gf_msg ("", GF_LOG_WARNING, 0, glusterfsd_msg_10, arg); goto out; } @@ -714,8 +695,7 @@ gf_remember_xlator_option (char *arg) equals = strchr (arg, '='); if (!equals) { - gf_log ("", GF_LOG_WARNING, - "xlator option %s is invalid", arg); + gf_msg ("", GF_LOG_WARNING, 0, glusterfsd_msg_10, arg); goto out; } @@ -727,8 +707,7 @@ gf_remember_xlator_option (char *arg) strncpy (option->key, dot + 1, (equals - dot - 1)); if (!*(equals + 1)) { - gf_log ("", GF_LOG_WARNING, - "xlator option %s is invalid", arg); + gf_msg ("", GF_LOG_WARNING, 0, glusterfsd_msg_10, arg); goto out; } @@ -1124,8 +1103,7 @@ cleanup_and_exit (int signum) if (!ctx) return; - gf_log_callingfn ("", GF_LOG_WARNING, - "received signum (%d), shutting down", signum); + gf_msg_callingfn ("", GF_LOG_WARNING, 0, glusterfsd_msg_32, signum); if (ctx->cleanup_started) return; @@ -1190,20 +1168,19 @@ reincarnate (int signum) cmd_args = &ctx->cmd_args; if (cmd_args->volfile_server) { - gf_log ("glusterfsd", GF_LOG_INFO, - "Fetching the volume file from server..."); + gf_msg ("glusterfsd", GF_LOG_INFO, 0, glusterfsd_msg_11); ret = glusterfs_volfile_fetch (ctx); } else { - gf_log ("glusterfsd", GF_LOG_DEBUG, - "Not reloading volume specification file on SIGHUP"); + gf_msg_debug ("glusterfsd", 0, + "Not reloading volume specification file" + " on SIGHUP"); } /* Also, SIGHUP should do logrotate */ gf_log_logrotate (1); if (ret < 0) - gf_log ("glusterfsd", GF_LOG_ERROR, - "volume initialization failed."); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_12); return; } @@ -1253,8 +1230,7 @@ glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx) ctx->process_uuid = generate_glusterfs_ctx_id (); if (!ctx->process_uuid) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs uuid generation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_13); goto out; } @@ -1262,22 +1238,19 @@ glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx) ctx->iobuf_pool = iobuf_pool_new (); if (!ctx->iobuf_pool) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs iobuf pool creation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_14, "iobuf"); goto out; } ctx->event_pool = event_pool_new (DEFAULT_EVENT_POOL_SIZE); if (!ctx->event_pool) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs event pool creation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_14, "event"); goto out; } ctx->pool = GF_CALLOC (1, sizeof (call_pool_t), gfd_mt_call_pool_t); if (!ctx->pool) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs call pool creation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_14, "call"); goto out; } @@ -1287,22 +1260,19 @@ glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx) /* frame_mem_pool size 112 * 4k */ ctx->pool->frame_mem_pool = mem_pool_new (call_frame_t, 4096); if (!ctx->pool->frame_mem_pool) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs frame pool creation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_14, "frame"); goto out; } /* stack_mem_pool size 256 * 1024 */ ctx->pool->stack_mem_pool = mem_pool_new (call_stack_t, 1024); if (!ctx->pool->stack_mem_pool) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs stack pool creation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_14, "stack"); goto out; } ctx->stub_mem_pool = mem_pool_new (call_stub_t, 1024); if (!ctx->stub_mem_pool) { - gf_log ("", GF_LOG_CRITICAL, - "ERROR: glusterfs stub pool creation failed"); + gf_msg ("", GF_LOG_CRITICAL, 0, glusterfsd_msg_14, "stub"); goto out; } @@ -1373,43 +1343,36 @@ logging_init (glusterfs_ctx_t *ctx, const char *progpath) { cmd_args_t *cmd_args = NULL; int ret = 0; - char ident[1024] = {0,}; - char *progname = NULL; - char *ptr = NULL; cmd_args = &ctx->cmd_args; if (cmd_args->log_file == NULL) { ret = gf_set_log_file_path (cmd_args); if (ret == -1) { - fprintf (stderr, "ERROR: failed to set the log file path\n"); + fprintf (stderr, "ERROR: failed to set the log file " + "path\n"); return -1; } } -#ifdef GF_USE_SYSLOG - progname = gf_strdup (progpath); - snprintf (ident, 1024, "%s_%s", basename(progname), - basename(cmd_args->log_file)); - GF_FREE (progname); - /* remove .log suffix */ - if (NULL != (ptr = strrchr(ident, '.'))) { - if (strcmp(ptr, ".log") == 0) { - /* note: ptr points to location in ident only */ - ptr[0] = '\0'; + if (cmd_args->log_ident == NULL) { + ret = gf_set_log_ident (cmd_args); + if (ret == -1) { + fprintf (stderr, "ERROR: failed to set the log " + "identity\n"); + return -1; } } - ptr = ident; -#endif - if (gf_log_init (ctx, cmd_args->log_file, ptr) == -1) { + /* finish log set parameters before init */ + gf_log_set_loglevel (cmd_args->log_level); + + if (gf_log_init (ctx, cmd_args->log_file, cmd_args->log_ident) == -1) { fprintf (stderr, "ERROR: failed to open logfile %s\n", cmd_args->log_file); return -1; } - gf_log_set_loglevel (cmd_args->log_level); - return 0; } @@ -1453,9 +1416,7 @@ parse_cmdline (int argc, char *argv[], glusterfs_ctx_t *ctx) /* Make sure after the parsing cli, if '--volfile-server' option is given, then '--volfile-id' is mandatory */ if (cmd_args->volfile_server && !cmd_args->volfile_id) { - gf_log ("glusterfs", GF_LOG_CRITICAL, - "ERROR: '--volfile-id' is mandatory if '-s' OR " - "'--volfile-server' option is given"); + gf_msg ("glusterfs", GF_LOG_CRITICAL, 0, glusterfsd_msg_15); ret = -1; goto out; } @@ -1473,9 +1434,8 @@ parse_cmdline (int argc, char *argv[], glusterfs_ctx_t *ctx) and exit */ ret = stat (cmd_args->volfile, &stbuf); if (ret) { - gf_log ("glusterfs", GF_LOG_CRITICAL, - "ERROR: parsing the volfile failed (%s)\n", - strerror (errno)); + gf_msg ("glusterfs", GF_LOG_CRITICAL, errno, + glusterfsd_msg_16); /* argp_usage (argp.) */ fprintf (stderr, "USAGE: %s [options] [mountpoint]\n", argv[0]); @@ -1551,9 +1511,8 @@ glusterfs_pidfile_setup (glusterfs_ctx_t *ctx) pidfp = fopen (cmd_args->pid_file, "a+"); if (!pidfp) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "pidfile %s error (%s)", - cmd_args->pid_file, strerror (errno)); + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_17, + cmd_args->pid_file); goto out; } @@ -1576,9 +1535,8 @@ glusterfs_pidfile_cleanup (glusterfs_ctx_t *ctx) if (!ctx->pidfp) return 0; - gf_log ("glusterfsd", GF_LOG_TRACE, - "pidfile %s cleanup", - cmd_args->pid_file); + gf_msg_trace ("glusterfsd", 0, "pidfile %s cleanup", + cmd_args->pid_file); if (ctx->cmd_args.pid_file) { unlink (ctx->cmd_args.pid_file); @@ -1607,39 +1565,34 @@ glusterfs_pidfile_update (glusterfs_ctx_t *ctx) ret = lockf (fileno (pidfp), F_TLOCK, 0); if (ret) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "pidfile %s lock failed", + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_18, cmd_args->pid_file); return ret; } ret = ftruncate (fileno (pidfp), 0); if (ret) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "pidfile %s truncation failed", + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_20, cmd_args->pid_file); return ret; } ret = fprintf (pidfp, "%d\n", getpid ()); if (ret <= 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "pidfile %s write failed", + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_21, cmd_args->pid_file); return ret; } ret = fflush (pidfp); if (ret) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "pidfile %s write failed", + gf_msg ("glusterfsd", GF_LOG_ERROR, errno, glusterfsd_msg_21, cmd_args->pid_file); return ret; } - gf_log ("glusterfsd", GF_LOG_DEBUG, - "pidfile %s updated with pid %d", - cmd_args->pid_file, getpid ()); + gf_msg_debug ("glusterfsd", 0, "pidfile %s updated with pid %d", + cmd_args->pid_file, getpid ()); return 0; } @@ -1723,9 +1676,7 @@ glusterfs_signals_setup (glusterfs_ctx_t *ctx) ret = pthread_sigmask (SIG_BLOCK, &set, NULL); if (ret) { - gf_log ("glusterfsd", GF_LOG_WARNING, - "failed to execute pthread_signmask %s", - strerror (errno)); + gf_msg ("glusterfsd", GF_LOG_WARNING, errno, glusterfsd_msg_22); return ret; } @@ -1737,9 +1688,7 @@ glusterfs_signals_setup (glusterfs_ctx_t *ctx) fallback to signals getting handled by other threads. setup the signal handlers */ - gf_log ("glusterfsd", GF_LOG_WARNING, - "failed to create pthread %s", - strerror (errno)); + gf_msg ("glusterfsd", GF_LOG_WARNING, errno, glusterfsd_msg_23); return ret; } @@ -1784,8 +1733,7 @@ daemonize (glusterfs_ctx_t *ctx) close (ctx->daemon_pipe[1]); } - gf_log ("daemonize", GF_LOG_ERROR, - "Daemonization failed: %s", strerror(errno)); + gf_msg ("daemonize", GF_LOG_ERROR, errno, glusterfsd_msg_24); goto out; case 0: /* child */ @@ -1800,8 +1748,8 @@ daemonize (glusterfs_ctx_t *ctx) if (ctx->mnt_pid > 0) { ret = waitpid (ctx->mnt_pid, &cstatus, 0); if (!(ret == ctx->mnt_pid && cstatus == 0)) { - gf_log ("daemonize", GF_LOG_ERROR, - "mount failed"); + gf_msg ("daemonize", GF_LOG_ERROR, 0, + glusterfsd_msg_25); exit (1); } } @@ -1831,15 +1779,14 @@ glusterfs_process_volfp (glusterfs_ctx_t *ctx, FILE *fp) graph = glusterfs_graph_construct (fp); if (!graph) { - gf_log ("", GF_LOG_ERROR, "failed to construct the graph"); + gf_msg ("", GF_LOG_ERROR, 0, glusterfsd_msg_26); goto out; } for (trav = graph->first; trav; trav = trav->next) { if (strcmp (trav->type, "mount/fuse") == 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "fuse xlator cannot be specified " - "in volume file"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, + glusterfsd_msg_27); goto out; } } @@ -1897,8 +1844,7 @@ glusterfs_volumes_init (glusterfs_ctx_t *ctx) fp = get_volfp (ctx); if (!fp) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "Cannot reach volume specification file"); + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_28); ret = -1; goto out; } @@ -1925,8 +1871,7 @@ main (int argc, char *argv[]) ctx = glusterfs_ctx_new (); if (!ctx) { - gf_log ("glusterfs", GF_LOG_CRITICAL, - "ERROR: glusterfs context not initialized"); + gf_msg ("glusterfs", GF_LOG_CRITICAL, 0, glusterfsd_msg_29); return ENOMEM; } glusterfsd_ctx = ctx; @@ -1965,8 +1910,7 @@ main (int argc, char *argv[]) strcat (cmdlinestr, " "); strcat (cmdlinestr, argv[i]); } - gf_log (argv[0], GF_LOG_INFO, - "Started running %s version %s (%s)", + gf_msg (argv[0], GF_LOG_INFO, 0, glusterfsd_msg_30, argv[0], PACKAGE_VERSION, cmdlinestr); } @@ -1982,8 +1926,7 @@ main (int argc, char *argv[]) ctx->env = syncenv_new (0, 0, 0); if (!ctx->env) { - gf_log ("", GF_LOG_ERROR, - "Could not create new sync-environment"); + gf_msg ("", GF_LOG_ERROR, 0, glusterfsd_msg_31); goto out; } diff --git a/libglusterfs/src/Makefile.am b/libglusterfs/src/Makefile.am index 634e217ed..b1a853687 100644 --- a/libglusterfs/src/Makefile.am +++ b/libglusterfs/src/Makefile.am @@ -42,7 +42,8 @@ noinst_HEADERS = common-utils.h defaults.h dict.h glusterfs.h hashfn.h timespec. $(CONTRIBDIR)/uuid/uuid.h $(CONTRIBDIR)/uuid/uuidP.h \ $(CONTRIB_BUILDDIR)/uuid/uuid_types.h syncop.h graph-utils.h trie.h \ run.h options.h lkowner.h fd-lk.h circ-buff.h event-history.h \ - gidcache.h client_t.h glusterfs-acl.h + gidcache.h client_t.h glusterfs-acl.h glfs-message-id.h \ + template-component-messages.h EXTRA_DIST = graph.l graph.y diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c index 1dfb418e4..1ccb27d16 100644 --- a/libglusterfs/src/common-utils.c +++ b/libglusterfs/src/common-utils.c @@ -281,26 +281,37 @@ err: struct xldump { int lineno; - FILE *logfp; }; - +/* to catch any format discrepencies that may arise in code */ +static int nprintf (struct xldump *dump, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); static int nprintf (struct xldump *dump, const char *fmt, ...) { - va_list ap; - int ret = 0; - + va_list ap; + char *msg = NULL; + char header[32]; + int ret = 0; - ret += fprintf (dump->logfp, "%3d: ", ++dump->lineno); + ret = snprintf (header, 32, "%3d:", ++dump->lineno); + if (ret < 0) + goto out; - va_start (ap, fmt); - ret += vfprintf (dump->logfp, fmt, ap); - va_end (ap); + va_start (ap, fmt); + ret = vasprintf (&msg, fmt, ap); + va_end (ap); + if (-1 == ret) + goto out; - ret += fprintf (dump->logfp, "\n"); + /* NOTE: No ret value from gf_msg_plain, so unable to compute printed + * characters. The return value from nprintf is not used, so for now + * living with it */ + gf_msg_plain (GF_LOG_WARNING, "%s %s", header, msg); - return ret; +out: + FREE (msg); + return 0; } @@ -349,175 +360,125 @@ xldump (xlator_t *each, void *d) xldump_subvolumes (each, d); nprintf (d, "end-volume"); - nprintf (d, ""); + nprintf (d, " "); } void gf_log_dump_graph (FILE *specfp, glusterfs_graph_t *graph) { - glusterfs_ctx_t *ctx; struct xldump xld = {0, }; - - ctx = THIS->ctx; - xld.logfp = ctx->log.gf_log_logfile; - - fprintf (ctx->log.gf_log_logfile, "Final graph:\n"); - fprintf (ctx->log.gf_log_logfile, - "+---------------------------------------" - "---------------------------------------+\n"); + gf_msg_plain (GF_LOG_WARNING, "Final graph:"); + gf_msg_plain (GF_LOG_WARNING, + "+---------------------------------------" + "---------------------------------------+"); xlator_foreach_depth_first (graph->top, xldump, &xld); - fprintf (ctx->log.gf_log_logfile, - "+---------------------------------------" - "---------------------------------------+\n"); - fflush (ctx->log.gf_log_logfile); + gf_msg_plain (GF_LOG_WARNING, + "+---------------------------------------" + "---------------------------------------+"); } static void -gf_dump_config_flags (int fd) +gf_dump_config_flags () { - int ret = 0; - - ret = write (fd, "configuration details:\n", 23); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "configuration details:"); /* have argp */ #ifdef HAVE_ARGP - ret = write (fd, "argp 1\n", 7); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "argp 1"); #endif /* ifdef if found backtrace */ #ifdef HAVE_BACKTRACE - ret = write (fd, "backtrace 1\n", 12); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "backtrace 1"); #endif /* Berkeley-DB version has cursor->get() */ #ifdef HAVE_BDB_CURSOR_GET - ret = write (fd, "bdb->cursor->get 1\n", 19); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "bdb->cursor->get 1"); #endif /* Define to 1 if you have the header file. */ #ifdef HAVE_DB_H - ret = write (fd, "db.h 1\n", 7); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "db.h 1"); #endif /* Define to 1 if you have the header file. */ #ifdef HAVE_DLFCN_H - ret = write (fd, "dlfcn 1\n", 8); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "dlfcn 1"); #endif /* define if fdatasync exists */ #ifdef HAVE_FDATASYNC - ret = write (fd, "fdatasync 1\n", 12); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "fdatasync 1"); #endif /* Define to 1 if you have the `pthread' library (-lpthread). */ #ifdef HAVE_LIBPTHREAD - ret = write (fd, "libpthread 1\n", 13); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "libpthread 1"); #endif /* define if llistxattr exists */ #ifdef HAVE_LLISTXATTR - ret = write (fd, "llistxattr 1\n", 13); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "llistxattr 1"); #endif /* define if found setfsuid setfsgid */ #ifdef HAVE_SET_FSID - ret = write (fd, "setfsid 1\n", 10); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "setfsid 1"); #endif /* define if found spinlock */ #ifdef HAVE_SPINLOCK - ret = write (fd, "spinlock 1\n", 11); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "spinlock 1"); #endif /* Define to 1 if you have the header file. */ #ifdef HAVE_SYS_EPOLL_H - ret = write (fd, "epoll.h 1\n", 10); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "epoll.h 1"); #endif /* Define to 1 if you have the header file. */ #ifdef HAVE_SYS_EXTATTR_H - ret = write (fd, "extattr.h 1\n", 12); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "extattr.h 1"); #endif /* Define to 1 if you have the header file. */ #ifdef HAVE_SYS_XATTR_H - ret = write (fd, "xattr.h 1\n", 10); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "xattr.h 1"); #endif /* define if found st_atim.tv_nsec */ #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC - ret = write (fd, "st_atim.tv_nsec 1\n", 18); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "st_atim.tv_nsec 1"); #endif /* define if found st_atimespec.tv_nsec */ #ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC - ret = write (fd, "st_atimespec.tv_nsec 1\n",23); - if (ret == -1) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, "st_atimespec.tv_nsec 1"); #endif /* Define to the full name and version of this package. */ #ifdef PACKAGE_STRING { char msg[128]; - sprintf (msg, "package-string: %s\n", PACKAGE_STRING); - ret = write (fd, msg, strlen (msg)); - if (ret == -1) - goto out; + sprintf (msg, "package-string: %s", PACKAGE_STRING); + gf_msg_plain_nomem (GF_LOG_ALERT, msg); } #endif -out: return; } -/* Obtain a backtrace and print it to stdout. */ -/* TODO: It looks like backtrace_symbols allocates memory, - it may be problem because mostly memory allocation/free causes 'sigsegv' */ - +/* Obtain a backtrace and print it to the log */ void gf_print_trace (int32_t signum, glusterfs_ctx_t *ctx) { char msg[1024] = {0,}; char timestr[64] = {0,}; - int ret = 0; - int fd = 0; - - fd = fileno (ctx->log.gf_log_logfile); /* Now every gf_log call will just write to a buffer and when the * buffer becomes full, its written to the log-file. Suppose the process @@ -526,75 +487,51 @@ gf_print_trace (int32_t signum, glusterfs_ctx_t *ctx) * contents of the buffer to the log file before printing the backtrace * which helps in debugging. */ - fflush (ctx->log.gf_log_logfile); + gf_log_flush(); /* Pending frames, (if any), list them in order */ - ret = write (fd, "pending frames:\n", 16); - if (ret < 0) - goto out; - + gf_msg_plain_nomem (GF_LOG_ALERT, "pending frames:"); { - struct list_head *trav = ((call_pool_t *)ctx->pool)->all_frames.next; + struct list_head *trav = + ((call_pool_t *)ctx->pool)->all_frames.next; while (trav != (&((call_pool_t *)ctx->pool)->all_frames)) { - call_frame_t *tmp = (call_frame_t *)(&((call_stack_t *)trav)->frames); + call_frame_t *tmp = + (call_frame_t *)(&((call_stack_t *)trav)->frames); if (tmp->root->type == GF_OP_TYPE_FOP) - sprintf (msg,"frame : type(%d) op(%s)\n", + sprintf (msg,"frame : type(%d) op(%s)", tmp->root->type, gf_fop_list[tmp->root->op]); else - sprintf (msg,"frame : type(%d) op(%d)\n", + sprintf (msg,"frame : type(%d) op(%d)", tmp->root->type, tmp->root->op); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; + gf_msg_plain_nomem (GF_LOG_ALERT, msg); trav = trav->next; } - ret = write (fd, "\n", 1); - if (ret < 0) - goto out; } - sprintf (msg, "patchset: %s\n", GLUSTERFS_REPOSITORY_REVISION); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; - - sprintf (msg, "signal received: %d\n", signum); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; + sprintf (msg, "patchset: %s", GLUSTERFS_REPOSITORY_REVISION); + gf_msg_plain_nomem (GF_LOG_ALERT, msg); + sprintf (msg, "signal received: %d", signum); + gf_msg_plain_nomem (GF_LOG_ALERT, msg); { /* Dump the timestamp of the crash too, so the previous logs can be related */ - gf_time_fmt (timestr, sizeof timestr, time (NULL), gf_timefmt_FT); - ret = write (fd, "time of crash: ", 15); - if (ret < 0) - goto out; - ret = write (fd, timestr, strlen (timestr)); - if (ret < 0) - goto out; + gf_time_fmt (timestr, sizeof timestr, time (NULL), + gf_timefmt_FT); + gf_msg_plain_nomem (GF_LOG_ALERT, "time of crash: "); + gf_msg_plain_nomem (GF_LOG_ALERT, timestr); } - gf_dump_config_flags (fd); + gf_dump_config_flags (); #if HAVE_BACKTRACE - /* Print 'backtrace' */ - { - void *array[200]; - size_t size; - - size = backtrace (array, 200); - backtrace_symbols_fd (&array[1], size-1, fd); - sprintf (msg, "---------\n"); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; - } + gf_msg_backtrace_nomem (GF_LOG_ALERT, 200); + sprintf (msg, "---------"); + gf_msg_plain_nomem (GF_LOG_ALERT, msg); #endif /* HAVE_BACKTRACE */ -out: /* Send a signal to terminate the process */ signal (signum, SIG_DFL); raise (signum); @@ -2918,6 +2855,42 @@ done: return ret; } +int +gf_set_log_ident (cmd_args_t *cmd_args) +{ + int ret = 0; + char *ptr = NULL; + + if (cmd_args->log_file == NULL) { + /* no ident source */ + return 0; + } + + /* TODO: Some idents would look like, etc-glusterfs-glusterd.vol, which + * seems ugly and can be bettered? */ + /* just get the filename as the ident */ + if (NULL != (ptr = strrchr (cmd_args->log_file, '/'))) { + ret = gf_asprintf (&cmd_args->log_ident, "%s", ptr + 1); + } else { + ret = gf_asprintf (&cmd_args->log_ident, "%s", + cmd_args->log_file); + } + + if (ret > 0) + ret = 0; + else + return ret; + + /* remove .log suffix */ + if (NULL != (ptr = strrchr (cmd_args->log_ident, '.'))) { + if (strcmp (ptr, ".log") == 0) { + ptr[0] = '\0'; + } + } + + return ret; +} + int gf_thread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) diff --git a/libglusterfs/src/common-utils.h b/libglusterfs/src/common-utils.h index f877590f4..ee4e5b7e6 100644 --- a/libglusterfs/src/common-utils.h +++ b/libglusterfs/src/common-utils.h @@ -121,6 +121,7 @@ int32_t gf_resolve_ip6 (const char *hostname, uint16_t port, int family, void gf_log_dump_graph (FILE *specfp, glusterfs_graph_t *graph); void gf_print_trace (int32_t signal, glusterfs_ctx_t *ctx); int gf_set_log_file_path (cmd_args_t *cmd_args); +int gf_set_log_ident (cmd_args_t *cmd_args); #define VECTORSIZE(count) (count * (sizeof (struct iovec))) diff --git a/libglusterfs/src/glfs-message-id.h b/libglusterfs/src/glfs-message-id.h new file mode 100644 index 000000000..42c16f12d --- /dev/null +++ b/libglusterfs/src/glfs-message-id.h @@ -0,0 +1,48 @@ +/* + Copyright (c) 2013 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _GLFS_MESSAGE_ID_H_ +#define _GLFS_MESSAGE_ID_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +/* Base of all message IDs, all message IDs would be + * greater than this */ +#define GLFS_MSGID_BASE 100000 + +/* Segment size of allocated range. Any component needing more than this + * segment size should take multiple segments (at times non contiguous, + * if extensions are being made post the next segment already allocated) */ +#define GLFS_MSGID_SEGMENT 1000 + +/* Per module message segments allocated */ +/* NOTE: For any new module add to the end the modules */ +#define GLFS_MSGID_COMP_GLUSTERFSD GLFS_MSGID_BASE +#define GLFS_MSGID_COMP_LIBGLUSTERFS GLFS_MSGID_COMP_GLUSTERFSD + \ + GLFS_MSGID_SEGMENT +#define GLFS_MSGID_COMP_RPC_LIB GLFS_MSGID_COMP_LIBGLUSTERFS + \ + GLFS_MSGID_SEGMENT +#define GLFS_MSGID_COMP_RPC_TRANSPORT GLFS_MSGID_COMP_RPC_LIB + \ + GLFS_MSGID_SEGMENT +#define GLFS_MSGID_COMP_API GLFS_MSGID_COMP_RPC_TRANSPORT + \ + GLFS_MSGID_SEGMENT +#define GLFS_MSGID_COMP_CLI GLFS_MSGID_COMP_API + \ + GLFS_MSGID_SEGMENT +/* glusterd has a lot of messages, taking 2 segments for the same */ +#define GLFS_MSGID_GLUSTERD GLFS_MSGID_COMP_CLI + \ + GLFS_MSGID_SEGMENT + \ + GLFS_MSGID_SEGMENT + +/* --- new segments for messages goes above this line --- */ + +#endif /* !_GLFS_MESSAGE_ID_H_ */ diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h index 5ce0d6e70..852d686c1 100644 --- a/libglusterfs/src/glusterfs.h +++ b/libglusterfs/src/glusterfs.h @@ -342,6 +342,7 @@ struct _cmd_args { char *log_server; gf_loglevel_t log_level; char *log_file; + char *log_ident; int32_t max_connect_attempts; /* advanced options */ uint32_t volfile_server_port; diff --git a/libglusterfs/src/logging.c b/libglusterfs/src/logging.c index 2bd40b2c2..f343731c7 100644 --- a/libglusterfs/src/logging.c +++ b/libglusterfs/src/logging.c @@ -22,7 +22,6 @@ #include #include -#ifdef GF_USE_SYSLOG #include #include #include @@ -32,7 +31,9 @@ #define GF_SYSLOG_CEE_FORMAT \ "@cee: {\"msg\": \"%s\", \"gf_code\": \"%u\", \"gf_message\": \"%s\"}" #define GF_LOG_CONTROL_FILE "/etc/glusterfs/logger.conf" -#endif /* GF_USE_SYSLOG */ +#define GF_LOG_BACKTRACE_DEPTH 5 +#define GF_LOG_BACKTRACE_SIZE 4096 +#define GF_LOG_TIMESTR_SIZE 256 #include "xlator.h" #include "logging.h" @@ -47,6 +48,19 @@ #include #endif +static char *gf_level_strings[] = {"", /* NONE */ + "M", /* EMERGENCY */ + "A", /* ALERT */ + "C", /* CRITICAL */ + "E", /* ERROR */ + "W", /* WARNING */ + "N", /* NOTICE */ + "I", /* INFO */ + "D", /* DEBUG */ + "T", /* TRACE */ + "" +}; + /* Ideally this should get moved to logging.h */ struct _msg_queue { struct list_head msgs; @@ -60,41 +74,77 @@ struct _log_msg { void gf_log_logrotate (int signum) { - THIS->ctx->log.logrotate = 1; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.logrotate = 1; } void gf_log_enable_syslog (void) { - THIS->ctx->log.gf_log_syslog = 1; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.gf_log_syslog = 1; } void gf_log_disable_syslog (void) { - THIS->ctx->log.gf_log_syslog = 0; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.gf_log_syslog = 0; } gf_loglevel_t gf_log_get_loglevel (void) { - return THIS->ctx->log.loglevel; + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + return ctx->log.loglevel; + else + /* return global defaluts (see gf_log_globals_init) */ + return GF_LOG_INFO; } void gf_log_set_loglevel (gf_loglevel_t level) { - THIS->ctx->log.loglevel = level; -} + glusterfs_ctx_t *ctx = NULL; + ctx = THIS->ctx; -gf_loglevel_t -gf_log_get_xl_loglevel (void *this) + if (ctx) + ctx->log.loglevel = level; +} + +void +gf_log_flush (void) { - xlator_t *xl = this; - if (!xl) - return 0; - return xl->loglevel; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (ctx && ctx->log.logger == gf_logger_glusterlog) { + pthread_mutex_lock (&ctx->log.logfile_mutex); + fflush (ctx->log.gf_log_logfile); + pthread_mutex_unlock (&ctx->log.logfile_mutex); + } + + return; } void @@ -107,9 +157,142 @@ gf_log_set_xl_loglevel (void *this, gf_loglevel_t level) xl->loglevel = level; } +/* TODO: The following get/set functions are yet not invoked from anywhere + * in the code. The _intention_ is to pass CLI arguments to various daemons + * that are started, which would then invoke these set APIs as required. + * + * glusterd would read the defaults from its .vol file configuration shipped + * as a part of the packages distributed. + * + * For any gluster* daemon that is started the shipped configuration becomes the + * default, if a volume has to change its logging format or logger, then a + * gluster CLI is invoked to set this property for the volume in question. + * + * The property is maintained by glusterd, and passed to the daemon as a CLI + * option, IOW persistence of the option is maintained by glusterd persistent + * storage (i.e .vol file) only + * + * care needs to be taken to configure and start daemons based on the versions + * that supports these features */ +gf_log_format_t +gf_log_get_logformat (void) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + return ctx->log.logformat; + else + /* return global defaluts (see gf_log_globals_init) */ + return gf_logformat_withmsgid; +} + +void +gf_log_set_logformat (gf_log_format_t format) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.logformat = format; +} + +gf_log_logger_t +gf_log_get_logger (void) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + return ctx->log.logger; + else + /* return global defaluts (see gf_log_globals_init) */ + return gf_logger_glusterlog; +} + +void +gf_log_set_logger (gf_log_logger_t logger) +{ + glusterfs_ctx_t *ctx = NULL; + + ctx = THIS->ctx; + + if (ctx) + ctx->log.logger = logger; +} + +gf_loglevel_t +gf_log_get_xl_loglevel (void *this) +{ + xlator_t *xl = this; + if (!xl) + return 0; + return xl->loglevel; +} + +static void +gf_log_rotate(glusterfs_ctx_t *ctx) +{ + int fd = -1; + FILE *new_logfile = NULL; + FILE *old_logfile = NULL; + + /* not involving locks on initial check to speed it up */ + if (ctx->log.logrotate) { + /* let only one winner through on races */ + pthread_mutex_lock (&ctx->log.logfile_mutex); + + if (!ctx->log.logrotate) { + pthread_mutex_unlock (&ctx->log.logfile_mutex); + return; + } else { + ctx->log.logrotate = 0; + pthread_mutex_unlock (&ctx->log.logfile_mutex); + } + + fd = open (ctx->log.filename, + O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) { + gf_log ("logrotate", GF_LOG_ERROR, + "%s", strerror (errno)); + return; + } + close (fd); + + new_logfile = fopen (ctx->log.filename, "a"); + if (!new_logfile) { + gf_log ("logrotate", GF_LOG_CRITICAL, + "failed to open logfile %s (%s)", + ctx->log.filename, strerror (errno)); + return; + } + + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) + old_logfile = ctx->log.logfile; + + ctx->log.gf_log_logfile = ctx->log.logfile = + new_logfile; + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + + if (old_logfile != NULL) + fclose (old_logfile); + } + + return; +} + void gf_log_globals_fini (void) { + /* TODO: Nobody is invoking the fini, but cleanup needs to happen here, + * needs cleanup for, log.ident, log.filename, closelog, log file close + * rotate state, possibly under a lock */ pthread_mutex_destroy (&THIS->ctx->log.logfile_mutex); } @@ -122,7 +305,8 @@ int gf_log_fini (void *data) { glusterfs_ctx_t *ctx = data; - int ret = 0; + int ret = 0; + FILE *old_logfile = NULL; if (ctx == NULL) { ret = -1; @@ -132,8 +316,8 @@ gf_log_fini (void *data) pthread_mutex_lock (&ctx->log.logfile_mutex); { if (ctx->log.logfile) { - if (fclose (ctx->log.logfile) != 0) - ret = -1; + old_logfile = ctx->log.logfile; + /* Logfile needs to be set to NULL, so that any call to gf_log after calling gf_log_fini, will log the message to stderr. @@ -144,11 +328,13 @@ gf_log_fini (void *data) } pthread_mutex_unlock (&ctx->log.logfile_mutex); + if (old_logfile && (fclose (old_logfile) != 0)) + ret = -1; + out: return ret; } -#ifdef GF_USE_SYSLOG /** * gf_get_error_message -function to get error message for given error code * @error_code: error code defined by log book @@ -186,10 +372,13 @@ gf_openlog (const char *ident, int option, int facility) _facility = LOG_LOCAL1; } + /* TODO: Should check for errors here and return appropriately */ setlocale(LC_ALL, ""); bindtextdomain("gluster", "/usr/share/locale"); textdomain("gluster"); + /* close the previous syslog if open as we are changing settings */ + closelog (); openlog(ident, _option, _facility); } @@ -347,7 +536,6 @@ gf_syslog (int error_code, int facility_priority, char *format, ...) } va_end (ap); } -#endif /* GF_USE_SYSLOG */ void gf_log_globals_init (void *data) @@ -359,47 +547,45 @@ gf_log_globals_init (void *data) ctx->log.loglevel = GF_LOG_INFO; ctx->log.gf_log_syslog = 1; ctx->log.sys_log_level = GF_LOG_CRITICAL; + ctx->log.logger = gf_logger_glusterlog; + ctx->log.logformat = gf_logformat_withmsgid; -#ifndef GF_USE_SYSLOG #ifdef GF_LINUX_HOST_OS /* For the 'syslog' output. one can grep 'GlusterFS' in syslog for serious logs */ openlog ("GlusterFS", LOG_PID, LOG_DAEMON); #endif -#endif + } int gf_log_init (void *data, const char *file, const char *ident) { glusterfs_ctx_t *ctx = NULL; - int fd = -1; + int fd = -1; + struct stat buf; ctx = data; -#if defined(GF_USE_SYSLOG) - { - /* use default ident and option */ - /* TODO: make FACILITY configurable than LOG_DAEMON */ - struct stat buf; - - if (stat (GF_LOG_CONTROL_FILE, &buf) == 0) { - /* use syslog logging */ - ctx->log.log_control_file_found = 1; - if (ident) { - /* we need to keep this value as */ - /* syslog uses it on every logging */ - ctx->log.ident = gf_strdup (ident); - gf_openlog (ctx->log.ident, -1, LOG_DAEMON); - } else { - gf_openlog (NULL, -1, LOG_DAEMON); - } - } else { - /* use old style logging */ - ctx->log.log_control_file_found = 0; - } + if (ident) { + ctx->log.ident = gf_strdup (ident); + } + + /* we keep the files and the syslog open, so that on logger change, we + * are ready to log anywhere, that the new value specifies */ + if (ctx->log.ident) { + gf_openlog (ctx->log.ident, -1, LOG_DAEMON); + } else { + gf_openlog (NULL, -1, LOG_DAEMON); + } + /* TODO: make FACILITY configurable than LOG_DAEMON */ + if (stat (GF_LOG_CONTROL_FILE, &buf) == 0) { + /* use syslog logging */ + ctx->log.log_control_file_found = 1; + } else { + /* use old style logging */ + ctx->log.log_control_file_found = 0; } -#endif if (!file){ fprintf (stderr, "ERROR: no filename specified\n"); @@ -407,7 +593,7 @@ gf_log_init (void *data, const char *file, const char *ident) } if (strcmp (file, "-") == 0) { - file = "/dev/stderr"; + file = "/dev/stderr"; } ctx->log.filename = gf_strdup (file); @@ -419,8 +605,8 @@ gf_log_init (void *data, const char *file, const char *ident) fd = open (file, O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR); if (fd < 0) { - fprintf (stderr, "ERROR: failed to create logfile \"%s\" (%s)\n", - file, strerror (errno)); + fprintf (stderr, "ERROR: failed to create logfile" + " \"%s\" (%s)\n", file, strerror (errno)); return -1; } close (fd); @@ -440,140 +626,13 @@ gf_log_init (void *data, const char *file, const char *ident) void set_sys_log_level (gf_loglevel_t level) { - THIS->ctx->log.sys_log_level = level; -} - -int -_gf_log_nomem (const char *domain, const char *file, - const char *function, int line, gf_loglevel_t level, - size_t size) -{ - const char *basename = NULL; - xlator_t *this = NULL; - struct timeval tv = {0,}; - int ret = 0; - char msg[8092] = {0,}; - char timestr[256] = {0,}; - char callstr[4096] = {0,}; glusterfs_ctx_t *ctx = NULL; - this = THIS; - ctx = this->ctx; - - if (ctx->log.gf_log_xl_log_set) { - if (this->loglevel && (level > this->loglevel)) - goto out; - } - if (level > ctx->log.loglevel) - goto out; - - static char *level_strings[] = {"", /* NONE */ - "M", /* EMERGENCY */ - "A", /* ALERT */ - "C", /* CRITICAL */ - "E", /* ERROR */ - "W", /* WARNING */ - "N", /* NOTICE */ - "I", /* INFO */ - "D", /* DEBUG */ - "T", /* TRACE */ - ""}; - - if (!domain || !file || !function) { - fprintf (stderr, - "logging: %s:%s():%d: invalid argument\n", - __FILE__, __PRETTY_FUNCTION__, __LINE__); - return -1; - } - - basename = strrchr (file, '/'); - if (basename) - basename++; - else - basename = file; - -#if HAVE_BACKTRACE - /* Print 'calling function' */ - do { - void *array[5]; - char **callingfn = NULL; - size_t bt_size = 0; - - bt_size = backtrace (array, 5); - if (bt_size) - callingfn = backtrace_symbols (&array[2], bt_size-2); - if (!callingfn) - break; - - if (bt_size == 5) - snprintf (callstr, 4096, "(-->%s (-->%s (-->%s)))", - callingfn[2], callingfn[1], callingfn[0]); - if (bt_size == 4) - snprintf (callstr, 4096, "(-->%s (-->%s))", - callingfn[1], callingfn[0]); - if (bt_size == 3) - snprintf (callstr, 4096, "(-->%s)", callingfn[0]); - - free (callingfn); - } while (0); -#endif /* HAVE_BACKTRACE */ - -#if defined(GF_USE_SYSLOG) - if (ctx->log.log_control_file_found) - { - int priority; - /* treat GF_LOG_TRACE and GF_LOG_NONE as LOG_DEBUG and - other level as is */ - if (GF_LOG_TRACE == level || GF_LOG_NONE == level) { - priority = LOG_DEBUG; - } else { - priority = level - 1; - } - gf_syslog (GF_ERR_DEV, priority, - "[%s:%d:%s] %s %s: no memory " - "available for size (%"GF_PRI_SIZET")", - basename, line, function, callstr, domain, - size); - goto out; - } -#endif /* GF_USE_SYSLOG */ - ret = gettimeofday (&tv, NULL); - if (-1 == ret) - goto out; - gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); - snprintf (timestr + strlen (timestr), sizeof timestr - strlen (timestr), - ".%"GF_PRI_SUSECONDS, tv.tv_usec); - - ret = sprintf (msg, "[%s] %s [%s:%d:%s] %s %s: no memory " - "available for size (%"GF_PRI_SIZET")", - timestr, level_strings[level], - basename, line, function, callstr, - domain, size); - if (-1 == ret) { - goto out; - } - - pthread_mutex_lock (&ctx->log.logfile_mutex); - { - if (ctx->log.logfile) { - fprintf (ctx->log.logfile, "%s\n", msg); - } else if (ctx->log.loglevel >= level) { - fprintf (stderr, "%s\n", msg); - } - -#ifdef GF_LINUX_HOST_OS - /* We want only serious log in 'syslog', not our debug - and trace logs */ - if (ctx->log.gf_log_syslog && level && - (level <= ctx->log.sys_log_level)) - syslog ((level-1), "%s\n", msg); -#endif - } + ctx = THIS->ctx; - pthread_mutex_unlock (&ctx->log.logfile_mutex); -out: - return ret; - } + if (ctx) + ctx->log.sys_log_level = level; +} int _gf_log_callingfn (const char *domain, const char *file, const char *function, @@ -653,7 +712,6 @@ _gf_log_callingfn (const char *domain, const char *file, const char *function, } while (0); #endif /* HAVE_BACKTRACE */ -#if defined(GF_USE_SYSLOG) if (ctx->log.log_control_file_found) { int priority; @@ -678,7 +736,7 @@ _gf_log_callingfn (const char *domain, const char *file, const char *function, goto out; } -#endif /* GF_USE_SYSLOG */ + ret = gettimeofday (&tv, NULL); if (-1 == ret) goto out; @@ -737,6 +795,676 @@ out: return ret; } +int +_gf_msg_plain_internal (gf_loglevel_t level, const char *msg) +{ + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + int priority; + + this = THIS; + ctx = this->ctx; + + /* log to the configured logging service */ + switch (ctx->log.logger) { + case gf_logger_syslog: + if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) { + SET_LOG_PRIO (level, priority); + + syslog (priority, "%s", msg); + break; + } + /* NOTE: If syslog control file is absent, which is another + * way to control logging to syslog, then we will fall through + * to the gluster log. The ideal way to do things would be to + * not have the extra control file check */ + case gf_logger_glusterlog: + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) { + fprintf (ctx->log.logfile, "%s\n", msg); + fflush (ctx->log.logfile); + } else { + fprintf (stderr, "%s\n", msg); + fflush (stderr); + } + +#ifdef GF_LINUX_HOST_OS + /* We want only serious logs in 'syslog', not our debug + * and trace logs */ + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) + syslog ((level-1), "%s\n", msg); +#endif + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + + break; + } + + return 0; +} + +int +_gf_msg_plain (gf_loglevel_t level, const char *fmt, ...) +{ + xlator_t *this = NULL; + int ret = 0; + va_list ap; + char *msg = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + va_start (ap, fmt); + ret = vasprintf (&msg, fmt, ap); + va_end (ap); + if (-1 == ret) { + goto out; + } + + ret = _gf_msg_plain_internal (level, msg); + + FREE (msg); + +out: + return ret; +} + +int +_gf_msg_vplain (gf_loglevel_t level, const char *fmt, va_list ap) +{ + xlator_t *this = NULL; + int ret = 0; + char *msg = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + ret = vasprintf (&msg, fmt, ap); + if (-1 == ret) { + goto out; + } + + ret = _gf_msg_plain_internal (level, msg); + + FREE (msg); +out: + return ret; +} + +int +_gf_msg_plain_nomem (gf_loglevel_t level, const char *msg) +{ + xlator_t *this = NULL; + int ret = 0; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + ret = _gf_msg_plain_internal (level, msg); + +out: + return ret; +} + +#if HAVE_BACKTRACE +void +_gf_msg_backtrace_nomem (gf_loglevel_t level, int stacksize) +{ + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + void *array[200]; + size_t bt_size = 0; + int fd = -1; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + /* syslog does not have fd support, hence no no-mem variant */ + if (ctx->log.logger != gf_logger_glusterlog) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + bt_size = backtrace (array, ((stacksize <= 200)? stacksize : 200)); + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + fd = ctx->log.logfile? + fileno (ctx->log.logfile) : + fileno (stderr); + if (bt_size && (fd != -1)) { + /* print to the file fd, to prevent any + * allocations from backtrace_symbols */ + backtrace_symbols_fd (&array[0], bt_size, fd); + } + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + +out: + return; +} + +int +_gf_msg_backtrace (int stacksize, char *callstr, size_t strsize) +{ + int ret = -1; + int i = 0; + int size = 0; + int savstrsize = strsize; + void *array[200]; + char **callingfn = NULL; + + /* We chop off last 2 anyway, so if request is less than tolerance + * nothing to do */ + if (stacksize < 3) + goto out; + + size = backtrace (array, ((stacksize <= 200)? stacksize : 200)); + if ((size - 3) < 0) + goto out; + if (size) + callingfn = backtrace_symbols (&array[2], size - 2); + if (!callingfn) + goto out; + + ret = snprintf (callstr, strsize, "("); + PRINT_SIZE_CHECK (ret, out, strsize); + + for ((i = size - 3); i >= 0; i--) { + ret = snprintf (callstr + savstrsize - strsize, strsize, + "-->%s ", callingfn[i]); + PRINT_SIZE_CHECK (ret, out, strsize); + } + + ret = snprintf (callstr + savstrsize - strsize, strsize, ")"); + PRINT_SIZE_CHECK (ret, out, strsize); +out: + FREE (callingfn); + return ret; +} +#endif /* HAVE_BACKTRACE */ + +int +_gf_msg_nomem (const char *domain, const char *file, + const char *function, int line, gf_loglevel_t level, + size_t size) +{ + const char *basename = NULL; + xlator_t *this = NULL; + struct timeval tv = {0,}; + int ret = 0; + int fd = -1; + char msg[2048] = {0,}; + char timestr[GF_LOG_TIMESTR_SIZE] = {0,}; + glusterfs_ctx_t *ctx = NULL; + int wlen = 0; + int priority; + + this = THIS; + ctx = this->ctx; + + if (!ctx) + goto out; + + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + + if (!domain || !file || !function) { + fprintf (stderr, + "logging: %s:%s():%d: invalid argument\n", + __FILE__, __PRETTY_FUNCTION__, __LINE__); + return -1; + } + + GET_FILE_NAME_TO_LOG (file, basename); + + ret = gettimeofday (&tv, NULL); + if (-1 == ret) + goto out; + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + ret = snprintf (timestr + strlen (timestr), + sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, tv.tv_usec); + if (-1 == ret) { + goto out; + } + + /* TODO: Currently we print in the enhanced format, with a message ID + * of 0. Need to enhance this to support format as configured */ + ret = snprintf (msg, sizeof msg, "[%s] %s [MSGID: %"PRIu64"]" + " [%s:%d:%s] %s: no memory " + "available for size (%"GF_PRI_SIZET")" + " [call stack follows]\n", + timestr, gf_level_strings[level], (uint64_t) 0, + basename, line, function, domain, size); + if (-1 == ret) { + goto out; + } + + /* log to the configured logging service */ + switch (ctx->log.logger) { + case gf_logger_syslog: + if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) { + SET_LOG_PRIO (level, priority); + + /* if syslog allocates, then this may fail, but we + * cannot do much about it at the moment */ + /* There is no fd for syslog, hence no stack printed */ + syslog (priority, "%s", msg); + break; + } + /* NOTE: If syslog control file is absent, which is another + * way to control logging to syslog, then we will fall through + * to the gluster log. The ideal way to do things would be to + * not have the extra control file check */ + case gf_logger_glusterlog: + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + fd = ctx->log.logfile? fileno (ctx->log.logfile) : + fileno (stderr); + if (fd == -1) { + pthread_mutex_unlock (&ctx->log.logfile_mutex); + goto out; + } + + wlen = strlen (msg); + + /* write directly to the fd to prevent out of order + * message and stack */ + ret = write (fd, msg, wlen); + if (ret == -1) { + pthread_mutex_unlock (&ctx->log.logfile_mutex); + goto out; + } +#ifdef GF_LINUX_HOST_OS + /* We want only serious log in 'syslog', not our debug + * and trace logs */ + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) + syslog ((level-1), "%s\n", msg); +#endif + } + pthread_mutex_unlock (&ctx->log.logfile_mutex); + +#ifdef HAVE_BACKTRACE + _gf_msg_backtrace_nomem (level, GF_LOG_BACKTRACE_DEPTH); +#endif + + break; + } + +out: + return ret; +} + +static int +gf_log_syslog (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, + uint64_t msgid, char **appmsgstr, char *callstr) +{ + int priority; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + SET_LOG_PRIO (level, priority); + + /* log with appropriate format */ + if (ctx->log.logformat == gf_logformat_traditional) { + if (!callstr) { + if (errnum) { + syslog (priority, "[%s:%d:%s] %d-%s: %s [%s]", + file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[%s:%d:%s] %d-%s: %s", + file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } else { + if (errnum) { + syslog (priority, "[%s:%d:%s] %s %d-%s:" + " %s [%s]", + file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[%s:%d:%s] %s %d-%s: %s", + file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } + } else if (ctx->log.logformat == gf_logformat_withmsgid) { + if (!callstr) { + if (errnum) { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %d-%s: %s [%s]", + msgid, file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %d-%s: %s", + msgid, file, line, function, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } else { + if (errnum) { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %s %d-%s: %s [%s]", + msgid, file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr, strerror(errnum)); + } else { + syslog (priority, "[MSGID: %"PRIu64"]" + " [%s:%d:%s] %s %d-%s: %s", + msgid, file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain, *appmsgstr); + } + } + } else if (ctx->log.logformat == gf_logformat_cee) { + /* TODO: Enhance CEE with additional parameters */ + gf_syslog (GF_ERR_DEV, priority, + "[%s:%d:%s] %d-%s: %s", + file, line, function, + ((this->graph) ? this->graph->id:0), + domain, *appmsgstr); + } else { + /* NOTE: should not get here without logging */ + } + + /* TODO: There can be no errors from gf_syslog? */ + return 0; +} + +static int +gf_log_glusterlog (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, + uint64_t msgid, char **appmsgstr, char *callstr) +{ + char timestr[GF_LOG_TIMESTR_SIZE] = {0,}; + struct timeval tv = {0,}; + char *header = NULL; + char *footer = NULL; + char *msg = NULL; + size_t hlen = 0, flen = 0, mlen = 0; + int ret = 0; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + /* rotate if required */ + gf_log_rotate(ctx); + + /* format the time stanp */ + ret = gettimeofday (&tv, NULL); + if (-1 == ret) + goto out; + gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); + snprintf (timestr + strlen (timestr), sizeof timestr - strlen (timestr), + ".%"GF_PRI_SUSECONDS, tv.tv_usec); + + /* generate header and footer */ + if (ctx->log.logformat == gf_logformat_traditional) { + if (!callstr) { + ret = gf_asprintf (&header, "[%s] %s [%s:%d:%s]" + " %d-%s: ", + timestr, gf_level_strings[level], + file, line, function, + ((this->graph)?this->graph->id:0), + domain); + } else { + ret = gf_asprintf (&header, "[%s] %s [%s:%d:%s] %s" + " %d-%s: ", + timestr, gf_level_strings[level], + file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain); + } + if (-1 == ret) { + goto err; + } + } else { /* gf_logformat_withmsgid */ + /* CEE log format unsupported in logger_glusterlog, so just + * print enhanced log format */ + if (!callstr) { + ret = gf_asprintf (&header, "[%s] %s [MSGID: %"PRIu64"]" + " [%s:%d:%s] %d-%s: ", + timestr, gf_level_strings[level], + msgid, file, line, function, + ((this->graph)?this->graph->id:0), + domain); + } else { + ret = gf_asprintf (&header, "[%s] %s [MSGID: %"PRIu64"]" + " [%s:%d:%s] %s %d-%s: ", + timestr, gf_level_strings[level], + msgid, file, line, function, callstr, + ((this->graph)?this->graph->id:0), + domain); + } + if (-1 == ret) { + goto err; + } + } + + if (errnum) { + ret = gf_asprintf (&footer, " [%s]",strerror(errnum)); + if (-1 == ret) { + goto err; + } + } + + /* generate the full message to log */ + hlen = strlen (header); + flen = footer? strlen (footer) : 0; + mlen = strlen (*appmsgstr); + msg = GF_MALLOC (hlen + flen + mlen + 1, gf_common_mt_char); + + strcpy (msg, header); + strcpy (msg + hlen, *appmsgstr); + if (footer) + strcpy (msg + hlen + mlen, footer); + + pthread_mutex_lock (&ctx->log.logfile_mutex); + { + if (ctx->log.logfile) { + fprintf (ctx->log.logfile, "%s\n", msg); + fflush (ctx->log.logfile); + } else if (ctx->log.loglevel >= level) { + fprintf (stderr, "%s\n", msg); + fflush (stderr); + } + +#ifdef GF_LINUX_HOST_OS + /* We want only serious logs in 'syslog', not our debug + * and trace logs */ + if (ctx->log.gf_log_syslog && level && + (level <= ctx->log.sys_log_level)) + syslog ((level-1), "%s\n", msg); +#endif + } + + /* TODO: Plugin in memory log buffer retention here. For logs not + * flushed during cores, it would be useful to retain some of the last + * few messages in memory */ + pthread_mutex_unlock (&ctx->log.logfile_mutex); + +err: + GF_FREE (msg); + GF_FREE (header); + GF_FREE (footer); + +out: + return ret; +} + +static int +_gf_msg_internal (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, uint64_t msgid, + char **appmsgstr, char *callstr) +{ + const char *basename = NULL; + int ret = -1; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + + this = THIS; + ctx = this->ctx; + + GET_FILE_NAME_TO_LOG (file, basename); + + /* TODO: Plug in repeated message suppression for gluster logs here. + * Comparison of last few messages stored based on, appmsgstr, errnum + * msgid. */ + + /* log to the configured logging service */ + switch (ctx->log.logger) { + case gf_logger_syslog: + if (ctx->log.log_control_file_found && ctx->log.gf_log_syslog) { + ret = gf_log_syslog (domain, basename, function, line, + level, errnum, msgid, appmsgstr, + callstr); + break; + } + /* NOTE: If syslog control file is absent, which is another + * way to control logging to syslog, then we will fall through + * to the gluster log. The ideal way to do things would be to + * not have the extra control file check */ + case gf_logger_glusterlog: + ret = gf_log_glusterlog (domain, basename, function, line, + level, errnum, msgid, appmsgstr, + callstr); + break; + } + + return ret; +} + +int +_gf_msg (const char *domain, const char *file, const char *function, + int32_t line, gf_loglevel_t level, int errnum, int trace, + uint64_t msgid, const char *fmt, ...) +{ + int ret = 0; + char *msgstr = NULL; + va_list ap; + xlator_t *this = NULL; + glusterfs_ctx_t *ctx = NULL; + char callstr[GF_LOG_BACKTRACE_SIZE] = {0,}; + int passcallstr = 0; + + /* in args check */ + if (!domain || !file || !function || !fmt) { + fprintf (stderr, + "logging: %s:%s():%d: invalid argument\n", + __FILE__, __PRETTY_FUNCTION__, __LINE__); + return -1; + } + + this = THIS; + ctx = this->ctx; + if (ctx == NULL) { + /* messages before context initialization are ignored */ + return -1; + } + + /* check if we should be logging */ + if (ctx->log.gf_log_xl_log_set) { + if (this->loglevel && (level > this->loglevel)) + goto out; + } + if (level > ctx->log.loglevel) + goto out; + +#if HAVE_BACKTRACE + if (trace) { + ret = _gf_msg_backtrace (GF_LOG_BACKTRACE_DEPTH, callstr, + GF_LOG_BACKTRACE_DEPTH); + if (ret >= 0) + passcallstr = 1; + else + ret = 0; + } +#endif /* HAVE_BACKTRACE */ + + /* form the message */ + va_start (ap, fmt); + ret = vasprintf (&msgstr, fmt, ap); + va_end (ap); + + /* log */ + if (ret != -1) + ret = _gf_msg_internal(domain, file, function, line, level, + errnum, msgid, &msgstr, + (passcallstr? callstr : NULL)); + else + /* man (3) vasprintf states on error strp contents + * are undefined, be safe */ + msgstr = NULL; + + FREE (msgstr); + +out: + return ret; +} + +/* TODO: Deprecate (delete) _gf_log, _gf_log_callingfn, + * once messages are changed to use _gf_msgXXX APIs for logging */ int _gf_log (const char *domain, const char *file, const char *function, int line, gf_loglevel_t level, const char *fmt, ...) @@ -744,7 +1472,7 @@ _gf_log (const char *domain, const char *file, const char *function, int line, const char *basename = NULL; FILE *new_logfile = NULL; va_list ap; - char timestr[256] = {0,}; + char timestr[GF_LOG_TIMESTR_SIZE] = {0,}; struct timeval tv = {0,}; char *str1 = NULL; char *str2 = NULL; @@ -790,7 +1518,6 @@ _gf_log (const char *domain, const char *file, const char *function, int line, else basename = file; -#if defined(GF_USE_SYSLOG) if (ctx->log.log_control_file_found) { int priority; @@ -812,7 +1539,6 @@ _gf_log (const char *domain, const char *file, const char *function, int line, ((this->graph) ? this->graph->id:0), domain, str2); goto err; } -#endif /* GF_USE_SYSLOG */ if (ctx->log.logrotate) { ctx->log.logrotate = 0; @@ -839,7 +1565,8 @@ _gf_log (const char *domain, const char *file, const char *function, int line, if (ctx->log.logfile) fclose (ctx->log.logfile); - ctx->log.gf_log_logfile = ctx->log.logfile = new_logfile; + ctx->log.gf_log_logfile = + ctx->log.logfile = new_logfile; } pthread_mutex_unlock (&ctx->log.logfile_mutex); @@ -1033,7 +1760,8 @@ gf_cmd_log (const char *domain, const char *fmt, ...) goto out; va_start (ap, fmt); gf_time_fmt (timestr, sizeof timestr, tv.tv_sec, gf_timefmt_FT); - snprintf (timestr + strlen (timestr), 256 - strlen (timestr), + snprintf (timestr + strlen (timestr), + GF_LOG_TIMESTR_SIZE - strlen (timestr), ".%"GF_PRI_SUSECONDS, tv.tv_usec); ret = gf_asprintf (&str1, "[%s] %s : ", diff --git a/libglusterfs/src/logging.h b/libglusterfs/src/logging.h index e2b7e664d..b6b13fbd0 100644 --- a/libglusterfs/src/logging.h +++ b/libglusterfs/src/logging.h @@ -61,6 +61,20 @@ typedef enum { GF_LOG_TRACE, /* full trace of operation */ } gf_loglevel_t; +/* format for the logs */ +typedef enum { + gf_logformat_traditional = 0, /* Format as in gluster 3.5 */ + gf_logformat_withmsgid, /* Format enhanced with MsgID, ident, errstr */ + gf_logformat_cee /* log enhanced format in cee */ +} gf_log_format_t; + +/* log infrastructure to log to */ +typedef enum { + gf_logger_glusterlog = 0, /* locations and files as in gluster 3.5 */ + gf_logger_syslog /* log to (r)syslog, based on (r)syslog conf */ + /* NOTE: In the future journald, lumberjack, next new thing here */ +} gf_log_logger_t; + #define DEFAULT_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" #define DEFAULT_LOG_LEVEL GF_LOG_INFO @@ -76,11 +90,10 @@ typedef struct gf_log_handle_ { FILE *gf_log_logfile; char *cmd_log_filename; FILE *cmdlogfile; -#ifdef GF_USE_SYSLOG - int log_control_file_found; + gf_log_logger_t logger; + gf_log_format_t logformat; char *ident; -#endif /* GF_USE_SYSLOG */ - + int log_control_file_found; } gf_log_handle_t; void gf_log_globals_init (void *ctx); @@ -90,25 +103,117 @@ void gf_log_logrotate (int signum); void gf_log_cleanup (void); +/* Internal interfaces to log messages with message IDs */ +int _gf_msg (const char *domain, const char *file, + const char *function, int32_t line, gf_loglevel_t level, + int errnum, int trace, uint64_t msgid, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 9, 10))); + +void _gf_msg_backtrace_nomem (gf_loglevel_t level, int stacksize); + +int _gf_msg_plain (gf_loglevel_t level, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + +int _gf_msg_plain_nomem (gf_loglevel_t level, const char *msg); + +int _gf_msg_vplain (gf_loglevel_t level, const char *fmt, va_list ap); + +int _gf_msg_nomem (const char *domain, const char *file, + const char *function, int line, gf_loglevel_t level, + size_t size); + int _gf_log (const char *domain, const char *file, const char *function, int32_t line, gf_loglevel_t level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 6, 7))); + int _gf_log_callingfn (const char *domain, const char *file, const char *function, int32_t line, gf_loglevel_t level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 6, 7))); -int _gf_log_nomem (const char *domain, const char *file, - const char *function, int line, gf_loglevel_t level, - size_t size); - int _gf_log_eh (const char *function, const char *fmt, ...); +/* treat GF_LOG_TRACE and GF_LOG_NONE as LOG_DEBUG and + * other level as is */ +#define SET_LOG_PRIO(level, priority) do { \ + if (GF_LOG_TRACE == (level) || GF_LOG_NONE == (level)) { \ + priority = LOG_DEBUG; \ + } else { \ + priority = (level) - 1; \ + } \ + } while (0) + +/* extract just the file name from the path */ +#define GET_FILE_NAME_TO_LOG(file, basename) do { \ + basename = strrchr ((file), '/'); \ + if (basename) \ + basename++; \ + else \ + basename = (file); \ + } while (0) + +#define PRINT_SIZE_CHECK(ret, label, strsize) do { \ + if (ret < 0) \ + goto label; \ + if ((strsize - ret) > 0) { \ + strsize -= ret; \ + } else { \ + ret = 0; \ + goto label; \ + } \ + } while (0) + #define FMT_WARN(fmt...) do { if (0) printf (fmt); } while (0) +/* Interface to log messages with message IDs */ +#define gf_msg(dom, levl, errnum, msgid, fmt...) do { \ + _gf_msg (dom, __FILE__, __FUNCTION__, __LINE__, \ + levl, errnum, 0, msgid, ##fmt); \ + } while (0) + +/* no frills, no thrills, just a vanilla message, used to print the graph */ +#define gf_msg_plain(levl, fmt...) do { \ + _gf_msg_plain (levl, ##fmt); \ + } while (0) + +#define gf_msg_plain_nomem(levl, msg) do { \ + _gf_msg_plain_nomem (levl, msg); \ + } while (0) + +#define gf_msg_vplain(levl, fmt, va) do { \ + _gf_msg_vplain (levl, fmt, va); \ + } while (0) + +#define gf_msg_backtrace_nomem(level, stacksize) do { \ + _gf_msg_backtrace_nomem (level, stacksize); \ + } while (0) + +#define gf_msg_callingfn(dom, levl, errnum, msgid, fmt...) do { \ + _gf_msg (dom, __FILE__, __FUNCTION__, __LINE__, \ + levl, errnum, 1, msgid, ##fmt); \ + } while (0) + +/* No malloc or calloc should be called in this function */ +#define gf_msg_nomem(dom, levl, size) do { \ + _gf_msg_nomem (dom, __FILE__, __FUNCTION__, __LINE__, \ + levl, size); \ + } while (0) + +/* Debug or trace messages do not need message IDs as these are more developer + * related. Hence, the following abstractions are provided for the same */ +#define gf_msg_debug(dom, errnum, fmt...) do { \ + _gf_msg (dom, __FILE__, __FUNCTION__, __LINE__, \ + GF_LOG_DEBUG, errnum, 0, 0, ##fmt); \ + } while (0) + +#define gf_msg_trace(dom, errnum, fmt...) do { \ + _gf_msg (dom, __FILE__, __FUNCTION__, __LINE__, \ + GF_LOG_TRACE, errnum, 0, 0, ##fmt); \ + } while (0) + #define gf_log(dom, levl, fmt...) do { \ FMT_WARN (fmt); \ _gf_log (dom, __FILE__, __FUNCTION__, __LINE__, \ @@ -127,13 +232,6 @@ int _gf_log_eh (const char *function, const char *fmt, ...); } while (0) -/* No malloc or calloc should be called in this function */ -#define gf_log_nomem(dom, levl, size) do { \ - _gf_log_nomem (dom, __FILE__, __FUNCTION__, __LINE__, \ - levl, size); \ - } while (0) - - /* Log once in GF_UNIVERSAL_ANSWER times */ #define GF_LOG_OCCASIONALLY(var, args...) if (!(var++%GF_UNIVERSAL_ANSWER)) { \ gf_log (args); \ @@ -143,6 +241,7 @@ void gf_log_disable_syslog (void); void gf_log_enable_syslog (void); gf_loglevel_t gf_log_get_loglevel (void); void gf_log_set_loglevel (gf_loglevel_t level); +void gf_log_flush (void); gf_loglevel_t gf_log_get_xl_loglevel (void *xl); void gf_log_set_xl_loglevel (void *xl, gf_loglevel_t level); diff --git a/libglusterfs/src/mem-pool.c b/libglusterfs/src/mem-pool.c index b92803d4d..c5ff58f4f 100644 --- a/libglusterfs/src/mem-pool.c +++ b/libglusterfs/src/mem-pool.c @@ -105,7 +105,7 @@ __gf_calloc (size_t nmemb, size_t size, uint32_t type) ptr = calloc (1, tot_size); if (!ptr) { - gf_log_nomem ("", GF_LOG_ALERT, tot_size); + gf_msg_nomem ("", GF_LOG_ALERT, tot_size); return NULL; } gf_mem_set_acct_info (xl, &ptr, req_size, type); @@ -129,7 +129,7 @@ __gf_malloc (size_t size, uint32_t type) ptr = malloc (tot_size); if (!ptr) { - gf_log_nomem ("", GF_LOG_ALERT, tot_size); + gf_msg_nomem ("", GF_LOG_ALERT, tot_size); return NULL; } gf_mem_set_acct_info (xl, &ptr, size, type); @@ -163,7 +163,7 @@ __gf_realloc (void *ptr, size_t size) new_ptr = realloc (orig_ptr, tot_size); if (!new_ptr) { - gf_log_nomem ("", GF_LOG_ALERT, tot_size); + gf_msg_nomem ("", GF_LOG_ALERT, tot_size); return NULL; } diff --git a/libglusterfs/src/mem-pool.h b/libglusterfs/src/mem-pool.h index aa6bf7843..9ffeef4da 100644 --- a/libglusterfs/src/mem-pool.h +++ b/libglusterfs/src/mem-pool.h @@ -75,7 +75,7 @@ void* __gf_default_malloc (size_t size) ptr = malloc (size); if (!ptr) - gf_log_nomem ("", GF_LOG_ALERT, size); + gf_msg_nomem ("", GF_LOG_ALERT, size); return ptr; } @@ -87,7 +87,7 @@ void* __gf_default_calloc (int cnt, size_t size) ptr = calloc (cnt, size); if (!ptr) - gf_log_nomem ("", GF_LOG_ALERT, (cnt * size)); + gf_msg_nomem ("", GF_LOG_ALERT, (cnt * size)); return ptr; } @@ -99,7 +99,7 @@ void* __gf_default_realloc (void *oldptr, size_t size) ptr = realloc (oldptr, size); if (!ptr) - gf_log_nomem ("", GF_LOG_ALERT, size); + gf_msg_nomem ("", GF_LOG_ALERT, size); return ptr; } diff --git a/libglusterfs/src/template-component-messages.h b/libglusterfs/src/template-component-messages.h new file mode 100644 index 000000000..c1ea38cf7 --- /dev/null +++ b/libglusterfs/src/template-component-messages.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2013 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. + */ + +#ifndef _component_MESSAGES_H_ +#define _component_MESSAGES_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glfs-message-id.h" + +/* NOTE: Rules for message additions + * 1) Each instance of a message is _better_ left with a unique message ID, even + * if the message format is the same. Reasoning is that, if the message + * format needs to change in one instance, the other instances are not + * impacted or the new change does not change the ID of the instance being + * modified. + * 2) Addition of a message, + * - Should increment the GLFS_NUM_MESSAGES + * - Append to the list of messages defined, towards the end + * - Retain macro naming as glfs_msg_X (for redability across developers) + * NOTE: Rules for message format modifications + * 3) Check acorss the code if the message ID macro in question is reused + * anywhere. If reused then then the modifications should ensure correctness + * everywhere, or needs a new message ID as (1) above was not adhered to. If + * not used anywhere, proceed with the required modification. + * NOTE: Rules for message deletion + * 4) Check (3) and if used anywhere else, then cannot be deleted. If not used + * anywhere, then can be deleted, but will leave a hole by design, as + * addition rules specify modification to the end of the list and not filling + * holes. + */ + +#define GLFS_COMP_BASE GLFS_MSGID_COMP_ +#define GLFS_NUM_MESSAGES 1 +#define GLFS_MSGID_END (GLFS_COMP_BASE + GLFS_NUM_MESSAGES + 1) +/* Messaged with message IDs */ +#define glfs_msg_start_x GLFS_COMP_BASE, "Invalid: Start of messages" +/*------------*/ +#define _msg_1 (GLFS_COMP_BASE + 1), "Test message, replace with"\ + " original when using the template" + +/*------------*/ +#define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages" + +#endif /* !_component_MESSAGES_H_ */ \ No newline at end of file diff --git a/libglusterfs/src/unittest/log_mock.c b/libglusterfs/src/unittest/log_mock.c index 676df7cfd..fec48bafc 100644 --- a/libglusterfs/src/unittest/log_mock.c +++ b/libglusterfs/src/unittest/log_mock.c @@ -39,5 +39,12 @@ int _gf_log_nomem (const char *domain, const char *file, return 0; } +int _gf_msg_nomem (const char *domain, const char *file, + const char *function, int line, gf_loglevel_t level, + size_t size) +{ + return 0; +} + void gf_log_globals_init (void *data) {} diff --git a/rpc/rpc-transport/rdma/src/rdma.c b/rpc/rpc-transport/rdma/src/rdma.c index 6e6099a98..c09067230 100644 --- a/rpc/rpc-transport/rdma/src/rdma.c +++ b/rpc/rpc-transport/rdma/src/rdma.c @@ -104,7 +104,7 @@ gf_rdma_new_post (rpc_transport_t *this, gf_rdma_device_t *device, int32_t len, post->buf = valloc (len); if (!post->buf) { - gf_log_nomem (GF_RDMA_LOG_NAME, GF_LOG_ERROR, len); + gf_msg_nomem (GF_RDMA_LOG_NAME, GF_LOG_ERROR, len); goto out; } diff --git a/tests/basic/logchecks-messages.h b/tests/basic/logchecks-messages.h new file mode 100644 index 000000000..50efe9dfa --- /dev/null +++ b/tests/basic/logchecks-messages.h @@ -0,0 +1,84 @@ +/* + Copyright (c) 2013 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. + */ + +#ifndef _LOGCHECKS_MESSAGES_H_ +#define _LOGCHECKS_MESSAGES_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glfs-message-id.h" + +/* NOTE: Rules for message additions + * 1) Each instance of a message is _better_ left with a unique message ID, even + * if the message format is the same. Reasoning is that, if the message + * format needs to change in one instance, the other instances are not + * impacted or the new change does not change the ID of the instance being + * modified. + * 2) Addition of a message, + * - Should increment the GLFS_NUM_MESSAGES + * - Append to the list of messages defined, towards the end + * - Retain macro naming as glfs_msg_X (for redability across developers) + * NOTE: Rules for message format modifications + * 3) Check acorss the code if the message ID macro in question is reused + * anywhere. If reused then then the modifications should ensure correctness + * everywhere, or needs a new message ID as (1) above was not adhered to. If + * not used anywhere, proceed with the required modification. + * NOTE: Rules for message deletion + * 4) Check (3) and if used anywhere else, then cannot be deleted. If not used + * anywhere, then can be deleted, but will leave a hole by design, as + * addition rules specify modification to the end of the list and not filling + * holes. + */ + +#define GLFS_COMP_BASE 1000 +#define GLFS_NUM_MESSAGES 19 +#define GLFS_MSGID_END (GLFS_COMP_BASE + GLFS_NUM_MESSAGES + 1) +/* Messaged with message IDs */ +#define glfs_msg_start_x GLFS_COMP_BASE, "Invalid: Start of messages" +/*------------*/ +#define logchecks_msg_1 (GLFS_COMP_BASE + 1), "Informational: Testing logging" \ + " in gluster" +#define logchecks_msg_2 (GLFS_COMP_BASE + 2), "Informational: Format testing:" \ + " %d:%s:%x" +#define logchecks_msg_3 (GLFS_COMP_BASE + 3), "Critical: Testing logging" \ + " in gluster" +#define logchecks_msg_4 (GLFS_COMP_BASE + 4), "Critical: Format testing:" \ + " %d:%s:%x" +#define logchecks_msg_5 (GLFS_COMP_BASE + 5), "Critical: Rotated the log" +#define logchecks_msg_6 (GLFS_COMP_BASE + 6), "Critical: Flushed the log" +#define logchecks_msg_7 (GLFS_COMP_BASE + 7), "Informational: gf_msg_callingfn" +#define logchecks_msg_8 (GLFS_COMP_BASE + 8), "Informational: " \ + "gf_msg_callingfn: Format testing: %d:%s:%x" +#define logchecks_msg_9 (GLFS_COMP_BASE + 9), "Critical: gf_msg_callingfn" +#define logchecks_msg_10 (GLFS_COMP_BASE + 10), "Critical: " \ + "gf_msg_callingfn: Format testing: %d:%s:%x" +#define logchecks_msg_11 (GLFS_COMP_BASE + 11), "==========================" +#define logchecks_msg_12 (GLFS_COMP_BASE + 12), "Test 1: Only stderr and" \ + " partial syslog" +#define logchecks_msg_13 (GLFS_COMP_BASE + 13), "Test 2: Only checklog and" \ + " partial syslog" +#define logchecks_msg_14 (GLFS_COMP_BASE + 14), "Test 5: Changing to" \ + " traditional format" +#define logchecks_msg_15 (GLFS_COMP_BASE + 15), "Test 6: Changing log level" \ + " to critical and above" +#define logchecks_msg_16 (GLFS_COMP_BASE + 16), "Test 7: Only to syslog" +#define logchecks_msg_17 (GLFS_COMP_BASE + 17), "Test 8: Only to syslog," \ + " traditional format" +#define logchecks_msg_18 (GLFS_COMP_BASE + 18), "Test 9: Only to syslog," \ + " only critical and above" +#define logchecks_msg_19 (GLFS_COMP_BASE + 19), "Pre init message, not to be" \ + " seen in logs" +/*------------*/ +#define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages" + +#endif /* !_component_MESSAGES_H_ */ \ No newline at end of file diff --git a/tests/basic/logchecks.c b/tests/basic/logchecks.c new file mode 100644 index 000000000..4f858a7fc --- /dev/null +++ b/tests/basic/logchecks.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2013 Red Hat, Inc. + * This file is part of GlusterFS. + * + * This file is licensed to you under your choice of the GNU Lesser + * General Public License, version 3 or any later version (LGPLv3 or + * later), or the GNU General Public License, version 2 (GPLv2), in all + * cases as published by the Free Software Foundation. + */ + +#include +#include + +#include "glusterfs.h" +#include "globals.h" +#include "logging.h" + +#include "logchecks-messages.h" +#include "../../libglusterfs/src/logging.h" + +glusterfs_ctx_t *ctx = NULL; + +#define TEST_FILENAME "/tmp/logchecks.log" +#define GF_LOG_CONTROL_FILE "/etc/glusterfs/logger.conf" + +int +go_log_vargs(gf_loglevel_t level, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + gf_msg_vplain (level, fmt, ap); + va_end (ap); + + return 0; +} + +int +go_log (void) +{ + /*** gf_msg ***/ + gf_msg ("logchecks", GF_LOG_INFO, 0, logchecks_msg_1); + gf_msg ("logchecks", GF_LOG_INFO, 22, logchecks_msg_2, 42, "Forty-Two", + 42); + /* change criticality */ + gf_msg ("logchecks", GF_LOG_CRITICAL, 0, logchecks_msg_3); + gf_msg ("logchecks", GF_LOG_CRITICAL, 22, logchecks_msg_4, 42, + "Forty-Two", 42); + + /*** msg_nomem ***/ + gf_msg_nomem ("logchecks", GF_LOG_ALERT, 555); + gf_msg_nomem ("logchecks", GF_LOG_INFO, 555); + + /*** msg_plain ***/ + gf_msg_plain (GF_LOG_INFO, "Informational: gf_msg_plain with" + " args %d:%s:%x", 42, "Forty-Two", 42); + gf_msg_plain (GF_LOG_ALERT, "Alert: gf_msg_plain with" + " args %d:%s:%x", 42, "Forty-Two", 42); + + /*** msg_vplain ***/ + go_log_vargs (GF_LOG_INFO, "Informational: gf_msg_vplain: No args!!!"); + go_log_vargs (GF_LOG_INFO, "Informational: gf_msg_vplain: Some" + " args %d:%s:%x", 42, "Forty-Two", 42); + go_log_vargs (GF_LOG_INFO, "Critical: gf_msg_vplain: No args!!!"); + go_log_vargs (GF_LOG_INFO, "Critical: gf_msg_vplain: Some" + " args %d:%s:%x", 42, "Forty-Two", 42); + + /*** msg_plain_nomem ***/ + gf_msg_plain_nomem (GF_LOG_INFO, "Informational: gf_msg_plain_nomem"); + gf_msg_plain_nomem (GF_LOG_ALERT, "Alert: gf_msg_plain_nomem"); + + /*** msg_backtrace_nomem ***/ + // TODO: Need to create a stack depth and then call + gf_msg_backtrace_nomem (GF_LOG_INFO, 5); + gf_msg_backtrace_nomem (GF_LOG_ALERT, 5); + + /*** gf_msg_callingfn ***/ + // TODO: Need to create a stack depth and then call + gf_msg_callingfn ("logchecks", GF_LOG_INFO, 0, logchecks_msg_7); + gf_msg_callingfn ("logchecks", GF_LOG_INFO, 0, logchecks_msg_8, 42, + "Forty-Two", 42); + gf_msg_callingfn ("logchecks", GF_LOG_CRITICAL, 0, logchecks_msg_9); + gf_msg_callingfn ("logchecks", GF_LOG_CRITICAL, 0, logchecks_msg_10, 42, + "Forty-Two", 42); + + /*** gf_msg_debug ***/ + gf_msg_debug ("logchecks", 0, "Debug: Hello World!!!"); + gf_msg_debug ("logchecks", 22, "Debug: With args %d:%s:%x", 42, + "Forty-Two", 42); + + /*** gf_msg_trace ***/ + gf_msg_trace ("logchecks", 0, "Trace: Hello World!!!"); + gf_msg_trace ("logchecks", 22, "Trace: With args %d:%s:%x", 42, + "Forty-Two", 42); + + /*** gf_msg_backtrace ***/ + // TODO: Test with lower callstr values to check truncation + + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret = -1; + + unlink (GF_LOG_CONTROL_FILE); + creat (GF_LOG_CONTROL_FILE, O_RDONLY); + ctx = glusterfs_ctx_new (); + if (!ctx) + return -1; + + ret = glusterfs_globals_init (ctx); + if (ret) { + printf ("Error from glusterfs_globals_init [%s]\n", + strerror (errno)); + return ret; + } + + /* Pre init test, message should not be printed */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_19); + + THIS->ctx = ctx; + + /* TEST 1: messages before initializing the log, goes to stderr + * and syslog based on criticality */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_12); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 2: messages post initialization, goes to glusterlog and + * syslog based on severity */ + ret = gf_log_init(ctx, TEST_FILENAME, "logchecks"); + if (ret != 0) { + printf ("Error from gf_log_init [%s]\n", strerror (errno)); + return -1; + } + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_13); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 3: Test rotation */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_logrotate (0); + gf_msg ("logchecks", GF_LOG_CRITICAL, 0, logchecks_msg_5); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 4: Check flush, nothing noticable should occur :) */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_flush (); + gf_msg ("logchecks", GF_LOG_CRITICAL, 0, logchecks_msg_6); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 5: Change format */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_set_logformat (gf_logformat_traditional); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_14); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 6: Change level */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_set_loglevel (GF_LOG_CRITICAL); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_15); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* Reset to run with syslog */ + gf_log_set_logformat (gf_logformat_withmsgid); + gf_log_set_loglevel (GF_LOG_INFO); + + /* Run tests with logger changed to syslog */ + /* TEST 7: No more gluster logs */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_set_logger (gf_logger_syslog); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_16); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 8: Change format */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_set_logformat (gf_logformat_traditional); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_14); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + /* TEST 9: Change level */ + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + gf_log_set_loglevel (GF_LOG_CRITICAL); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_15); + go_log (); + gf_msg ("logchecks", GF_LOG_ALERT, 0, logchecks_msg_11); + + // TODO: signal crash prints, but not yet feasible here + // TODO: Graph printing + // TODO: Multi threaded logging + + /* Close out the logging */ + gf_log_fini (ctx); + gf_log_globals_fini (); + + unlink (GF_LOG_CONTROL_FILE); + unlink (TEST_FILENAME); + + return 0; +} \ No newline at end of file -- cgit From 7c36d9ec0de67b9b9a0803fcd378ef8d3b453c87 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 25 Mar 2014 10:13:35 -0700 Subject: doc: Update manpages Change-Id: Id14c7c3229ed266cd15915a2136e3290ce2c5ed2 BUG: 1031328 Signed-off-by: Harshavardhana Reviewed-on: http://review.gluster.org/7338 Reviewed-by: Lalatendu Mohanty Reviewed-by: Vijay Bellur Tested-by: Vijay Bellur --- doc/gluster.8 | 7 ++++--- doc/mount.glusterfs.8 | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/doc/gluster.8 b/doc/gluster.8 index 3c78fb8b1..1d2a4d097 100644 --- a/doc/gluster.8 +++ b/doc/gluster.8 @@ -71,7 +71,10 @@ If you remove the brick, the data stored in that brick will not be available. Yo .B replace-brick option. .TP -\fB\ volume rebalance-brick ( ) start \fR +\fB\ volume replace-brick ( ) start|pause|abort|status|commit \fR +Replace the specified brick. +.TP +\fB\ volume rebalance start \fR Start rebalancing the specified volume. .TP \fB\ volume rebalance stop \fR @@ -80,8 +83,6 @@ Stop rebalancing the specified volume. \fB\ volume rebalance status \fR Display the rebalance status of the specified volume. .TP -\fB\ volume replace-brick ( ) start|pause|abort|status|commit \fR -Replace the specified brick. .SS "Log Commands" .TP \fB\ volume log filename [BRICK] \fB diff --git a/doc/mount.glusterfs.8 b/doc/mount.glusterfs.8 index e6061ffc6..32260ced0 100644 --- a/doc/mount.glusterfs.8 +++ b/doc/mount.glusterfs.8 @@ -62,6 +62,9 @@ Mount the filesystem read-only \fBenable\-ino32=\fRBOOL Use 32-bit inodes when mounting to workaround broken applications that don't support 64-bit inodes +.TP +\fBmem\-accounting +Enable internal memory accounting .PP .SS "Advanced options" @@ -108,6 +111,22 @@ Provide list of backup volfile servers in the following format [default: None] \fB :/ .TP +.TP +\fBfetch-attempts=\fRN +\fBDeprecated\fR option - placed here for backward compatibility [default: 1] +.TP +.TP +\fBbackground-qlen=\fRN +Set fuse module's background queue length to N [default: 64] +.TP +\fBno\-root\-squash=\fRBOOL +disable root squashing for the trusted client [default: off] +.TP +\fBroot\-squash=\fRBOOL +enable root squashing for the trusted client [default: on] +.TP +\fBuse\-readdirp=\fRBOOL +Use readdirp() mode in fuse kernel module [default: on] .PP .SH FILES .TP -- cgit From 283ae136d4974eefabd65880098449ae244b2d50 Mon Sep 17 00:00:00 2001 From: Pranith Kumar K Date: Wed, 26 Mar 2014 11:03:01 +0530 Subject: tests: Stale file lookup test Change-Id: I6edfc5b7ee42677e92d9cff6a7180692d20e9310 BUG: 1080759 Signed-off-by: Pranith Kumar K Reviewed-on: http://review.gluster.org/7341 Reviewed-by: Ravishankar N Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- tests/basic/afr/stale-file-lookup.t | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/basic/afr/stale-file-lookup.t diff --git a/tests/basic/afr/stale-file-lookup.t b/tests/basic/afr/stale-file-lookup.t new file mode 100644 index 000000000..24a478d5c --- /dev/null +++ b/tests/basic/afr/stale-file-lookup.t @@ -0,0 +1,30 @@ +#!/bin/bash + +#This file checks if stale file lookup fails or not. +#A file is deleted when a brick was down. Before self-heal could happen to it +#the file is accessed. It should fail. +. $(dirname $0)/../../include.rc +. $(dirname $0)/../../volume.rc + +cleanup; + +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/${V0}{0,1} +TEST $CLI volume set $V0 self-heal-daemon off +TEST $CLI volume set $V0 cluster.metadata-self-heal off +TEST $CLI volume set $V0 cluster.entry-self-heal off +TEST $CLI volume set $V0 cluster.data-self-heal off +TEST $CLI volume start $V0 + +TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 $M0 --attribute-timeout=0 --entry-timeout=0 +TEST touch $M0/a +TEST kill_brick $V0 $H0 $B0/${V0}0 +TEST rm -f $M0/a +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +TEST stat $B0/${V0}0/a +TEST ! stat $B0/${V0}1/a +TEST ! ls -l $M0/a + +cleanup -- cgit From 9a3de81fe5c42c0495dccc5877cecbc2edb81f3c Mon Sep 17 00:00:00 2001 From: Susant Palai Date: Tue, 18 Feb 2014 13:03:50 +0000 Subject: DHT/Rebalance : Hard link Migration Failure Probelm : __is_file_migratable used to return ENOTSUP for all the cases. Hence, it will add to the failure count. And the remove-brick status will show failure for all the files. Solution : Added 'ret = -2' to gf_defrag_handle_hardlink to be deemed as success. Otherwise dht_migrate_file will try to migrate each of the hard link, which not intended. Change-Id: Iff74f6634fb64e4b91fc5d016e87ff1290b7a0d6 BUG: 1066798 Signed-off-by: Susant Palai Reviewed-on: http://review.gluster.org/7124 Reviewed-by: Raghavendra G Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- tests/bugs/bug-1066798.t | 86 +++++++++++++++++++++++++++++++++ xlators/cluster/dht/src/dht-rebalance.c | 62 +++++++++++++++++++++--- 2 files changed, 142 insertions(+), 6 deletions(-) create mode 100755 tests/bugs/bug-1066798.t diff --git a/tests/bugs/bug-1066798.t b/tests/bugs/bug-1066798.t new file mode 100755 index 000000000..635b143f0 --- /dev/null +++ b/tests/bugs/bug-1066798.t @@ -0,0 +1,86 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc + +cleanup; + +TESTS_EXPECTED_IN_LOOP=200 + +## Start glusterd +TEST glusterd; +TEST pidof glusterd; +TEST $CLI volume info; + +## Lets create volume +TEST $CLI volume create $V0 $H0:$B0/${V0}{1,2}; + +## Verify volume is created +EXPECT "$V0" volinfo_field $V0 'Volume Name'; +EXPECT 'Created' volinfo_field $V0 'Status'; + +## Start volume and verify +TEST $CLI volume start $V0; +EXPECT 'Started' volinfo_field $V0 'Status'; +TEST glusterfs -s $H0 --volfile-id=$V0 $M0 + +############################################################ +#TEST_PLAN# +#Create a file +#Store the hashed brick information +#Create hard links to it +#Remove the hashed brick +#Check now all the hardlinks are migrated in to "OTHERBRICK" +#Check also in mount point for all the files +#check there is no failures and skips for migration +############################################################ + +TEST touch $M0/file1; + +file_perm=`ls -l $M0/file1 | grep file1 | awk '{print $1}'`; + +if [ -f $B0/${V0}1/file1 ] +then + HASHED=$B0/${V0}1 + OTHER=$B0/${V0}2 +else + HASHED=$B0/${V0}2 + OTHER=$B0/${V0}1 +fi + +#create hundred hard links +for i in {1..50}; +do +TEST_IN_LOOP ln $M0/file1 $M0/link$i; +done + + +TEST $CLI volume remove-brick $V0 $H0:${HASHED} start +EXPECT_WITHIN 20 "completed" remove_brick_status_completed_field "$V0" "$H0:${HASHED}"; + +#check consistency in mount point +#And also check all the links are migrated to OTHER +for i in {1..50} +do +TEST_IN_LOOP [ -f ${OTHER}/link${i} ]; +TEST_IN_LOOP [ -f ${M0}/link${i} ]; +done; + +#check in OTHER that all the files has proper permission (Means no +#linkto files) + +for i in {1..50} +do +link_perm=`ls -l $OTHER | grep -w link${i} | awk '{print $1}'`; +TEST_IN_LOOP [ "${file_perm}" == "${link_perm}" ] + +done + +#check that remove-brick status should not have any failed or skipped files + +var=`$CLI volume remove-brick $V0 $H0:${HASHED} status | grep completed` + +TEST [ `echo $var | awk '{print $5}'` = "0" ] +TEST [ `echo $var | awk '{print $6}'` = "0" ] + +cleanup diff --git a/xlators/cluster/dht/src/dht-rebalance.c b/xlators/cluster/dht/src/dht-rebalance.c index a17319ba6..4f78f5203 100644 --- a/xlators/cluster/dht/src/dht-rebalance.c +++ b/xlators/cluster/dht/src/dht-rebalance.c @@ -94,6 +94,41 @@ out: } +/* + return values: + -1 : failure + -2 : success + +Hard link migration is carried out in three stages. + +(Say there are n hardlinks) +Stage 1: Setting the new hashed subvol information on the 1st hardlink + encountered (linkto setxattr) + +Stage 2: Creating hardlinks on new hashed subvol for the 2nd to (n-1)th + hardlink + +Stage 3: Physical migration of the data file for nth hardlink + +Why to deem "-2" as success and not "0": + + dht_migrate_file expects return value "0" from _is_file_migratable if +the file has to be migrated. + + _is_file_migratable returns zero only when it is called with the +flag "GF_DHT_MIGRATE_HARDLINK_IN_PROGRESS". + + gf_defrag_handle_hardlink calls dht_migrate_file for physical migration +of the data file with the flag "GF_DHT_MIGRATE_HARDLINK_IN_PROGRESS" + +Hence, gf_defrag_handle_hardlink returning "0" for success will force +"dht_migrate_file" to migrate each of the hardlink which is not intended. + +For each of the three stage mentioned above "-2" will be returned and will +be converted to "0" in dht_migrate_file. + +*/ + int32_t gf_defrag_handle_hardlink (xlator_t *this, loc_t *loc, dict_t *xattrs, struct iatt *stbuf) @@ -164,6 +199,7 @@ gf_defrag_handle_hardlink (xlator_t *this, loc_t *loc, dict_t *xattrs, ret = -1; goto out; } + ret = -2; goto out; } else { linkto_subvol = dht_linkfile_subvol (this, NULL, NULL, xattrs); @@ -200,12 +236,19 @@ gf_defrag_handle_hardlink (xlator_t *this, loc_t *loc, dict_t *xattrs, if (ret) goto out; } - ret = 0; + ret = -2; out: return ret; } - +/* + return values + 0 : File will be migrated + -2 : File will not be migrated + (This is the return value from gf_defrag_handle_hardlink. Checkout + gf_defrag_handle_hardlink for description of "returning -2") + -1 : failure +*/ static inline int __is_file_migratable (xlator_t *this, loc_t *loc, struct iatt *stbuf, dict_t *xattrs, int flags) @@ -228,7 +271,12 @@ __is_file_migratable (xlator_t *this, loc_t *loc, if (flags == GF_DHT_MIGRATE_HARDLINK) { ret = gf_defrag_handle_hardlink (this, loc, xattrs, stbuf); - if (ret) { + + /* + Returning zero will force the file to be remigrated. + Checkout gf_defrag_handle_hardlink for more information. + */ + if (ret && ret != -2) { gf_log (this->name, GF_LOG_WARNING, "%s: failed to migrate file with link", loc->path); @@ -236,8 +284,8 @@ __is_file_migratable (xlator_t *this, loc_t *loc, } else { gf_log (this->name, GF_LOG_WARNING, "%s: file has hardlinks", loc->path); + ret = -ENOTSUP; } - ret = ENOTSUP; goto out; } @@ -743,9 +791,11 @@ dht_migrate_file (xlator_t *this, loc_t *loc, xlator_t *from, xlator_t *to, /* Check if file can be migrated */ ret = __is_file_migratable (this, loc, &stbuf, xattr_rsp, flag); - if (ret) + if (ret) { + if (ret == -2) + ret = 0; goto out; - + } /* Take care of the special files */ if (!IA_ISREG (stbuf.ia_type)) { /* Special files */ -- cgit From 36c7f8341540a1c93b5b0aa84688e58ed93422f8 Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Wed, 26 Mar 2014 11:41:37 +0530 Subject: tests/afr: select correct read-child for data OPs. Change-Id: If84bc489b6c45bde3bdb858da5f1600cea78c8a5 BUG: 1080759 Signed-off-by: Ravishankar N Reviewed-on: http://review.gluster.org/7345 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- tests/basic/afr/read-subvol-data.t | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/basic/afr/read-subvol-data.t diff --git a/tests/basic/afr/read-subvol-data.t b/tests/basic/afr/read-subvol-data.t new file mode 100644 index 000000000..7db4988fa --- /dev/null +++ b/tests/basic/afr/read-subvol-data.t @@ -0,0 +1,33 @@ +#!/bin/bash +#Test if the source is selected based on data transaction for a regular file. + +. $(dirname $0)/../../include.rc +. $(dirname $0)/../../volume.rc +cleanup; + +#Init +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/brick{0,1} +TEST $CLI volume set $V0 self-heal-daemon off +TEST $CLI volume set $V0 stat-prefetch off +TEST $CLI volume start $V0 +TEST $CLI volume set $V0 cluster.background-self-heal-count 0 +TEST glusterfs --volfile-id=$V0 --volfile-server=$H0 $M0 --entry-timeout=0 --attribute-timeout=0; + +#Test +TEST $CLI volume set $V0 cluster.read-subvolume $V0-client-1 +TEST $CLI volume set $V0 cluster.data-self-heal off +TEST $CLI volume set $V0 cluster.metadata-self-heal off +TEST $CLI volume set $V0 cluster.entry-self-heal off +TEST dd if=/dev/urandom of=$M0/afr_success_5.txt bs=1M count=1 +TEST kill_brick $V0 $H0 $B0/brick0 +TEST dd if=/dev/urandom of=$M0/afr_success_5.txt bs=1M count=10 +TEST $CLI volume start $V0 force +EXPECT_WITHIN 5 "10485760" echo `ls -l $M0/afr_success_5.txt | awk '{ print $5}'` + +#Cleanup +TEST umount $M0 +TEST $CLI volume stop $V0 +TEST $CLI volume delete $V0 +TEST rm -rf $B0/* -- cgit From e7dcc7f8240ef3f54f39b2f243c1eb0eb1cd3844 Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Wed, 26 Mar 2014 11:09:17 +0530 Subject: tests/afr: gfid mismatch test Change-Id: I12bae9c4035d5b28292e8085a5b600a3e22abaf4 BUG: 1080759 Signed-off-by: Ravishankar N Reviewed-on: http://review.gluster.org/7342 Reviewed-by: Vijay Bellur Tested-by: Gluster Build System --- tests/basic/afr/gfid-mismatch.t | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/basic/afr/gfid-mismatch.t diff --git a/tests/basic/afr/gfid-mismatch.t b/tests/basic/afr/gfid-mismatch.t new file mode 100644 index 000000000..05f48d43a --- /dev/null +++ b/tests/basic/afr/gfid-mismatch.t @@ -0,0 +1,26 @@ +#!/bin/bash +#Test that GFID mismatches result in EIO + +. $(dirname $0)/../../include.rc +cleanup; + +#Init +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/brick{0,1} +TEST $CLI volume set $V0 self-heal-daemon off +TEST $CLI volume set $V0 stat-prefetch off +TEST $CLI volume start $V0 +TEST $CLI volume set $V0 cluster.background-self-heal-count 0 +TEST glusterfs --volfile-id=$V0 --volfile-server=$H0 $M0 --entry-timeout=0 --attribute-timeout=0; + +#Test +TEST touch $M0/file +TEST setfattr -n trusted.gfid -v 0sBfz5vAdHTEK1GZ99qjqTIg== $B0/brick0/file +TEST ! "find $M0/file | xargs stat" + +#Cleanup +TEST umount $M0 +TEST $CLI volume stop $V0 +TEST $CLI volume delete $V0 +TEST rm -rf $B0/* -- cgit From 0c1d78f5c52c69268ec3a1d8d5fcb1a1bf15f243 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sat, 22 Mar 2014 01:33:06 -0700 Subject: gfapi: glfs_set_volfile_server() now entertains multiple calls Previous API: glfs_set_volfile_server (..., const char *host, ...) - single call New API's: glfs_set_volfile_server (..., const char *host1, ...) glfs_set_volfile_server (..., const char *host2, ...) Multiple calls to this function with different volfile servers, port or transport-type would create a list of volfile servers which would be polled during `volfile_fetch_attempts()` glfs_unset_volfile_server (..., const char *host, ...) to remove a server from the list (this is provided for future usage) Change-Id: I313efbd3efbd0214e2a71465f33195788df406cc BUG: 986429 Signed-off-by: Harshavardhana Reviewed-on: http://review.gluster.org/7317 Tested-by: Gluster Build System Reviewed-by: Niels de Vos Reviewed-by: Anand Avati --- api/examples/getvolfile.py | 4 +- api/src/glfs-mem-types.h | 2 +- api/src/glfs-mgmt.c | 82 ++++++++++++++++++++++------ api/src/glfs.c | 124 ++++++++++++++++++++++++++++++++++++------- api/src/glfs.h | 33 ++++++------ libglusterfs/src/glusterfs.h | 2 + 6 files changed, 192 insertions(+), 55 deletions(-) diff --git a/api/examples/getvolfile.py b/api/examples/getvolfile.py index 82d9db055..184586c63 100755 --- a/api/examples/getvolfile.py +++ b/api/examples/getvolfile.py @@ -5,8 +5,8 @@ import ctypes.util api = ctypes.CDLL(ctypes.util.find_library("gfapi")) api.glfs_get_volfile.argtypes = [ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_ulong] + ctypes.c_void_p, + ctypes.c_ulong] api.glfs_get_volfile.restype = ctypes.c_long; def get_volfile (host, volume): diff --git a/api/src/glfs-mem-types.h b/api/src/glfs-mem-types.h index 3301b3da5..76f4fc774 100644 --- a/api/src/glfs-mem-types.h +++ b/api/src/glfs-mem-types.h @@ -23,10 +23,10 @@ enum glfs_mem_types_ { glfs_mt_glfs_io_t, glfs_mt_volfile_t, glfs_mt_xlator_cmdline_option_t, + glfs_mt_server_cmdline_t, glfs_mt_glfs_object_t, glfs_mt_readdirbuf_t, glfs_mt_end }; #endif - diff --git a/api/src/glfs-mgmt.c b/api/src/glfs-mgmt.c index 7f62fa259..e2a52c324 100644 --- a/api/src/glfs-mgmt.c +++ b/api/src/glfs-mgmt.c @@ -425,36 +425,85 @@ mgmt_rpc_notify (struct rpc_clnt *rpc, void *mydata, rpc_clnt_event_t event, void *data) { xlator_t *this = NULL; - cmd_args_t *cmd_args = NULL; glusterfs_ctx_t *ctx = NULL; + server_cmdline_t *server = NULL; + rpc_transport_t *rpc_trans = NULL; struct glfs *fs = NULL; int ret = 0; this = mydata; - ctx = this->ctx; + rpc_trans = rpc->conn.trans; + ctx = this->ctx; if (!ctx) goto out; fs = ((xlator_t *)ctx->master)->private; - cmd_args = &ctx->cmd_args; switch (event) { case RPC_CLNT_DISCONNECT: if (!ctx->active) { - cmd_args->max_connect_attempts--; - gf_log ("glfs-mgmt", GF_LOG_ERROR, - "failed to connect with remote-host: %s", - strerror (errno)); - gf_log ("glfs-mgmt", GF_LOG_INFO, - "%d connect attempts left", - cmd_args->max_connect_attempts); - if (0 >= cmd_args->max_connect_attempts) { - errno = ENOTCONN; - glfs_init_done (fs, -1); - } - } - break; + gf_log ("glfs-mgmt", GF_LOG_ERROR, + "failed to connect with remote-host: %s (%s)", + ctx->cmd_args.volfile_server, + strerror (errno)); + server = ctx->cmd_args.curr_server; + if (server->list.next == &ctx->cmd_args.volfile_servers) { + errno = ENOTCONN; + gf_log("glfs-mgmt", GF_LOG_INFO, + "Exhausted all volfile servers"); + glfs_init_done (fs, -1); + break; + } + server = list_entry (server->list.next, typeof(*server), + list); + ctx->cmd_args.curr_server = server; + ctx->cmd_args.volfile_server_port = server->port; + ctx->cmd_args.volfile_server = server->volfile_server; + ctx->cmd_args.volfile_server_transport = server->transport; + + ret = dict_set_int32 (rpc_trans->options, + "remote-port", + server->port); + if (ret != 0) { + gf_log ("glfs-mgmt", GF_LOG_ERROR, + "failed to set remote-port: %d", + server->port); + errno = ENOTCONN; + glfs_init_done (fs, -1); + break; + } + + ret = dict_set_str (rpc_trans->options, + "remote-host", + server->volfile_server); + if (ret != 0) { + gf_log ("glfs-mgmt", GF_LOG_ERROR, + "failed to set remote-host: %s", + server->volfile_server); + errno = ENOTCONN; + glfs_init_done (fs, -1); + break; + } + + ret = dict_set_str (rpc_trans->options, + "transport-type", + server->transport); + if (ret != 0) { + gf_log ("glfs-mgmt", GF_LOG_ERROR, + "failed to set transport-type: %s", + server->transport); + errno = ENOTCONN; + glfs_init_done (fs, -1); + break; + } + gf_log ("glfs-mgmt", GF_LOG_INFO, + "connecting to next volfile server %s" + " at port %d with transport: %s", + server->volfile_server, server->port, + server->transport); + } + break; case RPC_CLNT_CONNECT: rpc_clnt_set_connected (&((struct rpc_clnt*)ctx->mgmt)->conn); @@ -556,4 +605,3 @@ glfs_mgmt_init (struct glfs *fs) out: return ret; } - diff --git a/api/src/glfs.c b/api/src/glfs.c index d09c737f6..64c37abfa 100644 --- a/api/src/glfs.c +++ b/api/src/glfs.c @@ -131,6 +131,8 @@ glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx) INIT_LIST_HEAD (&pool->all_frames); INIT_LIST_HEAD (&ctx->cmd_args.xlator_options); + INIT_LIST_HEAD (&ctx->cmd_args.volfile_servers); + LOCK_INIT (&pool->lock); ctx->pool = pool; @@ -312,6 +314,108 @@ enomem: return -1; } +int +glfs_unset_volfile_server (struct glfs *fs, const char *transport, + const char *host, const int port) +{ + cmd_args_t *cmd_args = NULL; + server_cmdline_t *server = NULL; + int ret = -1; + + if (!transport || !host || !port) { + errno = EINVAL; + return ret; + } + + cmd_args = &fs->ctx->cmd_args; + list_for_each_entry(server, &cmd_args->curr_server->list, list) { + if ((!strcmp(server->volfile_server, host) && + !strcmp(server->transport, transport) && + (server->port == port))) { + list_del (&server->list); + ret = 0; + goto out; + } + } + +out: + return ret; +} + +int +glfs_set_volfile_server (struct glfs *fs, const char *transport, + const char *host, int port) +{ + cmd_args_t *cmd_args = NULL; + server_cmdline_t *server = NULL; + server_cmdline_t *tmp = NULL; + int ret = -1; + + if (!transport || !host || !port) { + errno = EINVAL; + return ret; + } + + cmd_args = &fs->ctx->cmd_args; + + cmd_args->max_connect_attempts = 1; + + server = GF_CALLOC (1, sizeof (server_cmdline_t), + glfs_mt_server_cmdline_t); + + if (!server) { + errno = ENOMEM; + goto out; + } + + INIT_LIST_HEAD (&server->list); + + server->volfile_server = gf_strdup (host); + if (!server->volfile_server) { + errno = ENOMEM; + goto out; + } + + server->transport = gf_strdup (transport); + if (!server->transport) { + errno = ENOMEM; + goto out; + } + + server->port = port; + + if (!cmd_args->volfile_server) { + cmd_args->volfile_server = server->volfile_server; + cmd_args->volfile_server_transport = server->transport; + cmd_args->volfile_server_port = server->port; + cmd_args->curr_server = server; + } + + list_for_each_entry(tmp, &cmd_args->volfile_servers, list) { + if ((!strcmp(tmp->volfile_server, host) && + !strcmp(tmp->transport, transport) && + (tmp->port == port))) { + errno = EEXIST; + ret = -1; + goto out; + } + } + + list_add_tail (&server->list, &cmd_args->volfile_servers); + + ret = 0; +out: + if (ret == -1) { + if (server) { + GF_FREE (server->volfile_server); + GF_FREE (server->transport); + GF_FREE (server); + } + } + + return ret; +} + int glfs_setfsuid (uid_t fsuid) { return syncopctx_setfsuid (&fsuid); @@ -463,26 +567,6 @@ glfs_set_volfile (struct glfs *fs, const char *volfile) } -int -glfs_set_volfile_server (struct glfs *fs, const char *transport, - const char *host, int port) -{ - cmd_args_t *cmd_args = NULL; - - cmd_args = &fs->ctx->cmd_args; - - if (vol_assigned (cmd_args)) - return -1; - - cmd_args->volfile_server = gf_strdup (host); - cmd_args->volfile_server_transport = gf_strdup (transport); - cmd_args->volfile_server_port = port; - cmd_args->max_connect_attempts = 2; - - return 0; -} - - int glfs_set_logging (struct glfs *fs, const char *logfile, int loglevel) { diff --git a/api/src/glfs.h b/api/src/glfs.h index af6b48990..4344df24d 100644 --- a/api/src/glfs.h +++ b/api/src/glfs.h @@ -115,12 +115,12 @@ int glfs_set_volfile (glfs_t *fs, const char *volfile); /* SYNOPSIS - glfs_set_volfile_server: Specify the address of management server. + glfs_set_volfile_server: Specify the list of addresses for management server. DESCRIPTION - This function specifies the address of the management server (glusterd) - to connect, and establish the volume configuration. The @volname + This function specifies the list of addresses for the management server + (glusterd) to connect, and establish the volume configuration. The @volname parameter passed to glfs_new() is the volume which will be virtually mounted as the glfs_t object. All operations performed by the CLI at the management server will automatically be reflected in the 'virtual @@ -136,19 +136,22 @@ int glfs_set_volfile (glfs_t *fs, const char *volfile); @transport: String specifying the transport used to connect to the management daemon. Specifying NULL will result in the usage - of the default (tcp) transport type. Permitted values - are those what you specify as transport-type in a volume - specification file (e.g "tcp", "rdma", "unix".) + of the default (tcp) transport type. Permitted values + are those what you specify as transport-type in a volume + specification file (e.g "tcp", "rdma" etc.) - @host: String specifying the address of where to find the management - daemon. Depending on the transport type this would either be - an FQDN (e.g: "storage01.company.com"), ASCII encoded IP - address "192.168.22.1", or a UNIX domain socket path (e.g - "/tmp/glusterd.socket".) + @host: String specifying the address where to find the management daemon. + This would either be + - FQDN (e.g: "storage01.company.com") or + - ASCII (e.g: "192.168.22.1") + + NOTE: This API is special, multiple calls to this function with different + volfile servers, port or transport-type would create a list of volfile + servers which would be polled during `volfile_fetch_attempts()` @port: The TCP port number where gluster management daemon is listening. Specifying 0 uses the default port number GF_DEFAULT_BASE_PORT. - This parameter is unused if you are using a UNIX domain socket. + This parameter is unused if you are using a UNIX domain socket. RETURN VALUES @@ -158,9 +161,9 @@ int glfs_set_volfile (glfs_t *fs, const char *volfile); */ int glfs_set_volfile_server (glfs_t *fs, const char *transport, - const char *host, int port) __THROW; - - + const char *host, int port) __THROW; +int glfs_unset_volfile_server (glfs_t *fs, const char *transport, + const char *host, int port) __THROW; /* SYNOPSIS diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h index 852d686c1..5dd26b451 100644 --- a/libglusterfs/src/glusterfs.h +++ b/libglusterfs/src/glusterfs.h @@ -325,6 +325,8 @@ typedef struct _xlator_cmdline_option xlator_cmdline_option_t; struct _server_cmdline { struct list_head list; char *volfile_server; + char *transport; + int port; }; typedef struct _server_cmdline server_cmdline_t; -- cgit From 1c1b8269d994c0885d753c8f0da8d5154876c7ae Mon Sep 17 00:00:00 2001 From: Varun Shastry Date: Tue, 25 Mar 2014 09:36:45 +0530 Subject: tests/quota: Wait till the rebalance is complete Change-Id: Ia6f0c81fb1542ce1de965a69a61535691df056c3 BUG: 1077159 Signed-off-by: Varun Shastry Reviewed-on: http://review.gluster.org/7380 Reviewed-by: Raghavendra G Tested-by: Gluster Build System Reviewed-by: Justin Clift Tested-by: Justin Clift Reviewed-by: Vijay Bellur --- tests/basic/quota.t | 18 ++++++++++++++---- tests/dht.rc | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/basic/quota.t b/tests/basic/quota.t index 81b1c2100..cfc4f0695 100755 --- a/tests/basic/quota.t +++ b/tests/basic/quota.t @@ -2,6 +2,7 @@ . $(dirname $0)/../include.rc . $(dirname $0)/../volume.rc +. $(dirname $0)/../dht.rc cleanup; @@ -110,7 +111,7 @@ EXPECT "150.0MB" hard_limit "/test_dir/in_test_dir"; ## ## ------------------------------------------------ ################################################### -QUOTALIMIT=1024 +QUOTALIMIT=100 QUOTALIMITROOT=2048 TESTDIR="addbricktest" @@ -135,8 +136,8 @@ done #53-62 for i in `seq 1 9`; do - TEST_IN_LOOP dd if=/dev/urandom of="$M0/$TESTDIR/dir1/100MBfile$i" \ - bs=1M count=100; + TEST_IN_LOOP dd if=/dev/urandom of="$M0/$TESTDIR/dir1/10MBfile$i" \ + bs=1M count=10; done # 63-64 @@ -145,11 +146,20 @@ done TEST $CLI volume add-brick $V0 $H0:$B0/brick{3,4} TEST $CLI volume rebalance $V0 start; +## Wait for rebalance +while true; do + rebalance_completed + if [ $? -eq 1 ]; then + sleep 1; + else + break; + fi +done ## ## -------------------------------- for i in `seq 1 200`; do - dd if=/dev/urandom of="$M0/$TESTDIR/dir1/10MBfile$i" bs=1M count=10 \ + dd if=/dev/urandom of="$M0/$TESTDIR/dir1/1MBfile$i" bs=1M count=1 \ &>/dev/null done diff --git a/tests/dht.rc b/tests/dht.rc index 663ea5431..54425c9dc 100644 --- a/tests/dht.rc +++ b/tests/dht.rc @@ -76,4 +76,5 @@ function rebalance_completed() fi echo $val + return $val } -- cgit From e75be8977ede9b9174d20b39c427e6fb4ccde567 Mon Sep 17 00:00:00 2001 From: Pranith Kumar K Date: Mon, 24 Mar 2014 22:54:03 +0530 Subject: cluster/afr: Remove eager-lock stub on finodelk failure Problem: For write fops afr's transaction eager-lock init adds transactions that can share eager-lock to fdctx list. But if eager-lock finodelk fop fails the stub remains in the list. This could later lead to corruption of the list and lead to infinite loop on the list leading to a mount hang. Fix: Remove the stub when finodelk fails. Change-Id: I0ed4bc6b62f26c5e891c1181a6871ee6e4f4f5fd BUG: 1063190 Signed-off-by: Pranith Kumar K Reviewed-on: http://review.gluster.org/6944 Tested-by: Gluster Build System Reviewed-by: Ravishankar N Reviewed-by: Anand Avati --- xlators/cluster/afr/src/afr-common.c | 19 +++++++++++++++++++ xlators/cluster/afr/src/afr-transaction.c | 8 ++------ xlators/cluster/afr/src/afr.h | 2 ++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/xlators/cluster/afr/src/afr-common.c b/xlators/cluster/afr/src/afr-common.c index 2bab0f853..6bd231600 100644 --- a/xlators/cluster/afr/src/afr-common.c +++ b/xlators/cluster/afr/src/afr-common.c @@ -889,6 +889,15 @@ afr_replies_wipe (afr_local_t *local, afr_private_t *priv) memset (local->replies, 0, sizeof(*local->replies) * priv->child_count); } +void +afr_remove_eager_lock_stub (afr_local_t *local) +{ + LOCK (&local->fd->lock); + { + list_del_init (&local->transaction.eager_locked); + } + UNLOCK (&local->fd->lock); +} void afr_local_cleanup (afr_local_t *local, xlator_t *this) @@ -900,6 +909,10 @@ afr_local_cleanup (afr_local_t *local, xlator_t *this) syncbarrier_destroy (&local->barrier); + if (local->transaction.eager_lock_on && + !list_empty (&local->transaction.eager_locked)) + afr_remove_eager_lock_stub (local); + afr_local_transaction_cleanup (local, this); priv = this->private; @@ -2106,6 +2119,12 @@ afr_cleanup_fd_ctx (xlator_t *this, fd_t *fd) fd_ctx = (afr_fd_ctx_t *)(long) ctx; if (fd_ctx) { + //no need to take any locks + if (!list_empty (&fd_ctx->eager_locked)) + gf_log (this->name, GF_LOG_WARNING, "%s: Stale " + "Eager-lock stubs found", + uuid_utoa (fd->inode->gfid)); + for (i = 0; i < AFR_NUM_CHANGE_LOGS; i++) GF_FREE (fd_ctx->pre_op_done[i]); diff --git a/xlators/cluster/afr/src/afr-transaction.c b/xlators/cluster/afr/src/afr-transaction.c index f974fdb59..205ff759e 100644 --- a/xlators/cluster/afr/src/afr-transaction.c +++ b/xlators/cluster/afr/src/afr-transaction.c @@ -1544,7 +1544,7 @@ afr_delayed_changelog_wake_up (xlator_t *this, fd_t *fd) } - int +int afr_transaction_resume (call_frame_t *frame, xlator_t *this) { afr_local_t *local = NULL; @@ -1555,11 +1555,7 @@ afr_transaction_resume (call_frame_t *frame, xlator_t *this) /* We don't need to retain "local" in the fd list anymore, writes to all subvols are finished by now */ - LOCK (&local->fd->lock); - { - list_del_init (&local->transaction.eager_locked); - } - UNLOCK (&local->fd->lock); + afr_remove_eager_lock_stub (local); } afr_restore_lk_owner (frame); diff --git a/xlators/cluster/afr/src/afr.h b/xlators/cluster/afr/src/afr.h index 2e1b78d1c..36042f7b2 100644 --- a/xlators/cluster/afr/src/afr.h +++ b/xlators/cluster/afr/src/afr.h @@ -971,4 +971,6 @@ afr_handle_open_fd_count (call_frame_t *frame, xlator_t *this); int afr_local_pathinfo (char *pathinfo, gf_boolean_t *is_local); +void +afr_remove_eager_lock_stub (afr_local_t *local); #endif /* __AFR_H__ */ -- cgit From 5dedef81b6ef91d462ce49ded4e148dfc17deee2 Mon Sep 17 00:00:00 2001 From: Atin Mukherjee Date: Wed, 19 Mar 2014 11:30:22 +0530 Subject: cli: remove-brick no longer defaults to commit-force Problem : When gluster volume remove-brick is executed with out any option, it defaults to force commit which results in data loss. Fix : remove-brick can not be executed with out explicit option, user needs to provide the option in the command line else the command will throw back an usage error. Earlier usage : volume remove-brick [replica ] ... [start|stop|status|commit|force] Current usage : volume remove-brick [replica ] ... Change-Id: I2a49131f782a6c0dcd03b4dc8ebe5907999b0b49 BUG: 1077682 Signed-off-by: Atin Mukherjee Reviewed-on: http://review.gluster.org/7292 Tested-by: Gluster Build System Reviewed-by: Shyamsundar Ranganathan Reviewed-by: Vijay Bellur --- cli/src/cli-cmd-parser.c | 10 ++++------ cli/src/cli-cmd-volume.c | 3 ++- tests/basic/volume.t | 2 +- tests/bugs/bug-1077682.t | 34 ++++++++++++++++++++++++++++++++++ tests/bugs/bug-867252.t | 2 +- tests/bugs/bug-878004.t | 4 ++-- tests/bugs/bug-961669.t | 2 +- 7 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 tests/bugs/bug-1077682.t diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index 1513e0c5e..9777e655f 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1118,7 +1118,7 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, GF_ASSERT (words); GF_ASSERT (options); - if (wordcount < 4) + if (wordcount < 5) goto out; dict = dict_new (); @@ -1136,7 +1136,7 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, brick_index = 3; w = str_getunamb (words[3], type_opword); if (w && !strcmp ("replica", w)) { - if (wordcount < 5) { + if (wordcount < 6) { ret = -1; goto out; } @@ -1158,10 +1158,8 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, w = str_getunamb (words[wordcount - 1], opwords); if (!w) { - /* Should be default 'force' */ - command = GF_OP_CMD_COMMIT_FORCE; - if (question) - *question = 1; + ret = -1; + goto out; } else { /* handled this option */ wordcount--; diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c index e334ddc84..3b927d2bd 100644 --- a/cli/src/cli-cmd-volume.c +++ b/cli/src/cli-cmd-volume.c @@ -2299,7 +2299,8 @@ struct cli_cmd volume_cmds[] = { cli_cmd_volume_add_brick_cbk, "add brick to volume "}, - { "volume remove-brick [replica ] ... [start|stop|status|commit|force]", + { "volume remove-brick [replica ] ..." + " ", cli_cmd_volume_remove_brick_cbk, "remove brick from volume "}, diff --git a/tests/basic/volume.t b/tests/basic/volume.t index 2f9096055..23b740af1 100755 --- a/tests/basic/volume.t +++ b/tests/basic/volume.t @@ -22,7 +22,7 @@ EXPECT 'Started' volinfo_field $V0 'Status'; TEST $CLI volume add-brick $V0 $H0:$B0/${V0}{9,10,11,12}; EXPECT '12' brick_count $V0 -TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}{1,2,3,4}; +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}{1,2,3,4} force; EXPECT '8' brick_count $V0 TEST $CLI volume stop $V0; diff --git a/tests/bugs/bug-1077682.t b/tests/bugs/bug-1077682.t new file mode 100644 index 000000000..2923c5f66 --- /dev/null +++ b/tests/bugs/bug-1077682.t @@ -0,0 +1,34 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc + +function get-task-status() +{ + $CLI $COMMAND | grep -o $PATTERN + if [ ${PIPESTATUS[0]} -ne 0 ]; + then + return 1 + fi + return 0 +} + +cleanup; + +TEST glusterd +TEST pidof glusterd + +TEST $CLI volume create $V0 $H0:$B0/${V0}{1,2,3,4} +TEST $CLI volume start $V0 +TEST ! $CLI volume remove-brick $V0 $H0:$B0/${V0}1 +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}2 force +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}3 start + +EXPECT_WITHIN 10 "completed" remove_brick_status_completed_field "$V0" \ +"$H0:$B0/${V0}3" + +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}3 commit +TEST killall glusterd +TEST glusterd + +cleanup diff --git a/tests/bugs/bug-867252.t b/tests/bugs/bug-867252.t index 8309ed9b9..17edcd9c5 100644 --- a/tests/bugs/bug-867252.t +++ b/tests/bugs/bug-867252.t @@ -35,7 +35,7 @@ EXPECT '1' brick_count $V0 TEST $CLI volume add-brick $V0 $H0:$B0/${V0}2; EXPECT '2' brick_count $V0 -TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}2; +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}2 force; EXPECT '1' brick_count $V0 cleanup; diff --git a/tests/bugs/bug-878004.t b/tests/bugs/bug-878004.t index 5bee4c62f..407fd6ecc 100644 --- a/tests/bugs/bug-878004.t +++ b/tests/bugs/bug-878004.t @@ -19,10 +19,10 @@ function brick_count() TEST $CLI volume start $V0 -TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}2; +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}2 force; EXPECT '2' brick_count $V0 -TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}3; +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}3 force; EXPECT '1' brick_count $V0 cleanup; diff --git a/tests/bugs/bug-961669.t b/tests/bugs/bug-961669.t index 751a63df2..77896481c 100644 --- a/tests/bugs/bug-961669.t +++ b/tests/bugs/bug-961669.t @@ -27,7 +27,7 @@ function remove_brick_start { } function remove_brick { - $CLI volume remove-brick $V0 replica 2 $H0:$B0/${V0}{1,4,7} 2>&1|grep -oE 'success|failed' + $CLI volume remove-brick $V0 replica 2 $H0:$B0/${V0}{1,4,7} force 2>&1|grep -oE 'success|failed' } #remove-brick start variant -- cgit From 997c89b6172116557f981510a94232486ec526b0 Mon Sep 17 00:00:00 2001 From: Kotresh H R Date: Tue, 25 Mar 2014 11:11:41 +0530 Subject: features/gfid-access: Fix possible inode memory corruption. During lookup, the inode is not ref'd. Added code to ref the inode in call path and unref in cbk path. Also fixed a case where we should always be putting linked inode into context as it is not guaranteed that we get same inode that we passed in a call to inode_link. Change-Id: Iaec083a9258658bef3047e83956729d3dbcd9a59 BUG: 1080295 Signed-off-by: Kotresh H R Reviewed-on: http://review.gluster.org/7329 Tested-by: Gluster Build System Reviewed-by: Raghavendra G Reviewed-by: Venky Shankar --- xlators/features/gfid-access/src/gfid-access.c | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/xlators/features/gfid-access/src/gfid-access.c b/xlators/features/gfid-access/src/gfid-access.c index 8e614397c..5cb6ecfbd 100644 --- a/xlators/features/gfid-access/src/gfid-access.c +++ b/xlators/features/gfid-access/src/gfid-access.c @@ -593,18 +593,19 @@ ga_virtual_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata, struct iatt *postparent) { - int j = 0; - int i = 0; - int ret = 0; - uint64_t temp_ino = 0; - inode_t *cbk_inode = NULL; - inode_t *true_inode = NULL; - uuid_t random_gfid = {0,}; + int j = 0; + int i = 0; + int ret = 0; + uint64_t temp_ino = 0; + inode_t *cbk_inode = NULL; + inode_t *true_inode = NULL; + uuid_t random_gfid = {0,}; + inode_t *linked_inode = NULL; if (frame->local) cbk_inode = frame->local; else - cbk_inode = inode; + cbk_inode = inode_ref (inode); frame->local = NULL; if (op_ret) @@ -619,6 +620,10 @@ ga_virtual_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, if its just previously discover()'d inode */ true_inode = inode_find (inode->table, buf->ia_gfid); if (!true_inode) { + /* This unref is for 'inode_ref()' done in beginning. + This is needed as cbk_inode is allocated new inode + whose unref is taken at the end*/ + inode_unref (cbk_inode); cbk_inode = inode_new (inode->table); if (!cbk_inode) { @@ -630,7 +635,8 @@ ga_virtual_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, path is not yet looked up. Use the current inode itself for now */ - inode_link (inode, NULL, NULL, buf); + linked_inode = inode_link (inode, NULL, NULL, buf); + inode = linked_inode; } else { /* 'inode_ref()' has been done in inode_find() */ inode = true_inode; @@ -674,6 +680,10 @@ unwind: STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, cbk_inode, buf, xdata, postparent); + /* Also handles inode_unref of frame->local if done in ga_lookup */ + if (cbk_inode) + inode_unref (cbk_inode); + return 0; } @@ -819,6 +829,8 @@ ga_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) /* time do another lookup and update the context with proper inode */ op_errno = ESTALE; + /* 'inode_ref()' done in inode_find */ + inode_unref (true_inode); goto err; } @@ -833,7 +845,7 @@ discover: /* if revalidate, then we need to have the proper reference */ if (inode) { tmp_loc.inode = inode_ref (inode); - frame->local = loc->inode; + frame->local = inode_ref (loc->inode); } else { tmp_loc.inode = inode_ref (loc->inode); } -- cgit From ca6af761fb63068b170b9e8b1143598af244e06c Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Tue, 25 Mar 2014 16:15:15 +0530 Subject: features/glupy: Add mem accounting support When glusterfs is built from source using -DDEBUG flag and glupy xlator is added to vol file, the brick process used to crash when mounting the volume. This fix is largely derived from the fix submitted for BZ #1035751. Thanks to Justin Clift for helping in tracking this down. BUG: 1035751 Change-Id: Id64f92eecc9335e34dd08812fe176774e7723c2c Signed-off-by: Prashanth Pai Reviewed-on: http://review.gluster.org/7332 Reviewed-by: Jeff Darcy Tested-by: Gluster Build System --- xlators/features/glupy/src/glupy.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/xlators/features/glupy/src/glupy.c b/xlators/features/glupy/src/glupy.c index 948b66f8d..7492124dd 100644 --- a/xlators/features/glupy/src/glupy.c +++ b/xlators/features/glupy/src/glupy.c @@ -2313,6 +2313,25 @@ get_rootunique (call_frame_t *frame) return frame->root->unique; } +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + if (!this) + return ret; + + ret = xlator_mem_acct_init (this, gf_glupy_mt_end); + + if (ret != 0) { + gf_log(this->name, GF_LOG_ERROR, "Memory accounting init" + " failed"); + return ret; + } + + return ret; +} + int32_t init (xlator_t *this) { -- cgit From 27bc6a07b4c82409845077aed0556114caa8dc99 Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Thu, 3 Apr 2014 18:33:19 +0100 Subject: tests: Adjust test 767095 to cope with long hostnames BUG: 1084147 Change-Id: Ie1ff8852a501690e681072c54620d305b5e20d6a Signed-off-by: Justin Clift Reviewed-on: http://review.gluster.org/7395 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- tests/bugs/bug-767095.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bugs/bug-767095.t b/tests/bugs/bug-767095.t index a8842bd54..82212c72d 100755 --- a/tests/bugs/bug-767095.t +++ b/tests/bugs/bug-767095.t @@ -31,7 +31,7 @@ TEST $CLI volume set $V0 server.statedump-path $dump_dir; TEST $CLI volume start $V0; EXPECT 'Started' volinfo_field $V0 'Status'; -TEST PID=`gluster volume status $V0 | grep patchy1 | awk {'print $5'}`; +TEST PID=`gluster --xml volume status patchy | grep -A 5 patchy1 | grep '' | cut -d '>' -f 2 | cut -d '<' -f 1` TEST kill -USR1 $PID; sleep 2; for file_name in $(ls $dump_dir) -- cgit From 49fbc578ef96b7952d4d77993fb8a7212ae486dd Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Wed, 26 Mar 2014 11:22:12 +0530 Subject: tests/afr: select correct read-child for entry OPs. Change-Id: If375c937579a18d603ed70232130a4664060e9d6 BUG: 1080759 Signed-off-by: Ravishankar N Reviewed-on: http://review.gluster.org/7344 Reviewed-by: Pranith Kumar Karampuri Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- tests/basic/afr/read-subvol-entry.t | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/basic/afr/read-subvol-entry.t diff --git a/tests/basic/afr/read-subvol-entry.t b/tests/basic/afr/read-subvol-entry.t new file mode 100644 index 000000000..91110b8cd --- /dev/null +++ b/tests/basic/afr/read-subvol-entry.t @@ -0,0 +1,35 @@ +#!/bin/bash +#Test if the read child is selected based on entry transaction for directory + +. $(dirname $0)/../../include.rc +. $(dirname $0)/../../volume.rc +cleanup; + +#Init +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/brick{0,1} +TEST $CLI volume set $V0 self-heal-daemon off +TEST $CLI volume set $V0 stat-prefetch off +TEST $CLI volume start $V0 +TEST $CLI volume set $V0 cluster.background-self-heal-count 0 +TEST glusterfs --volfile-id=$V0 --volfile-server=$H0 $M0 --entry-timeout=0 --attribute-timeout=0; + +#Test +TEST mkdir -p $M0/abc/def + +TEST $CLI volume set $V0 cluster.data-self-heal off +TEST $CLI volume set $V0 cluster.metadata-self-heal off +TEST $CLI volume set $V0 cluster.entry-self-heal off + +TEST kill_brick $V0 $H0 $B0/brick0 + +TEST touch $M0/abc/def/ghi +TEST $CLI volume start $V0 force +EXPECT_WITHIN 5 "ghi" echo `ls $M0/abc/def/` + +#Cleanup +TEST umount $M0 +TEST $CLI volume stop $V0 +TEST $CLI volume delete $V0 +TEST rm -rf $B0/* -- cgit From d5072db4c56c2351437aa4c2d340bf2766e318ce Mon Sep 17 00:00:00 2001 From: Atin Mukherjee Date: Thu, 3 Apr 2014 15:10:58 +0530 Subject: cli : Removal of dead code dead code reported by covscan is removed from cli-cmd-parser.c Fix for coverity CID: 1195423 Change-Id: Ice1771dc8b3ef47fd2e63b380b12e850dc1d5d95 BUG: 789278 Signed-off-by: Atin Mukherjee Reviewed-on: http://review.gluster.org/7389 Tested-by: Gluster Build System Reviewed-by: Krishnan Parthasarathi --- cli/src/cli-cmd-parser.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index 9777e655f..afa161104 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1184,11 +1184,6 @@ cli_cmd_volume_remove_brick_parse (const char **words, int wordcount, } } - if (wordcount < 4) { - ret = -1; - goto out; - } - ret = dict_set_int32 (dict, "command", command); if (ret) gf_log ("cli", GF_LOG_INFO, "failed to set 'command' %d", -- cgit From 50b33f4050e11876ecb8e3512880334de25e3f21 Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Fri, 4 Apr 2014 12:59:58 +0000 Subject: afr: Simple 1-liner fix for crash in Rackspace BUG: 1084485 Change-Id: I89ddf10add041638ef70baebbce0ec2807ef4b6d Signed-off-by: Justin Clift Reviewed-on: http://review.gluster.org/7402 Reviewed-by: Anand Avati --- xlators/cluster/afr/src/afr-inode-write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xlators/cluster/afr/src/afr-inode-write.c b/xlators/cluster/afr/src/afr-inode-write.c index 3dacfc8dd..1a5c51cb7 100644 --- a/xlators/cluster/afr/src/afr-inode-write.c +++ b/xlators/cluster/afr/src/afr-inode-write.c @@ -1588,7 +1588,7 @@ afr_discard (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, if (!transaction_frame) goto out; - local = AFR_FRAME_INIT (frame, op_errno); + local = AFR_FRAME_INIT (transaction_frame, op_errno); if (!local) goto out; -- cgit From d8dd4049143c191cea451bade470b906c67dbbe0 Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Fri, 4 Apr 2014 18:22:50 +0100 Subject: cli: Trivial wording improvement of a comment Change-Id: Ib569b39bdf0357c30c94c7a1b8d3ff87b811841c Reviewed-on: http://review.gluster.org/7403 Reviewed-by: Justin Clift Tested-by: Justin Clift Reviewed-by: Anand Avati --- cli/src/cli.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/cli.c b/cli/src/cli.c index a77200c66..2c703689b 100644 --- a/cli/src/cli.c +++ b/cli/src/cli.c @@ -553,9 +553,9 @@ cli_rpc_init (struct cli_state *state) if (!options) goto out; - /* Connect using to glusterd using the specified method, giving - * preference to unix socket connection. If nothing is specified connect - * to the default glusterd socket + /* Connect to glusterd using the specified method, giving preference + * to a unix socket connection. If nothing is specified, connect to + * the default glusterd socket. */ if (state->glusterd_sock) { gf_log ("cli", GF_LOG_INFO, "Connecting to glusterd using " -- cgit From b66568b6cb6694016f95e9d5a5220d3bde76907d Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 26 Mar 2014 16:55:12 -0700 Subject: build: move argp-standalone into contrib/ directory Change-Id: Iedcddf95c3577da644c0aebbb297b04c93f1b6fe BUG: 1081274 Signed-off-by: Harshavardhana Reviewed-on: http://review.gluster.org/7352 Tested-by: Gluster Build System Reviewed-by: Anand Avati --- .gitignore | 4 +- Makefile.am | 13 +- argp-standalone/Makefile.am | 38 - argp-standalone/acinclude.m4 | 1084 ------------------ argp-standalone/argp-ba.c | 26 - argp-standalone/argp-eexst.c | 36 - argp-standalone/argp-fmtstream.c | 477 -------- argp-standalone/argp-fmtstream.h | 327 ------ argp-standalone/argp-help.c | 1849 ------------------------------ argp-standalone/argp-namefrob.h | 96 -- argp-standalone/argp-parse.c | 1305 --------------------- argp-standalone/argp-pv.c | 25 - argp-standalone/argp-pvh.c | 32 - argp-standalone/argp.h | 602 ---------- argp-standalone/autogen.sh | 6 - argp-standalone/configure.ac | 102 -- argp-standalone/mempcpy.c | 21 - argp-standalone/strcasecmp.c | 29 - argp-standalone/strchrnul.c | 23 - argp-standalone/strndup.c | 34 - argp-standalone/vsnprintf.c | 839 -------------- autogen.sh | 4 +- configure.ac | 7 +- contrib/argp-standalone/Makefile.am | 38 + contrib/argp-standalone/acinclude.m4 | 1084 ++++++++++++++++++ contrib/argp-standalone/argp-ba.c | 26 + contrib/argp-standalone/argp-eexst.c | 36 + contrib/argp-standalone/argp-fmtstream.c | 477 ++++++++ contrib/argp-standalone/argp-fmtstream.h | 327 ++++++ contrib/argp-standalone/argp-help.c | 1849 ++++++++++++++++++++++++++++++ contrib/argp-standalone/argp-namefrob.h | 96 ++ contrib/argp-standalone/argp-parse.c | 1305 +++++++++++++++++++++ contrib/argp-standalone/argp-pv.c | 25 + contrib/argp-standalone/argp-pvh.c | 32 + contrib/argp-standalone/argp.h | 602 ++++++++++ contrib/argp-standalone/autogen.sh | 6 + contrib/argp-standalone/configure.ac | 102 ++ contrib/argp-standalone/mempcpy.c | 21 + contrib/argp-standalone/strcasecmp.c | 29 + contrib/argp-standalone/strchrnul.c | 23 + contrib/argp-standalone/strndup.c | 34 + contrib/argp-standalone/vsnprintf.c | 839 ++++++++++++++ 42 files changed, 6968 insertions(+), 6962 deletions(-) delete mode 100644 argp-standalone/Makefile.am delete mode 100644 argp-standalone/acinclude.m4 delete mode 100644 argp-standalone/argp-ba.c delete mode 100644 argp-standalone/argp-eexst.c delete mode 100644 argp-standalone/argp-fmtstream.c delete mode 100644 argp-standalone/argp-fmtstream.h delete mode 100644 argp-standalone/argp-help.c delete mode 100644 argp-standalone/argp-namefrob.h delete mode 100644 argp-standalone/argp-parse.c delete mode 100644 argp-standalone/argp-pv.c delete mode 100644 argp-standalone/argp-pvh.c delete mode 100644 argp-standalone/argp.h delete mode 100755 argp-standalone/autogen.sh delete mode 100644 argp-standalone/configure.ac delete mode 100644 argp-standalone/mempcpy.c delete mode 100644 argp-standalone/strcasecmp.c delete mode 100644 argp-standalone/strchrnul.c delete mode 100644 argp-standalone/strndup.c delete mode 100644 argp-standalone/vsnprintf.c create mode 100644 contrib/argp-standalone/Makefile.am create mode 100644 contrib/argp-standalone/acinclude.m4 create mode 100644 contrib/argp-standalone/argp-ba.c create mode 100644 contrib/argp-standalone/argp-eexst.c create mode 100644 contrib/argp-standalone/argp-fmtstream.c create mode 100644 contrib/argp-standalone/argp-fmtstream.h create mode 100644 contrib/argp-standalone/argp-help.c create mode 100644 contrib/argp-standalone/argp-namefrob.h create mode 100644 contrib/argp-standalone/argp-parse.c create mode 100644 contrib/argp-standalone/argp-pv.c create mode 100644 contrib/argp-standalone/argp-pvh.c create mode 100644 contrib/argp-standalone/argp.h create mode 100755 contrib/argp-standalone/autogen.sh create mode 100644 contrib/argp-standalone/configure.ac create mode 100644 contrib/argp-standalone/mempcpy.c create mode 100644 contrib/argp-standalone/strcasecmp.c create mode 100644 contrib/argp-standalone/strchrnul.c create mode 100644 contrib/argp-standalone/strndup.c create mode 100644 contrib/argp-standalone/vsnprintf.c diff --git a/.gitignore b/.gitignore index 08bd0dae2..adedb3585 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ py-compile *.o *.tar.gz *.rpm +*.diff +*.patch .libs .deps Makefile @@ -27,7 +29,7 @@ stamp-h1 api/examples/__init__.py api/examples/__init__.py? api/examples/setup.py -argp-standalone/libargp.a +contrib/argp-standalone/libargp.a contrib/uuid/uuid_types.h extras/init.d/glusterd-Debian extras/init.d/glusterd-Redhat diff --git a/Makefile.am b/Makefile.am index 598ebb410..fa0f52ea1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = autogen.sh \ gen-headers.py run-tests.sh \ $(shell find $(top_srcdir)/tests -type f -print) -SUBDIRS = argp-standalone libglusterfs rpc api xlators glusterfsd \ +SUBDIRS = contrib/argp-standalone libglusterfs rpc api xlators glusterfsd \ $(FUSERMOUNT_SUBDIR) doc extras cli @SYNCDAEMON_SUBDIR@ pkgconfigdir = @pkgconfigdir@ @@ -20,10 +20,13 @@ gitclean: distclean find . -name mount.glusterfs -exec rm -f {} \; rm -fr autom4te.cache rm -f missing aclocal.m4 config.h.in config.guess config.sub ltmain.sh install-sh configure depcomp - rm -fr argp-standalone/autom4te.cache - rm -f argp-standalone/aclocal.m4 argp-standalone/config.h.in - rm -f argp-standalone/configure argp-standalone/depcomp - rm -f argp-standalone/install-sh argp-standalone/missing + rm -fr $(CONTRIBDIR)/argp-standalone/autom4te.cache + rm -f $(CONTRIBDIR)/argp-standalone/aclocal.m4 + rm -f $(CONTRIBDIR)/argp-standalone/config.h.in + rm -f $(CONTRIBDIR)/argp-standalone/configure + rm -f $(CONTRIBDIR)/argp-standalone/depcomp + rm -f $(CONTRIBDIR)/argp-standalone/install-sh + rm -f $(CONTRIBDIR)/argp-standalone/missing dist-hook: (cd $(srcdir) && git diff && echo ===== git log ==== && git log) > $(distdir)/ChangeLog diff --git a/argp-standalone/Makefile.am b/argp-standalone/Makefile.am deleted file mode 100644 index 4775d4876..000000000 --- a/argp-standalone/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -# From glibc - -# Copyright (C) 1997, 2003, 2004 Free Software Foundation, Inc. -# This file is part of the GNU C Library. - -# The GNU C Library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. - -# The GNU C Library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Library General Public License for more details. - -# You should have received a copy of the GNU Library General Public -# License along with the GNU C Library; see the file COPYING.LIB. If -# not, write to the Free Software Foundation, Inc., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -AUTOMAKE_OPTIONS = foreign -SUBDIRS = . - -LIBOBJS = @LIBOBJS@ - -noinst_LIBRARIES = libargp.a - -noinst_HEADERS = argp.h argp-fmtstream.h argp-namefrob.h - -EXTRA_DIST = mempcpy.c strchrnul.c strndup.c strcasecmp.c vsnprintf.c autogen.sh - -# Leaves out argp-fs-xinl.c and argp-xinl.c -libargp_a_SOURCES = argp-ba.c argp-eexst.c argp-fmtstream.c \ - argp-help.c argp-parse.c argp-pv.c \ - argp-pvh.c - -libargp_a_LIBADD = $(LIBOBJS) - - diff --git a/argp-standalone/acinclude.m4 b/argp-standalone/acinclude.m4 deleted file mode 100644 index fb61e957d..000000000 --- a/argp-standalone/acinclude.m4 +++ /dev/null @@ -1,1084 +0,0 @@ -dnl Try to detect the type of the third arg to getsockname() et al -AC_DEFUN([LSH_TYPE_SOCKLEN_T], -[AH_TEMPLATE([socklen_t], [Length type used by getsockopt]) -AC_CACHE_CHECK([for socklen_t in sys/socket.h], ac_cv_type_socklen_t, -[AC_EGREP_HEADER(socklen_t, sys/socket.h, - [ac_cv_type_socklen_t=yes], [ac_cv_type_socklen_t=no])]) -if test $ac_cv_type_socklen_t = no; then - AC_MSG_CHECKING(for AIX) - AC_EGREP_CPP(yes, [ -#ifdef _AIX - yes -#endif -],[ -AC_MSG_RESULT(yes) -AC_DEFINE(socklen_t, size_t) -],[ -AC_MSG_RESULT(no) -AC_DEFINE(socklen_t, int) -]) -fi -]) - -dnl Choose cc flags for compiling position independent code -AC_DEFUN([LSH_CCPIC], -[AC_MSG_CHECKING(CCPIC) -AC_CACHE_VAL(lsh_cv_sys_ccpic,[ - if test -z "$CCPIC" ; then - if test "$GCC" = yes ; then - case `uname -sr` in - BSD/OS*) - case `uname -r` in - 4.*) CCPIC="-fPIC";; - *) CCPIC="";; - esac - ;; - Darwin*) - CCPIC="-fPIC" - ;; - SunOS\ 5.*) - # Could also use -fPIC, if there are a large number of symbol reference - CCPIC="-fPIC" - ;; - CYGWIN*) - CCPIC="" - ;; - *) - CCPIC="-fpic" - ;; - esac - else - case `uname -sr` in - Darwin*) - CCPIC="-fPIC" - ;; - IRIX*) - CCPIC="-share" - ;; - hp*|HP*) CCPIC="+z"; ;; - FreeBSD*) CCPIC="-fpic";; - SCO_SV*) CCPIC="-KPIC -dy -Bdynamic";; - UnixWare*|OpenUNIX*) CCPIC="-KPIC -dy -Bdynamic";; - Solaris*) CCPIC="-KPIC -Bdynamic";; - Windows_NT*) CCPIC="-shared" ;; - esac - fi - fi - OLD_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $CCPIC" - AC_TRY_COMPILE([], [exit(0);], - lsh_cv_sys_ccpic="$CCPIC", lsh_cv_sys_ccpic='') - CFLAGS="$OLD_CFLAGS" -]) -CCPIC="$lsh_cv_sys_ccpic" -AC_MSG_RESULT($CCPIC) -AC_SUBST([CCPIC])]) - -dnl LSH_PATH_ADD(path-id, directory) -AC_DEFUN([LSH_PATH_ADD], -[AC_MSG_CHECKING($2) -ac_exists=no -if test -d "$2/." ; then - ac_real_dir=`cd $2 && pwd` - if test -n "$ac_real_dir" ; then - ac_exists=yes - for old in $1_REAL_DIRS ; do - ac_found=no - if test x$ac_real_dir = x$old ; then - ac_found=yes; - break; - fi - done - if test $ac_found = yes ; then - AC_MSG_RESULT(already added) - else - AC_MSG_RESULT(added) - # LDFLAGS="$LDFLAGS -L $2" - $1_REAL_DIRS="$ac_real_dir [$]$1_REAL_DIRS" - $1_DIRS="$2 [$]$1_DIRS" - fi - fi -fi -if test $ac_exists = no ; then - AC_MSG_RESULT(not found) -fi -]) - -dnl LSH_RPATH_ADD(dir) -AC_DEFUN([LSH_RPATH_ADD], [LSH_PATH_ADD(RPATH_CANDIDATE, $1)]) - -dnl LSH_RPATH_INIT(candidates) -AC_DEFUN([LSH_RPATH_INIT], -[AC_MSG_CHECKING([for -R flag]) -RPATHFLAG='' -case `uname -sr` in - OSF1\ V4.*) - RPATHFLAG="-rpath " - ;; - IRIX\ 6.*) - RPATHFLAG="-rpath " - ;; - IRIX\ 5.*) - RPATHFLAG="-rpath " - ;; - SunOS\ 5.*) - if test "$TCC" = "yes"; then - # tcc doesn't know about -R - RPATHFLAG="-Wl,-R," - else - RPATHFLAG=-R - fi - ;; - Linux\ 2.*) - RPATHFLAG="-Wl,-rpath," - ;; - *) - : - ;; -esac - -if test x$RPATHFLAG = x ; then - AC_MSG_RESULT(none) -else - AC_MSG_RESULT([using $RPATHFLAG]) -fi - -RPATH_CANDIDATE_REAL_DIRS='' -RPATH_CANDIDATE_DIRS='' - -AC_MSG_RESULT([Searching for libraries]) - -for d in $1 ; do - LSH_RPATH_ADD($d) -done -]) - -dnl Try to execute a main program, and if it fails, try adding some -dnl -R flag. -dnl LSH_RPATH_FIX -AC_DEFUN([LSH_RPATH_FIX], -[if test $cross_compiling = no -a "x$RPATHFLAG" != x ; then - ac_success=no - AC_TRY_RUN([int main(int argc, char **argv) { return 0; }], - ac_success=yes, ac_success=no, :) - - if test $ac_success = no ; then - AC_MSG_CHECKING([Running simple test program failed. Trying -R flags]) -dnl echo RPATH_CANDIDATE_DIRS = $RPATH_CANDIDATE_DIRS - ac_remaining_dirs='' - ac_rpath_save_LDFLAGS="$LDFLAGS" - for d in $RPATH_CANDIDATE_DIRS ; do - if test $ac_success = yes ; then - ac_remaining_dirs="$ac_remaining_dirs $d" - else - LDFLAGS="$RPATHFLAG$d $LDFLAGS" -dnl echo LDFLAGS = $LDFLAGS - AC_TRY_RUN([int main(int argc, char **argv) { return 0; }], - [ac_success=yes - ac_rpath_save_LDFLAGS="$LDFLAGS" - AC_MSG_RESULT([adding $RPATHFLAG$d]) - ], - [ac_remaining_dirs="$ac_remaining_dirs $d"], :) - LDFLAGS="$ac_rpath_save_LDFLAGS" - fi - done - RPATH_CANDIDATE_DIRS=$ac_remaining_dirs - fi - if test $ac_success = no ; then - AC_MSG_RESULT(failed) - fi -fi -]) - -dnl Like AC_CHECK_LIB, but uses $KRB_LIBS rather than $LIBS. -dnl LSH_CHECK_KRB_LIB(LIBRARY, FUNCTION, [, ACTION-IF-FOUND [, -dnl ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]]) - -AC_DEFUN([LSH_CHECK_KRB_LIB], -[AC_CHECK_LIB([$1], [$2], - ifelse([$3], , - [[ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ - -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` - AC_DEFINE_UNQUOTED($ac_tr_lib) - KRB_LIBS="-l$1 $KRB_LIBS" - ]], [$3]), - ifelse([$4], , , [$4 -])dnl -, [$5 $KRB_LIBS]) -]) - -dnl LSH_LIB_ARGP(ACTION-IF-OK, ACTION-IF-BAD) -AC_DEFUN([LSH_LIB_ARGP], -[ ac_argp_save_LIBS="$LIBS" - ac_argp_save_LDFLAGS="$LDFLAGS" - ac_argp_ok=no - # First check if we can link with argp. - AC_SEARCH_LIBS(argp_parse, argp, - [ LSH_RPATH_FIX - AC_CACHE_CHECK([for working argp], - lsh_cv_lib_argp_works, - [ AC_TRY_RUN( -[#include -#include - -static const struct argp_option -options[] = -{ - { NULL, 0, NULL, 0, NULL, 0 } -}; - -struct child_state -{ - int n; -}; - -static error_t -child_parser(int key, char *arg, struct argp_state *state) -{ - struct child_state *input = (struct child_state *) state->input; - - switch(key) - { - default: - return ARGP_ERR_UNKNOWN; - case ARGP_KEY_END: - if (!input->n) - input->n = 1; - break; - } - return 0; -} - -const struct argp child_argp = -{ - options, - child_parser, - NULL, NULL, NULL, NULL, NULL -}; - -struct main_state -{ - struct child_state child; - int m; -}; - -static error_t -main_parser(int key, char *arg, struct argp_state *state) -{ - struct main_state *input = (struct main_state *) state->input; - - switch(key) - { - default: - return ARGP_ERR_UNKNOWN; - case ARGP_KEY_INIT: - state->child_inputs[0] = &input->child; - break; - case ARGP_KEY_END: - if (!input->m) - input->m = input->child.n; - - break; - } - return 0; -} - -static const struct argp_child -main_children[] = -{ - { &child_argp, 0, "", 0 }, - { NULL, 0, NULL, 0} -}; - -static const struct argp -main_argp = -{ options, main_parser, - NULL, - NULL, - main_children, - NULL, NULL -}; - -int main(int argc, char **argv) -{ - struct main_state input = { { 0 }, 0 }; - char *v[2] = { "foo", NULL }; - - argp_parse(&main_argp, 1, v, 0, NULL, &input); - - if ( (input.m == 1) && (input.child.n == 1) ) - return 0; - else - return 1; -} -], lsh_cv_lib_argp_works=yes, - lsh_cv_lib_argp_works=no, - lsh_cv_lib_argp_works=no)]) - - if test x$lsh_cv_lib_argp_works = xyes ; then - ac_argp_ok=yes - else - # Reset link flags - LIBS="$ac_argp_save_LIBS" - LDFLAGS="$ac_argp_save_LDFLAGS" - fi]) - - if test x$ac_argp_ok = xyes ; then - ifelse([$1],, true, [$1]) - else - ifelse([$2],, true, [$2]) - fi -]) - -dnl LSH_GCC_ATTRIBUTES -dnl Check for gcc's __attribute__ construction - -AC_DEFUN([LSH_GCC_ATTRIBUTES], -[AC_CACHE_CHECK(for __attribute__, - lsh_cv_c_attribute, -[ AC_TRY_COMPILE([ -#include -], -[ -static void foo(void) __attribute__ ((noreturn)); - -static void __attribute__ ((noreturn)) -foo(void) -{ - exit(1); -} -], -lsh_cv_c_attribute=yes, -lsh_cv_c_attribute=no)]) - -AH_TEMPLATE([HAVE_GCC_ATTRIBUTE], [Define if the compiler understands __attribute__]) -if test "x$lsh_cv_c_attribute" = "xyes"; then - AC_DEFINE(HAVE_GCC_ATTRIBUTE) -fi - -AH_BOTTOM( -[#if __GNUC__ || HAVE_GCC_ATTRIBUTE -# define NORETURN __attribute__ ((__noreturn__)) -# define PRINTF_STYLE(f, a) __attribute__ ((__format__ (__printf__, f, a))) -# define UNUSED __attribute__ ((__unused__)) -#else -# define NORETURN -# define PRINTF_STYLE(f, a) -# define UNUSED -#endif -])]) - -AC_DEFUN([LSH_GCC_FUNCTION_NAME], -[# Check for gcc's __FUNCTION__ variable -AH_TEMPLATE([HAVE_GCC_FUNCTION], - [Define if the compiler understands __FUNCTION__]) -AH_BOTTOM( -[#if HAVE_GCC_FUNCTION -# define FUNCTION_NAME __FUNCTION__ -#else -# define FUNCTION_NAME "Unknown" -#endif -]) - -AC_CACHE_CHECK(for __FUNCTION__, - lsh_cv_c_FUNCTION, - [ AC_TRY_COMPILE(, - [ #if __GNUC__ == 3 - # error __FUNCTION__ is broken in gcc-3 - #endif - void foo(void) { char c = __FUNCTION__[0]; } ], - lsh_cv_c_FUNCTION=yes, - lsh_cv_c_FUNCTION=no)]) - -if test "x$lsh_cv_c_FUNCTION" = "xyes"; then - AC_DEFINE(HAVE_GCC_FUNCTION) -fi -]) - -# Check for alloca, and include the standard blurb in config.h -AC_DEFUN([LSH_FUNC_ALLOCA], -[AC_FUNC_ALLOCA -AC_CHECK_HEADERS([malloc.h]) -AH_BOTTOM( -[/* AIX requires this to be the first thing in the file. */ -#ifndef __GNUC__ -# if HAVE_ALLOCA_H -# include -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca /* predefined by HP cc +Olibcalls */ -char *alloca (); -# endif -# endif -# endif -#else /* defined __GNUC__ */ -# if HAVE_ALLOCA_H -# include -# endif -#endif -/* Needed for alloca on windows */ -#if HAVE_MALLOC_H -# include -#endif -])]) - -AC_DEFUN([LSH_FUNC_STRERROR], -[AC_CHECK_FUNCS(strerror) -AH_BOTTOM( -[#if HAVE_STRERROR -#define STRERROR strerror -#else -#define STRERROR(x) (sys_errlist[x]) -#endif -])]) - -AC_DEFUN([LSH_FUNC_STRSIGNAL], -[AC_CHECK_FUNCS(strsignal) -AC_CHECK_DECLS([sys_siglist, _sys_siglist]) -AH_BOTTOM( -[#if HAVE_STRSIGNAL -# define STRSIGNAL strsignal -#else /* !HAVE_STRSIGNAL */ -# if HAVE_DECL_SYS_SIGLIST -# define STRSIGNAL(x) (sys_siglist[x]) -# else -# if HAVE_DECL__SYS_SIGLIST -# define STRSIGNAL(x) (_sys_siglist[x]) -# else -# define STRSIGNAL(x) "Unknown signal" -# if __GNUC__ -# warning Using dummy STRSIGNAL -# endif -# endif -# endif -#endif /* !HAVE_STRSIGNAL */ -])]) - -dnl LSH_MAKE_CONDITIONAL(symbol, test) -AC_DEFUN([LSH_MAKE_CONDITIONAL], -[if $2 ; then - IF_$1='' - UNLESS_$1='# ' -else - IF_$1='# ' - UNLESS_$1='' -fi -AC_SUBST(IF_$1) -AC_SUBST(UNLESS_$1)]) - -dnl LSH_DEPENDENCY_TRACKING - -dnl Defines compiler flags DEP_FLAGS to generate dependency -dnl information, and DEP_PROCESS that is any shell commands needed for -dnl massaging the dependency information further. Dependencies are -dnl generated as a side effect of compilation. Dependency files -dnl themselves are not treated as targets. - -AC_DEFUN([LSH_DEPENDENCY_TRACKING], -[AC_ARG_ENABLE(dependency_tracking, - AC_HELP_STRING([--disable-dependency-tracking], - [Disable dependency tracking. Dependency tracking doesn't work with BSD make]),, - [enable_dependency_tracking=yes]) - -DEP_FLAGS='' -DEP_PROCESS='true' -if test x$enable_dependency_tracking = xyes ; then - if test x$GCC = xyes ; then - gcc_version=`gcc --version | head -1` - case "$gcc_version" in - 2.*|*[[!0-9.]]2.*) - enable_dependency_tracking=no - AC_MSG_WARN([Dependency tracking disabled, gcc-3.x is needed]) - ;; - *) - DEP_FLAGS='-MT $[]@ -MD -MP -MF $[]@.d' - DEP_PROCESS='true' - ;; - esac - else - enable_dependency_tracking=no - AC_MSG_WARN([Dependency tracking disabled]) - fi -fi - -if test x$enable_dependency_tracking = xyes ; then - DEP_INCLUDE='include ' -else - DEP_INCLUDE='# ' -fi - -AC_SUBST([DEP_INCLUDE]) -AC_SUBST([DEP_FLAGS]) -AC_SUBST([DEP_PROCESS])]) - -dnl @synopsis AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEADERS-TO-CHECK])] -dnl -dnl the "ISO C9X: 7.18 Integer types " section requires the -dnl existence of an include file that defines a set of -dnl typedefs, especially uint8_t,int32_t,uintptr_t. -dnl Many older installations will not provide this file, but some will -dnl have the very same definitions in . In other enviroments -dnl we can use the inet-types in which would define the -dnl typedefs int8_t and u_int8_t respectivly. -dnl -dnl This macros will create a local "_stdint.h" or the headerfile given as -dnl an argument. In many cases that file will just "#include " -dnl or "#include ", while in other environments it will provide -dnl the set of basic 'stdint's definitions/typedefs: -dnl int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t -dnl int_least32_t.. int_fast32_t.. intmax_t -dnl which may or may not rely on the definitions of other files, -dnl or using the AC_CHECK_SIZEOF macro to determine the actual -dnl sizeof each type. -dnl -dnl if your header files require the stdint-types you will want to create an -dnl installable file mylib-int.h that all your other installable header -dnl may include. So if you have a library package named "mylib", just use -dnl AX_CREATE_STDINT_H(mylib-int.h) -dnl in configure.ac and go to install that very header file in Makefile.am -dnl along with the other headers (mylib.h) - and the mylib-specific headers -dnl can simply use "#include " to obtain the stdint-types. -dnl -dnl Remember, if the system already had a valid , the generated -dnl file will include it directly. No need for fuzzy HAVE_STDINT_H things... -dnl -dnl @, (status: used on new platforms) (see http://ac-archive.sf.net/gstdint/) -dnl @version $Id: acinclude.m4,v 1.27 2004/11/23 21:27:35 nisse Exp $ -dnl @author Guido Draheim - -AC_DEFUN([AX_CREATE_STDINT_H], -[# ------ AX CREATE STDINT H ------------------------------------- -AC_MSG_CHECKING([for stdint types]) -ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` -# try to shortcircuit - if the default include path of the compiler -# can find a "stdint.h" header then we assume that all compilers can. -AC_CACHE_VAL([ac_cv_header_stdint_t],[ -old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" -old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" -old_CFLAGS="$CFLAGS" ; CFLAGS="" -AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], -[ac_cv_stdint_result="(assuming C99 compatible system)" - ac_cv_header_stdint_t="stdint.h"; ], -[ac_cv_header_stdint_t=""]) -CXXFLAGS="$old_CXXFLAGS" -CPPFLAGS="$old_CPPFLAGS" -CFLAGS="$old_CFLAGS" ]) - -v="... $ac_cv_header_stdint_h" -if test "$ac_stdint_h" = "stdint.h" ; then - AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) -elif test "$ac_stdint_h" = "inttypes.h" ; then - AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) -elif test "_$ac_cv_header_stdint_t" = "_" ; then - AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) -else - ac_cv_header_stdint="$ac_cv_header_stdint_t" - AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) -fi - -if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. - -dnl .....intro message done, now do a few system checks..... -dnl btw, all CHECK_TYPE macros do automatically "DEFINE" a type, therefore -dnl we use the autoconf implementation detail _AC CHECK_TYPE_NEW instead - -inttype_headers=`echo $2 | sed -e 's/,/ /g'` - -ac_cv_stdint_result="(no helpful system typedefs seen)" -AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ - ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) - AC_MSG_RESULT([(..)]) - for i in stdint.h inttypes.h sys/inttypes.h $inttype_headers ; do - unset ac_cv_type_uintptr_t - unset ac_cv_type_uint64_t - _AC_CHECK_TYPE_NEW(uintptr_t,[ac_cv_header_stdint_x=$i],dnl - continue,[#include <$i>]) - AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) - ac_cv_stdint_result="(seen uintptr_t$and64 in $i)" - break; - done - AC_MSG_CHECKING([for stdint uintptr_t]) - ]) - -if test "_$ac_cv_header_stdint_x" = "_" ; then -AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ - ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) - AC_MSG_RESULT([(..)]) - for i in inttypes.h sys/inttypes.h stdint.h $inttype_headers ; do - unset ac_cv_type_uint32_t - unset ac_cv_type_uint64_t - AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],dnl - continue,[#include <$i>]) - AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) - ac_cv_stdint_result="(seen uint32_t$and64 in $i)" - break; - done - AC_MSG_CHECKING([for stdint uint32_t]) - ]) -fi - -if test "_$ac_cv_header_stdint_x" = "_" ; then -if test "_$ac_cv_header_stdint_o" = "_" ; then -AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ - ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) - AC_MSG_RESULT([(..)]) - for i in sys/types.h inttypes.h sys/inttypes.h $inttype_headers ; do - unset ac_cv_type_u_int32_t - unset ac_cv_type_u_int64_t - AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],dnl - continue,[#include <$i>]) - AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) - ac_cv_stdint_result="(seen u_int32_t$and64 in $i)" - break; - done - AC_MSG_CHECKING([for stdint u_int32_t]) - ]) -fi fi - -dnl if there was no good C99 header file, do some typedef checks... -if test "_$ac_cv_header_stdint_x" = "_" ; then - AC_MSG_CHECKING([for stdint datatype model]) - AC_MSG_RESULT([(..)]) - AC_CHECK_SIZEOF(char) - AC_CHECK_SIZEOF(short) - AC_CHECK_SIZEOF(int) - AC_CHECK_SIZEOF(long) - AC_CHECK_SIZEOF(void*) - ac_cv_stdint_char_model="" - ac_cv_stdint_char_model="$ac_cv_stdint_char_model$ac_cv_sizeof_char" - ac_cv_stdint_char_model="$ac_cv_stdint_char_model$ac_cv_sizeof_short" - ac_cv_stdint_char_model="$ac_cv_stdint_char_model$ac_cv_sizeof_int" - ac_cv_stdint_long_model="" - ac_cv_stdint_long_model="$ac_cv_stdint_long_model$ac_cv_sizeof_int" - ac_cv_stdint_long_model="$ac_cv_stdint_long_model$ac_cv_sizeof_long" - ac_cv_stdint_long_model="$ac_cv_stdint_long_model$ac_cv_sizeof_voidp" - name="$ac_cv_stdint_long_model" - case "$ac_cv_stdint_char_model/$ac_cv_stdint_long_model" in - 122/242) name="$name, IP16 (standard 16bit machine)" ;; - 122/244) name="$name, LP32 (standard 32bit mac/win)" ;; - 122/*) name="$name (unusual int16 model)" ;; - 124/444) name="$name, ILP32 (standard 32bit unixish)" ;; - 124/488) name="$name, LP64 (standard 64bit unixish)" ;; - 124/448) name="$name, LLP64 (unusual 64bit unixish)" ;; - 124/*) name="$name (unusual int32 model)" ;; - 128/888) name="$name, ILP64 (unusual 64bit numeric)" ;; - 128/*) name="$name (unusual int64 model)" ;; - 222/*|444/*) name="$name (unusual dsptype)" ;; - *) name="$name (very unusal model)" ;; - esac - AC_MSG_RESULT([combined for stdint datatype model... $name]) -fi - -if test "_$ac_cv_header_stdint_x" != "_" ; then - ac_cv_header_stdint="$ac_cv_header_stdint_x" -elif test "_$ac_cv_header_stdint_o" != "_" ; then - ac_cv_header_stdint="$ac_cv_header_stdint_o" -elif test "_$ac_cv_header_stdint_u" != "_" ; then - ac_cv_header_stdint="$ac_cv_header_stdint_u" -else - ac_cv_header_stdint="stddef.h" -fi - -AC_MSG_CHECKING([for extra inttypes in chosen header]) -AC_MSG_RESULT([($ac_cv_header_stdint)]) -dnl see if int_least and int_fast types are present in _this_ header. -unset ac_cv_type_int_least32_t -unset ac_cv_type_int_fast32_t -AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) -AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) -AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) - -fi # shortcircut to system "stdint.h" -# ------------------ PREPARE VARIABLES ------------------------------ -if test "$GCC" = "yes" ; then -ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` -else -ac_cv_stdint_message="using $CC" -fi - -AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl -$ac_cv_stdint_result]) - -# ----------------- DONE inttypes.h checks START header ------------- -AC_CONFIG_COMMANDS([$ac_stdint_h],[ -AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) -ac_stdint=$tmp/_stdint.h - -echo "#ifndef" $_ac_stdint_h >$ac_stdint -echo "#define" $_ac_stdint_h "1" >>$ac_stdint -echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint -echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint -echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint -if test "_$ac_cv_header_stdint_t" != "_" ; then -echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint -fi - -cat >>$ac_stdint < -#else -#include - -/* .................... configured part ............................ */ - -STDINT_EOF - -echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint -if test "_$ac_cv_header_stdint_x" != "_" ; then - ac_header="$ac_cv_header_stdint_x" - echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint -else - echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint -fi - -echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint -if test "_$ac_cv_header_stdint_o" != "_" ; then - ac_header="$ac_cv_header_stdint_o" - echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint -else - echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint -fi - -echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint -if test "_$ac_cv_header_stdint_u" != "_" ; then - ac_header="$ac_cv_header_stdint_u" - echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint -else - echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint -fi - -echo "" >>$ac_stdint - -if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then - echo "#include <$ac_header>" >>$ac_stdint - echo "" >>$ac_stdint -fi fi - -echo "/* which 64bit typedef has been found */" >>$ac_stdint -if test "$ac_cv_type_uint64_t" = "yes" ; then -echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint -else -echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint -fi -if test "$ac_cv_type_u_int64_t" = "yes" ; then -echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint -else -echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint -fi -echo "" >>$ac_stdint - -echo "/* which type model has been detected */" >>$ac_stdint -if test "_$ac_cv_stdint_char_model" != "_" ; then -echo "#define _STDINT_CHAR_MODEL" "$ac_cv_stdint_char_model" >>$ac_stdint -echo "#define _STDINT_LONG_MODEL" "$ac_cv_stdint_long_model" >>$ac_stdint -else -echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint -echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint -fi -echo "" >>$ac_stdint - -echo "/* whether int_least types were detected */" >>$ac_stdint -if test "$ac_cv_type_int_least32_t" = "yes"; then -echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint -else -echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint -fi -echo "/* whether int_fast types were detected */" >>$ac_stdint -if test "$ac_cv_type_int_fast32_t" = "yes"; then -echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint -else -echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint -fi -echo "/* whether intmax_t type was detected */" >>$ac_stdint -if test "$ac_cv_type_intmax_t" = "yes"; then -echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint -else -echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint -fi -echo "" >>$ac_stdint - - cat >>$ac_stdint <= 199901L -#define _HAVE_UINT64_T -typedef long long int64_t; -typedef unsigned long long uint64_t; - -#elif !defined __STRICT_ANSI__ -#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ -#define _HAVE_UINT64_T -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; - -#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ -/* note: all ELF-systems seem to have loff-support which needs 64-bit */ -#if !defined _NO_LONGLONG -#define _HAVE_UINT64_T -typedef long long int64_t; -typedef unsigned long long uint64_t; -#endif - -#elif defined __alpha || (defined __mips && defined _ABIN32) -#if !defined _NO_LONGLONG -typedef long int64_t; -typedef unsigned long uint64_t; -#endif - /* compiler/cpu type to define int64_t */ -#endif -#endif -#endif - -#if defined _STDINT_HAVE_U_INT_TYPES -/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ -typedef u_int8_t uint8_t; -typedef u_int16_t uint16_t; -typedef u_int32_t uint32_t; - -/* glibc compatibility */ -#ifndef __int8_t_defined -#define __int8_t_defined -#endif -#endif - -#ifdef _STDINT_NEED_INT_MODEL_T -/* we must guess all the basic types. Apart from byte-adressable system, */ -/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ -/* (btw, those nibble-addressable systems are way off, or so we assume) */ - -dnl /* have a look at "64bit and data size neutrality" at */ -dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ -dnl /* (the shorthand "ILP" types always have a "P" part) */ - -#if defined _STDINT_BYTE_MODEL -#if _STDINT_LONG_MODEL+0 == 242 -/* 2:4:2 = IP16 = a normal 16-bit system */ -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned long uint32_t; -#ifndef __int8_t_defined -#define __int8_t_defined -typedef char int8_t; -typedef short int16_t; -typedef long int32_t; -#endif -#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 -/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ -/* 4:4:4 = ILP32 = a normal 32-bit system */ -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#ifndef __int8_t_defined -#define __int8_t_defined -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -#endif -#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 -/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ -/* 4:8:8 = LP64 = a normal 64-bit system */ -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#ifndef __int8_t_defined -#define __int8_t_defined -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -#endif -/* this system has a "long" of 64bit */ -#ifndef _HAVE_UINT64_T -#define _HAVE_UINT64_T -typedef unsigned long uint64_t; -typedef long int64_t; -#endif -#elif _STDINT_LONG_MODEL+0 == 448 -/* LLP64 a 64-bit system derived from a 32-bit system */ -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#ifndef __int8_t_defined -#define __int8_t_defined -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -#endif -/* assuming the system has a "long long" */ -#ifndef _HAVE_UINT64_T -#define _HAVE_UINT64_T -typedef unsigned long long uint64_t; -typedef long long int64_t; -#endif -#else -#define _STDINT_NO_INT32_T -#endif -#else -#define _STDINT_NO_INT8_T -#define _STDINT_NO_INT32_T -#endif -#endif - -/* - * quote from SunOS-5.8 sys/inttypes.h: - * Use at your own risk. As of February 1996, the committee is squarely - * behind the fixed sized types; the "least" and "fast" types are still being - * discussed. The probability that the "fast" types may be removed before - * the standard is finalized is high enough that they are not currently - * implemented. - */ - -#if defined _STDINT_NEED_INT_LEAST_T -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -#ifdef _HAVE_UINT64_T -typedef int64_t int_least64_t; -#endif - -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -#ifdef _HAVE_UINT64_T -typedef uint64_t uint_least64_t; -#endif - /* least types */ -#endif - -#if defined _STDINT_NEED_INT_FAST_T -typedef int8_t int_fast8_t; -typedef int int_fast16_t; -typedef int32_t int_fast32_t; -#ifdef _HAVE_UINT64_T -typedef int64_t int_fast64_t; -#endif - -typedef uint8_t uint_fast8_t; -typedef unsigned uint_fast16_t; -typedef uint32_t uint_fast32_t; -#ifdef _HAVE_UINT64_T -typedef uint64_t uint_fast64_t; -#endif - /* fast types */ -#endif - -#ifdef _STDINT_NEED_INTMAX_T -#ifdef _HAVE_UINT64_T -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; -#else -typedef long intmax_t; -typedef unsigned long uintmax_t; -#endif -#endif - -#ifdef _STDINT_NEED_INTPTR_T -#ifndef __intptr_t_defined -#define __intptr_t_defined -/* we encourage using "long" to store pointer values, never use "int" ! */ -#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 -typedef unsinged int uintptr_t; -typedef int intptr_t; -#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 -typedef unsigned long uintptr_t; -typedef long intptr_t; -#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T -typedef uint64_t uintptr_t; -typedef int64_t intptr_t; -#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ -typedef unsigned long uintptr_t; -typedef long intptr_t; -#endif -#endif -#endif - - /* shortcircuit*/ -#endif - /* once */ -#endif -#endif -STDINT_EOF - if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then - AC_MSG_NOTICE([$ac_stdint_h is unchanged]) - else - ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` - AS_MKDIR_P(["$ac_dir"]) - rm -f $ac_stdint_h - mv $ac_stdint $ac_stdint_h - fi -],[# variables for create stdint.h replacement -PACKAGE="$PACKAGE" -VERSION="$VERSION" -ac_stdint_h="$ac_stdint_h" -_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) -ac_cv_stdint_message="$ac_cv_stdint_message" -ac_cv_header_stdint_t="$ac_cv_header_stdint_t" -ac_cv_header_stdint_x="$ac_cv_header_stdint_x" -ac_cv_header_stdint_o="$ac_cv_header_stdint_o" -ac_cv_header_stdint_u="$ac_cv_header_stdint_u" -ac_cv_type_uint64_t="$ac_cv_type_uint64_t" -ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" -ac_cv_stdint_char_model="$ac_cv_stdint_char_model" -ac_cv_stdint_long_model="$ac_cv_stdint_long_model" -ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" -ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" -ac_cv_type_intmax_t="$ac_cv_type_intmax_t" -]) -]) diff --git a/argp-standalone/argp-ba.c b/argp-standalone/argp-ba.c deleted file mode 100644 index 0d3958c11..000000000 --- a/argp-standalone/argp-ba.c +++ /dev/null @@ -1,26 +0,0 @@ -/* Default definition for ARGP_PROGRAM_BUG_ADDRESS. - Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* If set by the user program, it should point to string that is the - bug-reporting address for the program. It will be printed by argp_help if - the ARGP_HELP_BUG_ADDR flag is set (as it is by various standard help - messages), embedded in a sentence that says something like `Report bugs to - ADDR.'. */ -const char *argp_program_bug_address = 0; diff --git a/argp-standalone/argp-eexst.c b/argp-standalone/argp-eexst.c deleted file mode 100644 index 46b27847a..000000000 --- a/argp-standalone/argp-eexst.c +++ /dev/null @@ -1,36 +0,0 @@ -/* Default definition for ARGP_ERR_EXIT_STATUS - Copyright (C) 1997 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#if HAVE_SYSEXITS_H -# include -#else -# define EX_USAGE 64 -#endif - -#include "argp.h" - -/* The exit status that argp will use when exiting due to a parsing error. - If not defined or set by the user program, this defaults to EX_USAGE from - . */ -error_t argp_err_exit_status = EX_USAGE; diff --git a/argp-standalone/argp-fmtstream.c b/argp-standalone/argp-fmtstream.c deleted file mode 100644 index 494b6b31d..000000000 --- a/argp-standalone/argp-fmtstream.c +++ /dev/null @@ -1,477 +0,0 @@ -/* Word-wrapping and line-truncating streams - Copyright (C) 1997, 1998, 1999, 2001 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* This package emulates glibc `line_wrap_stream' semantics for systems that - don't have that. */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "argp-fmtstream.h" -#include "argp-namefrob.h" - -#ifndef ARGP_FMTSTREAM_USE_LINEWRAP - -#ifndef isblank -#define isblank(ch) ((ch)==' ' || (ch)=='\t') -#endif - -#if defined _LIBC && defined USE_IN_LIBIO -# include -# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) -#endif - -#define INIT_BUF_SIZE 200 -#define PRINTF_SIZE_GUESS 150 - -/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines - written on it with LMARGIN spaces and limits them to RMARGIN columns - total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by - replacing the whitespace before them with a newline and WMARGIN spaces. - Otherwise, chars beyond RMARGIN are simply dropped until a newline. - Returns NULL if there was an error. */ -argp_fmtstream_t -__argp_make_fmtstream (FILE *stream, - size_t lmargin, size_t rmargin, ssize_t wmargin) -{ - argp_fmtstream_t fs = malloc (sizeof (struct argp_fmtstream)); - if (fs) - { - fs->stream = stream; - - fs->lmargin = lmargin; - fs->rmargin = rmargin; - fs->wmargin = wmargin; - fs->point_col = 0; - fs->point_offs = 0; - - fs->buf = malloc (INIT_BUF_SIZE); - if (! fs->buf) - { - free (fs); - fs = 0; - } - else - { - fs->p = fs->buf; - fs->end = fs->buf + INIT_BUF_SIZE; - } - } - - return fs; -} -#ifdef weak_alias -weak_alias (__argp_make_fmtstream, argp_make_fmtstream) -#endif - -/* Flush FS to its stream, and free it (but don't close the stream). */ -void -__argp_fmtstream_free (argp_fmtstream_t fs) -{ - __argp_fmtstream_update (fs); - if (fs->p > fs->buf) - FWRITE_UNLOCKED (fs->buf, 1, fs->p - fs->buf, fs->stream); - free (fs->buf); - free (fs); -} -#ifdef weak_alias -weak_alias (__argp_fmtstream_free, argp_fmtstream_free) -#endif - -/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the - end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ -void -__argp_fmtstream_update (argp_fmtstream_t fs) -{ - char *buf, *nl; - size_t len; - - /* Scan the buffer for newlines. */ - buf = fs->buf + fs->point_offs; - while (buf < fs->p) - { - size_t r; - - if (fs->point_col == 0 && fs->lmargin != 0) - { - /* We are starting a new line. Print spaces to the left margin. */ - const size_t pad = fs->lmargin; - if (fs->p + pad < fs->end) - { - /* We can fit in them in the buffer by moving the - buffer text up and filling in the beginning. */ - memmove (buf + pad, buf, fs->p - buf); - fs->p += pad; /* Compensate for bigger buffer. */ - memset (buf, ' ', pad); /* Fill in the spaces. */ - buf += pad; /* Don't bother searching them. */ - } - else - { - /* No buffer space for spaces. Must flush. */ - size_t i; - for (i = 0; i < pad; i++) - PUTC_UNLOCKED (' ', fs->stream); - } - fs->point_col = pad; - } - - len = fs->p - buf; - nl = memchr (buf, '\n', len); - - if (fs->point_col < 0) - fs->point_col = 0; - - if (!nl) - { - /* The buffer ends in a partial line. */ - - if (fs->point_col + len < fs->rmargin) - { - /* The remaining buffer text is a partial line and fits - within the maximum line width. Advance point for the - characters to be written and stop scanning. */ - fs->point_col += len; - break; - } - else - /* Set the end-of-line pointer for the code below to - the end of the buffer. */ - nl = fs->p; - } - else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) - { - /* The buffer contains a full line that fits within the maximum - line width. Reset point and scan the next line. */ - fs->point_col = 0; - buf = nl + 1; - continue; - } - - /* This line is too long. */ - r = fs->rmargin - 1; - - if (fs->wmargin < 0) - { - /* Truncate the line by overwriting the excess with the - newline and anything after it in the buffer. */ - if (nl < fs->p) - { - memmove (buf + (r - fs->point_col), nl, fs->p - nl); - fs->p -= buf + (r - fs->point_col) - nl; - /* Reset point for the next line and start scanning it. */ - fs->point_col = 0; - buf += r + 1; /* Skip full line plus \n. */ - } - else - { - /* The buffer ends with a partial line that is beyond the - maximum line width. Advance point for the characters - written, and discard those past the max from the buffer. */ - fs->point_col += len; - fs->p -= fs->point_col - r; - break; - } - } - else - { - /* Do word wrap. Go to the column just past the maximum line - width and scan back for the beginning of the word there. - Then insert a line break. */ - - char *p, *nextline; - int i; - - p = buf + (r + 1 - fs->point_col); - while (p >= buf && !isblank (*p)) - --p; - nextline = p + 1; /* This will begin the next line. */ - - if (nextline > buf) - { - /* Swallow separating blanks. */ - if (p >= buf) - do - --p; - while (p >= buf && isblank (*p)); - nl = p + 1; /* The newline will replace the first blank. */ - } - else - { - /* A single word that is greater than the maximum line width. - Oh well. Put it on an overlong line by itself. */ - p = buf + (r + 1 - fs->point_col); - /* Find the end of the long word. */ - do - ++p; - while (p < nl && !isblank (*p)); - if (p == nl) - { - /* It already ends a line. No fussing required. */ - fs->point_col = 0; - buf = nl + 1; - continue; - } - /* We will move the newline to replace the first blank. */ - nl = p; - /* Swallow separating blanks. */ - do - ++p; - while (isblank (*p)); - /* The next line will start here. */ - nextline = p; - } - - /* Note: There are a bunch of tests below for - NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall - at the end of the buffer, and NEXTLINE is in fact empty (and so - we need not be careful to maintain its contents). */ - - if (nextline == buf + len + 1 - ? fs->end - nl < fs->wmargin + 1 - : nextline - (nl + 1) < fs->wmargin) - { - /* The margin needs more blanks than we removed. */ - if (fs->end - fs->p > fs->wmargin + 1) - /* Make some space for them. */ - { - size_t mv = fs->p - nextline; - memmove (nl + 1 + fs->wmargin, nextline, mv); - nextline = nl + 1 + fs->wmargin; - len = nextline + mv - buf; - *nl++ = '\n'; - } - else - /* Output the first line so we can use the space. */ - { - if (nl > fs->buf) - FWRITE_UNLOCKED (fs->buf, 1, nl - fs->buf, fs->stream); - PUTC_UNLOCKED ('\n', fs->stream); - len += buf - fs->buf; - nl = buf = fs->buf; - } - } - else - /* We can fit the newline and blanks in before - the next word. */ - *nl++ = '\n'; - - if (nextline - nl >= fs->wmargin - || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin)) - /* Add blanks up to the wrap margin column. */ - for (i = 0; i < fs->wmargin; ++i) - *nl++ = ' '; - else - for (i = 0; i < fs->wmargin; ++i) - PUTC_UNLOCKED (' ', fs->stream); - - /* Copy the tail of the original buffer into the current buffer - position. */ - if (nl < nextline) - memmove (nl, nextline, buf + len - nextline); - len -= nextline - buf; - - /* Continue the scan on the remaining lines in the buffer. */ - buf = nl; - - /* Restore bufp to include all the remaining text. */ - fs->p = nl + len; - - /* Reset the counter of what has been output this line. If wmargin - is 0, we want to avoid the lmargin getting added, so we set - point_col to a magic value of -1 in that case. */ - fs->point_col = fs->wmargin ? fs->wmargin : -1; - } - } - - /* Remember that we've scanned as far as the end of the buffer. */ - fs->point_offs = fs->p - fs->buf; -} - -/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by - growing the buffer, or by flushing it. True is returned iff we succeed. */ -int -__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) -{ - if ((size_t) (fs->end - fs->p) < amount) - { - ssize_t wrote; - - /* Flush FS's buffer. */ - __argp_fmtstream_update (fs); - - wrote = FWRITE_UNLOCKED (fs->buf, 1, fs->p - fs->buf, fs->stream); - if (wrote == fs->p - fs->buf) - { - fs->p = fs->buf; - fs->point_offs = 0; - } - else - { - fs->p -= wrote; - fs->point_offs -= wrote; - memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); - return 0; - } - - if ((size_t) (fs->end - fs->buf) < amount) - /* Gotta grow the buffer. */ - { - size_t new_size = fs->end - fs->buf + amount; - char *new_buf = realloc (fs->buf, new_size); - - if (! new_buf) - { - __set_errno (ENOMEM); - return 0; - } - - fs->buf = new_buf; - fs->end = new_buf + new_size; - fs->p = fs->buf; - } - } - - return 1; -} - -ssize_t -__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) -{ - size_t out; - size_t avail; - size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ - - do - { - va_list args; - - if (! __argp_fmtstream_ensure (fs, size_guess)) - return -1; - - va_start (args, fmt); - avail = fs->end - fs->p; - out = __vsnprintf (fs->p, avail, fmt, args); - va_end (args); - if (out >= avail) - size_guess = out + 1; - } - while (out >= avail); - - fs->p += out; - - return out; -} -#ifdef weak_alias -weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) -#endif - -#if __STDC_VERSION__ - 199900L < 1 -/* Duplicate the inline definitions in argp-fmtstream.h, for compilers - * that don't do inlining. */ -size_t -__argp_fmtstream_write (argp_fmtstream_t __fs, - __const char *__str, size_t __len) -{ - if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) - { - memcpy (__fs->p, __str, __len); - __fs->p += __len; - return __len; - } - else - return 0; -} - -int -__argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) -{ - size_t __len = strlen (__str); - if (__len) - { - size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); - return __wrote == __len ? 0 : -1; - } - else - return 0; -} - -int -__argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) -{ - if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) - return *__fs->p++ = __ch; - else - return EOF; -} - -/* Set __FS's left margin to __LMARGIN and return the old value. */ -size_t -__argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) -{ - size_t __old; - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - __old = __fs->lmargin; - __fs->lmargin = __lmargin; - return __old; -} - -/* Set __FS's right margin to __RMARGIN and return the old value. */ -size_t -__argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) -{ - size_t __old; - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - __old = __fs->rmargin; - __fs->rmargin = __rmargin; - return __old; -} - -/* Set FS's wrap margin to __WMARGIN and return the old value. */ -size_t -__argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) -{ - size_t __old; - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - __old = __fs->wmargin; - __fs->wmargin = __wmargin; - return __old; -} - -/* Return the column number of the current output point in __FS. */ -size_t -__argp_fmtstream_point (argp_fmtstream_t __fs) -{ - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - return __fs->point_col >= 0 ? __fs->point_col : 0; -} -#endif /* __STDC_VERSION__ - 199900L < 1 */ - -#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ diff --git a/argp-standalone/argp-fmtstream.h b/argp-standalone/argp-fmtstream.h deleted file mode 100644 index 828f4357d..000000000 --- a/argp-standalone/argp-fmtstream.h +++ /dev/null @@ -1,327 +0,0 @@ -/* Word-wrapping and line-truncating streams. - Copyright (C) 1997, 2003 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* This package emulates glibc `line_wrap_stream' semantics for systems that - don't have that. If the system does have it, it is just a wrapper for - that. This header file is only used internally while compiling argp, and - shouldn't be installed. */ - -#ifndef _ARGP_FMTSTREAM_H -#define _ARGP_FMTSTREAM_H - -#include -#include - -#if HAVE_UNISTD_H -# include -#else -/* This is a kludge to make the code compile on windows. Perhaps it - would be better to just replace ssize_t with int through out the - code. */ -# define ssize_t int -#endif - -#if _LIBC || (defined (HAVE_FLOCKFILE) && defined(HAVE_PUTC_UNLOCKED) \ - && defined (HAVE_FPUTS_UNLOCKED) && defined (HAVE_FWRITE_UNLOCKED) ) -/* Use locking funxtions */ -# define FLOCKFILE(f) flockfile(f) -# define FUNLOCKFILE(f) funlockfile(f) -# define PUTC_UNLOCKED(c, f) putc_unlocked((c), (f)) -# define FPUTS_UNLOCKED(s, f) fputs_unlocked((s), (f)) -# define FWRITE_UNLOCKED(b, s, n, f) fwrite_unlocked((b), (s), (n), (f)) -#else -/* Disable stdio locking */ -# define FLOCKFILE(f) -# define FUNLOCKFILE(f) -# define PUTC_UNLOCKED(c, f) putc((c), (f)) -# define FPUTS_UNLOCKED(s, f) fputs((s), (f)) -# define FWRITE_UNLOCKED(b, s, n, f) fwrite((b), (s), (n), (f)) -#endif /* No thread safe i/o */ - -#if (_LIBC - 0 && !defined (USE_IN_LIBIO)) \ - || (defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H)) -/* line_wrap_stream is available, so use that. */ -#define ARGP_FMTSTREAM_USE_LINEWRAP -#endif - -#ifdef ARGP_FMTSTREAM_USE_LINEWRAP -/* Just be a simple wrapper for line_wrap_stream; the semantics are - *slightly* different, as line_wrap_stream doesn't actually make a new - object, it just modifies the given stream (reversibly) to do - line-wrapping. Since we control who uses this code, it doesn't matter. */ - -#include - -typedef FILE *argp_fmtstream_t; - -#define argp_make_fmtstream line_wrap_stream -#define __argp_make_fmtstream line_wrap_stream -#define argp_fmtstream_free line_unwrap_stream -#define __argp_fmtstream_free line_unwrap_stream - -#define __argp_fmtstream_putc(fs,ch) putc(ch,fs) -#define argp_fmtstream_putc(fs,ch) putc(ch,fs) -#define __argp_fmtstream_puts(fs,str) fputs(str,fs) -#define argp_fmtstream_puts(fs,str) fputs(str,fs) -#define __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) -#define argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) -#define __argp_fmtstream_printf fprintf -#define argp_fmtstream_printf fprintf - -#define __argp_fmtstream_lmargin line_wrap_lmargin -#define argp_fmtstream_lmargin line_wrap_lmargin -#define __argp_fmtstream_set_lmargin line_wrap_set_lmargin -#define argp_fmtstream_set_lmargin line_wrap_set_lmargin -#define __argp_fmtstream_rmargin line_wrap_rmargin -#define argp_fmtstream_rmargin line_wrap_rmargin -#define __argp_fmtstream_set_rmargin line_wrap_set_rmargin -#define argp_fmtstream_set_rmargin line_wrap_set_rmargin -#define __argp_fmtstream_wmargin line_wrap_wmargin -#define argp_fmtstream_wmargin line_wrap_wmargin -#define __argp_fmtstream_set_wmargin line_wrap_set_wmargin -#define argp_fmtstream_set_wmargin line_wrap_set_wmargin -#define __argp_fmtstream_point line_wrap_point -#define argp_fmtstream_point line_wrap_point - -#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ -/* Guess we have to define our own version. */ - -#ifndef __const -#define __const const -#endif - - -struct argp_fmtstream -{ - FILE *stream; /* The stream we're outputting to. */ - - size_t lmargin, rmargin; /* Left and right margins. */ - ssize_t wmargin; /* Margin to wrap to, or -1 to truncate. */ - - /* Point in buffer to which we've processed for wrapping, but not output. */ - size_t point_offs; - /* Output column at POINT_OFFS, or -1 meaning 0 but don't add lmargin. */ - ssize_t point_col; - - char *buf; /* Output buffer. */ - char *p; /* Current end of text in BUF. */ - char *end; /* Absolute end of BUF. */ -}; - -typedef struct argp_fmtstream *argp_fmtstream_t; - -/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines - written on it with LMARGIN spaces and limits them to RMARGIN columns - total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by - replacing the whitespace before them with a newline and WMARGIN spaces. - Otherwise, chars beyond RMARGIN are simply dropped until a newline. - Returns NULL if there was an error. */ -extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, - size_t __lmargin, - size_t __rmargin, - ssize_t __wmargin); -extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, - size_t __lmargin, - size_t __rmargin, - ssize_t __wmargin); - -/* Flush __FS to its stream, and free it (but don't close the stream). */ -extern void __argp_fmtstream_free (argp_fmtstream_t __fs); -extern void argp_fmtstream_free (argp_fmtstream_t __fs); - -extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, - __const char *__fmt, ...) - PRINTF_STYLE(2,3); -extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, - __const char *__fmt, ...) - PRINTF_STYLE(2,3); - -#if __STDC_VERSION__ - 199900L < 1 -extern int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); -extern int argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); - -extern int __argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str); -extern int argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str); - -extern size_t __argp_fmtstream_write (argp_fmtstream_t __fs, - __const char *__str, size_t __len); -extern size_t argp_fmtstream_write (argp_fmtstream_t __fs, - __const char *__str, size_t __len); -#endif /* __STDC_VERSION__ - 199900L < 1 */ - -/* Access macros for various bits of state. */ -#define argp_fmtstream_lmargin(__fs) ((__fs)->lmargin) -#define argp_fmtstream_rmargin(__fs) ((__fs)->rmargin) -#define argp_fmtstream_wmargin(__fs) ((__fs)->wmargin) -#define __argp_fmtstream_lmargin argp_fmtstream_lmargin -#define __argp_fmtstream_rmargin argp_fmtstream_rmargin -#define __argp_fmtstream_wmargin argp_fmtstream_wmargin - -#if __STDC_VERSION__ - 199900L < 1 -/* Set __FS's left margin to LMARGIN and return the old value. */ -extern size_t argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, - size_t __lmargin); -extern size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, - size_t __lmargin); - -/* Set __FS's right margin to __RMARGIN and return the old value. */ -extern size_t argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, - size_t __rmargin); -extern size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, - size_t __rmargin); - -/* Set __FS's wrap margin to __WMARGIN and return the old value. */ -extern size_t argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, - size_t __wmargin); -extern size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, - size_t __wmargin); - -/* Return the column number of the current output point in __FS. */ -extern size_t argp_fmtstream_point (argp_fmtstream_t __fs); -extern size_t __argp_fmtstream_point (argp_fmtstream_t __fs); -#endif /* __STDC_VERSION__ - 199900L < 1 */ - -/* Internal routines. */ -extern void _argp_fmtstream_update (argp_fmtstream_t __fs); -extern void __argp_fmtstream_update (argp_fmtstream_t __fs); -extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); -extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); - -#ifdef __OPTIMIZE__ -/* Inline versions of above routines. */ - -#if !_LIBC -#define __argp_fmtstream_putc argp_fmtstream_putc -#define __argp_fmtstream_puts argp_fmtstream_puts -#define __argp_fmtstream_write argp_fmtstream_write -#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin -#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin -#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin -#define __argp_fmtstream_point argp_fmtstream_point -#define __argp_fmtstream_update _argp_fmtstream_update -#define __argp_fmtstream_ensure _argp_fmtstream_ensure -#endif - -#ifndef ARGP_FS_EI -#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) -#define ARGP_FS_EI extern inline -#else -#define ARGP_FS_EI inline -#endif -#endif - -ARGP_FS_EI size_t -__argp_fmtstream_write (argp_fmtstream_t __fs, - __const char *__str, size_t __len) -{ - if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) - { - memcpy (__fs->p, __str, __len); - __fs->p += __len; - return __len; - } - else - return 0; -} - -ARGP_FS_EI int -__argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) -{ - size_t __len = strlen (__str); - if (__len) - { - size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); - return __wrote == __len ? 0 : -1; - } - else - return 0; -} - -ARGP_FS_EI int -__argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) -{ - if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) - return *__fs->p++ = __ch; - else - return EOF; -} - -/* Set __FS's left margin to __LMARGIN and return the old value. */ -ARGP_FS_EI size_t -__argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) -{ - size_t __old; - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - __old = __fs->lmargin; - __fs->lmargin = __lmargin; - return __old; -} - -/* Set __FS's right margin to __RMARGIN and return the old value. */ -ARGP_FS_EI size_t -__argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) -{ - size_t __old; - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - __old = __fs->rmargin; - __fs->rmargin = __rmargin; - return __old; -} - -/* Set FS's wrap margin to __WMARGIN and return the old value. */ -ARGP_FS_EI size_t -__argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) -{ - size_t __old; - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - __old = __fs->wmargin; - __fs->wmargin = __wmargin; - return __old; -} - -/* Return the column number of the current output point in __FS. */ -ARGP_FS_EI size_t -__argp_fmtstream_point (argp_fmtstream_t __fs) -{ - if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) - __argp_fmtstream_update (__fs); - return __fs->point_col >= 0 ? __fs->point_col : 0; -} - -#if !_LIBC -#undef __argp_fmtstream_putc -#undef __argp_fmtstream_puts -#undef __argp_fmtstream_write -#undef __argp_fmtstream_set_lmargin -#undef __argp_fmtstream_set_rmargin -#undef __argp_fmtstream_set_wmargin -#undef __argp_fmtstream_point -#undef __argp_fmtstream_update -#undef __argp_fmtstream_ensure -#endif - -#endif /* __OPTIMIZE__ */ - -#endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ - -#endif /* argp-fmtstream.h */ diff --git a/argp-standalone/argp-help.c b/argp-standalone/argp-help.c deleted file mode 100644 index ced78c4cb..000000000 --- a/argp-standalone/argp-help.c +++ /dev/null @@ -1,1849 +0,0 @@ -/* Hierarchial argument parsing help output - Copyright (C) 1995,96,97,98,99,2000, 2003 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -#include -#endif - -#if HAVE_ALLOCA_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#if HAVE_MALLOC_H -/* Needed, for alloca on windows */ -# include -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. */ -# if defined HAVE_LIBINTL_H || defined _LIBC -# include -# ifdef _LIBC -# undef dgettext -# define dgettext(domain, msgid) __dcgettext (domain, msgid, LC_MESSAGES) -# endif -# else -# define dgettext(domain, msgid) (msgid) -# endif -#endif - -#include "argp.h" -#include "argp-fmtstream.h" -#include "argp-namefrob.h" - - -#ifndef _LIBC -# ifndef __strchrnul -# define __strchrnul strchrnul -# endif -# ifndef __mempcpy -# define __mempcpy mempcpy -# endif -/* We need to use a different name, as __strndup is likely a macro. */ -# define STRNDUP strndup -# if HAVE_STRERROR -# define STRERROR strerror -# else -# define STRERROR(x) (sys_errlist[x]) -# endif -#else /* _LIBC */ -# define FLOCKFILE __flockfile -# define FUNLOCKFILE __funlockfile -# define STRNDUP __strndup -# define STRERROR strerror -#endif - -#if !_LIBC -# if !HAVE_STRNDUP -char *strndup (const char *s, size_t size); -# endif /* !HAVE_STRNDUP */ - -# if !HAVE_MEMPCPY -void *mempcpy (void *to, const void *from, size_t size); -# endif /* !HAVE_MEMPCPY */ - -# if !HAVE_STRCHRNUL -char *strchrnul(const char *s, int c); -# endif /* !HAVE_STRCHRNUL */ - -# if !HAVE_STRCASECMP -int strcasecmp(const char *s1, const char *s2); -#endif - -#endif /* !_LIBC */ - - -/* User-selectable (using an environment variable) formatting parameters. - - These may be specified in an environment variable called `ARGP_HELP_FMT', - with a contents like: VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2 - Where VALn must be a positive integer. The list of variables is in the - UPARAM_NAMES vector, below. */ - -/* Default parameters. */ -#define DUP_ARGS 0 /* True if option argument can be duplicated. */ -#define DUP_ARGS_NOTE 1 /* True to print a note about duplicate args. */ -#define SHORT_OPT_COL 2 /* column in which short options start */ -#define LONG_OPT_COL 6 /* column in which long options start */ -#define DOC_OPT_COL 2 /* column in which doc options start */ -#define OPT_DOC_COL 29 /* column in which option text starts */ -#define HEADER_COL 1 /* column in which group headers are printed */ -#define USAGE_INDENT 12 /* indentation of wrapped usage lines */ -#define RMARGIN 79 /* right margin used for wrapping */ - -/* User-selectable (using an environment variable) formatting parameters. - They must all be of type `int' for the parsing code to work. */ -struct uparams -{ - /* If true, arguments for an option are shown with both short and long - options, even when a given option has both, e.g. `-x ARG, --longx=ARG'. - If false, then if an option has both, the argument is only shown with - the long one, e.g., `-x, --longx=ARG', and a message indicating that - this really means both is printed below the options. */ - int dup_args; - - /* This is true if when DUP_ARGS is false, and some duplicate arguments have - been suppressed, an explanatory message should be printed. */ - int dup_args_note; - - /* Various output columns. */ - int short_opt_col; - int long_opt_col; - int doc_opt_col; - int opt_doc_col; - int header_col; - int usage_indent; - int rmargin; - - int valid; /* True when the values in here are valid. */ -}; - -/* This is a global variable, as user options are only ever read once. */ -static struct uparams uparams = { - DUP_ARGS, DUP_ARGS_NOTE, - SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL, - USAGE_INDENT, RMARGIN, - 0 -}; - -/* A particular uparam, and what the user name is. */ -struct uparam_name -{ - const char *name; /* User name. */ - int is_bool; /* Whether it's `boolean'. */ - size_t uparams_offs; /* Location of the (int) field in UPARAMS. */ -}; - -/* The name-field mappings we know about. */ -static const struct uparam_name uparam_names[] = -{ - { "dup-args", 1, offsetof (struct uparams, dup_args) }, - { "dup-args-note", 1, offsetof (struct uparams, dup_args_note) }, - { "short-opt-col", 0, offsetof (struct uparams, short_opt_col) }, - { "long-opt-col", 0, offsetof (struct uparams, long_opt_col) }, - { "doc-opt-col", 0, offsetof (struct uparams, doc_opt_col) }, - { "opt-doc-col", 0, offsetof (struct uparams, opt_doc_col) }, - { "header-col", 0, offsetof (struct uparams, header_col) }, - { "usage-indent", 0, offsetof (struct uparams, usage_indent) }, - { "rmargin", 0, offsetof (struct uparams, rmargin) }, - { 0, 0, 0 } -}; - -/* Read user options from the environment, and fill in UPARAMS appropiately. */ -static void -fill_in_uparams (const struct argp_state *state) -{ - - const char *var = getenv ("ARGP_HELP_FMT"); - -#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0); - - if (var) - /* Parse var. */ - while (*var) - { - SKIPWS (var); - - if (isalpha (*var)) - { - size_t var_len; - const struct uparam_name *un; - int unspec = 0, val = 0; - const char *arg = var; - - while (isalnum (*arg) || *arg == '-' || *arg == '_') - arg++; - var_len = arg - var; - - SKIPWS (arg); - - if (*arg == '\0' || *arg == ',') - unspec = 1; - else if (*arg == '=') - { - arg++; - SKIPWS (arg); - } - - if (unspec) - { - if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') - { - val = 0; - var += 3; - var_len -= 3; - } - else - val = 1; - } - else if (isdigit (*arg)) - { - val = atoi (arg); - while (isdigit (*arg)) - arg++; - SKIPWS (arg); - } - - for (un = uparam_names; un->name; un++) - if (strlen (un->name) == var_len - && strncmp (var, un->name, var_len) == 0) - { - if (unspec && !un->is_bool) - __argp_failure (state, 0, 0, - dgettext (state->root_argp->argp_domain, "\ -%.*s: ARGP_HELP_FMT parameter requires a value"), - (int) var_len, var); - else - *(int *)((char *)&uparams + un->uparams_offs) = val; - break; - } - if (! un->name) - __argp_failure (state, 0, 0, - dgettext (state->root_argp->argp_domain, "\ -%.*s: Unknown ARGP_HELP_FMT parameter"), - (int) var_len, var); - - var = arg; - if (*var == ',') - var++; - } - else if (*var) - { - __argp_failure (state, 0, 0, - dgettext (state->root_argp->argp_domain, - "Garbage in ARGP_HELP_FMT: %s"), var); - break; - } - } -} - -/* Returns true if OPT hasn't been marked invisible. Visibility only affects - whether OPT is displayed or used in sorting, not option shadowing. */ -#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) - -/* Returns true if OPT is an alias for an earlier option. */ -#define oalias(opt) ((opt)->flags & OPTION_ALIAS) - -/* Returns true if OPT is an documentation-only entry. */ -#define odoc(opt) ((opt)->flags & OPTION_DOC) - -/* Returns true if OPT is the end-of-list marker for a list of options. */ -#define oend(opt) __option_is_end (opt) - -/* Returns true if OPT has a short option. */ -#define oshort(opt) __option_is_short (opt) - -/* - The help format for a particular option is like: - - -xARG, -yARG, --long1=ARG, --long2=ARG Documentation... - - Where ARG will be omitted if there's no argument, for this option, or - will be surrounded by "[" and "]" appropiately if the argument is - optional. The documentation string is word-wrapped appropiately, and if - the list of options is long enough, it will be started on a separate line. - If there are no short options for a given option, the first long option is - indented slighly in a way that's supposed to make most long options appear - to be in a separate column. - - For example, the following output (from ps): - - -p PID, --pid=PID List the process PID - --pgrp=PGRP List processes in the process group PGRP - -P, -x, --no-parent Include processes without parents - -Q, --all-fields Don't elide unusable fields (normally if there's - some reason ps can't print a field for any - process, it's removed from the output entirely) - -r, --reverse, --gratuitously-long-reverse-option - Reverse the order of any sort - --session[=SID] Add the processes from the session SID (which - defaults to the sid of the current process) - - Here are some more options: - -f ZOT, --foonly=ZOT Glork a foonly - -z, --zaza Snit a zar - - -?, --help Give this help list - --usage Give a short usage message - -V, --version Print program version - - The struct argp_option array for the above could look like: - - { - {"pid", 'p', "PID", 0, "List the process PID"}, - {"pgrp", OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"}, - {"no-parent", 'P', 0, 0, "Include processes without parents"}, - {0, 'x', 0, OPTION_ALIAS}, - {"all-fields",'Q', 0, 0, "Don't elide unusable fields (normally" - " if there's some reason ps can't" - " print a field for any process, it's" - " removed from the output entirely)" }, - {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, - {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, - {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL, - "Add the processes from the session" - " SID (which defaults to the sid of" - " the current process)" }, - - {0,0,0,0, "Here are some more options:"}, - {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, - {"zaza", 'z', 0, 0, "Snit a zar"}, - - {0} - } - - Note that the last three options are automatically supplied by argp_parse, - unless you tell it not to with ARGP_NO_HELP. - -*/ - -/* Returns true if CH occurs between BEG and END. */ -static int -find_char (char ch, char *beg, char *end) -{ - while (beg < end) - if (*beg == ch) - return 1; - else - beg++; - return 0; -} - -struct hol_cluster; /* fwd decl */ - -struct hol_entry -{ - /* First option. */ - const struct argp_option *opt; - /* Number of options (including aliases). */ - unsigned num; - - /* A pointers into the HOL's short_options field, to the first short option - letter for this entry. The order of the characters following this point - corresponds to the order of options pointed to by OPT, and there are at - most NUM. A short option recorded in a option following OPT is only - valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's - probably been shadowed by some other entry). */ - char *short_options; - - /* Entries are sorted by their group first, in the order: - 1, 2, ..., n, 0, -m, ..., -2, -1 - and then alphabetically within each group. The default is 0. */ - int group; - - /* The cluster of options this entry belongs to, or 0 if none. */ - struct hol_cluster *cluster; - - /* The argp from which this option came. */ - const struct argp *argp; -}; - -/* A cluster of entries to reflect the argp tree structure. */ -struct hol_cluster -{ - /* A descriptive header printed before options in this cluster. */ - const char *header; - - /* Used to order clusters within the same group with the same parent, - according to the order in which they occurred in the parent argp's child - list. */ - int index; - - /* How to sort this cluster with respect to options and other clusters at the - same depth (clusters always follow options in the same group). */ - int group; - - /* The cluster to which this cluster belongs, or 0 if it's at the base - level. */ - struct hol_cluster *parent; - - /* The argp from which this cluster is (eventually) derived. */ - const struct argp *argp; - - /* The distance this cluster is from the root. */ - int depth; - - /* Clusters in a given hol are kept in a linked list, to make freeing them - possible. */ - struct hol_cluster *next; -}; - -/* A list of options for help. */ -struct hol -{ - /* An array of hol_entry's. */ - struct hol_entry *entries; - /* The number of entries in this hol. If this field is zero, the others - are undefined. */ - unsigned num_entries; - - /* A string containing all short options in this HOL. Each entry contains - pointers into this string, so the order can't be messed with blindly. */ - char *short_options; - - /* Clusters of entries in this hol. */ - struct hol_cluster *clusters; -}; - -/* Create a struct hol from the options in ARGP. CLUSTER is the - hol_cluster in which these entries occur, or 0, if at the root. */ -static struct hol * -make_hol (const struct argp *argp, struct hol_cluster *cluster) -{ - char *so; - const struct argp_option *o; - const struct argp_option *opts = argp->options; - struct hol_entry *entry; - unsigned num_short_options = 0; - struct hol *hol = malloc (sizeof (struct hol)); - - assert (hol); - - hol->num_entries = 0; - hol->clusters = 0; - - if (opts) - { - int cur_group = 0; - - /* The first option must not be an alias. */ - assert (! oalias (opts)); - - /* Calculate the space needed. */ - for (o = opts; ! oend (o); o++) - { - if (! oalias (o)) - hol->num_entries++; - if (oshort (o)) - num_short_options++; /* This is an upper bound. */ - } - - hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); - hol->short_options = malloc (num_short_options + 1); - - assert (hol->entries && hol->short_options); - - /* Fill in the entries. */ - so = hol->short_options; - for (o = opts, entry = hol->entries; ! oend (o); entry++) - { - entry->opt = o; - entry->num = 0; - entry->short_options = so; - entry->group = cur_group = - o->group - ? o->group - : ((!o->name && !o->key) - ? cur_group + 1 - : cur_group); - entry->cluster = cluster; - entry->argp = argp; - - do - { - entry->num++; - if (oshort (o) && ! find_char (o->key, hol->short_options, so)) - /* O has a valid short option which hasn't already been used.*/ - *so++ = o->key; - o++; - } - while (! oend (o) && oalias (o)); - } - *so = '\0'; /* null terminated so we can find the length */ - } - - return hol; -} - -/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the - associated argp child list entry), INDEX, and PARENT, and return a pointer - to it. ARGP is the argp that this cluster results from. */ -static struct hol_cluster * -hol_add_cluster (struct hol *hol, int group, const char *header, int index, - struct hol_cluster *parent, const struct argp *argp) -{ - struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); - if (cl) - { - cl->group = group; - cl->header = header; - - cl->index = index; - cl->parent = parent; - cl->argp = argp; - cl->depth = parent ? parent->depth + 1 : 0; - - cl->next = hol->clusters; - hol->clusters = cl; - } - return cl; -} - -/* Free HOL and any resources it uses. */ -static void -hol_free (struct hol *hol) -{ - struct hol_cluster *cl = hol->clusters; - - while (cl) - { - struct hol_cluster *next = cl->next; - free (cl); - cl = next; - } - - if (hol->num_entries > 0) - { - free (hol->entries); - free (hol->short_options); - } - - free (hol); -} - -static inline int -hol_entry_short_iterate (const struct hol_entry *entry, - int (*func)(const struct argp_option *opt, - const struct argp_option *real, - const char *domain, void *cookie), - const char *domain, void *cookie) -{ - unsigned nopts; - int val = 0; - const struct argp_option *opt, *real = entry->opt; - char *so = entry->short_options; - - for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) - if (oshort (opt) && *so == opt->key) - { - if (!oalias (opt)) - real = opt; - if (ovisible (opt)) - val = (*func)(opt, real, domain, cookie); - so++; - } - - return val; -} - -static inline int -hol_entry_long_iterate (const struct hol_entry *entry, - int (*func)(const struct argp_option *opt, - const struct argp_option *real, - const char *domain, void *cookie), - const char *domain, void *cookie) -{ - unsigned nopts; - int val = 0; - const struct argp_option *opt, *real = entry->opt; - - for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) - if (opt->name) - { - if (!oalias (opt)) - real = opt; - if (ovisible (opt)) - val = (*func)(opt, real, domain, cookie); - } - - return val; -} - -/* Iterator that returns true for the first short option. */ -static inline int -until_short (const struct argp_option *opt, const struct argp_option *real UNUSED, - const char *domain UNUSED, void *cookie UNUSED) -{ - return oshort (opt) ? opt->key : 0; -} - -/* Returns the first valid short option in ENTRY, or 0 if there is none. */ -static char -hol_entry_first_short (const struct hol_entry *entry) -{ - return hol_entry_short_iterate (entry, until_short, - entry->argp->argp_domain, 0); -} - -/* Returns the first valid long option in ENTRY, or 0 if there is none. */ -static const char * -hol_entry_first_long (const struct hol_entry *entry) -{ - const struct argp_option *opt; - unsigned num; - for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) - if (opt->name && ovisible (opt)) - return opt->name; - return 0; -} - -/* Returns the entry in HOL with the long option name NAME, or 0 if there is - none. */ -static struct hol_entry * -hol_find_entry (struct hol *hol, const char *name) -{ - struct hol_entry *entry = hol->entries; - unsigned num_entries = hol->num_entries; - - while (num_entries-- > 0) - { - const struct argp_option *opt = entry->opt; - unsigned num_opts = entry->num; - - while (num_opts-- > 0) - if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) - return entry; - else - opt++; - - entry++; - } - - return 0; -} - -/* If an entry with the long option NAME occurs in HOL, set it's special - sort position to GROUP. */ -static void -hol_set_group (struct hol *hol, const char *name, int group) -{ - struct hol_entry *entry = hol_find_entry (hol, name); - if (entry) - entry->group = group; -} - -/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. - EQ is what to return if GROUP1 and GROUP2 are the same. */ -static int -group_cmp (int group1, int group2, int eq) -{ - if (group1 == group2) - return eq; - else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) - return group1 - group2; - else - return group2 - group1; -} - -/* Compare clusters CL1 & CL2 by the order that they should appear in - output. */ -static int -hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) -{ - /* If one cluster is deeper than the other, use its ancestor at the same - level, so that finding the common ancestor is straightforward. */ - while (cl1->depth < cl2->depth) - cl1 = cl1->parent; - while (cl2->depth < cl1->depth) - cl2 = cl2->parent; - - /* Now reduce both clusters to their ancestors at the point where both have - a common parent; these can be directly compared. */ - while (cl1->parent != cl2->parent) - cl1 = cl1->parent, cl2 = cl2->parent; - - return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); -} - -/* Return the ancestor of CL that's just below the root (i.e., has a parent - of 0). */ -static struct hol_cluster * -hol_cluster_base (struct hol_cluster *cl) -{ - while (cl->parent) - cl = cl->parent; - return cl; -} - -/* Return true if CL1 is a child of CL2. */ -static int -hol_cluster_is_child (const struct hol_cluster *cl1, - const struct hol_cluster *cl2) -{ - while (cl1 && cl1 != cl2) - cl1 = cl1->parent; - return cl1 == cl2; -} - -/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail - that should be used for comparisons, and returns true iff it should be - treated as a non-option. */ - -/* FIXME: Can we use unsigned char * for the argument? */ -static int -canon_doc_option (const char **name) -{ - int non_opt; - /* Skip initial whitespace. */ - while (isspace ( (unsigned char) **name)) - (*name)++; - /* Decide whether this looks like an option (leading `-') or not. */ - non_opt = (**name != '-'); - /* Skip until part of name used for sorting. */ - while (**name && !isalnum ( (unsigned char) **name)) - (*name)++; - return non_opt; -} - -/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help - listing. */ -static int -hol_entry_cmp (const struct hol_entry *entry1, - const struct hol_entry *entry2) -{ - /* The group numbers by which the entries should be ordered; if either is - in a cluster, then this is just the group within the cluster. */ - int group1 = entry1->group, group2 = entry2->group; - - if (entry1->cluster != entry2->cluster) - { - /* The entries are not within the same cluster, so we can't compare them - directly, we have to use the appropiate clustering level too. */ - if (! entry1->cluster) - /* ENTRY1 is at the `base level', not in a cluster, so we have to - compare it's group number with that of the base cluster in which - ENTRY2 resides. Note that if they're in the same group, the - clustered option always comes laster. */ - return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); - else if (! entry2->cluster) - /* Likewise, but ENTRY2's not in a cluster. */ - return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); - else - /* Both entries are in clusters, we can just compare the clusters. */ - return hol_cluster_cmp (entry1->cluster, entry2->cluster); - } - else if (group1 == group2) - /* The entries are both in the same cluster and group, so compare them - alphabetically. */ - { - int short1 = hol_entry_first_short (entry1); - int short2 = hol_entry_first_short (entry2); - int doc1 = odoc (entry1->opt); - int doc2 = odoc (entry2->opt); - /* FIXME: Can we use unsigned char * instead? */ - const char *long1 = hol_entry_first_long (entry1); - const char *long2 = hol_entry_first_long (entry2); - - if (doc1) - doc1 = canon_doc_option (&long1); - if (doc2) - doc2 = canon_doc_option (&long2); - - if (doc1 != doc2) - /* `documentation' options always follow normal options (or - documentation options that *look* like normal options). */ - return doc1 - doc2; - else if (!short1 && !short2 && long1 && long2) - /* Only long options. */ - return __strcasecmp (long1, long2); - else - /* Compare short/short, long/short, short/long, using the first - character of long options. Entries without *any* valid - options (such as options with OPTION_HIDDEN set) will be put - first, but as they're not displayed, it doesn't matter where - they are. */ - { - unsigned char first1 = short1 ? short1 : long1 ? *long1 : 0; - unsigned char first2 = short2 ? short2 : long2 ? *long2 : 0; -#ifdef _tolower - int lower_cmp = _tolower (first1) - _tolower (first2); -#else - int lower_cmp = tolower (first1) - tolower (first2); -#endif - /* Compare ignoring case, except when the options are both the - same letter, in which case lower-case always comes first. */ - /* NOTE: The subtraction below does the right thing - even with eight-bit chars: first1 and first2 are - converted to int *before* the subtraction. */ - return lower_cmp ? lower_cmp : first2 - first1; - } - } - else - /* Within the same cluster, but not the same group, so just compare - groups. */ - return group_cmp (group1, group2, 0); -} - -/* Version of hol_entry_cmp with correct signature for qsort. */ -static int -hol_entry_qcmp (const void *entry1_v, const void *entry2_v) -{ - return hol_entry_cmp (entry1_v, entry2_v); -} - -/* Sort HOL by group and alphabetically by option name (with short options - taking precedence over long). Since the sorting is for display purposes - only, the shadowing of options isn't effected. */ -static void -hol_sort (struct hol *hol) -{ - if (hol->num_entries > 0) - qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), - hol_entry_qcmp); -} - -/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow - any in MORE with the same name. */ -static void -hol_append (struct hol *hol, struct hol *more) -{ - struct hol_cluster **cl_end = &hol->clusters; - - /* Steal MORE's cluster list, and add it to the end of HOL's. */ - while (*cl_end) - cl_end = &(*cl_end)->next; - *cl_end = more->clusters; - more->clusters = 0; - - /* Merge entries. */ - if (more->num_entries > 0) - { - if (hol->num_entries == 0) - { - hol->num_entries = more->num_entries; - hol->entries = more->entries; - hol->short_options = more->short_options; - more->num_entries = 0; /* Mark MORE's fields as invalid. */ - } - else - /* Append the entries in MORE to those in HOL, taking care to only add - non-shadowed SHORT_OPTIONS values. */ - { - unsigned left; - char *so, *more_so; - struct hol_entry *e; - unsigned num_entries = hol->num_entries + more->num_entries; - struct hol_entry *entries = - malloc (num_entries * sizeof (struct hol_entry)); - unsigned hol_so_len = strlen (hol->short_options); - char *short_options = - malloc (hol_so_len + strlen (more->short_options) + 1); - - __mempcpy (__mempcpy (entries, hol->entries, - hol->num_entries * sizeof (struct hol_entry)), - more->entries, - more->num_entries * sizeof (struct hol_entry)); - - __mempcpy (short_options, hol->short_options, hol_so_len); - - /* Fix up the short options pointers from HOL. */ - for (e = entries, left = hol->num_entries; left > 0; e++, left--) - e->short_options += (short_options - hol->short_options); - - /* Now add the short options from MORE, fixing up its entries - too. */ - so = short_options + hol_so_len; - more_so = more->short_options; - for (left = more->num_entries; left > 0; e++, left--) - { - int opts_left; - const struct argp_option *opt; - - e->short_options = so; - - for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) - { - int ch = *more_so; - if (oshort (opt) && ch == opt->key) - /* The next short option in MORE_SO, CH, is from OPT. */ - { - if (! find_char (ch, short_options, - short_options + hol_so_len)) - /* The short option CH isn't shadowed by HOL's options, - so add it to the sum. */ - *so++ = ch; - more_so++; - } - } - } - - *so = '\0'; - - free (hol->entries); - free (hol->short_options); - - hol->entries = entries; - hol->num_entries = num_entries; - hol->short_options = short_options; - } - } - - hol_free (more); -} - -/* Inserts enough spaces to make sure STREAM is at column COL. */ -static void -indent_to (argp_fmtstream_t stream, unsigned col) -{ - int needed = col - __argp_fmtstream_point (stream); - while (needed-- > 0) - __argp_fmtstream_putc (stream, ' '); -} - -/* Output to STREAM either a space, or a newline if there isn't room for at - least ENSURE characters before the right margin. */ -static void -space (argp_fmtstream_t stream, size_t ensure) -{ - if (__argp_fmtstream_point (stream) + ensure - >= __argp_fmtstream_rmargin (stream)) - __argp_fmtstream_putc (stream, '\n'); - else - __argp_fmtstream_putc (stream, ' '); -} - -/* If the option REAL has an argument, we print it in using the printf - format REQ_FMT or OPT_FMT depending on whether it's a required or - optional argument. */ -static void -arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, - const char *domain UNUSED, argp_fmtstream_t stream) -{ - if (real->arg) - { - if (real->flags & OPTION_ARG_OPTIONAL) - __argp_fmtstream_printf (stream, opt_fmt, - dgettext (domain, real->arg)); - else - __argp_fmtstream_printf (stream, req_fmt, - dgettext (domain, real->arg)); - } -} - -/* Helper functions for hol_entry_help. */ - -/* State used during the execution of hol_help. */ -struct hol_help_state -{ - /* PREV_ENTRY should contain the previous entry printed, or 0. */ - struct hol_entry *prev_entry; - - /* If an entry is in a different group from the previous one, and SEP_GROUPS - is true, then a blank line will be printed before any output. */ - int sep_groups; - - /* True if a duplicate option argument was suppressed (only ever set if - UPARAMS.dup_args is false). */ - int suppressed_dup_arg; -}; - -/* Some state used while printing a help entry (used to communicate with - helper functions). See the doc for hol_entry_help for more info, as most - of the fields are copied from its arguments. */ -struct pentry_state -{ - const struct hol_entry *entry; - argp_fmtstream_t stream; - struct hol_help_state *hhstate; - - /* True if nothing's been printed so far. */ - int first; - - /* If non-zero, the state that was used to print this help. */ - const struct argp_state *state; -}; - -/* If a user doc filter should be applied to DOC, do so. */ -static const char * -filter_doc (const char *doc, int key, const struct argp *argp, - const struct argp_state *state) -{ - if (argp->help_filter) - /* We must apply a user filter to this output. */ - { - void *input = __argp_input (argp, state); - return (*argp->help_filter) (key, doc, input); - } - else - /* No filter. */ - return doc; -} - -/* Prints STR as a header line, with the margin lines set appropiately, and - notes the fact that groups should be separated with a blank line. ARGP is - the argp that should dictate any user doc filtering to take place. Note - that the previous wrap margin isn't restored, but the left margin is reset - to 0. */ -static void -print_header (const char *str, const struct argp *argp, - struct pentry_state *pest) -{ - const char *tstr = dgettext (argp->argp_domain, str); - const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state); - - if (fstr) - { - if (*fstr) - { - if (pest->hhstate->prev_entry) - /* Precede with a blank line. */ - __argp_fmtstream_putc (pest->stream, '\n'); - indent_to (pest->stream, uparams.header_col); - __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col); - __argp_fmtstream_set_wmargin (pest->stream, uparams.header_col); - __argp_fmtstream_puts (pest->stream, fstr); - __argp_fmtstream_set_lmargin (pest->stream, 0); - __argp_fmtstream_putc (pest->stream, '\n'); - } - - pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */ - } - - if (fstr != tstr) - free ((char *) fstr); -} - -/* Inserts a comma if this isn't the first item on the line, and then makes - sure we're at least to column COL. If this *is* the first item on a line, - prints any pending whitespace/headers that should precede this line. Also - clears FIRST. */ -static void -comma (unsigned col, struct pentry_state *pest) -{ - if (pest->first) - { - const struct hol_entry *pe = pest->hhstate->prev_entry; - const struct hol_cluster *cl = pest->entry->cluster; - - if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group) - __argp_fmtstream_putc (pest->stream, '\n'); - - if (cl && cl->header && *cl->header - && (!pe - || (pe->cluster != cl - && !hol_cluster_is_child (pe->cluster, cl)))) - /* If we're changing clusters, then this must be the start of the - ENTRY's cluster unless that is an ancestor of the previous one - (in which case we had just popped into a sub-cluster for a bit). - If so, then print the cluster's header line. */ - { - int old_wm = __argp_fmtstream_wmargin (pest->stream); - print_header (cl->header, cl->argp, pest); - __argp_fmtstream_set_wmargin (pest->stream, old_wm); - } - - pest->first = 0; - } - else - __argp_fmtstream_puts (pest->stream, ", "); - - indent_to (pest->stream, col); -} - -/* Print help for ENTRY to STREAM. */ -static void -hol_entry_help (struct hol_entry *entry, const struct argp_state *state, - argp_fmtstream_t stream, struct hol_help_state *hhstate) -{ - unsigned num; - const struct argp_option *real = entry->opt, *opt; - char *so = entry->short_options; - int have_long_opt = 0; /* We have any long options. */ - /* Saved margins. */ - int old_lm = __argp_fmtstream_set_lmargin (stream, 0); - int old_wm = __argp_fmtstream_wmargin (stream); - /* PEST is a state block holding some of our variables that we'd like to - share with helper functions. */ - - /* Decent initializers are a GNU extension, so don't use it here. */ - struct pentry_state pest; - pest.entry = entry; - pest.stream = stream; - pest.hhstate = hhstate; - pest.first = 1; - pest.state = state; - - if (! odoc (real)) - for (opt = real, num = entry->num; num > 0; opt++, num--) - if (opt->name && ovisible (opt)) - { - have_long_opt = 1; - break; - } - - /* First emit short options. */ - __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */ - for (opt = real, num = entry->num; num > 0; opt++, num--) - if (oshort (opt) && opt->key == *so) - /* OPT has a valid (non shadowed) short option. */ - { - if (ovisible (opt)) - { - comma (uparams.short_opt_col, &pest); - __argp_fmtstream_putc (stream, '-'); - __argp_fmtstream_putc (stream, *so); - if (!have_long_opt || uparams.dup_args) - arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream); - else if (real->arg) - hhstate->suppressed_dup_arg = 1; - } - so++; - } - - /* Now, long options. */ - if (odoc (real)) - /* A `documentation' option. */ - { - __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); - for (opt = real, num = entry->num; num > 0; opt++, num--) - if (opt->name && ovisible (opt)) - { - comma (uparams.doc_opt_col, &pest); - /* Calling gettext here isn't quite right, since sorting will - have been done on the original; but documentation options - should be pretty rare anyway... */ - __argp_fmtstream_puts (stream, - dgettext (state->root_argp->argp_domain, - opt->name)); - } - } - else - /* A real long option. */ - { - int first_long_opt = 1; - - __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col); - for (opt = real, num = entry->num; num > 0; opt++, num--) - if (opt->name && ovisible (opt)) - { - comma (uparams.long_opt_col, &pest); - __argp_fmtstream_printf (stream, "--%s", opt->name); - if (first_long_opt || uparams.dup_args) - arg (real, "=%s", "[=%s]", state->root_argp->argp_domain, - stream); - else if (real->arg) - hhstate->suppressed_dup_arg = 1; - } - } - - /* Next, documentation strings. */ - __argp_fmtstream_set_lmargin (stream, 0); - - if (pest.first) - { - /* Didn't print any switches, what's up? */ - if (!oshort (real) && !real->name) - /* This is a group header, print it nicely. */ - print_header (real->doc, entry->argp, &pest); - else - /* Just a totally shadowed option or null header; print nothing. */ - goto cleanup; /* Just return, after cleaning up. */ - } - else - { - const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain, - real->doc) : 0; - const char *fstr = filter_doc (tstr, real->key, entry->argp, state); - if (fstr && *fstr) - { - unsigned int col = __argp_fmtstream_point (stream); - - __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col); - __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col); - - if (col > (unsigned int) (uparams.opt_doc_col + 3)) - __argp_fmtstream_putc (stream, '\n'); - else if (col >= (unsigned int) uparams.opt_doc_col) - __argp_fmtstream_puts (stream, " "); - else - indent_to (stream, uparams.opt_doc_col); - - __argp_fmtstream_puts (stream, fstr); - } - if (fstr && fstr != tstr) - free ((char *) fstr); - - /* Reset the left margin. */ - __argp_fmtstream_set_lmargin (stream, 0); - __argp_fmtstream_putc (stream, '\n'); - } - - hhstate->prev_entry = entry; - -cleanup: - __argp_fmtstream_set_lmargin (stream, old_lm); - __argp_fmtstream_set_wmargin (stream, old_wm); -} - -/* Output a long help message about the options in HOL to STREAM. */ -static void -hol_help (struct hol *hol, const struct argp_state *state, - argp_fmtstream_t stream) -{ - unsigned num; - struct hol_entry *entry; - struct hol_help_state hhstate = { 0, 0, 0 }; - - for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) - hol_entry_help (entry, state, stream, &hhstate); - - if (hhstate.suppressed_dup_arg && uparams.dup_args_note) - { - const char *tstr = dgettext (state->root_argp->argp_domain, "\ -Mandatory or optional arguments to long options are also mandatory or \ -optional for any corresponding short options."); - const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, - state ? state->root_argp : 0, state); - if (fstr && *fstr) - { - __argp_fmtstream_putc (stream, '\n'); - __argp_fmtstream_puts (stream, fstr); - __argp_fmtstream_putc (stream, '\n'); - } - if (fstr && fstr != tstr) - free ((char *) fstr); - } -} - -/* Helper functions for hol_usage. */ - -/* If OPT is a short option without an arg, append its key to the string - pointer pointer to by COOKIE, and advance the pointer. */ -static int -add_argless_short_opt (const struct argp_option *opt, - const struct argp_option *real, - const char *domain UNUSED, void *cookie) -{ - char **snao_end = cookie; - if (!(opt->arg || real->arg) - && !((opt->flags | real->flags) & OPTION_NO_USAGE)) - *(*snao_end)++ = opt->key; - return 0; -} - -/* If OPT is a short option with an arg, output a usage entry for it to the - stream pointed at by COOKIE. */ -static int -usage_argful_short_opt (const struct argp_option *opt, - const struct argp_option *real, - const char *domain UNUSED, void *cookie) -{ - argp_fmtstream_t stream = cookie; - const char *arg = opt->arg; - int flags = opt->flags | real->flags; - - if (! arg) - arg = real->arg; - - if (arg && !(flags & OPTION_NO_USAGE)) - { - arg = dgettext (domain, arg); - - if (flags & OPTION_ARG_OPTIONAL) - __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg); - else - { - /* Manually do line wrapping so that it (probably) won't - get wrapped at the embedded space. */ - space (stream, 6 + strlen (arg)); - __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); - } - } - - return 0; -} - -/* Output a usage entry for the long option opt to the stream pointed at by - COOKIE. */ -static int -usage_long_opt (const struct argp_option *opt, - const struct argp_option *real, - const char *domain UNUSED, void *cookie) -{ - argp_fmtstream_t stream = cookie; - const char *arg = opt->arg; - int flags = opt->flags | real->flags; - - if (! arg) - arg = real->arg; - - if (! (flags & OPTION_NO_USAGE)) - { - if (arg) - { - arg = dgettext (domain, arg); - if (flags & OPTION_ARG_OPTIONAL) - __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); - else - __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); - } - else - __argp_fmtstream_printf (stream, " [--%s]", opt->name); - } - - return 0; -} - -/* Print a short usage description for the arguments in HOL to STREAM. */ -static void -hol_usage (struct hol *hol, argp_fmtstream_t stream) -{ - if (hol->num_entries > 0) - { - unsigned nentries; - struct hol_entry *entry; - char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1); - char *snao_end = short_no_arg_opts; - - /* First we put a list of short options without arguments. */ - for (entry = hol->entries, nentries = hol->num_entries - ; nentries > 0 - ; entry++, nentries--) - hol_entry_short_iterate (entry, add_argless_short_opt, - entry->argp->argp_domain, &snao_end); - if (snao_end > short_no_arg_opts) - { - *snao_end++ = 0; - __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts); - } - - /* Now a list of short options *with* arguments. */ - for (entry = hol->entries, nentries = hol->num_entries - ; nentries > 0 - ; entry++, nentries--) - hol_entry_short_iterate (entry, usage_argful_short_opt, - entry->argp->argp_domain, stream); - - /* Finally, a list of long options (whew!). */ - for (entry = hol->entries, nentries = hol->num_entries - ; nentries > 0 - ; entry++, nentries--) - hol_entry_long_iterate (entry, usage_long_opt, - entry->argp->argp_domain, stream); - } -} - -/* Make a HOL containing all levels of options in ARGP. CLUSTER is the - cluster in which ARGP's entries should be clustered, or 0. */ -static struct hol * -argp_hol (const struct argp *argp, struct hol_cluster *cluster) -{ - const struct argp_child *child = argp->children; - struct hol *hol = make_hol (argp, cluster); - if (child) - while (child->argp) - { - struct hol_cluster *child_cluster = - ((child->group || child->header) - /* Put CHILD->argp within its own cluster. */ - ? hol_add_cluster (hol, child->group, child->header, - child - argp->children, cluster, argp) - /* Just merge it into the parent's cluster. */ - : cluster); - hol_append (hol, argp_hol (child->argp, child_cluster)) ; - child++; - } - return hol; -} - -/* Calculate how many different levels with alternative args strings exist in - ARGP. */ -static size_t -argp_args_levels (const struct argp *argp) -{ - size_t levels = 0; - const struct argp_child *child = argp->children; - - if (argp->args_doc && strchr (argp->args_doc, '\n')) - levels++; - - if (child) - while (child->argp) - levels += argp_args_levels ((child++)->argp); - - return levels; -} - -/* Print all the non-option args documented in ARGP to STREAM. Any output is - preceded by a space. LEVELS is a pointer to a byte vector the length - returned by argp_args_levels; it should be initialized to zero, and - updated by this routine for the next call if ADVANCE is true. True is - returned as long as there are more patterns to output. */ -static int -argp_args_usage (const struct argp *argp, const struct argp_state *state, - char **levels, int advance, argp_fmtstream_t stream) -{ - char *our_level = *levels; - int multiple = 0; - const struct argp_child *child = argp->children; - const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0; - const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); - - if (fdoc) - { - const char *cp = fdoc; - nl = __strchrnul (cp, '\n'); - if (*nl != '\0') - /* This is a `multi-level' args doc; advance to the correct position - as determined by our state in LEVELS, and update LEVELS. */ - { - int i; - multiple = 1; - for (i = 0; i < *our_level; i++) - cp = nl + 1, nl = __strchrnul (cp, '\n'); - (*levels)++; - } - - /* Manually do line wrapping so that it (probably) won't get wrapped at - any embedded spaces. */ - space (stream, 1 + nl - cp); - - __argp_fmtstream_write (stream, cp, nl - cp); - } - if (fdoc && fdoc != tdoc) - free ((char *)fdoc); /* Free user's modified doc string. */ - - if (child) - while (child->argp) - advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream); - - if (advance && multiple) - { - /* Need to increment our level. */ - if (*nl) - /* There's more we can do here. */ - { - (*our_level)++; - advance = 0; /* Our parent shouldn't advance also. */ - } - else if (*our_level > 0) - /* We had multiple levels, but used them up; reset to zero. */ - *our_level = 0; - } - - return !advance; -} - -/* Print the documentation for ARGP to STREAM; if POST is false, then - everything preceeding a `\v' character in the documentation strings (or - the whole string, for those with none) is printed, otherwise, everything - following the `\v' character (nothing for strings without). Each separate - bit of documentation is separated a blank line, and if PRE_BLANK is true, - then the first is as well. If FIRST_ONLY is true, only the first - occurrence is output. Returns true if anything was output. */ -static int -argp_doc (const struct argp *argp, const struct argp_state *state, - int post, int pre_blank, int first_only, - argp_fmtstream_t stream) -{ - const char *text; - const char *inp_text; - void *input = 0; - int anything = 0; - size_t inp_text_limit = 0; - const char *doc = dgettext (argp->argp_domain, argp->doc); - const struct argp_child *child = argp->children; - - if (doc) - { - char *vt = strchr (doc, '\v'); - inp_text = post ? (vt ? vt + 1 : 0) : doc; - inp_text_limit = (!post && vt) ? (vt - doc) : 0; - } - else - inp_text = 0; - - if (argp->help_filter) - /* We have to filter the doc strings. */ - { - if (inp_text_limit) - /* Copy INP_TEXT so that it's nul-terminated. */ - inp_text = STRNDUP (inp_text, inp_text_limit); - input = __argp_input (argp, state); - text = - (*argp->help_filter) (post - ? ARGP_KEY_HELP_POST_DOC - : ARGP_KEY_HELP_PRE_DOC, - inp_text, input); - } - else - text = (const char *) inp_text; - - if (text) - { - if (pre_blank) - __argp_fmtstream_putc (stream, '\n'); - - if (text == inp_text && inp_text_limit) - __argp_fmtstream_write (stream, inp_text, inp_text_limit); - else - __argp_fmtstream_puts (stream, text); - - if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) - __argp_fmtstream_putc (stream, '\n'); - - anything = 1; - } - - if (text && text != inp_text) - free ((char *) text); /* Free TEXT returned from the help filter. */ - if (inp_text && inp_text_limit && argp->help_filter) - free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ - - if (post && argp->help_filter) - /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ - { - text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); - if (text) - { - if (anything || pre_blank) - __argp_fmtstream_putc (stream, '\n'); - __argp_fmtstream_puts (stream, text); - free ((char *) text); - if (__argp_fmtstream_point (stream) - > __argp_fmtstream_lmargin (stream)) - __argp_fmtstream_putc (stream, '\n'); - anything = 1; - } - } - - if (child) - while (child->argp && !(first_only && anything)) - anything |= - argp_doc ((child++)->argp, state, - post, anything || pre_blank, first_only, - stream); - - return anything; -} - -/* Output a usage message for ARGP to STREAM. If called from - argp_state_help, STATE is the relevent parsing state. FLAGS are from the - set ARGP_HELP_*. NAME is what to use wherever a `program name' is - needed. */ - -static void -_help (const struct argp *argp, const struct argp_state *state, FILE *stream, - unsigned flags, const char *name) -{ - int anything = 0; /* Whether we've output anything. */ - struct hol *hol = 0; - argp_fmtstream_t fs; - - if (! stream) - return; - - FLOCKFILE (stream); - - if (! uparams.valid) - fill_in_uparams (state); - - fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0); - if (! fs) - { - FUNLOCKFILE (stream); - return; - } - - if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) - { - hol = argp_hol (argp, 0); - - /* If present, these options always come last. */ - hol_set_group (hol, "help", -1); - hol_set_group (hol, "version", -1); - - hol_sort (hol); - } - - if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE)) - /* Print a short `Usage:' message. */ - { - int first_pattern = 1, more_patterns; - size_t num_pattern_levels = argp_args_levels (argp); - char *pattern_levels = alloca (num_pattern_levels); - - memset (pattern_levels, 0, num_pattern_levels); - - do - { - int old_lm; - int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent); - char *levels = pattern_levels; - - if (first_pattern) - __argp_fmtstream_printf (fs, "%s %s", - dgettext (argp->argp_domain, "Usage:"), - name); - else - __argp_fmtstream_printf (fs, "%s %s", - dgettext (argp->argp_domain, " or: "), - name); - - /* We set the lmargin as well as the wmargin, because hol_usage - manually wraps options with newline to avoid annoying breaks. */ - old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent); - - if (flags & ARGP_HELP_SHORT_USAGE) - /* Just show where the options go. */ - { - if (hol->num_entries > 0) - __argp_fmtstream_puts (fs, dgettext (argp->argp_domain, - " [OPTION...]")); - } - else - /* Actually print the options. */ - { - hol_usage (hol, fs); - flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. */ - } - - more_patterns = argp_args_usage (argp, state, &levels, 1, fs); - - __argp_fmtstream_set_wmargin (fs, old_wm); - __argp_fmtstream_set_lmargin (fs, old_lm); - - __argp_fmtstream_putc (fs, '\n'); - anything = 1; - - first_pattern = 0; - } - while (more_patterns); - } - - if (flags & ARGP_HELP_PRE_DOC) - anything |= argp_doc (argp, state, 0, 0, 1, fs); - - if (flags & ARGP_HELP_SEE) - { - __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\ -Try `%s --help' or `%s --usage' for more information.\n"), - name, name); - anything = 1; - } - - if (flags & ARGP_HELP_LONG) - /* Print a long, detailed help message. */ - { - /* Print info about all the options. */ - if (hol->num_entries > 0) - { - if (anything) - __argp_fmtstream_putc (fs, '\n'); - hol_help (hol, state, fs); - anything = 1; - } - } - - if (flags & ARGP_HELP_POST_DOC) - /* Print any documentation strings at the end. */ - anything |= argp_doc (argp, state, 1, anything, 0, fs); - - if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) - { - if (anything) - __argp_fmtstream_putc (fs, '\n'); - __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, - "Report bugs to %s.\n"), - argp_program_bug_address); - anything = 1; - } - - FUNLOCKFILE (stream); - - if (hol) - hol_free (hol); - - __argp_fmtstream_free (fs); -} - -/* Output a usage message for ARGP to STREAM. FLAGS are from the set - ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ -void __argp_help (const struct argp *argp, FILE *stream, - unsigned flags, char *name) -{ - _help (argp, 0, stream, flags, name); -} -#ifdef weak_alias -weak_alias (__argp_help, argp_help) -#endif - -char *__argp_basename(char *name) -{ - char *short_name = strrchr(name, '/'); - return short_name ? short_name + 1 : name; -} - -char * -__argp_short_program_name(const struct argp_state *state) -{ - if (state) - return state->name; -#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME - return program_invocation_short_name; -#elif HAVE_DECL_PROGRAM_INVOCATION_NAME - return __argp_basename(program_invocation_name); -#else /* !HAVE_DECL_PROGRAM_INVOCATION_NAME */ - /* FIXME: What now? Miles suggests that it is better to use NULL, - but currently the value is passed on directly to fputs_unlocked, - so that requires more changes. */ -# if __GNUC__ - return ""; -# endif /* __GNUC__ */ -#endif /* !HAVE_DECL_PROGRAM_INVOCATION_NAME */ -} - -/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are - from the set ARGP_HELP_*. */ -void -__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) -{ - if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream) - { - if (state && (state->flags & ARGP_LONG_ONLY)) - flags |= ARGP_HELP_LONG_ONLY; - - _help (state ? state->root_argp : 0, state, stream, flags, - __argp_short_program_name(state)); - - if (!state || ! (state->flags & ARGP_NO_EXIT)) - { - if (flags & ARGP_HELP_EXIT_ERR) - exit (argp_err_exit_status); - if (flags & ARGP_HELP_EXIT_OK) - exit (0); - } - } -} -#ifdef weak_alias -weak_alias (__argp_state_help, argp_state_help) -#endif - -/* If appropriate, print the printf string FMT and following args, preceded - by the program name and `:', to stderr, and followed by a `Try ... --help' - message, then exit (1). */ -void -__argp_error (const struct argp_state *state, const char *fmt, ...) -{ - if (!state || !(state->flags & ARGP_NO_ERRS)) - { - FILE *stream = state ? state->err_stream : stderr; - - if (stream) - { - va_list ap; - - FLOCKFILE (stream); - - FPUTS_UNLOCKED (__argp_short_program_name(state), - stream); - PUTC_UNLOCKED (':', stream); - PUTC_UNLOCKED (' ', stream); - - va_start (ap, fmt); - vfprintf (stream, fmt, ap); - va_end (ap); - - PUTC_UNLOCKED ('\n', stream); - - __argp_state_help (state, stream, ARGP_HELP_STD_ERR); - - FUNLOCKFILE (stream); - } - } -} -#ifdef weak_alias -weak_alias (__argp_error, argp_error) -#endif - -/* Similar to the standard gnu error-reporting function error(), but will - respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print - to STATE->err_stream. This is useful for argument parsing code that is - shared between program startup (when exiting is desired) and runtime - option parsing (when typically an error code is returned instead). The - difference between this function and argp_error is that the latter is for - *parsing errors*, and the former is for other problems that occur during - parsing but don't reflect a (syntactic) problem with the input. */ -void -__argp_failure (const struct argp_state *state, int status, int errnum, - const char *fmt, ...) -{ - if (!state || !(state->flags & ARGP_NO_ERRS)) - { - FILE *stream = state ? state->err_stream : stderr; - - if (stream) - { - FLOCKFILE (stream); - - FPUTS_UNLOCKED (__argp_short_program_name(state), - stream); - - if (fmt) - { - va_list ap; - - PUTC_UNLOCKED (':', stream); - PUTC_UNLOCKED (' ', stream); - - va_start (ap, fmt); - vfprintf (stream, fmt, ap); - va_end (ap); - } - - if (errnum) - { - PUTC_UNLOCKED (':', stream); - PUTC_UNLOCKED (' ', stream); - fputs (STRERROR (errnum), stream); - } - - PUTC_UNLOCKED ('\n', stream); - - FUNLOCKFILE (stream); - - if (status && (!state || !(state->flags & ARGP_NO_EXIT))) - exit (status); - } - } -} -#ifdef weak_alias -weak_alias (__argp_failure, argp_failure) -#endif diff --git a/argp-standalone/argp-namefrob.h b/argp-standalone/argp-namefrob.h deleted file mode 100644 index 0ce11481a..000000000 --- a/argp-standalone/argp-namefrob.h +++ /dev/null @@ -1,96 +0,0 @@ -/* Name frobnication for compiling argp outside of glibc - Copyright (C) 1997 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#if !_LIBC -/* This code is written for inclusion in gnu-libc, and uses names in the - namespace reserved for libc. If we're not compiling in libc, define those - names to be the normal ones instead. */ - -/* argp-parse functions */ -#undef __argp_parse -#define __argp_parse argp_parse -#undef __option_is_end -#define __option_is_end _option_is_end -#undef __option_is_short -#define __option_is_short _option_is_short -#undef __argp_input -#define __argp_input _argp_input - -/* argp-help functions */ -#undef __argp_help -#define __argp_help argp_help -#undef __argp_error -#define __argp_error argp_error -#undef __argp_failure -#define __argp_failure argp_failure -#undef __argp_state_help -#define __argp_state_help argp_state_help -#undef __argp_usage -#define __argp_usage argp_usage -#undef __argp_basename -#define __argp_basename _argp_basename -#undef __argp_short_program_name -#define __argp_short_program_name _argp_short_program_name - -/* argp-fmtstream functions */ -#undef __argp_make_fmtstream -#define __argp_make_fmtstream argp_make_fmtstream -#undef __argp_fmtstream_free -#define __argp_fmtstream_free argp_fmtstream_free -#undef __argp_fmtstream_putc -#define __argp_fmtstream_putc argp_fmtstream_putc -#undef __argp_fmtstream_puts -#define __argp_fmtstream_puts argp_fmtstream_puts -#undef __argp_fmtstream_write -#define __argp_fmtstream_write argp_fmtstream_write -#undef __argp_fmtstream_printf -#define __argp_fmtstream_printf argp_fmtstream_printf -#undef __argp_fmtstream_set_lmargin -#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin -#undef __argp_fmtstream_set_rmargin -#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin -#undef __argp_fmtstream_set_wmargin -#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin -#undef __argp_fmtstream_point -#define __argp_fmtstream_point argp_fmtstream_point -#undef __argp_fmtstream_update -#define __argp_fmtstream_update _argp_fmtstream_update -#undef __argp_fmtstream_ensure -#define __argp_fmtstream_ensure _argp_fmtstream_ensure -#undef __argp_fmtstream_lmargin -#define __argp_fmtstream_lmargin argp_fmtstream_lmargin -#undef __argp_fmtstream_rmargin -#define __argp_fmtstream_rmargin argp_fmtstream_rmargin -#undef __argp_fmtstream_wmargin -#define __argp_fmtstream_wmargin argp_fmtstream_wmargin - -/* normal libc functions we call */ -#undef __sleep -#define __sleep sleep -#undef __strcasecmp -#define __strcasecmp strcasecmp -#undef __vsnprintf -#define __vsnprintf vsnprintf - -#endif /* !_LIBC */ - -#ifndef __set_errno -#define __set_errno(e) (errno = (e)) -#endif diff --git a/argp-standalone/argp-parse.c b/argp-standalone/argp-parse.c deleted file mode 100644 index 78f7bf139..000000000 --- a/argp-standalone/argp-parse.c +++ /dev/null @@ -1,1305 +0,0 @@ -/* Hierarchial argument parsing - Copyright (C) 1995, 96, 97, 98, 99, 2000,2003 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifdef HAVE_CONFIG_H -#include -#endif - -#if HAVE_ALLOCA_H -#include -#endif - -#include -#include -#if HAVE_UNISTD_H -# include -#endif -#include -#include - -#if HAVE_MALLOC_H -/* Needed, for alloca on windows */ -# include -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. - When compiling libc, the _ macro is predefined. */ -# if defined HAVE_LIBINTL_H || defined _LIBC -# include -# ifdef _LIBC -# undef dgettext -# define dgettext(domain, msgid) __dcgettext (domain, msgid, LC_MESSAGES) -# endif -# else -# define dgettext(domain, msgid) (msgid) -# define gettext(msgid) (msgid) -# endif -#endif -#ifndef N_ -# define N_(msgid) (msgid) -#endif - -#if _LIBC - 0 -#include -#else -#ifdef HAVE_CTHREADS_H -#include -#endif -#endif /* _LIBC */ - -#include "argp.h" -#include "argp-namefrob.h" - - -/* The meta-argument used to prevent any further arguments being interpreted - as options. */ -#define QUOTE "--" - -/* EZ alias for ARGP_ERR_UNKNOWN. */ -#define EBADKEY ARGP_ERR_UNKNOWN - - -/* Default options. */ - -/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep - for one second intervals, decrementing _ARGP_HANG until it's zero. Thus - you can force the program to continue by attaching a debugger and setting - it to 0 yourself. */ -volatile int _argp_hang; - -#define OPT_PROGNAME -2 -#define OPT_USAGE -3 -#if HAVE_SLEEP && HAVE_GETPID -#define OPT_HANG -4 -#endif - -static const struct argp_option argp_default_options[] = -{ - {"help", '?', 0, 0, N_("Give this help list"), -1}, - {"usage", OPT_USAGE, 0, 0, N_("Give a short usage message"), 0 }, - {"program-name",OPT_PROGNAME,"NAME", OPTION_HIDDEN, - N_("Set the program name"), 0}, -#if OPT_HANG - {"HANG", OPT_HANG, "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN, - N_("Hang for SECS seconds (default 3600)"), 0 }, -#endif - {0, 0, 0, 0, 0, 0} -}; - -static error_t -argp_default_parser (int key, char *arg, struct argp_state *state) -{ - switch (key) - { - case '?': - __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); - break; - case OPT_USAGE: - __argp_state_help (state, state->out_stream, - ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); - break; - - case OPT_PROGNAME: /* Set the program name. */ -#if HAVE_DECL_PROGRAM_INVOCATION_NAME - program_invocation_name = arg; -#endif - /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka - __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined - to be that, so we have to be a bit careful here.] */ - - /* Update what we use for messages. */ - - state->name = __argp_basename(arg); - -#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME - program_invocation_short_name = state->name; -#endif - - if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) - == ARGP_PARSE_ARGV0) - /* Update what getopt uses too. */ - state->argv[0] = arg; - - break; - -#if OPT_HANG - case OPT_HANG: - _argp_hang = atoi (arg ? arg : "3600"); - fprintf(state->err_stream, "%s: pid = %ld\n", - state->name, (long) getpid()); - while (_argp_hang-- > 0) - __sleep (1); - break; -#endif - - default: - return EBADKEY; - } - return 0; -} - -static const struct argp argp_default_argp = - {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"}; - - -static const struct argp_option argp_version_options[] = -{ - {"version", 'V', 0, 0, N_("Print program version"), -1}, - {0, 0, 0, 0, 0, 0 } -}; - -static error_t -argp_version_parser (int key, char *arg UNUSED, struct argp_state *state) -{ - switch (key) - { - case 'V': - if (argp_program_version_hook) - (*argp_program_version_hook) (state->out_stream, state); - else if (argp_program_version) - fprintf (state->out_stream, "%s\n", argp_program_version); - else - __argp_error (state, dgettext (state->root_argp->argp_domain, - "(PROGRAM ERROR) No version known!?")); - if (! (state->flags & ARGP_NO_EXIT)) - exit (0); - break; - default: - return EBADKEY; - } - return 0; -} - -static const struct argp argp_version_argp = - {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"}; - - - -/* The state of a `group' during parsing. Each group corresponds to a - particular argp structure from the tree of such descending from the top - level argp passed to argp_parse. */ -struct group -{ - /* This group's parsing function. */ - argp_parser_t parser; - - /* Which argp this group is from. */ - const struct argp *argp; - - /* The number of non-option args sucessfully handled by this parser. */ - unsigned args_processed; - - /* This group's parser's parent's group. */ - struct group *parent; - unsigned parent_index; /* And the our position in the parent. */ - - /* These fields are swapped into and out of the state structure when - calling this group's parser. */ - void *input, **child_inputs; - void *hook; -}; - -/* Call GROUP's parser with KEY and ARG, swapping any group-specific info - from STATE before calling, and back into state afterwards. If GROUP has - no parser, EBADKEY is returned. */ -static error_t -group_parse (struct group *group, struct argp_state *state, int key, char *arg) -{ - if (group->parser) - { - error_t err; - state->hook = group->hook; - state->input = group->input; - state->child_inputs = group->child_inputs; - state->arg_num = group->args_processed; - err = (*group->parser)(key, arg, state); - group->hook = state->hook; - return err; - } - else - return EBADKEY; -} - -struct parser -{ - const struct argp *argp; - - const char *posixly_correct; - - /* True if there are only no-option arguments left, which are just - passed verbatim with ARGP_KEY_ARG. This is set if we encounter a - quote, or the end of the proper options, but may be cleared again - if the user moves the next argument pointer backwards. */ - int args_only; - - /* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, the default is - REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is - defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; stop option - processing when the first non-option is seen. This is what Unix - does. This mode of operation is selected by either setting the - environment variable POSIXLY_CORRECT, or using `+' as the first - character of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we - scan, so that eventually all the non-options are at the end. This - allows options to be given in any order, even with programs that - were not written to expect this. - - RETURN_IN_ORDER is an option available to programs that were - written to expect options and other ARGV-elements in any order - and that care about the ordering of the two. We describe each - non-option ARGV-element as if it were the argument of an option - with character code 1. Using `-' as the first character of the - list of option characters selects this mode of operation. - - */ - enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; - - /* A segment of non-option arguments that have been skipped for - later processing, after all options. `first_nonopt' is the index - in ARGV of the first of them; `last_nonopt' is the index after - the last of them. - - If quoted or args_only is non-zero, this segment should be empty. */ - - /* FIXME: I'd prefer to use unsigned, but it's more consistent to - use the same type as for state.next. */ - int first_nonopt; - int last_nonopt; - - /* String of all recognized short options. Needed for ARGP_LONG_ONLY. */ - /* FIXME: Perhaps change to a pointer to a suitable bitmap instead? */ - char *short_opts; - - /* For parsing combined short options. */ - char *nextchar; - - /* States of the various parsing groups. */ - struct group *groups; - /* The end of the GROUPS array. */ - struct group *egroup; - /* An vector containing storage for the CHILD_INPUTS field in all groups. */ - void **child_inputs; - - /* State block supplied to parsing routines. */ - struct argp_state state; - - /* Memory used by this parser. */ - void *storage; -}; - -/* Search for a group defining a short option. */ -static const struct argp_option * -find_short_option(struct parser *parser, int key, struct group **p) -{ - struct group *group; - - assert(key >= 0); - assert(isascii(key)); - - for (group = parser->groups; group < parser->egroup; group++) - { - const struct argp_option *opts; - - for (opts = group->argp->options; !__option_is_end(opts); opts++) - if (opts->key == key) - { - *p = group; - return opts; - } - } - return NULL; -} - -enum match_result { MATCH_EXACT, MATCH_PARTIAL, MATCH_NO }; - -/* If defined, allow complete.el-like abbreviations of long options. */ -#ifndef ARGP_COMPLETE -#define ARGP_COMPLETE 0 -#endif - -/* Matches an encountern long-option argument ARG against an option NAME. - * ARG is terminated by NUL or '='. */ -static enum match_result -match_option(const char *arg, const char *name) -{ - unsigned i, j; - for (i = j = 0;; i++, j++) - { - switch(arg[i]) - { - case '\0': - case '=': - return name[j] ? MATCH_PARTIAL : MATCH_EXACT; -#if ARGP_COMPLETE - case '-': - while (name[j] != '-') - if (!name[j++]) - return MATCH_NO; - break; -#endif - default: - if (arg[i] != name[j]) - return MATCH_NO; - } - } -} - -static const struct argp_option * -find_long_option(struct parser *parser, - const char *arg, - struct group **p) -{ - struct group *group; - - /* Partial match found so far. */ - struct group *matched_group = NULL; - const struct argp_option *matched_option = NULL; - - /* Number of partial matches. */ - int num_partial = 0; - - for (group = parser->groups; group < parser->egroup; group++) - { - const struct argp_option *opts; - - for (opts = group->argp->options; !__option_is_end(opts); opts++) - { - if (!opts->name) - continue; - switch (match_option(arg, opts->name)) - { - case MATCH_NO: - break; - case MATCH_PARTIAL: - num_partial++; - - matched_group = group; - matched_option = opts; - - break; - case MATCH_EXACT: - /* Exact match. */ - *p = group; - return opts; - } - } - } - if (num_partial == 1) - { - *p = matched_group; - return matched_option; - } - - return NULL; -} - - -/* The next usable entries in the various parser tables being filled in by - convert_options. */ -struct parser_convert_state -{ - struct parser *parser; - char *short_end; - void **child_inputs_end; -}; - -/* Initialize GROUP from ARGP. If CVT->SHORT_END is non-NULL, short - options are recorded in the short options string. Returns the next - unused group entry. CVT holds state used during the conversion. */ -static struct group * -convert_options (const struct argp *argp, - struct group *parent, unsigned parent_index, - struct group *group, struct parser_convert_state *cvt) -{ - const struct argp_option *opt = argp->options; - const struct argp_child *children = argp->children; - - if (opt || argp->parser) - { - /* This parser needs a group. */ - if (cvt->short_end) - { - /* Record any short options. */ - for ( ; !__option_is_end (opt); opt++) - if (__option_is_short(opt)) - *cvt->short_end++ = opt->key; - } - - group->parser = argp->parser; - group->argp = argp; - group->args_processed = 0; - group->parent = parent; - group->parent_index = parent_index; - group->input = 0; - group->hook = 0; - group->child_inputs = 0; - - if (children) - /* Assign GROUP's CHILD_INPUTS field some space from - CVT->child_inputs_end.*/ - { - unsigned num_children = 0; - while (children[num_children].argp) - num_children++; - group->child_inputs = cvt->child_inputs_end; - cvt->child_inputs_end += num_children; - } - parent = group++; - } - else - parent = 0; - - if (children) - { - unsigned index = 0; - while (children->argp) - group = - convert_options (children++->argp, parent, index++, group, cvt); - } - - return group; -} -/* Allocate and initialize the group structures, so that they are - ordered as if by traversing the corresponding argp parser tree in - pre-order. Also build the list of short options, if that is needed. */ -static void -parser_convert (struct parser *parser, const struct argp *argp) -{ - struct parser_convert_state cvt; - - cvt.parser = parser; - cvt.short_end = parser->short_opts; - cvt.child_inputs_end = parser->child_inputs; - - parser->argp = argp; - - if (argp) - parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); - else - parser->egroup = parser->groups; /* No parsers at all! */ - - if (parser->short_opts) - *cvt.short_end ='\0'; -} - -/* Lengths of various parser fields which we will allocated. */ -struct parser_sizes -{ - /* Needed only ARGP_LONG_ONLY */ - size_t short_len; /* Number of short options. */ - - size_t num_groups; /* Group structures we allocate. */ - size_t num_child_inputs; /* Child input slots. */ -}; - -/* For ARGP, increments the NUM_GROUPS field in SZS by the total - number of argp structures descended from it, and the SHORT_LEN by - the total number of short options. */ -static void -calc_sizes (const struct argp *argp, struct parser_sizes *szs) -{ - const struct argp_child *child = argp->children; - const struct argp_option *opt = argp->options; - - if (opt || argp->parser) - { - /* This parser needs a group. */ - szs->num_groups++; - if (opt) - { - while (__option_is_short (opt++)) - szs->short_len++; - } - } - - if (child) - while (child->argp) - { - calc_sizes ((child++)->argp, szs); - szs->num_child_inputs++; - } -} - -/* Initializes PARSER to parse ARGP in a manner described by FLAGS. */ -static error_t -parser_init (struct parser *parser, const struct argp *argp, - int argc, char **argv, int flags, void *input) -{ - error_t err = 0; - struct group *group; - struct parser_sizes szs; - - parser->posixly_correct = getenv ("POSIXLY_CORRECT"); - - if (flags & ARGP_IN_ORDER) - parser->ordering = RETURN_IN_ORDER; - else if (flags & ARGP_NO_ARGS) - parser->ordering = REQUIRE_ORDER; - else if (parser->posixly_correct) - parser->ordering = REQUIRE_ORDER; - else - parser->ordering = PERMUTE; - - szs.short_len = 0; - szs.num_groups = 0; - szs.num_child_inputs = 0; - - if (argp) - calc_sizes (argp, &szs); - - if (!(flags & ARGP_LONG_ONLY)) - /* We have no use for the short option array. */ - szs.short_len = 0; - - /* Lengths of the various bits of storage used by PARSER. */ -#define GLEN (szs.num_groups + 1) * sizeof (struct group) -#define CLEN (szs.num_child_inputs * sizeof (void *)) -#define SLEN (szs.short_len + 1) -#define STORAGE(offset) ((void *) (((char *) parser->storage) + (offset))) - - parser->storage = malloc (GLEN + CLEN + SLEN); - if (! parser->storage) - return ENOMEM; - - parser->groups = parser->storage; - - parser->child_inputs = STORAGE(GLEN); - memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *)); - - if (flags & ARGP_LONG_ONLY) - parser->short_opts = STORAGE(GLEN + CLEN); - else - parser->short_opts = NULL; - - parser_convert (parser, argp); - - memset (&parser->state, 0, sizeof (struct argp_state)); - - parser->state.root_argp = parser->argp; - parser->state.argc = argc; - parser->state.argv = argv; - parser->state.flags = flags; - parser->state.err_stream = stderr; - parser->state.out_stream = stdout; - parser->state.pstate = parser; - - parser->args_only = 0; - parser->nextchar = NULL; - parser->first_nonopt = parser->last_nonopt = 0; - - /* Call each parser for the first time, giving it a chance to propagate - values to child parsers. */ - if (parser->groups < parser->egroup) - parser->groups->input = input; - for (group = parser->groups; - group < parser->egroup && (!err || err == EBADKEY); - group++) - { - if (group->parent) - /* If a child parser, get the initial input value from the parent. */ - group->input = group->parent->child_inputs[group->parent_index]; - - if (!group->parser - && group->argp->children && group->argp->children->argp) - /* For the special case where no parsing function is supplied for an - argp, propagate its input to its first child, if any (this just - makes very simple wrapper argps more convenient). */ - group->child_inputs[0] = group->input; - - err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); - } - if (err == EBADKEY) - err = 0; /* Some parser didn't understand. */ - - if (err) - return err; - - if (argv[0] && !(parser->state.flags & ARGP_PARSE_ARGV0)) - /* There's an argv[0]; use it for messages. */ - { - parser->state.name = __argp_basename(argv[0]); - - /* Don't parse it as an argument. */ - parser->state.next = 1; - } - else - parser->state.name = __argp_short_program_name(NULL); - - return 0; -} - -/* Free any storage consumed by PARSER (but not PARSER itself). */ -static error_t -parser_finalize (struct parser *parser, - error_t err, int arg_ebadkey, int *end_index) -{ - struct group *group; - - if (err == EBADKEY && arg_ebadkey) - /* Suppress errors generated by unparsed arguments. */ - err = 0; - - if (! err) - { - if (parser->state.next == parser->state.argc) - /* We successfully parsed all arguments! Call all the parsers again, - just a few more times... */ - { - for (group = parser->groups; - group < parser->egroup && (!err || err==EBADKEY); - group++) - if (group->args_processed == 0) - err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); - for (group = parser->egroup - 1; - group >= parser->groups && (!err || err==EBADKEY); - group--) - err = group_parse (group, &parser->state, ARGP_KEY_END, 0); - - if (err == EBADKEY) - err = 0; /* Some parser didn't understand. */ - - /* Tell the user that all arguments are parsed. */ - if (end_index) - *end_index = parser->state.next; - } - else if (end_index) - /* Return any remaining arguments to the user. */ - *end_index = parser->state.next; - else - /* No way to return the remaining arguments, they must be bogus. */ - { - if (!(parser->state.flags & ARGP_NO_ERRS) - && parser->state.err_stream) - fprintf (parser->state.err_stream, - dgettext (parser->argp->argp_domain, - "%s: Too many arguments\n"), - parser->state.name); - err = EBADKEY; - } - } - - /* Okay, we're all done, with either an error or success; call the parsers - to indicate which one. */ - - if (err) - { - /* Maybe print an error message. */ - if (err == EBADKEY) - /* An appropriate message describing what the error was should have - been printed earlier. */ - __argp_state_help (&parser->state, parser->state.err_stream, - ARGP_HELP_STD_ERR); - - /* Since we didn't exit, give each parser an error indication. */ - for (group = parser->groups; group < parser->egroup; group++) - group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); - } - else - /* Notify parsers of success, and propagate back values from parsers. */ - { - /* We pass over the groups in reverse order so that child groups are - given a chance to do there processing before passing back a value to - the parent. */ - for (group = parser->egroup - 1 - ; group >= parser->groups && (!err || err == EBADKEY) - ; group--) - err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); - if (err == EBADKEY) - err = 0; /* Some parser didn't understand. */ - } - - /* Call parsers once more, to do any final cleanup. Errors are ignored. */ - for (group = parser->egroup - 1; group >= parser->groups; group--) - group_parse (group, &parser->state, ARGP_KEY_FINI, 0); - - if (err == EBADKEY) - err = EINVAL; - - free (parser->storage); - - return err; -} - -/* Call the user parsers to parse the non-option argument VAL, at the - current position, returning any error. The state NEXT pointer - should point to the argument; this function will adjust it - correctly to reflect however many args actually end up being - consumed. */ -static error_t -parser_parse_arg (struct parser *parser, char *val) -{ - /* Save the starting value of NEXT */ - int index = parser->state.next; - error_t err = EBADKEY; - struct group *group; - int key = 0; /* Which of ARGP_KEY_ARG[S] we used. */ - - /* Try to parse the argument in each parser. */ - for (group = parser->groups - ; group < parser->egroup && err == EBADKEY - ; group++) - { - parser->state.next++; /* For ARGP_KEY_ARG, consume the arg. */ - key = ARGP_KEY_ARG; - err = group_parse (group, &parser->state, key, val); - - if (err == EBADKEY) - /* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */ - { - parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. */ - key = ARGP_KEY_ARGS; - err = group_parse (group, &parser->state, key, 0); - } - } - - if (! err) - { - if (key == ARGP_KEY_ARGS) - /* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't - changed by the user, *all* arguments should be considered - consumed. */ - parser->state.next = parser->state.argc; - - if (parser->state.next > index) - /* Remember that we successfully processed a non-option - argument -- but only if the user hasn't gotten tricky and set - the clock back. */ - (--group)->args_processed += (parser->state.next - index); - else - /* The user wants to reparse some args, so try looking for options again. */ - parser->args_only = 0; - } - - return err; -} - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,next), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (struct parser *parser) -{ - int bottom = parser->first_nonopt; - int middle = parser->last_nonopt; - int top = parser->state.next; - char **argv = parser->state.argv; - - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - parser->first_nonopt += (parser->state.next - parser->last_nonopt); - parser->last_nonopt = parser->state.next; -} - - - -enum arg_type { ARG_ARG, ARG_SHORT_OPTION, - ARG_LONG_OPTION, ARG_LONG_ONLY_OPTION, - ARG_QUOTE }; - -static enum arg_type -classify_arg(struct parser *parser, char *arg, char **opt) -{ - if (arg[0] == '-') - /* Looks like an option... */ - switch (arg[1]) - { - case '\0': - /* "-" is not an option. */ - return ARG_ARG; - case '-': - /* Long option, or quote. */ - if (!arg[2]) - return ARG_QUOTE; - - /* A long option. */ - if (opt) - *opt = arg + 2; - return ARG_LONG_OPTION; - - default: - /* Short option. But if ARGP_LONG_ONLY, it can also be a long option. */ - - if (opt) - *opt = arg + 1; - - if (parser->state.flags & ARGP_LONG_ONLY) - { - /* Rules from getopt.c: - - If long_only and the ARGV-element has the form "-f", - where f is a valid short option, don't consider it an - abbreviated form of a long option that starts with f. - Otherwise there would be no way to give the -f short - option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an - abbreviation of the long option, just like "--fu", and - not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - assert(parser->short_opts); - - if (arg[2] || !strchr(parser->short_opts, arg[1])) - return ARG_LONG_ONLY_OPTION; - } - - return ARG_SHORT_OPTION; - } - - else - return ARG_ARG; -} - -/* Parse the next argument in PARSER (as indicated by PARSER->state.next). - Any error from the parsers is returned, and *ARGP_EBADKEY indicates - whether a value of EBADKEY is due to an unrecognized argument (which is - generally not fatal). */ -static error_t -parser_parse_next (struct parser *parser, int *arg_ebadkey) -{ - if (parser->state.quoted && parser->state.next < parser->state.quoted) - /* The next argument pointer has been moved to before the quoted - region, so pretend we never saw the quoting `--', and start - looking for options again. If the `--' is still there we'll just - process it one more time. */ - parser->state.quoted = parser->args_only = 0; - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if NEXT has been - moved back by the user (who may also have changed the arguments). */ - if (parser->last_nonopt > parser->state.next) - parser->last_nonopt = parser->state.next; - if (parser->first_nonopt > parser->state.next) - parser->first_nonopt = parser->state.next; - - if (parser->nextchar) - /* Deal with short options. */ - { - struct group *group; - char c; - const struct argp_option *option; - char *value = NULL;; - - assert(!parser->args_only); - - c = *parser->nextchar++; - - option = find_short_option(parser, c, &group); - if (!option) - { - if (parser->posixly_correct) - /* 1003.2 specifies the format of this message. */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: illegal option -- %c\n"), - parser->state.name, c); - else - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: invalid option -- %c\n"), - parser->state.name, c); - - *arg_ebadkey = 0; - return EBADKEY; - } - - if (!*parser->nextchar) - parser->nextchar = NULL; - - if (option->arg) - { - value = parser->nextchar; - parser->nextchar = NULL; - - if (!value - && !(option->flags & OPTION_ARG_OPTIONAL)) - /* We need an mandatory argument. */ - { - if (parser->state.next == parser->state.argc) - /* Missing argument */ - { - /* 1003.2 specifies the format of this message. */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: option requires an argument -- %c\n"), - parser->state.name, c); - - *arg_ebadkey = 0; - return EBADKEY; - } - value = parser->state.argv[parser->state.next++]; - } - } - return group_parse(group, &parser->state, - option->key, value); - } - else - /* Advance to the next ARGV-element. */ - { - if (parser->args_only) - { - *arg_ebadkey = 1; - if (parser->state.next >= parser->state.argc) - /* We're done. */ - return EBADKEY; - else - return parser_parse_arg(parser, - parser->state.argv[parser->state.next]); - } - - if (parser->state.next >= parser->state.argc) - /* Almost done. If there are non-options that we skipped - previously, we should process them now. */ - { - *arg_ebadkey = 1; - if (parser->first_nonopt != parser->last_nonopt) - { - exchange(parser); - - /* Start processing the arguments we skipped previously. */ - parser->state.next = parser->first_nonopt; - - parser->first_nonopt = parser->last_nonopt = 0; - - parser->args_only = 1; - return 0; - } - else - /* Indicate that we're really done. */ - return EBADKEY; - } - else - /* Look for options. */ - { - char *arg = parser->state.argv[parser->state.next]; - - char *optstart; - enum arg_type token = classify_arg(parser, arg, &optstart); - - switch (token) - { - case ARG_ARG: - switch (parser->ordering) - { - case PERMUTE: - if (parser->first_nonopt == parser->last_nonopt) - /* Skipped sequence is empty; start a new one. */ - parser->first_nonopt = parser->last_nonopt = parser->state.next; - - else if (parser->last_nonopt != parser->state.next) - /* We have a non-empty skipped sequence, and - we're not at the end-point, so move it. */ - exchange(parser); - - assert(parser->last_nonopt == parser->state.next); - - /* Skip this argument for now. */ - parser->state.next++; - parser->last_nonopt = parser->state.next; - - return 0; - - case REQUIRE_ORDER: - /* Implicit quote before the first argument. */ - parser->args_only = 1; - return 0; - - case RETURN_IN_ORDER: - *arg_ebadkey = 1; - return parser_parse_arg(parser, arg); - - default: - abort(); - } - case ARG_QUOTE: - /* Skip it, then exchange with any previous non-options. */ - parser->state.next++; - assert (parser->last_nonopt != parser->state.next); - - if (parser->first_nonopt != parser->last_nonopt) - { - exchange(parser); - - /* Start processing the skipped and the quoted - arguments. */ - - parser->state.quoted = parser->state.next = parser->first_nonopt; - - /* Also empty the skipped-list, to avoid confusion - if the user resets the next pointer. */ - parser->first_nonopt = parser->last_nonopt = 0; - } - else - parser->state.quoted = parser->state.next; - - parser->args_only = 1; - return 0; - - case ARG_LONG_ONLY_OPTION: - case ARG_LONG_OPTION: - { - struct group *group; - const struct argp_option *option; - char *value; - - parser->state.next++; - option = find_long_option(parser, optstart, &group); - - if (!option) - { - /* NOTE: This includes any "=something" in the output. */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: unrecognized option `%s'\n"), - parser->state.name, arg); - *arg_ebadkey = 0; - return EBADKEY; - } - - value = strchr(optstart, '='); - if (value) - value++; - - if (value && !option->arg) - /* Unexpected argument. */ - { - if (token == ARG_LONG_OPTION) - /* --option */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: option `--%s' doesn't allow an argument\n"), - parser->state.name, option->name); - else - /* +option or -option */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: option `%c%s' doesn't allow an argument\n"), - parser->state.name, arg[0], option->name); - - *arg_ebadkey = 0; - return EBADKEY; - } - - if (option->arg && !value - && !(option->flags & OPTION_ARG_OPTIONAL)) - /* We need an mandatory argument. */ - { - if (parser->state.next == parser->state.argc) - /* Missing argument */ - { - if (token == ARG_LONG_OPTION) - /* --option */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: option `--%s' requires an argument\n"), - parser->state.name, option->name); - else - /* +option or -option */ - fprintf (parser->state.err_stream, - dgettext(parser->state.root_argp->argp_domain, - "%s: option `%c%s' requires an argument\n"), - parser->state.name, arg[0], option->name); - - *arg_ebadkey = 0; - return EBADKEY; - } - - value = parser->state.argv[parser->state.next++]; - } - *arg_ebadkey = 0; - return group_parse(group, &parser->state, - option->key, value); - } - case ARG_SHORT_OPTION: - parser->state.next++; - parser->nextchar = optstart; - return 0; - - default: - abort(); - } - } - } -} - -/* Parse the options strings in ARGC & ARGV according to the argp in ARGP. - FLAGS is one of the ARGP_ flags above. If END_INDEX is non-NULL, the - index in ARGV of the first unparsed option is returned in it. If an - unknown option is present, EINVAL is returned; if some parser routine - returned a non-zero value, it is returned; otherwise 0 is returned. */ -error_t -__argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, - int *end_index, void *input) -{ - error_t err; - struct parser parser; - - /* If true, then err == EBADKEY is a result of a non-option argument failing - to be parsed (which in some cases isn't actually an error). */ - int arg_ebadkey = 0; - - if (! (flags & ARGP_NO_HELP)) - /* Add our own options. */ - { - struct argp_child *child = alloca (4 * sizeof (struct argp_child)); - struct argp *top_argp = alloca (sizeof (struct argp)); - - /* TOP_ARGP has no options, it just serves to group the user & default - argps. */ - memset (top_argp, 0, sizeof (*top_argp)); - top_argp->children = child; - - memset (child, 0, 4 * sizeof (struct argp_child)); - - if (argp) - (child++)->argp = argp; - (child++)->argp = &argp_default_argp; - if (argp_program_version || argp_program_version_hook) - (child++)->argp = &argp_version_argp; - child->argp = 0; - - argp = top_argp; - } - - /* Construct a parser for these arguments. */ - err = parser_init (&parser, argp, argc, argv, flags, input); - - if (! err) - /* Parse! */ - { - while (! err) - err = parser_parse_next (&parser, &arg_ebadkey); - err = parser_finalize (&parser, err, arg_ebadkey, end_index); - } - - return err; -} -#ifdef weak_alias -weak_alias (__argp_parse, argp_parse) -#endif - -/* Return the input field for ARGP in the parser corresponding to STATE; used - by the help routines. */ -void * -__argp_input (const struct argp *argp, const struct argp_state *state) -{ - if (state) - { - struct group *group; - struct parser *parser = state->pstate; - - for (group = parser->groups; group < parser->egroup; group++) - if (group->argp == argp) - return group->input; - } - - return 0; -} -#ifdef weak_alias -weak_alias (__argp_input, _argp_input) -#endif - -/* Defined here, in case a user is not inlining the definitions in - * argp.h */ -void -__argp_usage (__const struct argp_state *__state) -{ - __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); -} - -int -__option_is_short (__const struct argp_option *__opt) -{ - if (__opt->flags & OPTION_DOC) - return 0; - else - { - int __key = __opt->key; - /* FIXME: whether or not a particular key implies a short option - * ought not to be locale dependent. */ - return __key > 0 && isprint (__key); - } -} - -int -__option_is_end (__const struct argp_option *__opt) -{ - return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; -} diff --git a/argp-standalone/argp-pv.c b/argp-standalone/argp-pv.c deleted file mode 100644 index d7d374a66..000000000 --- a/argp-standalone/argp-pv.c +++ /dev/null @@ -1,25 +0,0 @@ -/* Default definition for ARGP_PROGRAM_VERSION. - Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -/* If set by the user program to a non-zero value, then a default option - --version is added (unless the ARGP_NO_HELP flag is used), which will - print this this string followed by a newline and exit (unless the - ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ -const char *argp_program_version = 0; diff --git a/argp-standalone/argp-pvh.c b/argp-standalone/argp-pvh.c deleted file mode 100644 index 829a1cda8..000000000 --- a/argp-standalone/argp-pvh.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Default definition for ARGP_PROGRAM_VERSION_HOOK. - Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "argp.h" - -/* If set by the user program to a non-zero value, then a default option - --version is added (unless the ARGP_NO_HELP flag is used), which calls - this function with a stream to print the version to and a pointer to the - current parsing state, and then exits (unless the ARGP_NO_EXIT flag is - used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ -void (*argp_program_version_hook) (FILE *stream, struct argp_state *state) = 0; diff --git a/argp-standalone/argp.h b/argp-standalone/argp.h deleted file mode 100644 index 29d3dfe97..000000000 --- a/argp-standalone/argp.h +++ /dev/null @@ -1,602 +0,0 @@ -/* Hierarchial argument parsing. - Copyright (C) 1995, 96, 97, 98, 99, 2003 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Written by Miles Bader . - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _ARGP_H -#define _ARGP_H - -#include -#include - -#define __need_error_t -#include - -#ifndef __THROW -# define __THROW -#endif - -#ifndef __const -# define __const const -#endif - -#ifndef __error_t_defined -typedef int error_t; -# define __error_t_defined -#endif - -/* FIXME: What's the right way to check for __restrict? Sun's cc seems - not to have it. Perhaps it's easiest to just delete the use of - __restrict from the prototypes. */ -#ifndef __restrict -# ifndef __GNUC___ -# define __restrict -# endif -#endif - -/* NOTE: We can't use the autoconf tests, since this is supposed to be - an installed header file and argp's config.h is of course not - installed. */ -#ifndef PRINTF_STYLE -# if __GNUC__ >= 2 -# define PRINTF_STYLE(f, a) __attribute__ ((__format__ (__printf__, f, a))) -# else -# define PRINTF_STYLE(f, a) -# endif -#endif - - -#ifdef __cplusplus -extern "C" { -#endif - -/* A description of a particular option. A pointer to an array of - these is passed in the OPTIONS field of an argp structure. Each option - entry can correspond to one long option and/or one short option; more - names for the same option can be added by following an entry in an option - array with options having the OPTION_ALIAS flag set. */ -struct argp_option -{ - /* The long option name. For more than one name for the same option, you - can use following options with the OPTION_ALIAS flag set. */ - __const char *name; - - /* What key is returned for this option. If > 0 and printable, then it's - also accepted as a short option. */ - int key; - - /* If non-NULL, this is the name of the argument associated with this - option, which is required unless the OPTION_ARG_OPTIONAL flag is set. */ - __const char *arg; - - /* OPTION_ flags. */ - int flags; - - /* The doc string for this option. If both NAME and KEY are 0, This string - will be printed outdented from the normal option column, making it - useful as a group header (it will be the first thing printed in its - group); in this usage, it's conventional to end the string with a `:'. */ - __const char *doc; - - /* The group this option is in. In a long help message, options are sorted - alphabetically within each group, and the groups presented in the order - 0, 1, 2, ..., n, -m, ..., -2, -1. Every entry in an options array with - if this field 0 will inherit the group number of the previous entry, or - zero if it's the first one, unless its a group header (NAME and KEY both - 0), in which case, the previous entry + 1 is the default. Automagic - options such as --help are put into group -1. */ - int group; -}; - -/* The argument associated with this option is optional. */ -#define OPTION_ARG_OPTIONAL 0x1 - -/* This option isn't displayed in any help messages. */ -#define OPTION_HIDDEN 0x2 - -/* This option is an alias for the closest previous non-alias option. This - means that it will be displayed in the same help entry, and will inherit - fields other than NAME and KEY from the aliased option. */ -#define OPTION_ALIAS 0x4 - -/* This option isn't actually an option (and so should be ignored by the - actual option parser), but rather an arbitrary piece of documentation that - should be displayed in much the same manner as the options. If this flag - is set, then the option NAME field is displayed unmodified (e.g., no `--' - prefix is added) at the left-margin (where a *short* option would normally - be displayed), and the documentation string in the normal place. For - purposes of sorting, any leading whitespace and puncuation is ignored, - except that if the first non-whitespace character is not `-', this entry - is displayed after all options (and OPTION_DOC entries with a leading `-') - in the same group. */ -#define OPTION_DOC 0x8 - -/* This option shouldn't be included in `long' usage messages (but is still - included in help messages). This is mainly intended for options that are - completely documented in an argp's ARGS_DOC field, in which case including - the option in the generic usage list would be redundant. For instance, - if ARGS_DOC is "FOO BAR\n-x BLAH", and the `-x' option's purpose is to - distinguish these two cases, -x should probably be marked - OPTION_NO_USAGE. */ -#define OPTION_NO_USAGE 0x10 - -struct argp; /* fwd declare this type */ -struct argp_state; /* " */ -struct argp_child; /* " */ - -/* The type of a pointer to an argp parsing function. */ -typedef error_t (*argp_parser_t) (int key, char *arg, - struct argp_state *state); - -/* What to return for unrecognized keys. For special ARGP_KEY_ keys, such - returns will simply be ignored. For user keys, this error will be turned - into EINVAL (if the call to argp_parse is such that errors are propagated - back to the user instead of exiting); returning EINVAL itself would result - in an immediate stop to parsing in *all* cases. */ -#define ARGP_ERR_UNKNOWN E2BIG /* Hurd should never need E2BIG. XXX */ - -/* Special values for the KEY argument to an argument parsing function. - ARGP_ERR_UNKNOWN should be returned if they aren't understood. - - The sequence of keys to a parsing function is either (where each - uppercased word should be prefixed by `ARGP_KEY_' and opt is a user key): - - INIT opt... NO_ARGS END SUCCESS -- No non-option arguments at all - or INIT (opt | ARG)... END SUCCESS -- All non-option args parsed - or INIT (opt | ARG)... SUCCESS -- Some non-option arg unrecognized - - The third case is where every parser returned ARGP_KEY_UNKNOWN for an - argument, in which case parsing stops at that argument (returning the - unparsed arguments to the caller of argp_parse if requested, or stopping - with an error message if not). - - If an error occurs (either detected by argp, or because the parsing - function returned an error value), then the parser is called with - ARGP_KEY_ERROR, and no further calls are made. */ - -/* This is not an option at all, but rather a command line argument. If a - parser receiving this key returns success, the fact is recorded, and the - ARGP_KEY_NO_ARGS case won't be used. HOWEVER, if while processing the - argument, a parser function decrements the NEXT field of the state it's - passed, the option won't be considered processed; this is to allow you to - actually modify the argument (perhaps into an option), and have it - processed again. */ -#define ARGP_KEY_ARG 0 -/* There are remaining arguments not parsed by any parser, which may be found - starting at (STATE->argv + STATE->next). If success is returned, but - STATE->next left untouched, it's assumed that all arguments were consume, - otherwise, the parser should adjust STATE->next to reflect any arguments - consumed. */ -#define ARGP_KEY_ARGS 0x1000006 -/* There are no more command line arguments at all. */ -#define ARGP_KEY_END 0x1000001 -/* Because it's common to want to do some special processing if there aren't - any non-option args, user parsers are called with this key if they didn't - successfully process any non-option arguments. Called just before - ARGP_KEY_END (where more general validity checks on previously parsed - arguments can take place). */ -#define ARGP_KEY_NO_ARGS 0x1000002 -/* Passed in before any parsing is done. Afterwards, the values of each - element of the CHILD_INPUT field, if any, in the state structure is - copied to each child's state to be the initial value of the INPUT field. */ -#define ARGP_KEY_INIT 0x1000003 -/* Use after all other keys, including SUCCESS & END. */ -#define ARGP_KEY_FINI 0x1000007 -/* Passed in when parsing has successfully been completed (even if there are - still arguments remaining). */ -#define ARGP_KEY_SUCCESS 0x1000004 -/* Passed in if an error occurs. */ -#define ARGP_KEY_ERROR 0x1000005 - -/* An argp structure contains a set of options declarations, a function to - deal with parsing one, documentation string, a possible vector of child - argp's, and perhaps a function to filter help output. When actually - parsing options, getopt is called with the union of all the argp - structures chained together through their CHILD pointers, with conflicts - being resolved in favor of the first occurrence in the chain. */ -struct argp -{ - /* An array of argp_option structures, terminated by an entry with both - NAME and KEY having a value of 0. */ - __const struct argp_option *options; - - /* What to do with an option from this structure. KEY is the key - associated with the option, and ARG is any associated argument (NULL if - none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should be - returned. If a non-zero, non-ARGP_ERR_UNKNOWN value is returned, then - parsing is stopped immediately, and that value is returned from - argp_parse(). For special (non-user-supplied) values of KEY, see the - ARGP_KEY_ definitions below. */ - argp_parser_t parser; - - /* A string describing what other arguments are wanted by this program. It - is only used by argp_usage to print the `Usage:' message. If it - contains newlines, the strings separated by them are considered - alternative usage patterns, and printed on separate lines (lines after - the first are prefix by ` or: ' instead of `Usage:'). */ - __const char *args_doc; - - /* If non-NULL, a string containing extra text to be printed before and - after the options in a long help message (separated by a vertical tab - `\v' character). */ - __const char *doc; - - /* A vector of argp_children structures, terminated by a member with a 0 - argp field, pointing to child argps should be parsed with this one. Any - conflicts are resolved in favor of this argp, or early argps in the - CHILDREN list. This field is useful if you use libraries that supply - their own argp structure, which you want to use in conjunction with your - own. */ - __const struct argp_child *children; - - /* If non-zero, this should be a function to filter the output of help - messages. KEY is either a key from an option, in which case TEXT is - that option's help text, or a special key from the ARGP_KEY_HELP_ - defines, below, describing which other help text TEXT is. The function - should return either TEXT, if it should be used as-is, a replacement - string, which should be malloced, and will be freed by argp, or NULL, - meaning `print nothing'. The value for TEXT is *after* any translation - has been done, so if any of the replacement text also needs translation, - that should be done by the filter function. INPUT is either the input - supplied to argp_parse, or NULL, if argp_help was called directly. */ - char *(*help_filter) (int __key, __const char *__text, void *__input); - - /* If non-zero the strings used in the argp library are translated using - the domain described by this string. Otherwise the currently installed - default domain is used. */ - const char *argp_domain; -}; - -/* Possible KEY arguments to a help filter function. */ -#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ -#define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ -#define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ -#define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; - TEXT is NULL for this key. */ -/* Explanatory note emitted when duplicate option arguments have been - suppressed. */ -#define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 -#define ARGP_KEY_HELP_ARGS_DOC 0x2000006 /* Argument doc string. */ - -/* When an argp has a non-zero CHILDREN field, it should point to a vector of - argp_child structures, each of which describes a subsidiary argp. */ -struct argp_child -{ - /* The child parser. */ - __const struct argp *argp; - - /* Flags for this child. */ - int flags; - - /* If non-zero, an optional header to be printed in help output before the - child options. As a side-effect, a non-zero value forces the child - options to be grouped together; to achieve this effect without actually - printing a header string, use a value of "". */ - __const char *header; - - /* Where to group the child options relative to the other (`consolidated') - options in the parent argp; the values are the same as the GROUP field - in argp_option structs, but all child-groupings follow parent options at - a particular group level. If both this field and HEADER are zero, then - they aren't grouped at all, but rather merged with the parent options - (merging the child's grouping levels with the parents). */ - int group; -}; - -/* Parsing state. This is provided to parsing functions called by argp, - which may examine and, as noted, modify fields. */ -struct argp_state -{ - /* The top level ARGP being parsed. */ - __const struct argp *root_argp; - - /* The argument vector being parsed. May be modified. */ - int argc; - char **argv; - - /* The index in ARGV of the next arg that to be parsed. May be modified. */ - int next; - - /* The flags supplied to argp_parse. May be modified. */ - unsigned flags; - - /* While calling a parsing function with a key of ARGP_KEY_ARG, this is the - number of the current arg, starting at zero, and incremented after each - such call returns. At all other times, this is the number of such - arguments that have been processed. */ - unsigned arg_num; - - /* If non-zero, the index in ARGV of the first argument following a special - `--' argument (which prevents anything following being interpreted as an - option). Only set once argument parsing has proceeded past this point. */ - int quoted; - - /* An arbitrary pointer passed in from the user. */ - void *input; - /* Values to pass to child parsers. This vector will be the same length as - the number of children for the current parser. */ - void **child_inputs; - - /* For the parser's use. Initialized to 0. */ - void *hook; - - /* The name used when printing messages. This is initialized to ARGV[0], - or PROGRAM_INVOCATION_NAME if that is unavailable. */ - char *name; - - /* Streams used when argp prints something. */ - FILE *err_stream; /* For errors; initialized to stderr. */ - FILE *out_stream; /* For information; initialized to stdout. */ - - void *pstate; /* Private, for use by argp. */ -}; - -/* Flags for argp_parse (note that the defaults are those that are - convenient for program command line parsing): */ - -/* Don't ignore the first element of ARGV. Normally (and always unless - ARGP_NO_ERRS is set) the first element of the argument vector is - skipped for option parsing purposes, as it corresponds to the program name - in a command line. */ -#define ARGP_PARSE_ARGV0 0x01 - -/* Don't print error messages for unknown options to stderr; unless this flag - is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program - name in the error messages. This flag implies ARGP_NO_EXIT (on the - assumption that silent exiting upon errors is bad behaviour). */ -#define ARGP_NO_ERRS 0x02 - -/* Don't parse any non-option args. Normally non-option args are parsed by - calling the parse functions with a key of ARGP_KEY_ARG, and the actual arg - as the value. Since it's impossible to know which parse function wants to - handle it, each one is called in turn, until one returns 0 or an error - other than ARGP_ERR_UNKNOWN; if an argument is handled by no one, the - argp_parse returns prematurely (but with a return value of 0). If all - args have been parsed without error, all parsing functions are called one - last time with a key of ARGP_KEY_END. This flag needn't normally be set, - as the normal behavior is to stop parsing as soon as some argument can't - be handled. */ -#define ARGP_NO_ARGS 0x04 - -/* Parse options and arguments in the same order they occur on the command - line -- normally they're rearranged so that all options come first. */ -#define ARGP_IN_ORDER 0x08 - -/* Don't provide the standard long option --help, which causes usage and - option help information to be output to stdout, and exit (0) called. */ -#define ARGP_NO_HELP 0x10 - -/* Don't exit on errors (they may still result in error messages). */ -#define ARGP_NO_EXIT 0x20 - -/* Use the gnu getopt `long-only' rules for parsing arguments. */ -#define ARGP_LONG_ONLY 0x40 - -/* Turns off any message-printing/exiting options. */ -#define ARGP_SILENT (ARGP_NO_EXIT | ARGP_NO_ERRS | ARGP_NO_HELP) - -/* Parse the options strings in ARGC & ARGV according to the options in ARGP. - FLAGS is one of the ARGP_ flags above. If ARG_INDEX is non-NULL, the - index in ARGV of the first unparsed option is returned in it. If an - unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser - routine returned a non-zero value, it is returned; otherwise 0 is - returned. This function may also call exit unless the ARGP_NO_HELP flag - is set. INPUT is a pointer to a value to be passed in to the parser. */ -extern error_t argp_parse (__const struct argp *__restrict argp, - int argc, char **__restrict argv, - unsigned flags, int *__restrict arg_index, - void *__restrict input) __THROW; -extern error_t __argp_parse (__const struct argp *__restrict argp, - int argc, char **__restrict argv, - unsigned flags, int *__restrict arg_index, - void *__restrict input) __THROW; - -/* Global variables. */ - -/* If defined or set by the user program to a non-zero value, then a default - option --version is added (unless the ARGP_NO_HELP flag is used), which - will print this string followed by a newline and exit (unless the - ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ -extern __const char *argp_program_version; - -/* If defined or set by the user program to a non-zero value, then a default - option --version is added (unless the ARGP_NO_HELP flag is used), which - calls this function with a stream to print the version to and a pointer to - the current parsing state, and then exits (unless the ARGP_NO_EXIT flag is - used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ -extern void (*argp_program_version_hook) (FILE *__restrict __stream, - struct argp_state *__restrict - __state); - -/* If defined or set by the user program, it should point to string that is - the bug-reporting address for the program. It will be printed by - argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various - standard help messages), embedded in a sentence that says something like - `Report bugs to ADDR.'. */ -extern __const char *argp_program_bug_address; - -/* The exit status that argp will use when exiting due to a parsing error. - If not defined or set by the user program, this defaults to EX_USAGE from - . */ -extern error_t argp_err_exit_status; - -/* Flags for argp_help. */ -#define ARGP_HELP_USAGE 0x01 /* a Usage: message. */ -#define ARGP_HELP_SHORT_USAGE 0x02 /* " but don't actually print options. */ -#define ARGP_HELP_SEE 0x04 /* a `Try ... for more help' message. */ -#define ARGP_HELP_LONG 0x08 /* a long help message. */ -#define ARGP_HELP_PRE_DOC 0x10 /* doc string preceding long help. */ -#define ARGP_HELP_POST_DOC 0x20 /* doc string following long help. */ -#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC) -#define ARGP_HELP_BUG_ADDR 0x40 /* bug report address */ -#define ARGP_HELP_LONG_ONLY 0x80 /* modify output appropriately to - reflect ARGP_LONG_ONLY mode. */ - -/* These ARGP_HELP flags are only understood by argp_state_help. */ -#define ARGP_HELP_EXIT_ERR 0x100 /* Call exit(1) instead of returning. */ -#define ARGP_HELP_EXIT_OK 0x200 /* Call exit(0) instead of returning. */ - -/* The standard thing to do after a program command line parsing error, if an - error message has already been printed. */ -#define ARGP_HELP_STD_ERR \ - (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) -/* The standard thing to do after a program command line parsing error, if no - more specific error message has been printed. */ -#define ARGP_HELP_STD_USAGE \ - (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) -/* The standard thing to do in response to a --help option. */ -#define ARGP_HELP_STD_HELP \ - (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \ - | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR) - -/* Output a usage message for ARGP to STREAM. FLAGS are from the set - ARGP_HELP_*. */ -extern void argp_help (__const struct argp *__restrict __argp, - FILE *__restrict __stream, - unsigned __flags, char *__restrict __name) __THROW; -extern void __argp_help (__const struct argp *__restrict __argp, - FILE *__restrict __stream, unsigned __flags, - char *__name) __THROW; - -/* The following routines are intended to be called from within an argp - parsing routine (thus taking an argp_state structure as the first - argument). They may or may not print an error message and exit, depending - on the flags in STATE -- in any case, the caller should be prepared for - them *not* to exit, and should return an appropiate error after calling - them. [argp_usage & argp_error should probably be called argp_state_..., - but they're used often enough that they should be short] */ - -/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are - from the set ARGP_HELP_*. */ -extern void argp_state_help (__const struct argp_state *__restrict __state, - FILE *__restrict __stream, - unsigned int __flags) __THROW; -extern void __argp_state_help (__const struct argp_state *__restrict __state, - FILE *__restrict __stream, - unsigned int __flags) __THROW; - -/* Possibly output the standard usage message for ARGP to stderr and exit. */ -extern void argp_usage (__const struct argp_state *__state) __THROW; -extern void __argp_usage (__const struct argp_state *__state) __THROW; - -/* If appropriate, print the printf string FMT and following args, preceded - by the program name and `:', to stderr, and followed by a `Try ... --help' - message, then exit (1). */ -extern void argp_error (__const struct argp_state *__restrict __state, - __const char *__restrict __fmt, ...) __THROW - PRINTF_STYLE(2,3); -extern void __argp_error (__const struct argp_state *__restrict __state, - __const char *__restrict __fmt, ...) __THROW - PRINTF_STYLE(2,3); - -/* Similar to the standard gnu error-reporting function error(), but will - respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print - to STATE->err_stream. This is useful for argument parsing code that is - shared between program startup (when exiting is desired) and runtime - option parsing (when typically an error code is returned instead). The - difference between this function and argp_error is that the latter is for - *parsing errors*, and the former is for other problems that occur during - parsing but don't reflect a (syntactic) problem with the input. */ -extern void argp_failure (__const struct argp_state *__restrict __state, - int __status, int __errnum, - __const char *__restrict __fmt, ...) __THROW - PRINTF_STYLE(4,5); -extern void __argp_failure (__const struct argp_state *__restrict __state, - int __status, int __errnum, - __const char *__restrict __fmt, ...) __THROW - PRINTF_STYLE(4,5); - -/* Returns true if the option OPT is a valid short option. */ -extern int _option_is_short (__const struct argp_option *__opt) __THROW; -extern int __option_is_short (__const struct argp_option *__opt) __THROW; - -/* Returns true if the option OPT is in fact the last (unused) entry in an - options array. */ -extern int _option_is_end (__const struct argp_option *__opt) __THROW; -extern int __option_is_end (__const struct argp_option *__opt) __THROW; - -/* Return the input field for ARGP in the parser corresponding to STATE; used - by the help routines. */ -extern void *_argp_input (__const struct argp *__restrict __argp, - __const struct argp_state *__restrict __state) - __THROW; -extern void *__argp_input (__const struct argp *__restrict __argp, - __const struct argp_state *__restrict __state) - __THROW; - -/* Used for extracting the program name from argv[0] */ -extern char *_argp_basename(char *name) __THROW; -extern char *__argp_basename(char *name) __THROW; - -/* Getting the program name given an argp state */ -extern char * -_argp_short_program_name(const struct argp_state *state) __THROW; -extern char * -__argp_short_program_name(const struct argp_state *state) __THROW; - - -#ifdef __USE_EXTERN_INLINES - -# if !_LIBC -# define __argp_usage argp_usage -# define __argp_state_help argp_state_help -# define __option_is_short _option_is_short -# define __option_is_end _option_is_end -# endif - -# ifndef ARGP_EI -# define ARGP_EI extern __inline__ -# endif - -ARGP_EI void -__argp_usage (__const struct argp_state *__state) -{ - __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); -} - -ARGP_EI int -__option_is_short (__const struct argp_option *__opt) -{ - if (__opt->flags & OPTION_DOC) - return 0; - else - { - int __key = __opt->key; - return __key > 0 && isprint (__key); - } -} - -ARGP_EI int -__option_is_end (__const struct argp_option *__opt) -{ - return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; -} - -# if !_LIBC -# undef __argp_usage -# undef __argp_state_help -# undef __option_is_short -# undef __option_is_end -# endif -#endif /* Use extern inlines. */ - -#ifdef __cplusplus -} -#endif - -#endif /* argp.h */ diff --git a/argp-standalone/autogen.sh b/argp-standalone/autogen.sh deleted file mode 100755 index 8337353b5..000000000 --- a/argp-standalone/autogen.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -aclocal -I . -autoheader -autoconf -automake --add-missing --copy --foreign diff --git a/argp-standalone/configure.ac b/argp-standalone/configure.ac deleted file mode 100644 index 2ecd2a801..000000000 --- a/argp-standalone/configure.ac +++ /dev/null @@ -1,102 +0,0 @@ -dnl Process this file with autoconf to produce a configure script. - -dnl This configure.ac is only for building a standalone argp library. -AC_INIT([argp], [standalone-1.3]) -AC_PREREQ(2.54) -AC_CONFIG_SRCDIR([argp-ba.c]) -# Needed to stop autoconf from looking for files in parent directories. -AC_CONFIG_AUX_DIR([.]) - -AM_INIT_AUTOMAKE -AC_CONFIG_HEADERS(config.h) - -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) - -# GNU libc defaults to supplying the ISO C library functions only. The -# _GNU_SOURCE define enables these extensions, in particular we want -# errno.h to declare program_invocation_name. Enable it on all -# systems; no problems have been reported with it so far. -AC_GNU_SOURCE - -# Checks for programs. -AC_PROG_CC -AC_PROG_MAKE_SET -AC_PROG_RANLIB -AC_PROG_CC - -if test "x$am_cv_prog_cc_stdc" = xno ; then - AC_ERROR([the C compiler doesn't handle ANSI-C]) -fi - -# Checks for libraries. - -# Checks for header files. -AC_HEADER_STDC -AC_CHECK_HEADERS(limits.h malloc.h unistd.h sysexits.h stdarg.h) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_C_INLINE -AC_TYPE_SIZE_T - -LSH_GCC_ATTRIBUTES - -# Checks for library functions. -AC_FUNC_ALLOCA -AC_FUNC_VPRINTF -AC_CHECK_FUNCS(strerror sleep getpid snprintf) - -AC_REPLACE_FUNCS(mempcpy strndup strchrnul strcasecmp vsnprintf) - -dnl ARGP_CHECK_FUNC(includes, function-call [, if-found [, if-not-found]]) -AC_DEFUN([ARGP_CHECK_FUNC], - [AS_VAR_PUSHDEF([ac_func], m4_substr([$2], 0, m4_index([$2], [(]))) - AS_VAR_PUSHDEF([ac_var], [ac_cv_func_call_]ac_func) - AH_TEMPLATE(AS_TR_CPP(HAVE_[]ac_func), - [Define to 1 if you have the `]ac_func[' function.]) - AC_CACHE_CHECK([for $2], ac_var, - [AC_TRY_LINK([$1], [$2], - [AS_VAR_SET(ac_var, yes)], - [AS_VAR_SET(ac_var, no)])]) - if test AS_VAR_GET(ac_var) = yes ; then - ifelse([$3],, - [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]ac_func))], - [$3 -]) - else - ifelse([$4],, true, [$4]) - fi - AS_VAR_POPDEF([ac_var]) - AS_VAR_POPDEF([ac_func]) - ]) - -# At least on freebsd, putc_unlocked is a macro, so the standard -# AC_CHECK_FUNCS doesn't work well. -ARGP_CHECK_FUNC([#include ], [putc_unlocked('x', stdout)]) - -AC_CHECK_FUNCS(flockfile) -AC_CHECK_FUNCS(fputs_unlocked fwrite_unlocked) - -# Used only by argp-test.c, so don't use AC_REPLACE_FUNCS. -AC_CHECK_FUNCS(strdup asprintf) - -AC_CHECK_DECLS([program_invocation_name, program_invocation_short_name], - [], [], [[#include ]]) - -# Set these flags *last*, or else the test programs won't compile -if test x$GCC = xyes ; then - # Using -ggdb3 makes (some versions of) Redhat's gcc-2.96 dump core - if "$CC" --version | grep '^2\.96$' 1>/dev/null 2>&1; then - true - else - CFLAGS="$CFLAGS -ggdb3" - fi - CFLAGS="$CFLAGS -Wall -W \ - -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes \ - -Waggregate-return \ - -Wpointer-arith -Wbad-function-cast -Wnested-externs" -fi - -CPPFLAGS="$CPPFLAGS -I$srcdir" - -AC_OUTPUT(Makefile) diff --git a/argp-standalone/mempcpy.c b/argp-standalone/mempcpy.c deleted file mode 100644 index 21d8bd2ed..000000000 --- a/argp-standalone/mempcpy.c +++ /dev/null @@ -1,21 +0,0 @@ -/* strndup.c - * - */ - -/* Written by Niels Möller - * - * This file is hereby placed in the public domain. - */ - -#include - -void * -mempcpy (void *, const void *, size_t) ; - -void * -mempcpy (void *to, const void *from, size_t size) -{ - memcpy(to, from, size); - return (char *) to + size; -} - diff --git a/argp-standalone/strcasecmp.c b/argp-standalone/strcasecmp.c deleted file mode 100644 index 9c1637232..000000000 --- a/argp-standalone/strcasecmp.c +++ /dev/null @@ -1,29 +0,0 @@ -/* strcasecmp.c - * - */ - -/* Written by Niels Möller - * - * This file is hereby placed in the public domain. - */ - -#include -int strcasecmp(const char *, const char *); - -int strcasecmp(const char *s1, const char *s2) -{ - unsigned i; - - for (i = 0; s1[i] && s2[i]; i++) - { - unsigned char c1 = tolower( (unsigned char) s1[i]); - unsigned char c2 = tolower( (unsigned char) s2[i]); - - if (c1 < c2) - return -1; - else if (c1 > c2) - return 1; - } - - return !s2[i] - !s1[i]; -} diff --git a/argp-standalone/strchrnul.c b/argp-standalone/strchrnul.c deleted file mode 100644 index ee4145e4e..000000000 --- a/argp-standalone/strchrnul.c +++ /dev/null @@ -1,23 +0,0 @@ -/* strchrnul.c - * - */ - -/* Written by Niels Möller - * - * This file is hereby placed in the public domain. - */ - -/* FIXME: What is this function supposed to do? My guess is that it is - * like strchr, but returns a pointer to the NUL character, not a NULL - * pointer, if the character isn't found. */ - -char *strchrnul(const char *, int ); - -char *strchrnul(const char *s, int c) -{ - const char *p = s; - while (*p && (*p != c)) - p++; - - return (char *) p; -} diff --git a/argp-standalone/strndup.c b/argp-standalone/strndup.c deleted file mode 100644 index 4147b7a20..000000000 --- a/argp-standalone/strndup.c +++ /dev/null @@ -1,34 +0,0 @@ -/* strndup.c - * - */ - -/* Written by Niels Möller - * - * This file is hereby placed in the public domain. - */ - -#include -#include - -char * -strndup (const char *, size_t); - -char * -strndup (const char *s, size_t size) -{ - char *r; - char *end = memchr(s, 0, size); - - if (end) - /* Length + 1 */ - size = end - s + 1; - - r = malloc(size); - - if (size) - { - memcpy(r, s, size-1); - r[size-1] = '\0'; - } - return r; -} diff --git a/argp-standalone/vsnprintf.c b/argp-standalone/vsnprintf.c deleted file mode 100644 index 33c9a5d00..000000000 --- a/argp-standalone/vsnprintf.c +++ /dev/null @@ -1,839 +0,0 @@ -/* Copied from http://www.fiction.net/blong/programs/snprintf.c */ - -/* - * Copyright Patrick Powell 1995 - * This code is based on code written by Patrick Powell (papowell@astart.com) - * It may be used for any purpose as long as this notice remains intact - * on all source code distributions - */ - -/************************************************************** - * Original: - * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (dopr) included. - * Sigh. This sort of thing is always nasty do deal with. Note that - * the version here does not include floating point... - * - * snprintf() is used instead of sprintf() as it does limit checks - * for string length. This covers a nasty loophole. - * - * The other functions are there to prevent NULL pointers from - * causing nast effects. - * - * More Recently: - * Brandon Long 9/15/96 for mutt 0.43 - * This was ugly. It is still ugly. I opted out of floating point - * numbers, but the formatter understands just about everything - * from the normal C string format, at least as far as I can tell from - * the Solaris 2.5 printf(3S) man page. - * - * Brandon Long 10/22/97 for mutt 0.87.1 - * Ok, added some minimal floating point support, which means this - * probably requires libm on most operating systems. Don't yet - * support the exponent (e,E) and sigfig (g,G). Also, fmtint() - * was pretty badly broken, it just wasn't being exercised in ways - * which showed it, so that's been fixed. Also, formated the code - * to mutt conventions, and removed dead code left over from the - * original. Also, there is now a builtin-test, just compile with: - * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm - * and run snprintf for results. - * - * Thomas Roessler 01/27/98 for mutt 0.89i - * The PGP code was using unsigned hexadecimal formats. - * Unfortunately, unsigned formats simply didn't work. - * - * Michael Elkins 03/05/98 for mutt 0.90.8 - * The original code assumed that both snprintf() and vsnprintf() were - * missing. Some systems only have snprintf() but not vsnprintf(), so - * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. - * - * Andrew Tridgell (tridge@samba.org) Oct 1998 - * fixed handling of %.0f - * added test for HAVE_LONG_DOUBLE - * - * Russ Allbery 2000-08-26 - * fixed return value to comply with C99 - * fixed handling of snprintf(NULL, ...) - * - * Niels Möller 2004-03-05 - * fixed calls to isdigit to use unsigned char. - * fixed calls to va_arg; short arguments are always passed as int. - * - **************************************************************/ - -#if HAVE_CONFIG_H -# include "config.h" -#endif - -#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) - -#include -#include -#include - -/* Define this as a fall through, HAVE_STDARG_H is probably already set */ - -#define HAVE_VARARGS_H - - -/* varargs declarations: */ - -#if defined(HAVE_STDARG_H) -# include -# define HAVE_STDARGS /* let's hope that works everywhere (mj) */ -# define VA_LOCAL_DECL va_list ap -# define VA_START(f) va_start(ap, f) -# define VA_SHIFT(v,t) ; /* no-op for ANSI */ -# define VA_END va_end(ap) -#else -# if defined(HAVE_VARARGS_H) -# include -# undef HAVE_STDARGS -# define VA_LOCAL_DECL va_list ap -# define VA_START(f) va_start(ap) /* f is ignored! */ -# define VA_SHIFT(v,t) v = va_arg(ap,t) -# define VA_END va_end(ap) -# else -/*XX ** NO VARARGS ** XX*/ -# endif -#endif - -#ifdef HAVE_LONG_DOUBLE -#define LDOUBLE long double -#else -#define LDOUBLE double -#endif - -int snprintf (char *str, size_t count, const char *fmt, ...); -int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); - -static int dopr (char *buffer, size_t maxlen, const char *format, - va_list args); -static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max); -static int fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags); -static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, - LDOUBLE fvalue, int min, int max, int flags); -static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); - -/* - * dopr(): poor man's version of doprintf - */ - -/* format read states */ -#define DP_S_DEFAULT 0 -#define DP_S_FLAGS 1 -#define DP_S_MIN 2 -#define DP_S_DOT 3 -#define DP_S_MAX 4 -#define DP_S_MOD 5 -#define DP_S_CONV 6 -#define DP_S_DONE 7 - -/* format flags - Bits */ -#define DP_F_MINUS (1 << 0) -#define DP_F_PLUS (1 << 1) -#define DP_F_SPACE (1 << 2) -#define DP_F_NUM (1 << 3) -#define DP_F_ZERO (1 << 4) -#define DP_F_UP (1 << 5) -#define DP_F_UNSIGNED (1 << 6) - -/* Conversion Flags */ -#define DP_C_SHORT 1 -#define DP_C_LONG 2 -#define DP_C_LDOUBLE 3 - -#define char_to_int(p) (p - '0') -#define MAX(p,q) ((p >= q) ? p : q) -#define MIN(p,q) ((p <= q) ? p : q) - -static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) -{ - unsigned char ch; - long value; - LDOUBLE fvalue; - char *strvalue; - int min; - int max; - int state; - int flags; - int cflags; - int total; - size_t currlen; - - state = DP_S_DEFAULT; - currlen = flags = cflags = min = 0; - max = -1; - ch = *format++; - total = 0; - - while (state != DP_S_DONE) - { - if (ch == '\0') - state = DP_S_DONE; - - switch(state) - { - case DP_S_DEFAULT: - if (ch == '%') - state = DP_S_FLAGS; - else - total += dopr_outch (buffer, &currlen, maxlen, ch); - ch = *format++; - break; - case DP_S_FLAGS: - switch (ch) - { - case '-': - flags |= DP_F_MINUS; - ch = *format++; - break; - case '+': - flags |= DP_F_PLUS; - ch = *format++; - break; - case ' ': - flags |= DP_F_SPACE; - ch = *format++; - break; - case '#': - flags |= DP_F_NUM; - ch = *format++; - break; - case '0': - flags |= DP_F_ZERO; - ch = *format++; - break; - default: - state = DP_S_MIN; - break; - } - break; - case DP_S_MIN: - if (isdigit(ch)) - { - min = 10*min + char_to_int (ch); - ch = *format++; - } - else if (ch == '*') - { - min = va_arg (args, int); - ch = *format++; - state = DP_S_DOT; - } - else - state = DP_S_DOT; - break; - case DP_S_DOT: - if (ch == '.') - { - state = DP_S_MAX; - ch = *format++; - } - else - state = DP_S_MOD; - break; - case DP_S_MAX: - if (isdigit(ch)) - { - if (max < 0) - max = 0; - max = 10*max + char_to_int (ch); - ch = *format++; - } - else if (ch == '*') - { - max = va_arg (args, int); - ch = *format++; - state = DP_S_MOD; - } - else - state = DP_S_MOD; - break; - case DP_S_MOD: - /* Currently, we don't support Long Long, bummer */ - switch (ch) - { - case 'h': - cflags = DP_C_SHORT; - ch = *format++; - break; - case 'l': - cflags = DP_C_LONG; - ch = *format++; - break; - case 'L': - cflags = DP_C_LDOUBLE; - ch = *format++; - break; - default: - break; - } - state = DP_S_CONV; - break; - case DP_S_CONV: - switch (ch) - { - case 'd': - case 'i': - if (cflags == DP_C_SHORT) - value = (short) va_arg (args, int); - else if (cflags == DP_C_LONG) - value = va_arg (args, long int); - else - value = va_arg (args, int); - total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); - break; - case 'o': - flags |= DP_F_UNSIGNED; - if (cflags == DP_C_SHORT) - value = (unsigned short) va_arg (args, unsigned); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); - break; - case 'u': - flags |= DP_F_UNSIGNED; - if (cflags == DP_C_SHORT) - value = (unsigned short) va_arg (args, unsigned); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); - break; - case 'X': - flags |= DP_F_UP; - case 'x': - flags |= DP_F_UNSIGNED; - if (cflags == DP_C_SHORT) - value = (unsigned short) va_arg (args, unsigned); - else if (cflags == DP_C_LONG) - value = va_arg (args, unsigned long int); - else - value = va_arg (args, unsigned int); - total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); - break; - case 'f': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, LDOUBLE); - else - fvalue = va_arg (args, double); - /* um, floating point? */ - total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); - break; - case 'E': - flags |= DP_F_UP; - case 'e': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, LDOUBLE); - else - fvalue = va_arg (args, double); - break; - case 'G': - flags |= DP_F_UP; - case 'g': - if (cflags == DP_C_LDOUBLE) - fvalue = va_arg (args, LDOUBLE); - else - fvalue = va_arg (args, double); - break; - case 'c': - total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); - break; - case 's': - strvalue = va_arg (args, char *); - total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); - break; - case 'p': - strvalue = va_arg (args, void *); - total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, - max, flags); - break; - case 'n': - if (cflags == DP_C_SHORT) - { - short int *num; - num = va_arg (args, short int *); - *num = currlen; - } - else if (cflags == DP_C_LONG) - { - long int *num; - num = va_arg (args, long int *); - *num = currlen; - } - else - { - int *num; - num = va_arg (args, int *); - *num = currlen; - } - break; - case '%': - total += dopr_outch (buffer, &currlen, maxlen, ch); - break; - case 'w': - /* not supported yet, treat as next char */ - ch = *format++; - break; - default: - /* Unknown, skip */ - break; - } - ch = *format++; - state = DP_S_DEFAULT; - flags = cflags = min = 0; - max = -1; - break; - case DP_S_DONE: - break; - default: - /* hmm? */ - break; /* some picky compilers need this */ - } - } - if (buffer != NULL) - { - if (currlen < maxlen - 1) - buffer[currlen] = '\0'; - else - buffer[maxlen - 1] = '\0'; - } - return total; -} - -static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max) -{ - int padlen, strln; /* amount to pad */ - int cnt = 0; - int total = 0; - - if (value == 0) - { - value = ""; - } - - for (strln = 0; value[strln]; ++strln); /* strlen */ - if (max >= 0 && max < strln) - strln = max; - padlen = min - strln; - if (padlen < 0) - padlen = 0; - if (flags & DP_F_MINUS) - padlen = -padlen; /* Left Justify */ - - while (padlen > 0) - { - total += dopr_outch (buffer, currlen, maxlen, ' '); - --padlen; - } - while (*value && ((max < 0) || (cnt < max))) - { - total += dopr_outch (buffer, currlen, maxlen, *value++); - ++cnt; - } - while (padlen < 0) - { - total += dopr_outch (buffer, currlen, maxlen, ' '); - ++padlen; - } - return total; -} - -/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ - -static int fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags) -{ - int signvalue = 0; - unsigned long uvalue; - char convert[20]; - int place = 0; - int spadlen = 0; /* amount to space pad */ - int zpadlen = 0; /* amount to zero pad */ - int caps = 0; - int total = 0; - - if (max < 0) - max = 0; - - uvalue = value; - - if(!(flags & DP_F_UNSIGNED)) - { - if( value < 0 ) { - signvalue = '-'; - uvalue = -value; - } - else - if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else - if (flags & DP_F_SPACE) - signvalue = ' '; - } - - if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ - - do { - convert[place++] = - (caps? "0123456789ABCDEF":"0123456789abcdef") - [uvalue % (unsigned)base ]; - uvalue = (uvalue / (unsigned)base ); - } while(uvalue && (place < 20)); - if (place == 20) place--; - convert[place] = 0; - - zpadlen = max - place; - spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); - if (zpadlen < 0) zpadlen = 0; - if (spadlen < 0) spadlen = 0; - if (flags & DP_F_ZERO) - { - zpadlen = MAX(zpadlen, spadlen); - spadlen = 0; - } - if (flags & DP_F_MINUS) - spadlen = -spadlen; /* Left Justifty */ - -#ifdef DEBUG_SNPRINTF - dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", - zpadlen, spadlen, min, max, place)); -#endif - - /* Spaces */ - while (spadlen > 0) - { - total += dopr_outch (buffer, currlen, maxlen, ' '); - --spadlen; - } - - /* Sign */ - if (signvalue) - total += dopr_outch (buffer, currlen, maxlen, signvalue); - - /* Zeros */ - if (zpadlen > 0) - { - while (zpadlen > 0) - { - total += dopr_outch (buffer, currlen, maxlen, '0'); - --zpadlen; - } - } - - /* Digits */ - while (place > 0) - total += dopr_outch (buffer, currlen, maxlen, convert[--place]); - - /* Left Justified spaces */ - while (spadlen < 0) { - total += dopr_outch (buffer, currlen, maxlen, ' '); - ++spadlen; - } - - return total; -} - -static LDOUBLE abs_val (LDOUBLE value) -{ - LDOUBLE result = value; - - if (value < 0) - result = -value; - - return result; -} - -static LDOUBLE pow10_argp (int exp) -{ - LDOUBLE result = 1; - - while (exp) - { - result *= 10; - exp--; - } - - return result; -} - -static long round_argp (LDOUBLE value) -{ - long intpart; - - intpart = value; - value = value - intpart; - if (value >= 0.5) - intpart++; - - return intpart; -} - -static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, - LDOUBLE fvalue, int min, int max, int flags) -{ - int signvalue = 0; - LDOUBLE ufvalue; - char iconvert[20]; - char fconvert[20]; - int iplace = 0; - int fplace = 0; - int padlen = 0; /* amount to pad */ - int zpadlen = 0; - int caps = 0; - int total = 0; - long intpart; - long fracpart; - - /* - * AIX manpage says the default is 0, but Solaris says the default - * is 6, and sprintf on AIX defaults to 6 - */ - if (max < 0) - max = 6; - - ufvalue = abs_val (fvalue); - - if (fvalue < 0) - signvalue = '-'; - else - if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else - if (flags & DP_F_SPACE) - signvalue = ' '; - -#if 0 - if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ -#endif - - intpart = ufvalue; - - /* - * Sorry, we only support 9 digits past the decimal because of our - * conversion method - */ - if (max > 9) - max = 9; - - /* We "cheat" by converting the fractional part to integer by - * multiplying by a factor of 10 - */ - fracpart = round_argp ((pow10_argp (max)) * (ufvalue - intpart)); - - if (fracpart >= pow10_argp (max)) - { - intpart++; - fracpart -= pow10_argp (max); - } - -#ifdef DEBUG_SNPRINTF - dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); -#endif - - /* Convert integer part */ - do { - iconvert[iplace++] = - (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; - intpart = (intpart / 10); - } while(intpart && (iplace < 20)); - if (iplace == 20) iplace--; - iconvert[iplace] = 0; - - /* Convert fractional part */ - do { - fconvert[fplace++] = - (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; - fracpart = (fracpart / 10); - } while(fracpart && (fplace < 20)); - if (fplace == 20) fplace--; - fconvert[fplace] = 0; - - /* -1 for decimal point, another -1 if we are printing a sign */ - padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); - zpadlen = max - fplace; - if (zpadlen < 0) - zpadlen = 0; - if (padlen < 0) - padlen = 0; - if (flags & DP_F_MINUS) - padlen = -padlen; /* Left Justifty */ - - if ((flags & DP_F_ZERO) && (padlen > 0)) - { - if (signvalue) - { - total += dopr_outch (buffer, currlen, maxlen, signvalue); - --padlen; - signvalue = 0; - } - while (padlen > 0) - { - total += dopr_outch (buffer, currlen, maxlen, '0'); - --padlen; - } - } - while (padlen > 0) - { - total += dopr_outch (buffer, currlen, maxlen, ' '); - --padlen; - } - if (signvalue) - total += dopr_outch (buffer, currlen, maxlen, signvalue); - - while (iplace > 0) - total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); - - /* - * Decimal point. This should probably use locale to find the correct - * char to print out. - */ - if (max > 0) - { - total += dopr_outch (buffer, currlen, maxlen, '.'); - - while (fplace > 0) - total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); - } - - while (zpadlen > 0) - { - total += dopr_outch (buffer, currlen, maxlen, '0'); - --zpadlen; - } - - while (padlen < 0) - { - total += dopr_outch (buffer, currlen, maxlen, ' '); - ++padlen; - } - - return total; -} - -static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) -{ - if (*currlen + 1 < maxlen) - buffer[(*currlen)++] = c; - return 1; -} - -#ifndef HAVE_VSNPRINTF -int vsnprintf (char *str, size_t count, const char *fmt, va_list args) -{ - if (str != NULL) - str[0] = 0; - return dopr(str, count, fmt, args); -} -#endif /* !HAVE_VSNPRINTF */ - -#ifndef HAVE_SNPRINTF -/* VARARGS3 */ -#ifdef HAVE_STDARGS -int snprintf (char *str,size_t count,const char *fmt,...) -#else -int snprintf (va_alist) va_dcl -#endif -{ -#ifndef HAVE_STDARGS - char *str; - size_t count; - char *fmt; -#endif - VA_LOCAL_DECL; - int total; - - VA_START (fmt); - VA_SHIFT (str, char *); - VA_SHIFT (count, size_t ); - VA_SHIFT (fmt, char *); - total = vsnprintf(str, count, fmt, ap); - VA_END; - return total; -} -#endif /* !HAVE_SNPRINTF */ - -#ifdef TEST_SNPRINTF -#ifndef LONG_STRING -#define LONG_STRING 1024 -#endif -int main (void) -{ - char buf1[LONG_STRING]; - char buf2[LONG_STRING]; - char *fp_fmt[] = { - "%-1.5f", - "%1.5f", - "%123.9f", - "%10.5f", - "% 10.5f", - "%+22.9f", - "%+4.9f", - "%01.3f", - "%4f", - "%3.1f", - "%3.2f", - "%.0f", - "%.1f", - NULL - }; - double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, - 0.9996, 1.996, 4.136, 0}; - char *int_fmt[] = { - "%-1.5d", - "%1.5d", - "%123.9d", - "%5.5d", - "%10.5d", - "% 10.5d", - "%+22.33d", - "%01.3d", - "%4d", - NULL - }; - long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; - int x, y; - int fail = 0; - int num = 0; - - printf ("Testing snprintf format codes against system sprintf...\n"); - - for (x = 0; fp_fmt[x] != NULL ; x++) - for (y = 0; fp_nums[y] != 0 ; y++) - { - snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); - sprintf (buf2, fp_fmt[x], fp_nums[y]); - if (strcmp (buf1, buf2)) - { - printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", - fp_fmt[x], buf1, buf2); - fail++; - } - num++; - } - - for (x = 0; int_fmt[x] != NULL ; x++) - for (y = 0; int_nums[y] != 0 ; y++) - { - snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); - sprintf (buf2, int_fmt[x], int_nums[y]); - if (strcmp (buf1, buf2)) - { - printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", - int_fmt[x], buf1, buf2); - fail++; - } - num++; - } - printf ("%d tests failed out of %d.\n", fail, num); -} -#endif /* SNPRINTF_TEST */ - -#endif /* !HAVE_SNPRINTF */ diff --git a/autogen.sh b/autogen.sh index 8642deba2..eb869d52e 100755 --- a/autogen.sh +++ b/autogen.sh @@ -64,7 +64,7 @@ fi if [ "x${PYTHONBIN}" = "x" ]; then PYTHONBIN=python fi -env ${PYTHONBIN} -V > /dev/null 2>&1 +env ${PYTHONBIN} -V > /dev/null 2>&1 if [ $? -ne 0 ]; then MISSING="$MISSING python" fi @@ -108,7 +108,7 @@ $AUTOMAKE --add-missing --copy --foreign # Run autogen in the argp-standalone sub-directory echo "Running autogen.sh in argp-standalone ..." -( cd argp-standalone;./autogen.sh ) +( cd contrib/argp-standalone;./autogen.sh ) # Instruct user on next steps echo diff --git a/configure.ac b/configure.ac index 3a3d8712b..696ebfa36 100644 --- a/configure.ac +++ b/configure.ac @@ -193,6 +193,7 @@ AC_PROG_CC AC_DISABLE_STATIC AC_PROG_LIBTOOL + # Initialize CFLAGS before usage AC_ARG_ENABLE([debug], AC_HELP_STRING([--enable-debug], @@ -641,13 +642,13 @@ AC_CHECK_FUNC([clock_gettime], [has_monotonic_clock=yes], AC_CHECK_LIB([rt], [cl dnl Check for argp AC_CHECK_HEADER([argp.h], AC_DEFINE(HAVE_ARGP, 1, [have argp])) -AC_CONFIG_SUBDIRS(argp-standalone) +AC_CONFIG_SUBDIRS(contrib/argp-standalone) BUILD_ARGP_STANDALONE=no if test "x${ac_cv_header_argp_h}" = "xno"; then BUILD_ARGP_STANDALONE=yes - ARGP_STANDALONE_CPPFLAGS='-I${top_srcdir}/argp-standalone' - ARGP_STANDALONE_LDADD='${top_builddir}/argp-standalone/libargp.a' + ARGP_STANDALONE_CPPFLAGS='-I${top_srcdir}/contrib/argp-standalone' + ARGP_STANDALONE_LDADD='${top_builddir}/contrib/argp-standalone/libargp.a' fi AC_SUBST(ARGP_STANDALONE_CPPFLAGS) diff --git a/contrib/argp-standalone/Makefile.am b/contrib/argp-standalone/Makefile.am new file mode 100644 index 000000000..4775d4876 --- /dev/null +++ b/contrib/argp-standalone/Makefile.am @@ -0,0 +1,38 @@ +# From glibc + +# Copyright (C) 1997, 2003, 2004 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If +# not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +AUTOMAKE_OPTIONS = foreign +SUBDIRS = . + +LIBOBJS = @LIBOBJS@ + +noinst_LIBRARIES = libargp.a + +noinst_HEADERS = argp.h argp-fmtstream.h argp-namefrob.h + +EXTRA_DIST = mempcpy.c strchrnul.c strndup.c strcasecmp.c vsnprintf.c autogen.sh + +# Leaves out argp-fs-xinl.c and argp-xinl.c +libargp_a_SOURCES = argp-ba.c argp-eexst.c argp-fmtstream.c \ + argp-help.c argp-parse.c argp-pv.c \ + argp-pvh.c + +libargp_a_LIBADD = $(LIBOBJS) + + diff --git a/contrib/argp-standalone/acinclude.m4 b/contrib/argp-standalone/acinclude.m4 new file mode 100644 index 000000000..fb61e957d --- /dev/null +++ b/contrib/argp-standalone/acinclude.m4 @@ -0,0 +1,1084 @@ +dnl Try to detect the type of the third arg to getsockname() et al +AC_DEFUN([LSH_TYPE_SOCKLEN_T], +[AH_TEMPLATE([socklen_t], [Length type used by getsockopt]) +AC_CACHE_CHECK([for socklen_t in sys/socket.h], ac_cv_type_socklen_t, +[AC_EGREP_HEADER(socklen_t, sys/socket.h, + [ac_cv_type_socklen_t=yes], [ac_cv_type_socklen_t=no])]) +if test $ac_cv_type_socklen_t = no; then + AC_MSG_CHECKING(for AIX) + AC_EGREP_CPP(yes, [ +#ifdef _AIX + yes +#endif +],[ +AC_MSG_RESULT(yes) +AC_DEFINE(socklen_t, size_t) +],[ +AC_MSG_RESULT(no) +AC_DEFINE(socklen_t, int) +]) +fi +]) + +dnl Choose cc flags for compiling position independent code +AC_DEFUN([LSH_CCPIC], +[AC_MSG_CHECKING(CCPIC) +AC_CACHE_VAL(lsh_cv_sys_ccpic,[ + if test -z "$CCPIC" ; then + if test "$GCC" = yes ; then + case `uname -sr` in + BSD/OS*) + case `uname -r` in + 4.*) CCPIC="-fPIC";; + *) CCPIC="";; + esac + ;; + Darwin*) + CCPIC="-fPIC" + ;; + SunOS\ 5.*) + # Could also use -fPIC, if there are a large number of symbol reference + CCPIC="-fPIC" + ;; + CYGWIN*) + CCPIC="" + ;; + *) + CCPIC="-fpic" + ;; + esac + else + case `uname -sr` in + Darwin*) + CCPIC="-fPIC" + ;; + IRIX*) + CCPIC="-share" + ;; + hp*|HP*) CCPIC="+z"; ;; + FreeBSD*) CCPIC="-fpic";; + SCO_SV*) CCPIC="-KPIC -dy -Bdynamic";; + UnixWare*|OpenUNIX*) CCPIC="-KPIC -dy -Bdynamic";; + Solaris*) CCPIC="-KPIC -Bdynamic";; + Windows_NT*) CCPIC="-shared" ;; + esac + fi + fi + OLD_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $CCPIC" + AC_TRY_COMPILE([], [exit(0);], + lsh_cv_sys_ccpic="$CCPIC", lsh_cv_sys_ccpic='') + CFLAGS="$OLD_CFLAGS" +]) +CCPIC="$lsh_cv_sys_ccpic" +AC_MSG_RESULT($CCPIC) +AC_SUBST([CCPIC])]) + +dnl LSH_PATH_ADD(path-id, directory) +AC_DEFUN([LSH_PATH_ADD], +[AC_MSG_CHECKING($2) +ac_exists=no +if test -d "$2/." ; then + ac_real_dir=`cd $2 && pwd` + if test -n "$ac_real_dir" ; then + ac_exists=yes + for old in $1_REAL_DIRS ; do + ac_found=no + if test x$ac_real_dir = x$old ; then + ac_found=yes; + break; + fi + done + if test $ac_found = yes ; then + AC_MSG_RESULT(already added) + else + AC_MSG_RESULT(added) + # LDFLAGS="$LDFLAGS -L $2" + $1_REAL_DIRS="$ac_real_dir [$]$1_REAL_DIRS" + $1_DIRS="$2 [$]$1_DIRS" + fi + fi +fi +if test $ac_exists = no ; then + AC_MSG_RESULT(not found) +fi +]) + +dnl LSH_RPATH_ADD(dir) +AC_DEFUN([LSH_RPATH_ADD], [LSH_PATH_ADD(RPATH_CANDIDATE, $1)]) + +dnl LSH_RPATH_INIT(candidates) +AC_DEFUN([LSH_RPATH_INIT], +[AC_MSG_CHECKING([for -R flag]) +RPATHFLAG='' +case `uname -sr` in + OSF1\ V4.*) + RPATHFLAG="-rpath " + ;; + IRIX\ 6.*) + RPATHFLAG="-rpath " + ;; + IRIX\ 5.*) + RPATHFLAG="-rpath " + ;; + SunOS\ 5.*) + if test "$TCC" = "yes"; then + # tcc doesn't know about -R + RPATHFLAG="-Wl,-R," + else + RPATHFLAG=-R + fi + ;; + Linux\ 2.*) + RPATHFLAG="-Wl,-rpath," + ;; + *) + : + ;; +esac + +if test x$RPATHFLAG = x ; then + AC_MSG_RESULT(none) +else + AC_MSG_RESULT([using $RPATHFLAG]) +fi + +RPATH_CANDIDATE_REAL_DIRS='' +RPATH_CANDIDATE_DIRS='' + +AC_MSG_RESULT([Searching for libraries]) + +for d in $1 ; do + LSH_RPATH_ADD($d) +done +]) + +dnl Try to execute a main program, and if it fails, try adding some +dnl -R flag. +dnl LSH_RPATH_FIX +AC_DEFUN([LSH_RPATH_FIX], +[if test $cross_compiling = no -a "x$RPATHFLAG" != x ; then + ac_success=no + AC_TRY_RUN([int main(int argc, char **argv) { return 0; }], + ac_success=yes, ac_success=no, :) + + if test $ac_success = no ; then + AC_MSG_CHECKING([Running simple test program failed. Trying -R flags]) +dnl echo RPATH_CANDIDATE_DIRS = $RPATH_CANDIDATE_DIRS + ac_remaining_dirs='' + ac_rpath_save_LDFLAGS="$LDFLAGS" + for d in $RPATH_CANDIDATE_DIRS ; do + if test $ac_success = yes ; then + ac_remaining_dirs="$ac_remaining_dirs $d" + else + LDFLAGS="$RPATHFLAG$d $LDFLAGS" +dnl echo LDFLAGS = $LDFLAGS + AC_TRY_RUN([int main(int argc, char **argv) { return 0; }], + [ac_success=yes + ac_rpath_save_LDFLAGS="$LDFLAGS" + AC_MSG_RESULT([adding $RPATHFLAG$d]) + ], + [ac_remaining_dirs="$ac_remaining_dirs $d"], :) + LDFLAGS="$ac_rpath_save_LDFLAGS" + fi + done + RPATH_CANDIDATE_DIRS=$ac_remaining_dirs + fi + if test $ac_success = no ; then + AC_MSG_RESULT(failed) + fi +fi +]) + +dnl Like AC_CHECK_LIB, but uses $KRB_LIBS rather than $LIBS. +dnl LSH_CHECK_KRB_LIB(LIBRARY, FUNCTION, [, ACTION-IF-FOUND [, +dnl ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]]) + +AC_DEFUN([LSH_CHECK_KRB_LIB], +[AC_CHECK_LIB([$1], [$2], + ifelse([$3], , + [[ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + AC_DEFINE_UNQUOTED($ac_tr_lib) + KRB_LIBS="-l$1 $KRB_LIBS" + ]], [$3]), + ifelse([$4], , , [$4 +])dnl +, [$5 $KRB_LIBS]) +]) + +dnl LSH_LIB_ARGP(ACTION-IF-OK, ACTION-IF-BAD) +AC_DEFUN([LSH_LIB_ARGP], +[ ac_argp_save_LIBS="$LIBS" + ac_argp_save_LDFLAGS="$LDFLAGS" + ac_argp_ok=no + # First check if we can link with argp. + AC_SEARCH_LIBS(argp_parse, argp, + [ LSH_RPATH_FIX + AC_CACHE_CHECK([for working argp], + lsh_cv_lib_argp_works, + [ AC_TRY_RUN( +[#include +#include + +static const struct argp_option +options[] = +{ + { NULL, 0, NULL, 0, NULL, 0 } +}; + +struct child_state +{ + int n; +}; + +static error_t +child_parser(int key, char *arg, struct argp_state *state) +{ + struct child_state *input = (struct child_state *) state->input; + + switch(key) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_END: + if (!input->n) + input->n = 1; + break; + } + return 0; +} + +const struct argp child_argp = +{ + options, + child_parser, + NULL, NULL, NULL, NULL, NULL +}; + +struct main_state +{ + struct child_state child; + int m; +}; + +static error_t +main_parser(int key, char *arg, struct argp_state *state) +{ + struct main_state *input = (struct main_state *) state->input; + + switch(key) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_INIT: + state->child_inputs[0] = &input->child; + break; + case ARGP_KEY_END: + if (!input->m) + input->m = input->child.n; + + break; + } + return 0; +} + +static const struct argp_child +main_children[] = +{ + { &child_argp, 0, "", 0 }, + { NULL, 0, NULL, 0} +}; + +static const struct argp +main_argp = +{ options, main_parser, + NULL, + NULL, + main_children, + NULL, NULL +}; + +int main(int argc, char **argv) +{ + struct main_state input = { { 0 }, 0 }; + char *v[2] = { "foo", NULL }; + + argp_parse(&main_argp, 1, v, 0, NULL, &input); + + if ( (input.m == 1) && (input.child.n == 1) ) + return 0; + else + return 1; +} +], lsh_cv_lib_argp_works=yes, + lsh_cv_lib_argp_works=no, + lsh_cv_lib_argp_works=no)]) + + if test x$lsh_cv_lib_argp_works = xyes ; then + ac_argp_ok=yes + else + # Reset link flags + LIBS="$ac_argp_save_LIBS" + LDFLAGS="$ac_argp_save_LDFLAGS" + fi]) + + if test x$ac_argp_ok = xyes ; then + ifelse([$1],, true, [$1]) + else + ifelse([$2],, true, [$2]) + fi +]) + +dnl LSH_GCC_ATTRIBUTES +dnl Check for gcc's __attribute__ construction + +AC_DEFUN([LSH_GCC_ATTRIBUTES], +[AC_CACHE_CHECK(for __attribute__, + lsh_cv_c_attribute, +[ AC_TRY_COMPILE([ +#include +], +[ +static void foo(void) __attribute__ ((noreturn)); + +static void __attribute__ ((noreturn)) +foo(void) +{ + exit(1); +} +], +lsh_cv_c_attribute=yes, +lsh_cv_c_attribute=no)]) + +AH_TEMPLATE([HAVE_GCC_ATTRIBUTE], [Define if the compiler understands __attribute__]) +if test "x$lsh_cv_c_attribute" = "xyes"; then + AC_DEFINE(HAVE_GCC_ATTRIBUTE) +fi + +AH_BOTTOM( +[#if __GNUC__ || HAVE_GCC_ATTRIBUTE +# define NORETURN __attribute__ ((__noreturn__)) +# define PRINTF_STYLE(f, a) __attribute__ ((__format__ (__printf__, f, a))) +# define UNUSED __attribute__ ((__unused__)) +#else +# define NORETURN +# define PRINTF_STYLE(f, a) +# define UNUSED +#endif +])]) + +AC_DEFUN([LSH_GCC_FUNCTION_NAME], +[# Check for gcc's __FUNCTION__ variable +AH_TEMPLATE([HAVE_GCC_FUNCTION], + [Define if the compiler understands __FUNCTION__]) +AH_BOTTOM( +[#if HAVE_GCC_FUNCTION +# define FUNCTION_NAME __FUNCTION__ +#else +# define FUNCTION_NAME "Unknown" +#endif +]) + +AC_CACHE_CHECK(for __FUNCTION__, + lsh_cv_c_FUNCTION, + [ AC_TRY_COMPILE(, + [ #if __GNUC__ == 3 + # error __FUNCTION__ is broken in gcc-3 + #endif + void foo(void) { char c = __FUNCTION__[0]; } ], + lsh_cv_c_FUNCTION=yes, + lsh_cv_c_FUNCTION=no)]) + +if test "x$lsh_cv_c_FUNCTION" = "xyes"; then + AC_DEFINE(HAVE_GCC_FUNCTION) +fi +]) + +# Check for alloca, and include the standard blurb in config.h +AC_DEFUN([LSH_FUNC_ALLOCA], +[AC_FUNC_ALLOCA +AC_CHECK_HEADERS([malloc.h]) +AH_BOTTOM( +[/* AIX requires this to be the first thing in the file. */ +#ifndef __GNUC__ +# if HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +#else /* defined __GNUC__ */ +# if HAVE_ALLOCA_H +# include +# endif +#endif +/* Needed for alloca on windows */ +#if HAVE_MALLOC_H +# include +#endif +])]) + +AC_DEFUN([LSH_FUNC_STRERROR], +[AC_CHECK_FUNCS(strerror) +AH_BOTTOM( +[#if HAVE_STRERROR +#define STRERROR strerror +#else +#define STRERROR(x) (sys_errlist[x]) +#endif +])]) + +AC_DEFUN([LSH_FUNC_STRSIGNAL], +[AC_CHECK_FUNCS(strsignal) +AC_CHECK_DECLS([sys_siglist, _sys_siglist]) +AH_BOTTOM( +[#if HAVE_STRSIGNAL +# define STRSIGNAL strsignal +#else /* !HAVE_STRSIGNAL */ +# if HAVE_DECL_SYS_SIGLIST +# define STRSIGNAL(x) (sys_siglist[x]) +# else +# if HAVE_DECL__SYS_SIGLIST +# define STRSIGNAL(x) (_sys_siglist[x]) +# else +# define STRSIGNAL(x) "Unknown signal" +# if __GNUC__ +# warning Using dummy STRSIGNAL +# endif +# endif +# endif +#endif /* !HAVE_STRSIGNAL */ +])]) + +dnl LSH_MAKE_CONDITIONAL(symbol, test) +AC_DEFUN([LSH_MAKE_CONDITIONAL], +[if $2 ; then + IF_$1='' + UNLESS_$1='# ' +else + IF_$1='# ' + UNLESS_$1='' +fi +AC_SUBST(IF_$1) +AC_SUBST(UNLESS_$1)]) + +dnl LSH_DEPENDENCY_TRACKING + +dnl Defines compiler flags DEP_FLAGS to generate dependency +dnl information, and DEP_PROCESS that is any shell commands needed for +dnl massaging the dependency information further. Dependencies are +dnl generated as a side effect of compilation. Dependency files +dnl themselves are not treated as targets. + +AC_DEFUN([LSH_DEPENDENCY_TRACKING], +[AC_ARG_ENABLE(dependency_tracking, + AC_HELP_STRING([--disable-dependency-tracking], + [Disable dependency tracking. Dependency tracking doesn't work with BSD make]),, + [enable_dependency_tracking=yes]) + +DEP_FLAGS='' +DEP_PROCESS='true' +if test x$enable_dependency_tracking = xyes ; then + if test x$GCC = xyes ; then + gcc_version=`gcc --version | head -1` + case "$gcc_version" in + 2.*|*[[!0-9.]]2.*) + enable_dependency_tracking=no + AC_MSG_WARN([Dependency tracking disabled, gcc-3.x is needed]) + ;; + *) + DEP_FLAGS='-MT $[]@ -MD -MP -MF $[]@.d' + DEP_PROCESS='true' + ;; + esac + else + enable_dependency_tracking=no + AC_MSG_WARN([Dependency tracking disabled]) + fi +fi + +if test x$enable_dependency_tracking = xyes ; then + DEP_INCLUDE='include ' +else + DEP_INCLUDE='# ' +fi + +AC_SUBST([DEP_INCLUDE]) +AC_SUBST([DEP_FLAGS]) +AC_SUBST([DEP_PROCESS])]) + +dnl @synopsis AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEADERS-TO-CHECK])] +dnl +dnl the "ISO C9X: 7.18 Integer types " section requires the +dnl existence of an include file that defines a set of +dnl typedefs, especially uint8_t,int32_t,uintptr_t. +dnl Many older installations will not provide this file, but some will +dnl have the very same definitions in . In other enviroments +dnl we can use the inet-types in which would define the +dnl typedefs int8_t and u_int8_t respectivly. +dnl +dnl This macros will create a local "_stdint.h" or the headerfile given as +dnl an argument. In many cases that file will just "#include " +dnl or "#include ", while in other environments it will provide +dnl the set of basic 'stdint's definitions/typedefs: +dnl int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +dnl int_least32_t.. int_fast32_t.. intmax_t +dnl which may or may not rely on the definitions of other files, +dnl or using the AC_CHECK_SIZEOF macro to determine the actual +dnl sizeof each type. +dnl +dnl if your header files require the stdint-types you will want to create an +dnl installable file mylib-int.h that all your other installable header +dnl may include. So if you have a library package named "mylib", just use +dnl AX_CREATE_STDINT_H(mylib-int.h) +dnl in configure.ac and go to install that very header file in Makefile.am +dnl along with the other headers (mylib.h) - and the mylib-specific headers +dnl can simply use "#include " to obtain the stdint-types. +dnl +dnl Remember, if the system already had a valid , the generated +dnl file will include it directly. No need for fuzzy HAVE_STDINT_H things... +dnl +dnl @, (status: used on new platforms) (see http://ac-archive.sf.net/gstdint/) +dnl @version $Id: acinclude.m4,v 1.27 2004/11/23 21:27:35 nisse Exp $ +dnl @author Guido Draheim + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all CHECK_TYPE macros do automatically "DEFINE" a type, therefore +dnl we use the autoconf implementation detail _AC CHECK_TYPE_NEW instead + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in stdint.h inttypes.h sys/inttypes.h $inttype_headers ; do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + _AC_CHECK_TYPE_NEW(uintptr_t,[ac_cv_header_stdint_x=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)" + break; + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in inttypes.h sys/inttypes.h stdint.h $inttype_headers ; do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + ac_cv_stdint_result="(seen uint32_t$and64 in $i)" + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in sys/types.h inttypes.h sys/inttypes.h $inttype_headers ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],dnl + continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)" + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_stdint_char_model="" + ac_cv_stdint_char_model="$ac_cv_stdint_char_model$ac_cv_sizeof_char" + ac_cv_stdint_char_model="$ac_cv_stdint_char_model$ac_cv_sizeof_short" + ac_cv_stdint_char_model="$ac_cv_stdint_char_model$ac_cv_sizeof_int" + ac_cv_stdint_long_model="" + ac_cv_stdint_long_model="$ac_cv_stdint_long_model$ac_cv_sizeof_int" + ac_cv_stdint_long_model="$ac_cv_stdint_long_model$ac_cv_sizeof_long" + ac_cv_stdint_long_model="$ac_cv_stdint_long_model$ac_cv_sizeof_voidp" + name="$ac_cv_stdint_long_model" + case "$ac_cv_stdint_char_model/$ac_cv_stdint_long_model" in + 122/242) name="$name, IP16 (standard 16bit machine)" ;; + 122/244) name="$name, LP32 (standard 32bit mac/win)" ;; + 122/*) name="$name (unusual int16 model)" ;; + 124/444) name="$name, ILP32 (standard 32bit unixish)" ;; + 124/488) name="$name, LP64 (standard 64bit unixish)" ;; + 124/448) name="$name, LLP64 (unusual 64bit unixish)" ;; + 124/*) name="$name (unusual int32 model)" ;; + 128/888) name="$name, ILP64 (unusual 64bit numeric)" ;; + 128/*) name="$name (unusual int64 model)" ;; + 222/*|444/*) name="$name (unusual dsptype)" ;; + *) name="$name (very unusal model)" ;; + esac + AC_MSG_RESULT([combined for stdint datatype model... $name]) +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +fi + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_stdint_char_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_stdint_char_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_stdint_long_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsinged int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_stdint_char_model="$ac_cv_stdint_char_model" +ac_cv_stdint_long_model="$ac_cv_stdint_long_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) diff --git a/contrib/argp-standalone/argp-ba.c b/contrib/argp-standalone/argp-ba.c new file mode 100644 index 000000000..0d3958c11 --- /dev/null +++ b/contrib/argp-standalone/argp-ba.c @@ -0,0 +1,26 @@ +/* Default definition for ARGP_PROGRAM_BUG_ADDRESS. + Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* If set by the user program, it should point to string that is the + bug-reporting address for the program. It will be printed by argp_help if + the ARGP_HELP_BUG_ADDR flag is set (as it is by various standard help + messages), embedded in a sentence that says something like `Report bugs to + ADDR.'. */ +const char *argp_program_bug_address = 0; diff --git a/contrib/argp-standalone/argp-eexst.c b/contrib/argp-standalone/argp-eexst.c new file mode 100644 index 000000000..46b27847a --- /dev/null +++ b/contrib/argp-standalone/argp-eexst.c @@ -0,0 +1,36 @@ +/* Default definition for ARGP_ERR_EXIT_STATUS + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if HAVE_SYSEXITS_H +# include +#else +# define EX_USAGE 64 +#endif + +#include "argp.h" + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +error_t argp_err_exit_status = EX_USAGE; diff --git a/contrib/argp-standalone/argp-fmtstream.c b/contrib/argp-standalone/argp-fmtstream.c new file mode 100644 index 000000000..494b6b31d --- /dev/null +++ b/contrib/argp-standalone/argp-fmtstream.c @@ -0,0 +1,477 @@ +/* Word-wrapping and line-truncating streams + Copyright (C) 1997, 1998, 1999, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This package emulates glibc `line_wrap_stream' semantics for systems that + don't have that. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + +#ifndef ARGP_FMTSTREAM_USE_LINEWRAP + +#ifndef isblank +#define isblank(ch) ((ch)==' ' || (ch)=='\t') +#endif + +#if defined _LIBC && defined USE_IN_LIBIO +# include +# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) +#endif + +#define INIT_BUF_SIZE 200 +#define PRINTF_SIZE_GUESS 150 + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +argp_fmtstream_t +__argp_make_fmtstream (FILE *stream, + size_t lmargin, size_t rmargin, ssize_t wmargin) +{ + argp_fmtstream_t fs = malloc (sizeof (struct argp_fmtstream)); + if (fs) + { + fs->stream = stream; + + fs->lmargin = lmargin; + fs->rmargin = rmargin; + fs->wmargin = wmargin; + fs->point_col = 0; + fs->point_offs = 0; + + fs->buf = malloc (INIT_BUF_SIZE); + if (! fs->buf) + { + free (fs); + fs = 0; + } + else + { + fs->p = fs->buf; + fs->end = fs->buf + INIT_BUF_SIZE; + } + } + + return fs; +} +#ifdef weak_alias +weak_alias (__argp_make_fmtstream, argp_make_fmtstream) +#endif + +/* Flush FS to its stream, and free it (but don't close the stream). */ +void +__argp_fmtstream_free (argp_fmtstream_t fs) +{ + __argp_fmtstream_update (fs); + if (fs->p > fs->buf) + FWRITE_UNLOCKED (fs->buf, 1, fs->p - fs->buf, fs->stream); + free (fs->buf); + free (fs); +} +#ifdef weak_alias +weak_alias (__argp_fmtstream_free, argp_fmtstream_free) +#endif + +/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the + end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ +void +__argp_fmtstream_update (argp_fmtstream_t fs) +{ + char *buf, *nl; + size_t len; + + /* Scan the buffer for newlines. */ + buf = fs->buf + fs->point_offs; + while (buf < fs->p) + { + size_t r; + + if (fs->point_col == 0 && fs->lmargin != 0) + { + /* We are starting a new line. Print spaces to the left margin. */ + const size_t pad = fs->lmargin; + if (fs->p + pad < fs->end) + { + /* We can fit in them in the buffer by moving the + buffer text up and filling in the beginning. */ + memmove (buf + pad, buf, fs->p - buf); + fs->p += pad; /* Compensate for bigger buffer. */ + memset (buf, ' ', pad); /* Fill in the spaces. */ + buf += pad; /* Don't bother searching them. */ + } + else + { + /* No buffer space for spaces. Must flush. */ + size_t i; + for (i = 0; i < pad; i++) + PUTC_UNLOCKED (' ', fs->stream); + } + fs->point_col = pad; + } + + len = fs->p - buf; + nl = memchr (buf, '\n', len); + + if (fs->point_col < 0) + fs->point_col = 0; + + if (!nl) + { + /* The buffer ends in a partial line. */ + + if (fs->point_col + len < fs->rmargin) + { + /* The remaining buffer text is a partial line and fits + within the maximum line width. Advance point for the + characters to be written and stop scanning. */ + fs->point_col += len; + break; + } + else + /* Set the end-of-line pointer for the code below to + the end of the buffer. */ + nl = fs->p; + } + else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) + { + /* The buffer contains a full line that fits within the maximum + line width. Reset point and scan the next line. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + + /* This line is too long. */ + r = fs->rmargin - 1; + + if (fs->wmargin < 0) + { + /* Truncate the line by overwriting the excess with the + newline and anything after it in the buffer. */ + if (nl < fs->p) + { + memmove (buf + (r - fs->point_col), nl, fs->p - nl); + fs->p -= buf + (r - fs->point_col) - nl; + /* Reset point for the next line and start scanning it. */ + fs->point_col = 0; + buf += r + 1; /* Skip full line plus \n. */ + } + else + { + /* The buffer ends with a partial line that is beyond the + maximum line width. Advance point for the characters + written, and discard those past the max from the buffer. */ + fs->point_col += len; + fs->p -= fs->point_col - r; + break; + } + } + else + { + /* Do word wrap. Go to the column just past the maximum line + width and scan back for the beginning of the word there. + Then insert a line break. */ + + char *p, *nextline; + int i; + + p = buf + (r + 1 - fs->point_col); + while (p >= buf && !isblank (*p)) + --p; + nextline = p + 1; /* This will begin the next line. */ + + if (nextline > buf) + { + /* Swallow separating blanks. */ + if (p >= buf) + do + --p; + while (p >= buf && isblank (*p)); + nl = p + 1; /* The newline will replace the first blank. */ + } + else + { + /* A single word that is greater than the maximum line width. + Oh well. Put it on an overlong line by itself. */ + p = buf + (r + 1 - fs->point_col); + /* Find the end of the long word. */ + do + ++p; + while (p < nl && !isblank (*p)); + if (p == nl) + { + /* It already ends a line. No fussing required. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + /* We will move the newline to replace the first blank. */ + nl = p; + /* Swallow separating blanks. */ + do + ++p; + while (isblank (*p)); + /* The next line will start here. */ + nextline = p; + } + + /* Note: There are a bunch of tests below for + NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall + at the end of the buffer, and NEXTLINE is in fact empty (and so + we need not be careful to maintain its contents). */ + + if (nextline == buf + len + 1 + ? fs->end - nl < fs->wmargin + 1 + : nextline - (nl + 1) < fs->wmargin) + { + /* The margin needs more blanks than we removed. */ + if (fs->end - fs->p > fs->wmargin + 1) + /* Make some space for them. */ + { + size_t mv = fs->p - nextline; + memmove (nl + 1 + fs->wmargin, nextline, mv); + nextline = nl + 1 + fs->wmargin; + len = nextline + mv - buf; + *nl++ = '\n'; + } + else + /* Output the first line so we can use the space. */ + { + if (nl > fs->buf) + FWRITE_UNLOCKED (fs->buf, 1, nl - fs->buf, fs->stream); + PUTC_UNLOCKED ('\n', fs->stream); + len += buf - fs->buf; + nl = buf = fs->buf; + } + } + else + /* We can fit the newline and blanks in before + the next word. */ + *nl++ = '\n'; + + if (nextline - nl >= fs->wmargin + || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin)) + /* Add blanks up to the wrap margin column. */ + for (i = 0; i < fs->wmargin; ++i) + *nl++ = ' '; + else + for (i = 0; i < fs->wmargin; ++i) + PUTC_UNLOCKED (' ', fs->stream); + + /* Copy the tail of the original buffer into the current buffer + position. */ + if (nl < nextline) + memmove (nl, nextline, buf + len - nextline); + len -= nextline - buf; + + /* Continue the scan on the remaining lines in the buffer. */ + buf = nl; + + /* Restore bufp to include all the remaining text. */ + fs->p = nl + len; + + /* Reset the counter of what has been output this line. If wmargin + is 0, we want to avoid the lmargin getting added, so we set + point_col to a magic value of -1 in that case. */ + fs->point_col = fs->wmargin ? fs->wmargin : -1; + } + } + + /* Remember that we've scanned as far as the end of the buffer. */ + fs->point_offs = fs->p - fs->buf; +} + +/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by + growing the buffer, or by flushing it. True is returned iff we succeed. */ +int +__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) +{ + if ((size_t) (fs->end - fs->p) < amount) + { + ssize_t wrote; + + /* Flush FS's buffer. */ + __argp_fmtstream_update (fs); + + wrote = FWRITE_UNLOCKED (fs->buf, 1, fs->p - fs->buf, fs->stream); + if (wrote == fs->p - fs->buf) + { + fs->p = fs->buf; + fs->point_offs = 0; + } + else + { + fs->p -= wrote; + fs->point_offs -= wrote; + memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); + return 0; + } + + if ((size_t) (fs->end - fs->buf) < amount) + /* Gotta grow the buffer. */ + { + size_t new_size = fs->end - fs->buf + amount; + char *new_buf = realloc (fs->buf, new_size); + + if (! new_buf) + { + __set_errno (ENOMEM); + return 0; + } + + fs->buf = new_buf; + fs->end = new_buf + new_size; + fs->p = fs->buf; + } + } + + return 1; +} + +ssize_t +__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) +{ + size_t out; + size_t avail; + size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ + + do + { + va_list args; + + if (! __argp_fmtstream_ensure (fs, size_guess)) + return -1; + + va_start (args, fmt); + avail = fs->end - fs->p; + out = __vsnprintf (fs->p, avail, fmt, args); + va_end (args); + if (out >= avail) + size_guess = out + 1; + } + while (out >= avail); + + fs->p += out; + + return out; +} +#ifdef weak_alias +weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) +#endif + +#if __STDC_VERSION__ - 199900L < 1 +/* Duplicate the inline definitions in argp-fmtstream.h, for compilers + * that don't do inlining. */ +size_t +__argp_fmtstream_write (argp_fmtstream_t __fs, + __const char *__str, size_t __len) +{ + if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) + { + memcpy (__fs->p, __str, __len); + __fs->p += __len; + return __len; + } + else + return 0; +} + +int +__argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) +{ + size_t __len = strlen (__str); + if (__len) + { + size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); + return __wrote == __len ? 0 : -1; + } + else + return 0; +} + +int +__argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) +{ + if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) + return *__fs->p++ = __ch; + else + return EOF; +} + +/* Set __FS's left margin to __LMARGIN and return the old value. */ +size_t +__argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->lmargin; + __fs->lmargin = __lmargin; + return __old; +} + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +size_t +__argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->rmargin; + __fs->rmargin = __rmargin; + return __old; +} + +/* Set FS's wrap margin to __WMARGIN and return the old value. */ +size_t +__argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->wmargin; + __fs->wmargin = __wmargin; + return __old; +} + +/* Return the column number of the current output point in __FS. */ +size_t +__argp_fmtstream_point (argp_fmtstream_t __fs) +{ + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + return __fs->point_col >= 0 ? __fs->point_col : 0; +} +#endif /* __STDC_VERSION__ - 199900L < 1 */ + +#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ diff --git a/contrib/argp-standalone/argp-fmtstream.h b/contrib/argp-standalone/argp-fmtstream.h new file mode 100644 index 000000000..828f4357d --- /dev/null +++ b/contrib/argp-standalone/argp-fmtstream.h @@ -0,0 +1,327 @@ +/* Word-wrapping and line-truncating streams. + Copyright (C) 1997, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This package emulates glibc `line_wrap_stream' semantics for systems that + don't have that. If the system does have it, it is just a wrapper for + that. This header file is only used internally while compiling argp, and + shouldn't be installed. */ + +#ifndef _ARGP_FMTSTREAM_H +#define _ARGP_FMTSTREAM_H + +#include +#include + +#if HAVE_UNISTD_H +# include +#else +/* This is a kludge to make the code compile on windows. Perhaps it + would be better to just replace ssize_t with int through out the + code. */ +# define ssize_t int +#endif + +#if _LIBC || (defined (HAVE_FLOCKFILE) && defined(HAVE_PUTC_UNLOCKED) \ + && defined (HAVE_FPUTS_UNLOCKED) && defined (HAVE_FWRITE_UNLOCKED) ) +/* Use locking funxtions */ +# define FLOCKFILE(f) flockfile(f) +# define FUNLOCKFILE(f) funlockfile(f) +# define PUTC_UNLOCKED(c, f) putc_unlocked((c), (f)) +# define FPUTS_UNLOCKED(s, f) fputs_unlocked((s), (f)) +# define FWRITE_UNLOCKED(b, s, n, f) fwrite_unlocked((b), (s), (n), (f)) +#else +/* Disable stdio locking */ +# define FLOCKFILE(f) +# define FUNLOCKFILE(f) +# define PUTC_UNLOCKED(c, f) putc((c), (f)) +# define FPUTS_UNLOCKED(s, f) fputs((s), (f)) +# define FWRITE_UNLOCKED(b, s, n, f) fwrite((b), (s), (n), (f)) +#endif /* No thread safe i/o */ + +#if (_LIBC - 0 && !defined (USE_IN_LIBIO)) \ + || (defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H)) +/* line_wrap_stream is available, so use that. */ +#define ARGP_FMTSTREAM_USE_LINEWRAP +#endif + +#ifdef ARGP_FMTSTREAM_USE_LINEWRAP +/* Just be a simple wrapper for line_wrap_stream; the semantics are + *slightly* different, as line_wrap_stream doesn't actually make a new + object, it just modifies the given stream (reversibly) to do + line-wrapping. Since we control who uses this code, it doesn't matter. */ + +#include + +typedef FILE *argp_fmtstream_t; + +#define argp_make_fmtstream line_wrap_stream +#define __argp_make_fmtstream line_wrap_stream +#define argp_fmtstream_free line_unwrap_stream +#define __argp_fmtstream_free line_unwrap_stream + +#define __argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define __argp_fmtstream_puts(fs,str) fputs(str,fs) +#define argp_fmtstream_puts(fs,str) fputs(str,fs) +#define __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define __argp_fmtstream_printf fprintf +#define argp_fmtstream_printf fprintf + +#define __argp_fmtstream_lmargin line_wrap_lmargin +#define argp_fmtstream_lmargin line_wrap_lmargin +#define __argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define __argp_fmtstream_rmargin line_wrap_rmargin +#define argp_fmtstream_rmargin line_wrap_rmargin +#define __argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define __argp_fmtstream_wmargin line_wrap_wmargin +#define argp_fmtstream_wmargin line_wrap_wmargin +#define __argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define __argp_fmtstream_point line_wrap_point +#define argp_fmtstream_point line_wrap_point + +#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ +/* Guess we have to define our own version. */ + +#ifndef __const +#define __const const +#endif + + +struct argp_fmtstream +{ + FILE *stream; /* The stream we're outputting to. */ + + size_t lmargin, rmargin; /* Left and right margins. */ + ssize_t wmargin; /* Margin to wrap to, or -1 to truncate. */ + + /* Point in buffer to which we've processed for wrapping, but not output. */ + size_t point_offs; + /* Output column at POINT_OFFS, or -1 meaning 0 but don't add lmargin. */ + ssize_t point_col; + + char *buf; /* Output buffer. */ + char *p; /* Current end of text in BUF. */ + char *end; /* Absolute end of BUF. */ +}; + +typedef struct argp_fmtstream *argp_fmtstream_t; + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); +extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); + +/* Flush __FS to its stream, and free it (but don't close the stream). */ +extern void __argp_fmtstream_free (argp_fmtstream_t __fs); +extern void argp_fmtstream_free (argp_fmtstream_t __fs); + +extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, + __const char *__fmt, ...) + PRINTF_STYLE(2,3); +extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, + __const char *__fmt, ...) + PRINTF_STYLE(2,3); + +#if __STDC_VERSION__ - 199900L < 1 +extern int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); +extern int argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); + +extern int __argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str); +extern int argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str); + +extern size_t __argp_fmtstream_write (argp_fmtstream_t __fs, + __const char *__str, size_t __len); +extern size_t argp_fmtstream_write (argp_fmtstream_t __fs, + __const char *__str, size_t __len); +#endif /* __STDC_VERSION__ - 199900L < 1 */ + +/* Access macros for various bits of state. */ +#define argp_fmtstream_lmargin(__fs) ((__fs)->lmargin) +#define argp_fmtstream_rmargin(__fs) ((__fs)->rmargin) +#define argp_fmtstream_wmargin(__fs) ((__fs)->wmargin) +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +#if __STDC_VERSION__ - 199900L < 1 +/* Set __FS's left margin to LMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, + size_t __lmargin); +extern size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, + size_t __lmargin); + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, + size_t __rmargin); +extern size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, + size_t __rmargin); + +/* Set __FS's wrap margin to __WMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, + size_t __wmargin); +extern size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, + size_t __wmargin); + +/* Return the column number of the current output point in __FS. */ +extern size_t argp_fmtstream_point (argp_fmtstream_t __fs); +extern size_t __argp_fmtstream_point (argp_fmtstream_t __fs); +#endif /* __STDC_VERSION__ - 199900L < 1 */ + +/* Internal routines. */ +extern void _argp_fmtstream_update (argp_fmtstream_t __fs); +extern void __argp_fmtstream_update (argp_fmtstream_t __fs); +extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); +extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); + +#ifdef __OPTIMIZE__ +/* Inline versions of above routines. */ + +#if !_LIBC +#define __argp_fmtstream_putc argp_fmtstream_putc +#define __argp_fmtstream_puts argp_fmtstream_puts +#define __argp_fmtstream_write argp_fmtstream_write +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#define __argp_fmtstream_point argp_fmtstream_point +#define __argp_fmtstream_update _argp_fmtstream_update +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#endif + +#ifndef ARGP_FS_EI +#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) +#define ARGP_FS_EI extern inline +#else +#define ARGP_FS_EI inline +#endif +#endif + +ARGP_FS_EI size_t +__argp_fmtstream_write (argp_fmtstream_t __fs, + __const char *__str, size_t __len) +{ + if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) + { + memcpy (__fs->p, __str, __len); + __fs->p += __len; + return __len; + } + else + return 0; +} + +ARGP_FS_EI int +__argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) +{ + size_t __len = strlen (__str); + if (__len) + { + size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); + return __wrote == __len ? 0 : -1; + } + else + return 0; +} + +ARGP_FS_EI int +__argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) +{ + if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) + return *__fs->p++ = __ch; + else + return EOF; +} + +/* Set __FS's left margin to __LMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->lmargin; + __fs->lmargin = __lmargin; + return __old; +} + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->rmargin; + __fs->rmargin = __rmargin; + return __old; +} + +/* Set FS's wrap margin to __WMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->wmargin; + __fs->wmargin = __wmargin; + return __old; +} + +/* Return the column number of the current output point in __FS. */ +ARGP_FS_EI size_t +__argp_fmtstream_point (argp_fmtstream_t __fs) +{ + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + return __fs->point_col >= 0 ? __fs->point_col : 0; +} + +#if !_LIBC +#undef __argp_fmtstream_putc +#undef __argp_fmtstream_puts +#undef __argp_fmtstream_write +#undef __argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#undef __argp_fmtstream_update +#undef __argp_fmtstream_ensure +#endif + +#endif /* __OPTIMIZE__ */ + +#endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ + +#endif /* argp-fmtstream.h */ diff --git a/contrib/argp-standalone/argp-help.c b/contrib/argp-standalone/argp-help.c new file mode 100644 index 000000000..ced78c4cb --- /dev/null +++ b/contrib/argp-standalone/argp-help.c @@ -0,0 +1,1849 @@ +/* Hierarchial argument parsing help output + Copyright (C) 1995,96,97,98,99,2000, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if HAVE_ALLOCA_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#if HAVE_MALLOC_H +/* Needed, for alloca on windows */ +# include +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include +# ifdef _LIBC +# undef dgettext +# define dgettext(domain, msgid) __dcgettext (domain, msgid, LC_MESSAGES) +# endif +# else +# define dgettext(domain, msgid) (msgid) +# endif +#endif + +#include "argp.h" +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + + +#ifndef _LIBC +# ifndef __strchrnul +# define __strchrnul strchrnul +# endif +# ifndef __mempcpy +# define __mempcpy mempcpy +# endif +/* We need to use a different name, as __strndup is likely a macro. */ +# define STRNDUP strndup +# if HAVE_STRERROR +# define STRERROR strerror +# else +# define STRERROR(x) (sys_errlist[x]) +# endif +#else /* _LIBC */ +# define FLOCKFILE __flockfile +# define FUNLOCKFILE __funlockfile +# define STRNDUP __strndup +# define STRERROR strerror +#endif + +#if !_LIBC +# if !HAVE_STRNDUP +char *strndup (const char *s, size_t size); +# endif /* !HAVE_STRNDUP */ + +# if !HAVE_MEMPCPY +void *mempcpy (void *to, const void *from, size_t size); +# endif /* !HAVE_MEMPCPY */ + +# if !HAVE_STRCHRNUL +char *strchrnul(const char *s, int c); +# endif /* !HAVE_STRCHRNUL */ + +# if !HAVE_STRCASECMP +int strcasecmp(const char *s1, const char *s2); +#endif + +#endif /* !_LIBC */ + + +/* User-selectable (using an environment variable) formatting parameters. + + These may be specified in an environment variable called `ARGP_HELP_FMT', + with a contents like: VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2 + Where VALn must be a positive integer. The list of variables is in the + UPARAM_NAMES vector, below. */ + +/* Default parameters. */ +#define DUP_ARGS 0 /* True if option argument can be duplicated. */ +#define DUP_ARGS_NOTE 1 /* True to print a note about duplicate args. */ +#define SHORT_OPT_COL 2 /* column in which short options start */ +#define LONG_OPT_COL 6 /* column in which long options start */ +#define DOC_OPT_COL 2 /* column in which doc options start */ +#define OPT_DOC_COL 29 /* column in which option text starts */ +#define HEADER_COL 1 /* column in which group headers are printed */ +#define USAGE_INDENT 12 /* indentation of wrapped usage lines */ +#define RMARGIN 79 /* right margin used for wrapping */ + +/* User-selectable (using an environment variable) formatting parameters. + They must all be of type `int' for the parsing code to work. */ +struct uparams +{ + /* If true, arguments for an option are shown with both short and long + options, even when a given option has both, e.g. `-x ARG, --longx=ARG'. + If false, then if an option has both, the argument is only shown with + the long one, e.g., `-x, --longx=ARG', and a message indicating that + this really means both is printed below the options. */ + int dup_args; + + /* This is true if when DUP_ARGS is false, and some duplicate arguments have + been suppressed, an explanatory message should be printed. */ + int dup_args_note; + + /* Various output columns. */ + int short_opt_col; + int long_opt_col; + int doc_opt_col; + int opt_doc_col; + int header_col; + int usage_indent; + int rmargin; + + int valid; /* True when the values in here are valid. */ +}; + +/* This is a global variable, as user options are only ever read once. */ +static struct uparams uparams = { + DUP_ARGS, DUP_ARGS_NOTE, + SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL, + USAGE_INDENT, RMARGIN, + 0 +}; + +/* A particular uparam, and what the user name is. */ +struct uparam_name +{ + const char *name; /* User name. */ + int is_bool; /* Whether it's `boolean'. */ + size_t uparams_offs; /* Location of the (int) field in UPARAMS. */ +}; + +/* The name-field mappings we know about. */ +static const struct uparam_name uparam_names[] = +{ + { "dup-args", 1, offsetof (struct uparams, dup_args) }, + { "dup-args-note", 1, offsetof (struct uparams, dup_args_note) }, + { "short-opt-col", 0, offsetof (struct uparams, short_opt_col) }, + { "long-opt-col", 0, offsetof (struct uparams, long_opt_col) }, + { "doc-opt-col", 0, offsetof (struct uparams, doc_opt_col) }, + { "opt-doc-col", 0, offsetof (struct uparams, opt_doc_col) }, + { "header-col", 0, offsetof (struct uparams, header_col) }, + { "usage-indent", 0, offsetof (struct uparams, usage_indent) }, + { "rmargin", 0, offsetof (struct uparams, rmargin) }, + { 0, 0, 0 } +}; + +/* Read user options from the environment, and fill in UPARAMS appropiately. */ +static void +fill_in_uparams (const struct argp_state *state) +{ + + const char *var = getenv ("ARGP_HELP_FMT"); + +#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0); + + if (var) + /* Parse var. */ + while (*var) + { + SKIPWS (var); + + if (isalpha (*var)) + { + size_t var_len; + const struct uparam_name *un; + int unspec = 0, val = 0; + const char *arg = var; + + while (isalnum (*arg) || *arg == '-' || *arg == '_') + arg++; + var_len = arg - var; + + SKIPWS (arg); + + if (*arg == '\0' || *arg == ',') + unspec = 1; + else if (*arg == '=') + { + arg++; + SKIPWS (arg); + } + + if (unspec) + { + if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') + { + val = 0; + var += 3; + var_len -= 3; + } + else + val = 1; + } + else if (isdigit (*arg)) + { + val = atoi (arg); + while (isdigit (*arg)) + arg++; + SKIPWS (arg); + } + + for (un = uparam_names; un->name; un++) + if (strlen (un->name) == var_len + && strncmp (var, un->name, var_len) == 0) + { + if (unspec && !un->is_bool) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, "\ +%.*s: ARGP_HELP_FMT parameter requires a value"), + (int) var_len, var); + else + *(int *)((char *)&uparams + un->uparams_offs) = val; + break; + } + if (! un->name) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, "\ +%.*s: Unknown ARGP_HELP_FMT parameter"), + (int) var_len, var); + + var = arg; + if (*var == ',') + var++; + } + else if (*var) + { + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "Garbage in ARGP_HELP_FMT: %s"), var); + break; + } + } +} + +/* Returns true if OPT hasn't been marked invisible. Visibility only affects + whether OPT is displayed or used in sorting, not option shadowing. */ +#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) + +/* Returns true if OPT is an alias for an earlier option. */ +#define oalias(opt) ((opt)->flags & OPTION_ALIAS) + +/* Returns true if OPT is an documentation-only entry. */ +#define odoc(opt) ((opt)->flags & OPTION_DOC) + +/* Returns true if OPT is the end-of-list marker for a list of options. */ +#define oend(opt) __option_is_end (opt) + +/* Returns true if OPT has a short option. */ +#define oshort(opt) __option_is_short (opt) + +/* + The help format for a particular option is like: + + -xARG, -yARG, --long1=ARG, --long2=ARG Documentation... + + Where ARG will be omitted if there's no argument, for this option, or + will be surrounded by "[" and "]" appropiately if the argument is + optional. The documentation string is word-wrapped appropiately, and if + the list of options is long enough, it will be started on a separate line. + If there are no short options for a given option, the first long option is + indented slighly in a way that's supposed to make most long options appear + to be in a separate column. + + For example, the following output (from ps): + + -p PID, --pid=PID List the process PID + --pgrp=PGRP List processes in the process group PGRP + -P, -x, --no-parent Include processes without parents + -Q, --all-fields Don't elide unusable fields (normally if there's + some reason ps can't print a field for any + process, it's removed from the output entirely) + -r, --reverse, --gratuitously-long-reverse-option + Reverse the order of any sort + --session[=SID] Add the processes from the session SID (which + defaults to the sid of the current process) + + Here are some more options: + -f ZOT, --foonly=ZOT Glork a foonly + -z, --zaza Snit a zar + + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + + The struct argp_option array for the above could look like: + + { + {"pid", 'p', "PID", 0, "List the process PID"}, + {"pgrp", OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"}, + {"no-parent", 'P', 0, 0, "Include processes without parents"}, + {0, 'x', 0, OPTION_ALIAS}, + {"all-fields",'Q', 0, 0, "Don't elide unusable fields (normally" + " if there's some reason ps can't" + " print a field for any process, it's" + " removed from the output entirely)" }, + {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, + {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, + {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL, + "Add the processes from the session" + " SID (which defaults to the sid of" + " the current process)" }, + + {0,0,0,0, "Here are some more options:"}, + {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, + {"zaza", 'z', 0, 0, "Snit a zar"}, + + {0} + } + + Note that the last three options are automatically supplied by argp_parse, + unless you tell it not to with ARGP_NO_HELP. + +*/ + +/* Returns true if CH occurs between BEG and END. */ +static int +find_char (char ch, char *beg, char *end) +{ + while (beg < end) + if (*beg == ch) + return 1; + else + beg++; + return 0; +} + +struct hol_cluster; /* fwd decl */ + +struct hol_entry +{ + /* First option. */ + const struct argp_option *opt; + /* Number of options (including aliases). */ + unsigned num; + + /* A pointers into the HOL's short_options field, to the first short option + letter for this entry. The order of the characters following this point + corresponds to the order of options pointed to by OPT, and there are at + most NUM. A short option recorded in a option following OPT is only + valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's + probably been shadowed by some other entry). */ + char *short_options; + + /* Entries are sorted by their group first, in the order: + 1, 2, ..., n, 0, -m, ..., -2, -1 + and then alphabetically within each group. The default is 0. */ + int group; + + /* The cluster of options this entry belongs to, or 0 if none. */ + struct hol_cluster *cluster; + + /* The argp from which this option came. */ + const struct argp *argp; +}; + +/* A cluster of entries to reflect the argp tree structure. */ +struct hol_cluster +{ + /* A descriptive header printed before options in this cluster. */ + const char *header; + + /* Used to order clusters within the same group with the same parent, + according to the order in which they occurred in the parent argp's child + list. */ + int index; + + /* How to sort this cluster with respect to options and other clusters at the + same depth (clusters always follow options in the same group). */ + int group; + + /* The cluster to which this cluster belongs, or 0 if it's at the base + level. */ + struct hol_cluster *parent; + + /* The argp from which this cluster is (eventually) derived. */ + const struct argp *argp; + + /* The distance this cluster is from the root. */ + int depth; + + /* Clusters in a given hol are kept in a linked list, to make freeing them + possible. */ + struct hol_cluster *next; +}; + +/* A list of options for help. */ +struct hol +{ + /* An array of hol_entry's. */ + struct hol_entry *entries; + /* The number of entries in this hol. If this field is zero, the others + are undefined. */ + unsigned num_entries; + + /* A string containing all short options in this HOL. Each entry contains + pointers into this string, so the order can't be messed with blindly. */ + char *short_options; + + /* Clusters of entries in this hol. */ + struct hol_cluster *clusters; +}; + +/* Create a struct hol from the options in ARGP. CLUSTER is the + hol_cluster in which these entries occur, or 0, if at the root. */ +static struct hol * +make_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + char *so; + const struct argp_option *o; + const struct argp_option *opts = argp->options; + struct hol_entry *entry; + unsigned num_short_options = 0; + struct hol *hol = malloc (sizeof (struct hol)); + + assert (hol); + + hol->num_entries = 0; + hol->clusters = 0; + + if (opts) + { + int cur_group = 0; + + /* The first option must not be an alias. */ + assert (! oalias (opts)); + + /* Calculate the space needed. */ + for (o = opts; ! oend (o); o++) + { + if (! oalias (o)) + hol->num_entries++; + if (oshort (o)) + num_short_options++; /* This is an upper bound. */ + } + + hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); + hol->short_options = malloc (num_short_options + 1); + + assert (hol->entries && hol->short_options); + + /* Fill in the entries. */ + so = hol->short_options; + for (o = opts, entry = hol->entries; ! oend (o); entry++) + { + entry->opt = o; + entry->num = 0; + entry->short_options = so; + entry->group = cur_group = + o->group + ? o->group + : ((!o->name && !o->key) + ? cur_group + 1 + : cur_group); + entry->cluster = cluster; + entry->argp = argp; + + do + { + entry->num++; + if (oshort (o) && ! find_char (o->key, hol->short_options, so)) + /* O has a valid short option which hasn't already been used.*/ + *so++ = o->key; + o++; + } + while (! oend (o) && oalias (o)); + } + *so = '\0'; /* null terminated so we can find the length */ + } + + return hol; +} + +/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the + associated argp child list entry), INDEX, and PARENT, and return a pointer + to it. ARGP is the argp that this cluster results from. */ +static struct hol_cluster * +hol_add_cluster (struct hol *hol, int group, const char *header, int index, + struct hol_cluster *parent, const struct argp *argp) +{ + struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); + if (cl) + { + cl->group = group; + cl->header = header; + + cl->index = index; + cl->parent = parent; + cl->argp = argp; + cl->depth = parent ? parent->depth + 1 : 0; + + cl->next = hol->clusters; + hol->clusters = cl; + } + return cl; +} + +/* Free HOL and any resources it uses. */ +static void +hol_free (struct hol *hol) +{ + struct hol_cluster *cl = hol->clusters; + + while (cl) + { + struct hol_cluster *next = cl->next; + free (cl); + cl = next; + } + + if (hol->num_entries > 0) + { + free (hol->entries); + free (hol->short_options); + } + + free (hol); +} + +static inline int +hol_entry_short_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + char *so = entry->short_options; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (oshort (opt) && *so == opt->key) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + so++; + } + + return val; +} + +static inline int +hol_entry_long_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (opt->name) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + } + + return val; +} + +/* Iterator that returns true for the first short option. */ +static inline int +until_short (const struct argp_option *opt, const struct argp_option *real UNUSED, + const char *domain UNUSED, void *cookie UNUSED) +{ + return oshort (opt) ? opt->key : 0; +} + +/* Returns the first valid short option in ENTRY, or 0 if there is none. */ +static char +hol_entry_first_short (const struct hol_entry *entry) +{ + return hol_entry_short_iterate (entry, until_short, + entry->argp->argp_domain, 0); +} + +/* Returns the first valid long option in ENTRY, or 0 if there is none. */ +static const char * +hol_entry_first_long (const struct hol_entry *entry) +{ + const struct argp_option *opt; + unsigned num; + for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + return opt->name; + return 0; +} + +/* Returns the entry in HOL with the long option name NAME, or 0 if there is + none. */ +static struct hol_entry * +hol_find_entry (struct hol *hol, const char *name) +{ + struct hol_entry *entry = hol->entries; + unsigned num_entries = hol->num_entries; + + while (num_entries-- > 0) + { + const struct argp_option *opt = entry->opt; + unsigned num_opts = entry->num; + + while (num_opts-- > 0) + if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) + return entry; + else + opt++; + + entry++; + } + + return 0; +} + +/* If an entry with the long option NAME occurs in HOL, set it's special + sort position to GROUP. */ +static void +hol_set_group (struct hol *hol, const char *name, int group) +{ + struct hol_entry *entry = hol_find_entry (hol, name); + if (entry) + entry->group = group; +} + +/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. + EQ is what to return if GROUP1 and GROUP2 are the same. */ +static int +group_cmp (int group1, int group2, int eq) +{ + if (group1 == group2) + return eq; + else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) + return group1 - group2; + else + return group2 - group1; +} + +/* Compare clusters CL1 & CL2 by the order that they should appear in + output. */ +static int +hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) +{ + /* If one cluster is deeper than the other, use its ancestor at the same + level, so that finding the common ancestor is straightforward. */ + while (cl1->depth < cl2->depth) + cl1 = cl1->parent; + while (cl2->depth < cl1->depth) + cl2 = cl2->parent; + + /* Now reduce both clusters to their ancestors at the point where both have + a common parent; these can be directly compared. */ + while (cl1->parent != cl2->parent) + cl1 = cl1->parent, cl2 = cl2->parent; + + return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); +} + +/* Return the ancestor of CL that's just below the root (i.e., has a parent + of 0). */ +static struct hol_cluster * +hol_cluster_base (struct hol_cluster *cl) +{ + while (cl->parent) + cl = cl->parent; + return cl; +} + +/* Return true if CL1 is a child of CL2. */ +static int +hol_cluster_is_child (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + while (cl1 && cl1 != cl2) + cl1 = cl1->parent; + return cl1 == cl2; +} + +/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail + that should be used for comparisons, and returns true iff it should be + treated as a non-option. */ + +/* FIXME: Can we use unsigned char * for the argument? */ +static int +canon_doc_option (const char **name) +{ + int non_opt; + /* Skip initial whitespace. */ + while (isspace ( (unsigned char) **name)) + (*name)++; + /* Decide whether this looks like an option (leading `-') or not. */ + non_opt = (**name != '-'); + /* Skip until part of name used for sorting. */ + while (**name && !isalnum ( (unsigned char) **name)) + (*name)++; + return non_opt; +} + +/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help + listing. */ +static int +hol_entry_cmp (const struct hol_entry *entry1, + const struct hol_entry *entry2) +{ + /* The group numbers by which the entries should be ordered; if either is + in a cluster, then this is just the group within the cluster. */ + int group1 = entry1->group, group2 = entry2->group; + + if (entry1->cluster != entry2->cluster) + { + /* The entries are not within the same cluster, so we can't compare them + directly, we have to use the appropiate clustering level too. */ + if (! entry1->cluster) + /* ENTRY1 is at the `base level', not in a cluster, so we have to + compare it's group number with that of the base cluster in which + ENTRY2 resides. Note that if they're in the same group, the + clustered option always comes laster. */ + return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); + else if (! entry2->cluster) + /* Likewise, but ENTRY2's not in a cluster. */ + return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); + else + /* Both entries are in clusters, we can just compare the clusters. */ + return hol_cluster_cmp (entry1->cluster, entry2->cluster); + } + else if (group1 == group2) + /* The entries are both in the same cluster and group, so compare them + alphabetically. */ + { + int short1 = hol_entry_first_short (entry1); + int short2 = hol_entry_first_short (entry2); + int doc1 = odoc (entry1->opt); + int doc2 = odoc (entry2->opt); + /* FIXME: Can we use unsigned char * instead? */ + const char *long1 = hol_entry_first_long (entry1); + const char *long2 = hol_entry_first_long (entry2); + + if (doc1) + doc1 = canon_doc_option (&long1); + if (doc2) + doc2 = canon_doc_option (&long2); + + if (doc1 != doc2) + /* `documentation' options always follow normal options (or + documentation options that *look* like normal options). */ + return doc1 - doc2; + else if (!short1 && !short2 && long1 && long2) + /* Only long options. */ + return __strcasecmp (long1, long2); + else + /* Compare short/short, long/short, short/long, using the first + character of long options. Entries without *any* valid + options (such as options with OPTION_HIDDEN set) will be put + first, but as they're not displayed, it doesn't matter where + they are. */ + { + unsigned char first1 = short1 ? short1 : long1 ? *long1 : 0; + unsigned char first2 = short2 ? short2 : long2 ? *long2 : 0; +#ifdef _tolower + int lower_cmp = _tolower (first1) - _tolower (first2); +#else + int lower_cmp = tolower (first1) - tolower (first2); +#endif + /* Compare ignoring case, except when the options are both the + same letter, in which case lower-case always comes first. */ + /* NOTE: The subtraction below does the right thing + even with eight-bit chars: first1 and first2 are + converted to int *before* the subtraction. */ + return lower_cmp ? lower_cmp : first2 - first1; + } + } + else + /* Within the same cluster, but not the same group, so just compare + groups. */ + return group_cmp (group1, group2, 0); +} + +/* Version of hol_entry_cmp with correct signature for qsort. */ +static int +hol_entry_qcmp (const void *entry1_v, const void *entry2_v) +{ + return hol_entry_cmp (entry1_v, entry2_v); +} + +/* Sort HOL by group and alphabetically by option name (with short options + taking precedence over long). Since the sorting is for display purposes + only, the shadowing of options isn't effected. */ +static void +hol_sort (struct hol *hol) +{ + if (hol->num_entries > 0) + qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), + hol_entry_qcmp); +} + +/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow + any in MORE with the same name. */ +static void +hol_append (struct hol *hol, struct hol *more) +{ + struct hol_cluster **cl_end = &hol->clusters; + + /* Steal MORE's cluster list, and add it to the end of HOL's. */ + while (*cl_end) + cl_end = &(*cl_end)->next; + *cl_end = more->clusters; + more->clusters = 0; + + /* Merge entries. */ + if (more->num_entries > 0) + { + if (hol->num_entries == 0) + { + hol->num_entries = more->num_entries; + hol->entries = more->entries; + hol->short_options = more->short_options; + more->num_entries = 0; /* Mark MORE's fields as invalid. */ + } + else + /* Append the entries in MORE to those in HOL, taking care to only add + non-shadowed SHORT_OPTIONS values. */ + { + unsigned left; + char *so, *more_so; + struct hol_entry *e; + unsigned num_entries = hol->num_entries + more->num_entries; + struct hol_entry *entries = + malloc (num_entries * sizeof (struct hol_entry)); + unsigned hol_so_len = strlen (hol->short_options); + char *short_options = + malloc (hol_so_len + strlen (more->short_options) + 1); + + __mempcpy (__mempcpy (entries, hol->entries, + hol->num_entries * sizeof (struct hol_entry)), + more->entries, + more->num_entries * sizeof (struct hol_entry)); + + __mempcpy (short_options, hol->short_options, hol_so_len); + + /* Fix up the short options pointers from HOL. */ + for (e = entries, left = hol->num_entries; left > 0; e++, left--) + e->short_options += (short_options - hol->short_options); + + /* Now add the short options from MORE, fixing up its entries + too. */ + so = short_options + hol_so_len; + more_so = more->short_options; + for (left = more->num_entries; left > 0; e++, left--) + { + int opts_left; + const struct argp_option *opt; + + e->short_options = so; + + for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) + { + int ch = *more_so; + if (oshort (opt) && ch == opt->key) + /* The next short option in MORE_SO, CH, is from OPT. */ + { + if (! find_char (ch, short_options, + short_options + hol_so_len)) + /* The short option CH isn't shadowed by HOL's options, + so add it to the sum. */ + *so++ = ch; + more_so++; + } + } + } + + *so = '\0'; + + free (hol->entries); + free (hol->short_options); + + hol->entries = entries; + hol->num_entries = num_entries; + hol->short_options = short_options; + } + } + + hol_free (more); +} + +/* Inserts enough spaces to make sure STREAM is at column COL. */ +static void +indent_to (argp_fmtstream_t stream, unsigned col) +{ + int needed = col - __argp_fmtstream_point (stream); + while (needed-- > 0) + __argp_fmtstream_putc (stream, ' '); +} + +/* Output to STREAM either a space, or a newline if there isn't room for at + least ENSURE characters before the right margin. */ +static void +space (argp_fmtstream_t stream, size_t ensure) +{ + if (__argp_fmtstream_point (stream) + ensure + >= __argp_fmtstream_rmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + else + __argp_fmtstream_putc (stream, ' '); +} + +/* If the option REAL has an argument, we print it in using the printf + format REQ_FMT or OPT_FMT depending on whether it's a required or + optional argument. */ +static void +arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, + const char *domain UNUSED, argp_fmtstream_t stream) +{ + if (real->arg) + { + if (real->flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, opt_fmt, + dgettext (domain, real->arg)); + else + __argp_fmtstream_printf (stream, req_fmt, + dgettext (domain, real->arg)); + } +} + +/* Helper functions for hol_entry_help. */ + +/* State used during the execution of hol_help. */ +struct hol_help_state +{ + /* PREV_ENTRY should contain the previous entry printed, or 0. */ + struct hol_entry *prev_entry; + + /* If an entry is in a different group from the previous one, and SEP_GROUPS + is true, then a blank line will be printed before any output. */ + int sep_groups; + + /* True if a duplicate option argument was suppressed (only ever set if + UPARAMS.dup_args is false). */ + int suppressed_dup_arg; +}; + +/* Some state used while printing a help entry (used to communicate with + helper functions). See the doc for hol_entry_help for more info, as most + of the fields are copied from its arguments. */ +struct pentry_state +{ + const struct hol_entry *entry; + argp_fmtstream_t stream; + struct hol_help_state *hhstate; + + /* True if nothing's been printed so far. */ + int first; + + /* If non-zero, the state that was used to print this help. */ + const struct argp_state *state; +}; + +/* If a user doc filter should be applied to DOC, do so. */ +static const char * +filter_doc (const char *doc, int key, const struct argp *argp, + const struct argp_state *state) +{ + if (argp->help_filter) + /* We must apply a user filter to this output. */ + { + void *input = __argp_input (argp, state); + return (*argp->help_filter) (key, doc, input); + } + else + /* No filter. */ + return doc; +} + +/* Prints STR as a header line, with the margin lines set appropiately, and + notes the fact that groups should be separated with a blank line. ARGP is + the argp that should dictate any user doc filtering to take place. Note + that the previous wrap margin isn't restored, but the left margin is reset + to 0. */ +static void +print_header (const char *str, const struct argp *argp, + struct pentry_state *pest) +{ + const char *tstr = dgettext (argp->argp_domain, str); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state); + + if (fstr) + { + if (*fstr) + { + if (pest->hhstate->prev_entry) + /* Precede with a blank line. */ + __argp_fmtstream_putc (pest->stream, '\n'); + indent_to (pest->stream, uparams.header_col); + __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col); + __argp_fmtstream_set_wmargin (pest->stream, uparams.header_col); + __argp_fmtstream_puts (pest->stream, fstr); + __argp_fmtstream_set_lmargin (pest->stream, 0); + __argp_fmtstream_putc (pest->stream, '\n'); + } + + pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */ + } + + if (fstr != tstr) + free ((char *) fstr); +} + +/* Inserts a comma if this isn't the first item on the line, and then makes + sure we're at least to column COL. If this *is* the first item on a line, + prints any pending whitespace/headers that should precede this line. Also + clears FIRST. */ +static void +comma (unsigned col, struct pentry_state *pest) +{ + if (pest->first) + { + const struct hol_entry *pe = pest->hhstate->prev_entry; + const struct hol_cluster *cl = pest->entry->cluster; + + if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group) + __argp_fmtstream_putc (pest->stream, '\n'); + + if (cl && cl->header && *cl->header + && (!pe + || (pe->cluster != cl + && !hol_cluster_is_child (pe->cluster, cl)))) + /* If we're changing clusters, then this must be the start of the + ENTRY's cluster unless that is an ancestor of the previous one + (in which case we had just popped into a sub-cluster for a bit). + If so, then print the cluster's header line. */ + { + int old_wm = __argp_fmtstream_wmargin (pest->stream); + print_header (cl->header, cl->argp, pest); + __argp_fmtstream_set_wmargin (pest->stream, old_wm); + } + + pest->first = 0; + } + else + __argp_fmtstream_puts (pest->stream, ", "); + + indent_to (pest->stream, col); +} + +/* Print help for ENTRY to STREAM. */ +static void +hol_entry_help (struct hol_entry *entry, const struct argp_state *state, + argp_fmtstream_t stream, struct hol_help_state *hhstate) +{ + unsigned num; + const struct argp_option *real = entry->opt, *opt; + char *so = entry->short_options; + int have_long_opt = 0; /* We have any long options. */ + /* Saved margins. */ + int old_lm = __argp_fmtstream_set_lmargin (stream, 0); + int old_wm = __argp_fmtstream_wmargin (stream); + /* PEST is a state block holding some of our variables that we'd like to + share with helper functions. */ + + /* Decent initializers are a GNU extension, so don't use it here. */ + struct pentry_state pest; + pest.entry = entry; + pest.stream = stream; + pest.hhstate = hhstate; + pest.first = 1; + pest.state = state; + + if (! odoc (real)) + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + have_long_opt = 1; + break; + } + + /* First emit short options. */ + __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */ + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (oshort (opt) && opt->key == *so) + /* OPT has a valid (non shadowed) short option. */ + { + if (ovisible (opt)) + { + comma (uparams.short_opt_col, &pest); + __argp_fmtstream_putc (stream, '-'); + __argp_fmtstream_putc (stream, *so); + if (!have_long_opt || uparams.dup_args) + arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + so++; + } + + /* Now, long options. */ + if (odoc (real)) + /* A `documentation' option. */ + { + __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.doc_opt_col, &pest); + /* Calling gettext here isn't quite right, since sorting will + have been done on the original; but documentation options + should be pretty rare anyway... */ + __argp_fmtstream_puts (stream, + dgettext (state->root_argp->argp_domain, + opt->name)); + } + } + else + /* A real long option. */ + { + int first_long_opt = 1; + + __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.long_opt_col, &pest); + __argp_fmtstream_printf (stream, "--%s", opt->name); + if (first_long_opt || uparams.dup_args) + arg (real, "=%s", "[=%s]", state->root_argp->argp_domain, + stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + } + + /* Next, documentation strings. */ + __argp_fmtstream_set_lmargin (stream, 0); + + if (pest.first) + { + /* Didn't print any switches, what's up? */ + if (!oshort (real) && !real->name) + /* This is a group header, print it nicely. */ + print_header (real->doc, entry->argp, &pest); + else + /* Just a totally shadowed option or null header; print nothing. */ + goto cleanup; /* Just return, after cleaning up. */ + } + else + { + const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain, + real->doc) : 0; + const char *fstr = filter_doc (tstr, real->key, entry->argp, state); + if (fstr && *fstr) + { + unsigned int col = __argp_fmtstream_point (stream); + + __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col); + __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col); + + if (col > (unsigned int) (uparams.opt_doc_col + 3)) + __argp_fmtstream_putc (stream, '\n'); + else if (col >= (unsigned int) uparams.opt_doc_col) + __argp_fmtstream_puts (stream, " "); + else + indent_to (stream, uparams.opt_doc_col); + + __argp_fmtstream_puts (stream, fstr); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + + /* Reset the left margin. */ + __argp_fmtstream_set_lmargin (stream, 0); + __argp_fmtstream_putc (stream, '\n'); + } + + hhstate->prev_entry = entry; + +cleanup: + __argp_fmtstream_set_lmargin (stream, old_lm); + __argp_fmtstream_set_wmargin (stream, old_wm); +} + +/* Output a long help message about the options in HOL to STREAM. */ +static void +hol_help (struct hol *hol, const struct argp_state *state, + argp_fmtstream_t stream) +{ + unsigned num; + struct hol_entry *entry; + struct hol_help_state hhstate = { 0, 0, 0 }; + + for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) + hol_entry_help (entry, state, stream, &hhstate); + + if (hhstate.suppressed_dup_arg && uparams.dup_args_note) + { + const char *tstr = dgettext (state->root_argp->argp_domain, "\ +Mandatory or optional arguments to long options are also mandatory or \ +optional for any corresponding short options."); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, + state ? state->root_argp : 0, state); + if (fstr && *fstr) + { + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, fstr); + __argp_fmtstream_putc (stream, '\n'); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + } +} + +/* Helper functions for hol_usage. */ + +/* If OPT is a short option without an arg, append its key to the string + pointer pointer to by COOKIE, and advance the pointer. */ +static int +add_argless_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain UNUSED, void *cookie) +{ + char **snao_end = cookie; + if (!(opt->arg || real->arg) + && !((opt->flags | real->flags) & OPTION_NO_USAGE)) + *(*snao_end)++ = opt->key; + return 0; +} + +/* If OPT is a short option with an arg, output a usage entry for it to the + stream pointed at by COOKIE. */ +static int +usage_argful_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain UNUSED, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (arg && !(flags & OPTION_NO_USAGE)) + { + arg = dgettext (domain, arg); + + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg); + else + { + /* Manually do line wrapping so that it (probably) won't + get wrapped at the embedded space. */ + space (stream, 6 + strlen (arg)); + __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); + } + } + + return 0; +} + +/* Output a usage entry for the long option opt to the stream pointed at by + COOKIE. */ +static int +usage_long_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain UNUSED, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (! (flags & OPTION_NO_USAGE)) + { + if (arg) + { + arg = dgettext (domain, arg); + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); + else + __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); + } + else + __argp_fmtstream_printf (stream, " [--%s]", opt->name); + } + + return 0; +} + +/* Print a short usage description for the arguments in HOL to STREAM. */ +static void +hol_usage (struct hol *hol, argp_fmtstream_t stream) +{ + if (hol->num_entries > 0) + { + unsigned nentries; + struct hol_entry *entry; + char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1); + char *snao_end = short_no_arg_opts; + + /* First we put a list of short options without arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, add_argless_short_opt, + entry->argp->argp_domain, &snao_end); + if (snao_end > short_no_arg_opts) + { + *snao_end++ = 0; + __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts); + } + + /* Now a list of short options *with* arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, usage_argful_short_opt, + entry->argp->argp_domain, stream); + + /* Finally, a list of long options (whew!). */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_long_iterate (entry, usage_long_opt, + entry->argp->argp_domain, stream); + } +} + +/* Make a HOL containing all levels of options in ARGP. CLUSTER is the + cluster in which ARGP's entries should be clustered, or 0. */ +static struct hol * +argp_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + const struct argp_child *child = argp->children; + struct hol *hol = make_hol (argp, cluster); + if (child) + while (child->argp) + { + struct hol_cluster *child_cluster = + ((child->group || child->header) + /* Put CHILD->argp within its own cluster. */ + ? hol_add_cluster (hol, child->group, child->header, + child - argp->children, cluster, argp) + /* Just merge it into the parent's cluster. */ + : cluster); + hol_append (hol, argp_hol (child->argp, child_cluster)) ; + child++; + } + return hol; +} + +/* Calculate how many different levels with alternative args strings exist in + ARGP. */ +static size_t +argp_args_levels (const struct argp *argp) +{ + size_t levels = 0; + const struct argp_child *child = argp->children; + + if (argp->args_doc && strchr (argp->args_doc, '\n')) + levels++; + + if (child) + while (child->argp) + levels += argp_args_levels ((child++)->argp); + + return levels; +} + +/* Print all the non-option args documented in ARGP to STREAM. Any output is + preceded by a space. LEVELS is a pointer to a byte vector the length + returned by argp_args_levels; it should be initialized to zero, and + updated by this routine for the next call if ADVANCE is true. True is + returned as long as there are more patterns to output. */ +static int +argp_args_usage (const struct argp *argp, const struct argp_state *state, + char **levels, int advance, argp_fmtstream_t stream) +{ + char *our_level = *levels; + int multiple = 0; + const struct argp_child *child = argp->children; + const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0; + const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); + + if (fdoc) + { + const char *cp = fdoc; + nl = __strchrnul (cp, '\n'); + if (*nl != '\0') + /* This is a `multi-level' args doc; advance to the correct position + as determined by our state in LEVELS, and update LEVELS. */ + { + int i; + multiple = 1; + for (i = 0; i < *our_level; i++) + cp = nl + 1, nl = __strchrnul (cp, '\n'); + (*levels)++; + } + + /* Manually do line wrapping so that it (probably) won't get wrapped at + any embedded spaces. */ + space (stream, 1 + nl - cp); + + __argp_fmtstream_write (stream, cp, nl - cp); + } + if (fdoc && fdoc != tdoc) + free ((char *)fdoc); /* Free user's modified doc string. */ + + if (child) + while (child->argp) + advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream); + + if (advance && multiple) + { + /* Need to increment our level. */ + if (*nl) + /* There's more we can do here. */ + { + (*our_level)++; + advance = 0; /* Our parent shouldn't advance also. */ + } + else if (*our_level > 0) + /* We had multiple levels, but used them up; reset to zero. */ + *our_level = 0; + } + + return !advance; +} + +/* Print the documentation for ARGP to STREAM; if POST is false, then + everything preceeding a `\v' character in the documentation strings (or + the whole string, for those with none) is printed, otherwise, everything + following the `\v' character (nothing for strings without). Each separate + bit of documentation is separated a blank line, and if PRE_BLANK is true, + then the first is as well. If FIRST_ONLY is true, only the first + occurrence is output. Returns true if anything was output. */ +static int +argp_doc (const struct argp *argp, const struct argp_state *state, + int post, int pre_blank, int first_only, + argp_fmtstream_t stream) +{ + const char *text; + const char *inp_text; + void *input = 0; + int anything = 0; + size_t inp_text_limit = 0; + const char *doc = dgettext (argp->argp_domain, argp->doc); + const struct argp_child *child = argp->children; + + if (doc) + { + char *vt = strchr (doc, '\v'); + inp_text = post ? (vt ? vt + 1 : 0) : doc; + inp_text_limit = (!post && vt) ? (vt - doc) : 0; + } + else + inp_text = 0; + + if (argp->help_filter) + /* We have to filter the doc strings. */ + { + if (inp_text_limit) + /* Copy INP_TEXT so that it's nul-terminated. */ + inp_text = STRNDUP (inp_text, inp_text_limit); + input = __argp_input (argp, state); + text = + (*argp->help_filter) (post + ? ARGP_KEY_HELP_POST_DOC + : ARGP_KEY_HELP_PRE_DOC, + inp_text, input); + } + else + text = (const char *) inp_text; + + if (text) + { + if (pre_blank) + __argp_fmtstream_putc (stream, '\n'); + + if (text == inp_text && inp_text_limit) + __argp_fmtstream_write (stream, inp_text, inp_text_limit); + else + __argp_fmtstream_puts (stream, text); + + if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + + anything = 1; + } + + if (text && text != inp_text) + free ((char *) text); /* Free TEXT returned from the help filter. */ + if (inp_text && inp_text_limit && argp->help_filter) + free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ + + if (post && argp->help_filter) + /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ + { + text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); + if (text) + { + if (anything || pre_blank) + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, text); + free ((char *) text); + if (__argp_fmtstream_point (stream) + > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + anything = 1; + } + } + + if (child) + while (child->argp && !(first_only && anything)) + anything |= + argp_doc ((child++)->argp, state, + post, anything || pre_blank, first_only, + stream); + + return anything; +} + +/* Output a usage message for ARGP to STREAM. If called from + argp_state_help, STATE is the relevent parsing state. FLAGS are from the + set ARGP_HELP_*. NAME is what to use wherever a `program name' is + needed. */ + +static void +_help (const struct argp *argp, const struct argp_state *state, FILE *stream, + unsigned flags, const char *name) +{ + int anything = 0; /* Whether we've output anything. */ + struct hol *hol = 0; + argp_fmtstream_t fs; + + if (! stream) + return; + + FLOCKFILE (stream); + + if (! uparams.valid) + fill_in_uparams (state); + + fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0); + if (! fs) + { + FUNLOCKFILE (stream); + return; + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) + { + hol = argp_hol (argp, 0); + + /* If present, these options always come last. */ + hol_set_group (hol, "help", -1); + hol_set_group (hol, "version", -1); + + hol_sort (hol); + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE)) + /* Print a short `Usage:' message. */ + { + int first_pattern = 1, more_patterns; + size_t num_pattern_levels = argp_args_levels (argp); + char *pattern_levels = alloca (num_pattern_levels); + + memset (pattern_levels, 0, num_pattern_levels); + + do + { + int old_lm; + int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent); + char *levels = pattern_levels; + + if (first_pattern) + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, "Usage:"), + name); + else + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, " or: "), + name); + + /* We set the lmargin as well as the wmargin, because hol_usage + manually wraps options with newline to avoid annoying breaks. */ + old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent); + + if (flags & ARGP_HELP_SHORT_USAGE) + /* Just show where the options go. */ + { + if (hol->num_entries > 0) + __argp_fmtstream_puts (fs, dgettext (argp->argp_domain, + " [OPTION...]")); + } + else + /* Actually print the options. */ + { + hol_usage (hol, fs); + flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. */ + } + + more_patterns = argp_args_usage (argp, state, &levels, 1, fs); + + __argp_fmtstream_set_wmargin (fs, old_wm); + __argp_fmtstream_set_lmargin (fs, old_lm); + + __argp_fmtstream_putc (fs, '\n'); + anything = 1; + + first_pattern = 0; + } + while (more_patterns); + } + + if (flags & ARGP_HELP_PRE_DOC) + anything |= argp_doc (argp, state, 0, 0, 1, fs); + + if (flags & ARGP_HELP_SEE) + { + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\ +Try `%s --help' or `%s --usage' for more information.\n"), + name, name); + anything = 1; + } + + if (flags & ARGP_HELP_LONG) + /* Print a long, detailed help message. */ + { + /* Print info about all the options. */ + if (hol->num_entries > 0) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + hol_help (hol, state, fs); + anything = 1; + } + } + + if (flags & ARGP_HELP_POST_DOC) + /* Print any documentation strings at the end. */ + anything |= argp_doc (argp, state, 1, anything, 0, fs); + + if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, + "Report bugs to %s.\n"), + argp_program_bug_address); + anything = 1; + } + + FUNLOCKFILE (stream); + + if (hol) + hol_free (hol); + + __argp_fmtstream_free (fs); +} + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ +void __argp_help (const struct argp *argp, FILE *stream, + unsigned flags, char *name) +{ + _help (argp, 0, stream, flags, name); +} +#ifdef weak_alias +weak_alias (__argp_help, argp_help) +#endif + +char *__argp_basename(char *name) +{ + char *short_name = strrchr(name, '/'); + return short_name ? short_name + 1 : name; +} + +char * +__argp_short_program_name(const struct argp_state *state) +{ + if (state) + return state->name; +#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + return program_invocation_short_name; +#elif HAVE_DECL_PROGRAM_INVOCATION_NAME + return __argp_basename(program_invocation_name); +#else /* !HAVE_DECL_PROGRAM_INVOCATION_NAME */ + /* FIXME: What now? Miles suggests that it is better to use NULL, + but currently the value is passed on directly to fputs_unlocked, + so that requires more changes. */ +# if __GNUC__ + return ""; +# endif /* __GNUC__ */ +#endif /* !HAVE_DECL_PROGRAM_INVOCATION_NAME */ +} + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +void +__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) +{ + if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream) + { + if (state && (state->flags & ARGP_LONG_ONLY)) + flags |= ARGP_HELP_LONG_ONLY; + + _help (state ? state->root_argp : 0, state, stream, flags, + __argp_short_program_name(state)); + + if (!state || ! (state->flags & ARGP_NO_EXIT)) + { + if (flags & ARGP_HELP_EXIT_ERR) + exit (argp_err_exit_status); + if (flags & ARGP_HELP_EXIT_OK) + exit (0); + } + } +} +#ifdef weak_alias +weak_alias (__argp_state_help, argp_state_help) +#endif + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and `:', to stderr, and followed by a `Try ... --help' + message, then exit (1). */ +void +__argp_error (const struct argp_state *state, const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { + va_list ap; + + FLOCKFILE (stream); + + FPUTS_UNLOCKED (__argp_short_program_name(state), + stream); + PUTC_UNLOCKED (':', stream); + PUTC_UNLOCKED (' ', stream); + + va_start (ap, fmt); + vfprintf (stream, fmt, ap); + va_end (ap); + + PUTC_UNLOCKED ('\n', stream); + + __argp_state_help (state, stream, ARGP_HELP_STD_ERR); + + FUNLOCKFILE (stream); + } + } +} +#ifdef weak_alias +weak_alias (__argp_error, argp_error) +#endif + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +void +__argp_failure (const struct argp_state *state, int status, int errnum, + const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { + FLOCKFILE (stream); + + FPUTS_UNLOCKED (__argp_short_program_name(state), + stream); + + if (fmt) + { + va_list ap; + + PUTC_UNLOCKED (':', stream); + PUTC_UNLOCKED (' ', stream); + + va_start (ap, fmt); + vfprintf (stream, fmt, ap); + va_end (ap); + } + + if (errnum) + { + PUTC_UNLOCKED (':', stream); + PUTC_UNLOCKED (' ', stream); + fputs (STRERROR (errnum), stream); + } + + PUTC_UNLOCKED ('\n', stream); + + FUNLOCKFILE (stream); + + if (status && (!state || !(state->flags & ARGP_NO_EXIT))) + exit (status); + } + } +} +#ifdef weak_alias +weak_alias (__argp_failure, argp_failure) +#endif diff --git a/contrib/argp-standalone/argp-namefrob.h b/contrib/argp-standalone/argp-namefrob.h new file mode 100644 index 000000000..0ce11481a --- /dev/null +++ b/contrib/argp-standalone/argp-namefrob.h @@ -0,0 +1,96 @@ +/* Name frobnication for compiling argp outside of glibc + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#if !_LIBC +/* This code is written for inclusion in gnu-libc, and uses names in the + namespace reserved for libc. If we're not compiling in libc, define those + names to be the normal ones instead. */ + +/* argp-parse functions */ +#undef __argp_parse +#define __argp_parse argp_parse +#undef __option_is_end +#define __option_is_end _option_is_end +#undef __option_is_short +#define __option_is_short _option_is_short +#undef __argp_input +#define __argp_input _argp_input + +/* argp-help functions */ +#undef __argp_help +#define __argp_help argp_help +#undef __argp_error +#define __argp_error argp_error +#undef __argp_failure +#define __argp_failure argp_failure +#undef __argp_state_help +#define __argp_state_help argp_state_help +#undef __argp_usage +#define __argp_usage argp_usage +#undef __argp_basename +#define __argp_basename _argp_basename +#undef __argp_short_program_name +#define __argp_short_program_name _argp_short_program_name + +/* argp-fmtstream functions */ +#undef __argp_make_fmtstream +#define __argp_make_fmtstream argp_make_fmtstream +#undef __argp_fmtstream_free +#define __argp_fmtstream_free argp_fmtstream_free +#undef __argp_fmtstream_putc +#define __argp_fmtstream_putc argp_fmtstream_putc +#undef __argp_fmtstream_puts +#define __argp_fmtstream_puts argp_fmtstream_puts +#undef __argp_fmtstream_write +#define __argp_fmtstream_write argp_fmtstream_write +#undef __argp_fmtstream_printf +#define __argp_fmtstream_printf argp_fmtstream_printf +#undef __argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#define __argp_fmtstream_point argp_fmtstream_point +#undef __argp_fmtstream_update +#define __argp_fmtstream_update _argp_fmtstream_update +#undef __argp_fmtstream_ensure +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#undef __argp_fmtstream_lmargin +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#undef __argp_fmtstream_rmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#undef __argp_fmtstream_wmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +/* normal libc functions we call */ +#undef __sleep +#define __sleep sleep +#undef __strcasecmp +#define __strcasecmp strcasecmp +#undef __vsnprintf +#define __vsnprintf vsnprintf + +#endif /* !_LIBC */ + +#ifndef __set_errno +#define __set_errno(e) (errno = (e)) +#endif diff --git a/contrib/argp-standalone/argp-parse.c b/contrib/argp-standalone/argp-parse.c new file mode 100644 index 000000000..78f7bf139 --- /dev/null +++ b/contrib/argp-standalone/argp-parse.c @@ -0,0 +1,1305 @@ +/* Hierarchial argument parsing + Copyright (C) 1995, 96, 97, 98, 99, 2000,2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if HAVE_ALLOCA_H +#include +#endif + +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#include +#include + +#if HAVE_MALLOC_H +/* Needed, for alloca on windows */ +# include +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include +# ifdef _LIBC +# undef dgettext +# define dgettext(domain, msgid) __dcgettext (domain, msgid, LC_MESSAGES) +# endif +# else +# define dgettext(domain, msgid) (msgid) +# define gettext(msgid) (msgid) +# endif +#endif +#ifndef N_ +# define N_(msgid) (msgid) +#endif + +#if _LIBC - 0 +#include +#else +#ifdef HAVE_CTHREADS_H +#include +#endif +#endif /* _LIBC */ + +#include "argp.h" +#include "argp-namefrob.h" + + +/* The meta-argument used to prevent any further arguments being interpreted + as options. */ +#define QUOTE "--" + +/* EZ alias for ARGP_ERR_UNKNOWN. */ +#define EBADKEY ARGP_ERR_UNKNOWN + + +/* Default options. */ + +/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep + for one second intervals, decrementing _ARGP_HANG until it's zero. Thus + you can force the program to continue by attaching a debugger and setting + it to 0 yourself. */ +volatile int _argp_hang; + +#define OPT_PROGNAME -2 +#define OPT_USAGE -3 +#if HAVE_SLEEP && HAVE_GETPID +#define OPT_HANG -4 +#endif + +static const struct argp_option argp_default_options[] = +{ + {"help", '?', 0, 0, N_("Give this help list"), -1}, + {"usage", OPT_USAGE, 0, 0, N_("Give a short usage message"), 0 }, + {"program-name",OPT_PROGNAME,"NAME", OPTION_HIDDEN, + N_("Set the program name"), 0}, +#if OPT_HANG + {"HANG", OPT_HANG, "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN, + N_("Hang for SECS seconds (default 3600)"), 0 }, +#endif + {0, 0, 0, 0, 0, 0} +}; + +static error_t +argp_default_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case '?': + __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); + break; + case OPT_USAGE: + __argp_state_help (state, state->out_stream, + ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); + break; + + case OPT_PROGNAME: /* Set the program name. */ +#if HAVE_DECL_PROGRAM_INVOCATION_NAME + program_invocation_name = arg; +#endif + /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka + __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined + to be that, so we have to be a bit careful here.] */ + + /* Update what we use for messages. */ + + state->name = __argp_basename(arg); + +#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + program_invocation_short_name = state->name; +#endif + + if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) + == ARGP_PARSE_ARGV0) + /* Update what getopt uses too. */ + state->argv[0] = arg; + + break; + +#if OPT_HANG + case OPT_HANG: + _argp_hang = atoi (arg ? arg : "3600"); + fprintf(state->err_stream, "%s: pid = %ld\n", + state->name, (long) getpid()); + while (_argp_hang-- > 0) + __sleep (1); + break; +#endif + + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_default_argp = + {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"}; + + +static const struct argp_option argp_version_options[] = +{ + {"version", 'V', 0, 0, N_("Print program version"), -1}, + {0, 0, 0, 0, 0, 0 } +}; + +static error_t +argp_version_parser (int key, char *arg UNUSED, struct argp_state *state) +{ + switch (key) + { + case 'V': + if (argp_program_version_hook) + (*argp_program_version_hook) (state->out_stream, state); + else if (argp_program_version) + fprintf (state->out_stream, "%s\n", argp_program_version); + else + __argp_error (state, dgettext (state->root_argp->argp_domain, + "(PROGRAM ERROR) No version known!?")); + if (! (state->flags & ARGP_NO_EXIT)) + exit (0); + break; + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_version_argp = + {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"}; + + + +/* The state of a `group' during parsing. Each group corresponds to a + particular argp structure from the tree of such descending from the top + level argp passed to argp_parse. */ +struct group +{ + /* This group's parsing function. */ + argp_parser_t parser; + + /* Which argp this group is from. */ + const struct argp *argp; + + /* The number of non-option args sucessfully handled by this parser. */ + unsigned args_processed; + + /* This group's parser's parent's group. */ + struct group *parent; + unsigned parent_index; /* And the our position in the parent. */ + + /* These fields are swapped into and out of the state structure when + calling this group's parser. */ + void *input, **child_inputs; + void *hook; +}; + +/* Call GROUP's parser with KEY and ARG, swapping any group-specific info + from STATE before calling, and back into state afterwards. If GROUP has + no parser, EBADKEY is returned. */ +static error_t +group_parse (struct group *group, struct argp_state *state, int key, char *arg) +{ + if (group->parser) + { + error_t err; + state->hook = group->hook; + state->input = group->input; + state->child_inputs = group->child_inputs; + state->arg_num = group->args_processed; + err = (*group->parser)(key, arg, state); + group->hook = state->hook; + return err; + } + else + return EBADKEY; +} + +struct parser +{ + const struct argp *argp; + + const char *posixly_correct; + + /* True if there are only no-option arguments left, which are just + passed verbatim with ARGP_KEY_ARG. This is set if we encounter a + quote, or the end of the proper options, but may be cleared again + if the user moves the next argument pointer backwards. */ + int args_only; + + /* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, the default is + REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is + defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; stop option + processing when the first non-option is seen. This is what Unix + does. This mode of operation is selected by either setting the + environment variable POSIXLY_CORRECT, or using `+' as the first + character of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we + scan, so that eventually all the non-options are at the end. This + allows options to be given in any order, even with programs that + were not written to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. Using `-' as the first character of the + list of option characters selects this mode of operation. + + */ + enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; + + /* A segment of non-option arguments that have been skipped for + later processing, after all options. `first_nonopt' is the index + in ARGV of the first of them; `last_nonopt' is the index after + the last of them. + + If quoted or args_only is non-zero, this segment should be empty. */ + + /* FIXME: I'd prefer to use unsigned, but it's more consistent to + use the same type as for state.next. */ + int first_nonopt; + int last_nonopt; + + /* String of all recognized short options. Needed for ARGP_LONG_ONLY. */ + /* FIXME: Perhaps change to a pointer to a suitable bitmap instead? */ + char *short_opts; + + /* For parsing combined short options. */ + char *nextchar; + + /* States of the various parsing groups. */ + struct group *groups; + /* The end of the GROUPS array. */ + struct group *egroup; + /* An vector containing storage for the CHILD_INPUTS field in all groups. */ + void **child_inputs; + + /* State block supplied to parsing routines. */ + struct argp_state state; + + /* Memory used by this parser. */ + void *storage; +}; + +/* Search for a group defining a short option. */ +static const struct argp_option * +find_short_option(struct parser *parser, int key, struct group **p) +{ + struct group *group; + + assert(key >= 0); + assert(isascii(key)); + + for (group = parser->groups; group < parser->egroup; group++) + { + const struct argp_option *opts; + + for (opts = group->argp->options; !__option_is_end(opts); opts++) + if (opts->key == key) + { + *p = group; + return opts; + } + } + return NULL; +} + +enum match_result { MATCH_EXACT, MATCH_PARTIAL, MATCH_NO }; + +/* If defined, allow complete.el-like abbreviations of long options. */ +#ifndef ARGP_COMPLETE +#define ARGP_COMPLETE 0 +#endif + +/* Matches an encountern long-option argument ARG against an option NAME. + * ARG is terminated by NUL or '='. */ +static enum match_result +match_option(const char *arg, const char *name) +{ + unsigned i, j; + for (i = j = 0;; i++, j++) + { + switch(arg[i]) + { + case '\0': + case '=': + return name[j] ? MATCH_PARTIAL : MATCH_EXACT; +#if ARGP_COMPLETE + case '-': + while (name[j] != '-') + if (!name[j++]) + return MATCH_NO; + break; +#endif + default: + if (arg[i] != name[j]) + return MATCH_NO; + } + } +} + +static const struct argp_option * +find_long_option(struct parser *parser, + const char *arg, + struct group **p) +{ + struct group *group; + + /* Partial match found so far. */ + struct group *matched_group = NULL; + const struct argp_option *matched_option = NULL; + + /* Number of partial matches. */ + int num_partial = 0; + + for (group = parser->groups; group < parser->egroup; group++) + { + const struct argp_option *opts; + + for (opts = group->argp->options; !__option_is_end(opts); opts++) + { + if (!opts->name) + continue; + switch (match_option(arg, opts->name)) + { + case MATCH_NO: + break; + case MATCH_PARTIAL: + num_partial++; + + matched_group = group; + matched_option = opts; + + break; + case MATCH_EXACT: + /* Exact match. */ + *p = group; + return opts; + } + } + } + if (num_partial == 1) + { + *p = matched_group; + return matched_option; + } + + return NULL; +} + + +/* The next usable entries in the various parser tables being filled in by + convert_options. */ +struct parser_convert_state +{ + struct parser *parser; + char *short_end; + void **child_inputs_end; +}; + +/* Initialize GROUP from ARGP. If CVT->SHORT_END is non-NULL, short + options are recorded in the short options string. Returns the next + unused group entry. CVT holds state used during the conversion. */ +static struct group * +convert_options (const struct argp *argp, + struct group *parent, unsigned parent_index, + struct group *group, struct parser_convert_state *cvt) +{ + const struct argp_option *opt = argp->options; + const struct argp_child *children = argp->children; + + if (opt || argp->parser) + { + /* This parser needs a group. */ + if (cvt->short_end) + { + /* Record any short options. */ + for ( ; !__option_is_end (opt); opt++) + if (__option_is_short(opt)) + *cvt->short_end++ = opt->key; + } + + group->parser = argp->parser; + group->argp = argp; + group->args_processed = 0; + group->parent = parent; + group->parent_index = parent_index; + group->input = 0; + group->hook = 0; + group->child_inputs = 0; + + if (children) + /* Assign GROUP's CHILD_INPUTS field some space from + CVT->child_inputs_end.*/ + { + unsigned num_children = 0; + while (children[num_children].argp) + num_children++; + group->child_inputs = cvt->child_inputs_end; + cvt->child_inputs_end += num_children; + } + parent = group++; + } + else + parent = 0; + + if (children) + { + unsigned index = 0; + while (children->argp) + group = + convert_options (children++->argp, parent, index++, group, cvt); + } + + return group; +} +/* Allocate and initialize the group structures, so that they are + ordered as if by traversing the corresponding argp parser tree in + pre-order. Also build the list of short options, if that is needed. */ +static void +parser_convert (struct parser *parser, const struct argp *argp) +{ + struct parser_convert_state cvt; + + cvt.parser = parser; + cvt.short_end = parser->short_opts; + cvt.child_inputs_end = parser->child_inputs; + + parser->argp = argp; + + if (argp) + parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); + else + parser->egroup = parser->groups; /* No parsers at all! */ + + if (parser->short_opts) + *cvt.short_end ='\0'; +} + +/* Lengths of various parser fields which we will allocated. */ +struct parser_sizes +{ + /* Needed only ARGP_LONG_ONLY */ + size_t short_len; /* Number of short options. */ + + size_t num_groups; /* Group structures we allocate. */ + size_t num_child_inputs; /* Child input slots. */ +}; + +/* For ARGP, increments the NUM_GROUPS field in SZS by the total + number of argp structures descended from it, and the SHORT_LEN by + the total number of short options. */ +static void +calc_sizes (const struct argp *argp, struct parser_sizes *szs) +{ + const struct argp_child *child = argp->children; + const struct argp_option *opt = argp->options; + + if (opt || argp->parser) + { + /* This parser needs a group. */ + szs->num_groups++; + if (opt) + { + while (__option_is_short (opt++)) + szs->short_len++; + } + } + + if (child) + while (child->argp) + { + calc_sizes ((child++)->argp, szs); + szs->num_child_inputs++; + } +} + +/* Initializes PARSER to parse ARGP in a manner described by FLAGS. */ +static error_t +parser_init (struct parser *parser, const struct argp *argp, + int argc, char **argv, int flags, void *input) +{ + error_t err = 0; + struct group *group; + struct parser_sizes szs; + + parser->posixly_correct = getenv ("POSIXLY_CORRECT"); + + if (flags & ARGP_IN_ORDER) + parser->ordering = RETURN_IN_ORDER; + else if (flags & ARGP_NO_ARGS) + parser->ordering = REQUIRE_ORDER; + else if (parser->posixly_correct) + parser->ordering = REQUIRE_ORDER; + else + parser->ordering = PERMUTE; + + szs.short_len = 0; + szs.num_groups = 0; + szs.num_child_inputs = 0; + + if (argp) + calc_sizes (argp, &szs); + + if (!(flags & ARGP_LONG_ONLY)) + /* We have no use for the short option array. */ + szs.short_len = 0; + + /* Lengths of the various bits of storage used by PARSER. */ +#define GLEN (szs.num_groups + 1) * sizeof (struct group) +#define CLEN (szs.num_child_inputs * sizeof (void *)) +#define SLEN (szs.short_len + 1) +#define STORAGE(offset) ((void *) (((char *) parser->storage) + (offset))) + + parser->storage = malloc (GLEN + CLEN + SLEN); + if (! parser->storage) + return ENOMEM; + + parser->groups = parser->storage; + + parser->child_inputs = STORAGE(GLEN); + memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *)); + + if (flags & ARGP_LONG_ONLY) + parser->short_opts = STORAGE(GLEN + CLEN); + else + parser->short_opts = NULL; + + parser_convert (parser, argp); + + memset (&parser->state, 0, sizeof (struct argp_state)); + + parser->state.root_argp = parser->argp; + parser->state.argc = argc; + parser->state.argv = argv; + parser->state.flags = flags; + parser->state.err_stream = stderr; + parser->state.out_stream = stdout; + parser->state.pstate = parser; + + parser->args_only = 0; + parser->nextchar = NULL; + parser->first_nonopt = parser->last_nonopt = 0; + + /* Call each parser for the first time, giving it a chance to propagate + values to child parsers. */ + if (parser->groups < parser->egroup) + parser->groups->input = input; + for (group = parser->groups; + group < parser->egroup && (!err || err == EBADKEY); + group++) + { + if (group->parent) + /* If a child parser, get the initial input value from the parent. */ + group->input = group->parent->child_inputs[group->parent_index]; + + if (!group->parser + && group->argp->children && group->argp->children->argp) + /* For the special case where no parsing function is supplied for an + argp, propagate its input to its first child, if any (this just + makes very simple wrapper argps more convenient). */ + group->child_inputs[0] = group->input; + + err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); + } + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + if (err) + return err; + + if (argv[0] && !(parser->state.flags & ARGP_PARSE_ARGV0)) + /* There's an argv[0]; use it for messages. */ + { + parser->state.name = __argp_basename(argv[0]); + + /* Don't parse it as an argument. */ + parser->state.next = 1; + } + else + parser->state.name = __argp_short_program_name(NULL); + + return 0; +} + +/* Free any storage consumed by PARSER (but not PARSER itself). */ +static error_t +parser_finalize (struct parser *parser, + error_t err, int arg_ebadkey, int *end_index) +{ + struct group *group; + + if (err == EBADKEY && arg_ebadkey) + /* Suppress errors generated by unparsed arguments. */ + err = 0; + + if (! err) + { + if (parser->state.next == parser->state.argc) + /* We successfully parsed all arguments! Call all the parsers again, + just a few more times... */ + { + for (group = parser->groups; + group < parser->egroup && (!err || err==EBADKEY); + group++) + if (group->args_processed == 0) + err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); + for (group = parser->egroup - 1; + group >= parser->groups && (!err || err==EBADKEY); + group--) + err = group_parse (group, &parser->state, ARGP_KEY_END, 0); + + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + /* Tell the user that all arguments are parsed. */ + if (end_index) + *end_index = parser->state.next; + } + else if (end_index) + /* Return any remaining arguments to the user. */ + *end_index = parser->state.next; + else + /* No way to return the remaining arguments, they must be bogus. */ + { + if (!(parser->state.flags & ARGP_NO_ERRS) + && parser->state.err_stream) + fprintf (parser->state.err_stream, + dgettext (parser->argp->argp_domain, + "%s: Too many arguments\n"), + parser->state.name); + err = EBADKEY; + } + } + + /* Okay, we're all done, with either an error or success; call the parsers + to indicate which one. */ + + if (err) + { + /* Maybe print an error message. */ + if (err == EBADKEY) + /* An appropriate message describing what the error was should have + been printed earlier. */ + __argp_state_help (&parser->state, parser->state.err_stream, + ARGP_HELP_STD_ERR); + + /* Since we didn't exit, give each parser an error indication. */ + for (group = parser->groups; group < parser->egroup; group++) + group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); + } + else + /* Notify parsers of success, and propagate back values from parsers. */ + { + /* We pass over the groups in reverse order so that child groups are + given a chance to do there processing before passing back a value to + the parent. */ + for (group = parser->egroup - 1 + ; group >= parser->groups && (!err || err == EBADKEY) + ; group--) + err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + } + + /* Call parsers once more, to do any final cleanup. Errors are ignored. */ + for (group = parser->egroup - 1; group >= parser->groups; group--) + group_parse (group, &parser->state, ARGP_KEY_FINI, 0); + + if (err == EBADKEY) + err = EINVAL; + + free (parser->storage); + + return err; +} + +/* Call the user parsers to parse the non-option argument VAL, at the + current position, returning any error. The state NEXT pointer + should point to the argument; this function will adjust it + correctly to reflect however many args actually end up being + consumed. */ +static error_t +parser_parse_arg (struct parser *parser, char *val) +{ + /* Save the starting value of NEXT */ + int index = parser->state.next; + error_t err = EBADKEY; + struct group *group; + int key = 0; /* Which of ARGP_KEY_ARG[S] we used. */ + + /* Try to parse the argument in each parser. */ + for (group = parser->groups + ; group < parser->egroup && err == EBADKEY + ; group++) + { + parser->state.next++; /* For ARGP_KEY_ARG, consume the arg. */ + key = ARGP_KEY_ARG; + err = group_parse (group, &parser->state, key, val); + + if (err == EBADKEY) + /* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */ + { + parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. */ + key = ARGP_KEY_ARGS; + err = group_parse (group, &parser->state, key, 0); + } + } + + if (! err) + { + if (key == ARGP_KEY_ARGS) + /* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't + changed by the user, *all* arguments should be considered + consumed. */ + parser->state.next = parser->state.argc; + + if (parser->state.next > index) + /* Remember that we successfully processed a non-option + argument -- but only if the user hasn't gotten tricky and set + the clock back. */ + (--group)->args_processed += (parser->state.next - index); + else + /* The user wants to reparse some args, so try looking for options again. */ + parser->args_only = 0; + } + + return err; +} + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,next), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (struct parser *parser) +{ + int bottom = parser->first_nonopt; + int middle = parser->last_nonopt; + int top = parser->state.next; + char **argv = parser->state.argv; + + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + parser->first_nonopt += (parser->state.next - parser->last_nonopt); + parser->last_nonopt = parser->state.next; +} + + + +enum arg_type { ARG_ARG, ARG_SHORT_OPTION, + ARG_LONG_OPTION, ARG_LONG_ONLY_OPTION, + ARG_QUOTE }; + +static enum arg_type +classify_arg(struct parser *parser, char *arg, char **opt) +{ + if (arg[0] == '-') + /* Looks like an option... */ + switch (arg[1]) + { + case '\0': + /* "-" is not an option. */ + return ARG_ARG; + case '-': + /* Long option, or quote. */ + if (!arg[2]) + return ARG_QUOTE; + + /* A long option. */ + if (opt) + *opt = arg + 2; + return ARG_LONG_OPTION; + + default: + /* Short option. But if ARGP_LONG_ONLY, it can also be a long option. */ + + if (opt) + *opt = arg + 1; + + if (parser->state.flags & ARGP_LONG_ONLY) + { + /* Rules from getopt.c: + + If long_only and the ARGV-element has the form "-f", + where f is a valid short option, don't consider it an + abbreviated form of a long option that starts with f. + Otherwise there would be no way to give the -f short + option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an + abbreviation of the long option, just like "--fu", and + not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + assert(parser->short_opts); + + if (arg[2] || !strchr(parser->short_opts, arg[1])) + return ARG_LONG_ONLY_OPTION; + } + + return ARG_SHORT_OPTION; + } + + else + return ARG_ARG; +} + +/* Parse the next argument in PARSER (as indicated by PARSER->state.next). + Any error from the parsers is returned, and *ARGP_EBADKEY indicates + whether a value of EBADKEY is due to an unrecognized argument (which is + generally not fatal). */ +static error_t +parser_parse_next (struct parser *parser, int *arg_ebadkey) +{ + if (parser->state.quoted && parser->state.next < parser->state.quoted) + /* The next argument pointer has been moved to before the quoted + region, so pretend we never saw the quoting `--', and start + looking for options again. If the `--' is still there we'll just + process it one more time. */ + parser->state.quoted = parser->args_only = 0; + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if NEXT has been + moved back by the user (who may also have changed the arguments). */ + if (parser->last_nonopt > parser->state.next) + parser->last_nonopt = parser->state.next; + if (parser->first_nonopt > parser->state.next) + parser->first_nonopt = parser->state.next; + + if (parser->nextchar) + /* Deal with short options. */ + { + struct group *group; + char c; + const struct argp_option *option; + char *value = NULL;; + + assert(!parser->args_only); + + c = *parser->nextchar++; + + option = find_short_option(parser, c, &group); + if (!option) + { + if (parser->posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: illegal option -- %c\n"), + parser->state.name, c); + else + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: invalid option -- %c\n"), + parser->state.name, c); + + *arg_ebadkey = 0; + return EBADKEY; + } + + if (!*parser->nextchar) + parser->nextchar = NULL; + + if (option->arg) + { + value = parser->nextchar; + parser->nextchar = NULL; + + if (!value + && !(option->flags & OPTION_ARG_OPTIONAL)) + /* We need an mandatory argument. */ + { + if (parser->state.next == parser->state.argc) + /* Missing argument */ + { + /* 1003.2 specifies the format of this message. */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: option requires an argument -- %c\n"), + parser->state.name, c); + + *arg_ebadkey = 0; + return EBADKEY; + } + value = parser->state.argv[parser->state.next++]; + } + } + return group_parse(group, &parser->state, + option->key, value); + } + else + /* Advance to the next ARGV-element. */ + { + if (parser->args_only) + { + *arg_ebadkey = 1; + if (parser->state.next >= parser->state.argc) + /* We're done. */ + return EBADKEY; + else + return parser_parse_arg(parser, + parser->state.argv[parser->state.next]); + } + + if (parser->state.next >= parser->state.argc) + /* Almost done. If there are non-options that we skipped + previously, we should process them now. */ + { + *arg_ebadkey = 1; + if (parser->first_nonopt != parser->last_nonopt) + { + exchange(parser); + + /* Start processing the arguments we skipped previously. */ + parser->state.next = parser->first_nonopt; + + parser->first_nonopt = parser->last_nonopt = 0; + + parser->args_only = 1; + return 0; + } + else + /* Indicate that we're really done. */ + return EBADKEY; + } + else + /* Look for options. */ + { + char *arg = parser->state.argv[parser->state.next]; + + char *optstart; + enum arg_type token = classify_arg(parser, arg, &optstart); + + switch (token) + { + case ARG_ARG: + switch (parser->ordering) + { + case PERMUTE: + if (parser->first_nonopt == parser->last_nonopt) + /* Skipped sequence is empty; start a new one. */ + parser->first_nonopt = parser->last_nonopt = parser->state.next; + + else if (parser->last_nonopt != parser->state.next) + /* We have a non-empty skipped sequence, and + we're not at the end-point, so move it. */ + exchange(parser); + + assert(parser->last_nonopt == parser->state.next); + + /* Skip this argument for now. */ + parser->state.next++; + parser->last_nonopt = parser->state.next; + + return 0; + + case REQUIRE_ORDER: + /* Implicit quote before the first argument. */ + parser->args_only = 1; + return 0; + + case RETURN_IN_ORDER: + *arg_ebadkey = 1; + return parser_parse_arg(parser, arg); + + default: + abort(); + } + case ARG_QUOTE: + /* Skip it, then exchange with any previous non-options. */ + parser->state.next++; + assert (parser->last_nonopt != parser->state.next); + + if (parser->first_nonopt != parser->last_nonopt) + { + exchange(parser); + + /* Start processing the skipped and the quoted + arguments. */ + + parser->state.quoted = parser->state.next = parser->first_nonopt; + + /* Also empty the skipped-list, to avoid confusion + if the user resets the next pointer. */ + parser->first_nonopt = parser->last_nonopt = 0; + } + else + parser->state.quoted = parser->state.next; + + parser->args_only = 1; + return 0; + + case ARG_LONG_ONLY_OPTION: + case ARG_LONG_OPTION: + { + struct group *group; + const struct argp_option *option; + char *value; + + parser->state.next++; + option = find_long_option(parser, optstart, &group); + + if (!option) + { + /* NOTE: This includes any "=something" in the output. */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: unrecognized option `%s'\n"), + parser->state.name, arg); + *arg_ebadkey = 0; + return EBADKEY; + } + + value = strchr(optstart, '='); + if (value) + value++; + + if (value && !option->arg) + /* Unexpected argument. */ + { + if (token == ARG_LONG_OPTION) + /* --option */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: option `--%s' doesn't allow an argument\n"), + parser->state.name, option->name); + else + /* +option or -option */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: option `%c%s' doesn't allow an argument\n"), + parser->state.name, arg[0], option->name); + + *arg_ebadkey = 0; + return EBADKEY; + } + + if (option->arg && !value + && !(option->flags & OPTION_ARG_OPTIONAL)) + /* We need an mandatory argument. */ + { + if (parser->state.next == parser->state.argc) + /* Missing argument */ + { + if (token == ARG_LONG_OPTION) + /* --option */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: option `--%s' requires an argument\n"), + parser->state.name, option->name); + else + /* +option or -option */ + fprintf (parser->state.err_stream, + dgettext(parser->state.root_argp->argp_domain, + "%s: option `%c%s' requires an argument\n"), + parser->state.name, arg[0], option->name); + + *arg_ebadkey = 0; + return EBADKEY; + } + + value = parser->state.argv[parser->state.next++]; + } + *arg_ebadkey = 0; + return group_parse(group, &parser->state, + option->key, value); + } + case ARG_SHORT_OPTION: + parser->state.next++; + parser->nextchar = optstart; + return 0; + + default: + abort(); + } + } + } +} + +/* Parse the options strings in ARGC & ARGV according to the argp in ARGP. + FLAGS is one of the ARGP_ flags above. If END_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, EINVAL is returned; if some parser routine + returned a non-zero value, it is returned; otherwise 0 is returned. */ +error_t +__argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, + int *end_index, void *input) +{ + error_t err; + struct parser parser; + + /* If true, then err == EBADKEY is a result of a non-option argument failing + to be parsed (which in some cases isn't actually an error). */ + int arg_ebadkey = 0; + + if (! (flags & ARGP_NO_HELP)) + /* Add our own options. */ + { + struct argp_child *child = alloca (4 * sizeof (struct argp_child)); + struct argp *top_argp = alloca (sizeof (struct argp)); + + /* TOP_ARGP has no options, it just serves to group the user & default + argps. */ + memset (top_argp, 0, sizeof (*top_argp)); + top_argp->children = child; + + memset (child, 0, 4 * sizeof (struct argp_child)); + + if (argp) + (child++)->argp = argp; + (child++)->argp = &argp_default_argp; + if (argp_program_version || argp_program_version_hook) + (child++)->argp = &argp_version_argp; + child->argp = 0; + + argp = top_argp; + } + + /* Construct a parser for these arguments. */ + err = parser_init (&parser, argp, argc, argv, flags, input); + + if (! err) + /* Parse! */ + { + while (! err) + err = parser_parse_next (&parser, &arg_ebadkey); + err = parser_finalize (&parser, err, arg_ebadkey, end_index); + } + + return err; +} +#ifdef weak_alias +weak_alias (__argp_parse, argp_parse) +#endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +void * +__argp_input (const struct argp *argp, const struct argp_state *state) +{ + if (state) + { + struct group *group; + struct parser *parser = state->pstate; + + for (group = parser->groups; group < parser->egroup; group++) + if (group->argp == argp) + return group->input; + } + + return 0; +} +#ifdef weak_alias +weak_alias (__argp_input, _argp_input) +#endif + +/* Defined here, in case a user is not inlining the definitions in + * argp.h */ +void +__argp_usage (__const struct argp_state *__state) +{ + __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); +} + +int +__option_is_short (__const struct argp_option *__opt) +{ + if (__opt->flags & OPTION_DOC) + return 0; + else + { + int __key = __opt->key; + /* FIXME: whether or not a particular key implies a short option + * ought not to be locale dependent. */ + return __key > 0 && isprint (__key); + } +} + +int +__option_is_end (__const struct argp_option *__opt) +{ + return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; +} diff --git a/contrib/argp-standalone/argp-pv.c b/contrib/argp-standalone/argp-pv.c new file mode 100644 index 000000000..d7d374a66 --- /dev/null +++ b/contrib/argp-standalone/argp-pv.c @@ -0,0 +1,25 @@ +/* Default definition for ARGP_PROGRAM_VERSION. + Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which will + print this this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +const char *argp_program_version = 0; diff --git a/contrib/argp-standalone/argp-pvh.c b/contrib/argp-standalone/argp-pvh.c new file mode 100644 index 000000000..829a1cda8 --- /dev/null +++ b/contrib/argp-standalone/argp-pvh.c @@ -0,0 +1,32 @@ +/* Default definition for ARGP_PROGRAM_VERSION_HOOK. + Copyright (C) 1996, 1997, 1999, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "argp.h" + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which calls + this function with a stream to print the version to and a pointer to the + current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +void (*argp_program_version_hook) (FILE *stream, struct argp_state *state) = 0; diff --git a/contrib/argp-standalone/argp.h b/contrib/argp-standalone/argp.h new file mode 100644 index 000000000..29d3dfe97 --- /dev/null +++ b/contrib/argp-standalone/argp.h @@ -0,0 +1,602 @@ +/* Hierarchial argument parsing. + Copyright (C) 1995, 96, 97, 98, 99, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _ARGP_H +#define _ARGP_H + +#include +#include + +#define __need_error_t +#include + +#ifndef __THROW +# define __THROW +#endif + +#ifndef __const +# define __const const +#endif + +#ifndef __error_t_defined +typedef int error_t; +# define __error_t_defined +#endif + +/* FIXME: What's the right way to check for __restrict? Sun's cc seems + not to have it. Perhaps it's easiest to just delete the use of + __restrict from the prototypes. */ +#ifndef __restrict +# ifndef __GNUC___ +# define __restrict +# endif +#endif + +/* NOTE: We can't use the autoconf tests, since this is supposed to be + an installed header file and argp's config.h is of course not + installed. */ +#ifndef PRINTF_STYLE +# if __GNUC__ >= 2 +# define PRINTF_STYLE(f, a) __attribute__ ((__format__ (__printf__, f, a))) +# else +# define PRINTF_STYLE(f, a) +# endif +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* A description of a particular option. A pointer to an array of + these is passed in the OPTIONS field of an argp structure. Each option + entry can correspond to one long option and/or one short option; more + names for the same option can be added by following an entry in an option + array with options having the OPTION_ALIAS flag set. */ +struct argp_option +{ + /* The long option name. For more than one name for the same option, you + can use following options with the OPTION_ALIAS flag set. */ + __const char *name; + + /* What key is returned for this option. If > 0 and printable, then it's + also accepted as a short option. */ + int key; + + /* If non-NULL, this is the name of the argument associated with this + option, which is required unless the OPTION_ARG_OPTIONAL flag is set. */ + __const char *arg; + + /* OPTION_ flags. */ + int flags; + + /* The doc string for this option. If both NAME and KEY are 0, This string + will be printed outdented from the normal option column, making it + useful as a group header (it will be the first thing printed in its + group); in this usage, it's conventional to end the string with a `:'. */ + __const char *doc; + + /* The group this option is in. In a long help message, options are sorted + alphabetically within each group, and the groups presented in the order + 0, 1, 2, ..., n, -m, ..., -2, -1. Every entry in an options array with + if this field 0 will inherit the group number of the previous entry, or + zero if it's the first one, unless its a group header (NAME and KEY both + 0), in which case, the previous entry + 1 is the default. Automagic + options such as --help are put into group -1. */ + int group; +}; + +/* The argument associated with this option is optional. */ +#define OPTION_ARG_OPTIONAL 0x1 + +/* This option isn't displayed in any help messages. */ +#define OPTION_HIDDEN 0x2 + +/* This option is an alias for the closest previous non-alias option. This + means that it will be displayed in the same help entry, and will inherit + fields other than NAME and KEY from the aliased option. */ +#define OPTION_ALIAS 0x4 + +/* This option isn't actually an option (and so should be ignored by the + actual option parser), but rather an arbitrary piece of documentation that + should be displayed in much the same manner as the options. If this flag + is set, then the option NAME field is displayed unmodified (e.g., no `--' + prefix is added) at the left-margin (where a *short* option would normally + be displayed), and the documentation string in the normal place. For + purposes of sorting, any leading whitespace and puncuation is ignored, + except that if the first non-whitespace character is not `-', this entry + is displayed after all options (and OPTION_DOC entries with a leading `-') + in the same group. */ +#define OPTION_DOC 0x8 + +/* This option shouldn't be included in `long' usage messages (but is still + included in help messages). This is mainly intended for options that are + completely documented in an argp's ARGS_DOC field, in which case including + the option in the generic usage list would be redundant. For instance, + if ARGS_DOC is "FOO BAR\n-x BLAH", and the `-x' option's purpose is to + distinguish these two cases, -x should probably be marked + OPTION_NO_USAGE. */ +#define OPTION_NO_USAGE 0x10 + +struct argp; /* fwd declare this type */ +struct argp_state; /* " */ +struct argp_child; /* " */ + +/* The type of a pointer to an argp parsing function. */ +typedef error_t (*argp_parser_t) (int key, char *arg, + struct argp_state *state); + +/* What to return for unrecognized keys. For special ARGP_KEY_ keys, such + returns will simply be ignored. For user keys, this error will be turned + into EINVAL (if the call to argp_parse is such that errors are propagated + back to the user instead of exiting); returning EINVAL itself would result + in an immediate stop to parsing in *all* cases. */ +#define ARGP_ERR_UNKNOWN E2BIG /* Hurd should never need E2BIG. XXX */ + +/* Special values for the KEY argument to an argument parsing function. + ARGP_ERR_UNKNOWN should be returned if they aren't understood. + + The sequence of keys to a parsing function is either (where each + uppercased word should be prefixed by `ARGP_KEY_' and opt is a user key): + + INIT opt... NO_ARGS END SUCCESS -- No non-option arguments at all + or INIT (opt | ARG)... END SUCCESS -- All non-option args parsed + or INIT (opt | ARG)... SUCCESS -- Some non-option arg unrecognized + + The third case is where every parser returned ARGP_KEY_UNKNOWN for an + argument, in which case parsing stops at that argument (returning the + unparsed arguments to the caller of argp_parse if requested, or stopping + with an error message if not). + + If an error occurs (either detected by argp, or because the parsing + function returned an error value), then the parser is called with + ARGP_KEY_ERROR, and no further calls are made. */ + +/* This is not an option at all, but rather a command line argument. If a + parser receiving this key returns success, the fact is recorded, and the + ARGP_KEY_NO_ARGS case won't be used. HOWEVER, if while processing the + argument, a parser function decrements the NEXT field of the state it's + passed, the option won't be considered processed; this is to allow you to + actually modify the argument (perhaps into an option), and have it + processed again. */ +#define ARGP_KEY_ARG 0 +/* There are remaining arguments not parsed by any parser, which may be found + starting at (STATE->argv + STATE->next). If success is returned, but + STATE->next left untouched, it's assumed that all arguments were consume, + otherwise, the parser should adjust STATE->next to reflect any arguments + consumed. */ +#define ARGP_KEY_ARGS 0x1000006 +/* There are no more command line arguments at all. */ +#define ARGP_KEY_END 0x1000001 +/* Because it's common to want to do some special processing if there aren't + any non-option args, user parsers are called with this key if they didn't + successfully process any non-option arguments. Called just before + ARGP_KEY_END (where more general validity checks on previously parsed + arguments can take place). */ +#define ARGP_KEY_NO_ARGS 0x1000002 +/* Passed in before any parsing is done. Afterwards, the values of each + element of the CHILD_INPUT field, if any, in the state structure is + copied to each child's state to be the initial value of the INPUT field. */ +#define ARGP_KEY_INIT 0x1000003 +/* Use after all other keys, including SUCCESS & END. */ +#define ARGP_KEY_FINI 0x1000007 +/* Passed in when parsing has successfully been completed (even if there are + still arguments remaining). */ +#define ARGP_KEY_SUCCESS 0x1000004 +/* Passed in if an error occurs. */ +#define ARGP_KEY_ERROR 0x1000005 + +/* An argp structure contains a set of options declarations, a function to + deal with parsing one, documentation string, a possible vector of child + argp's, and perhaps a function to filter help output. When actually + parsing options, getopt is called with the union of all the argp + structures chained together through their CHILD pointers, with conflicts + being resolved in favor of the first occurrence in the chain. */ +struct argp +{ + /* An array of argp_option structures, terminated by an entry with both + NAME and KEY having a value of 0. */ + __const struct argp_option *options; + + /* What to do with an option from this structure. KEY is the key + associated with the option, and ARG is any associated argument (NULL if + none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should be + returned. If a non-zero, non-ARGP_ERR_UNKNOWN value is returned, then + parsing is stopped immediately, and that value is returned from + argp_parse(). For special (non-user-supplied) values of KEY, see the + ARGP_KEY_ definitions below. */ + argp_parser_t parser; + + /* A string describing what other arguments are wanted by this program. It + is only used by argp_usage to print the `Usage:' message. If it + contains newlines, the strings separated by them are considered + alternative usage patterns, and printed on separate lines (lines after + the first are prefix by ` or: ' instead of `Usage:'). */ + __const char *args_doc; + + /* If non-NULL, a string containing extra text to be printed before and + after the options in a long help message (separated by a vertical tab + `\v' character). */ + __const char *doc; + + /* A vector of argp_children structures, terminated by a member with a 0 + argp field, pointing to child argps should be parsed with this one. Any + conflicts are resolved in favor of this argp, or early argps in the + CHILDREN list. This field is useful if you use libraries that supply + their own argp structure, which you want to use in conjunction with your + own. */ + __const struct argp_child *children; + + /* If non-zero, this should be a function to filter the output of help + messages. KEY is either a key from an option, in which case TEXT is + that option's help text, or a special key from the ARGP_KEY_HELP_ + defines, below, describing which other help text TEXT is. The function + should return either TEXT, if it should be used as-is, a replacement + string, which should be malloced, and will be freed by argp, or NULL, + meaning `print nothing'. The value for TEXT is *after* any translation + has been done, so if any of the replacement text also needs translation, + that should be done by the filter function. INPUT is either the input + supplied to argp_parse, or NULL, if argp_help was called directly. */ + char *(*help_filter) (int __key, __const char *__text, void *__input); + + /* If non-zero the strings used in the argp library are translated using + the domain described by this string. Otherwise the currently installed + default domain is used. */ + const char *argp_domain; +}; + +/* Possible KEY arguments to a help filter function. */ +#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ +#define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ +#define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ +#define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; + TEXT is NULL for this key. */ +/* Explanatory note emitted when duplicate option arguments have been + suppressed. */ +#define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 +#define ARGP_KEY_HELP_ARGS_DOC 0x2000006 /* Argument doc string. */ + +/* When an argp has a non-zero CHILDREN field, it should point to a vector of + argp_child structures, each of which describes a subsidiary argp. */ +struct argp_child +{ + /* The child parser. */ + __const struct argp *argp; + + /* Flags for this child. */ + int flags; + + /* If non-zero, an optional header to be printed in help output before the + child options. As a side-effect, a non-zero value forces the child + options to be grouped together; to achieve this effect without actually + printing a header string, use a value of "". */ + __const char *header; + + /* Where to group the child options relative to the other (`consolidated') + options in the parent argp; the values are the same as the GROUP field + in argp_option structs, but all child-groupings follow parent options at + a particular group level. If both this field and HEADER are zero, then + they aren't grouped at all, but rather merged with the parent options + (merging the child's grouping levels with the parents). */ + int group; +}; + +/* Parsing state. This is provided to parsing functions called by argp, + which may examine and, as noted, modify fields. */ +struct argp_state +{ + /* The top level ARGP being parsed. */ + __const struct argp *root_argp; + + /* The argument vector being parsed. May be modified. */ + int argc; + char **argv; + + /* The index in ARGV of the next arg that to be parsed. May be modified. */ + int next; + + /* The flags supplied to argp_parse. May be modified. */ + unsigned flags; + + /* While calling a parsing function with a key of ARGP_KEY_ARG, this is the + number of the current arg, starting at zero, and incremented after each + such call returns. At all other times, this is the number of such + arguments that have been processed. */ + unsigned arg_num; + + /* If non-zero, the index in ARGV of the first argument following a special + `--' argument (which prevents anything following being interpreted as an + option). Only set once argument parsing has proceeded past this point. */ + int quoted; + + /* An arbitrary pointer passed in from the user. */ + void *input; + /* Values to pass to child parsers. This vector will be the same length as + the number of children for the current parser. */ + void **child_inputs; + + /* For the parser's use. Initialized to 0. */ + void *hook; + + /* The name used when printing messages. This is initialized to ARGV[0], + or PROGRAM_INVOCATION_NAME if that is unavailable. */ + char *name; + + /* Streams used when argp prints something. */ + FILE *err_stream; /* For errors; initialized to stderr. */ + FILE *out_stream; /* For information; initialized to stdout. */ + + void *pstate; /* Private, for use by argp. */ +}; + +/* Flags for argp_parse (note that the defaults are those that are + convenient for program command line parsing): */ + +/* Don't ignore the first element of ARGV. Normally (and always unless + ARGP_NO_ERRS is set) the first element of the argument vector is + skipped for option parsing purposes, as it corresponds to the program name + in a command line. */ +#define ARGP_PARSE_ARGV0 0x01 + +/* Don't print error messages for unknown options to stderr; unless this flag + is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program + name in the error messages. This flag implies ARGP_NO_EXIT (on the + assumption that silent exiting upon errors is bad behaviour). */ +#define ARGP_NO_ERRS 0x02 + +/* Don't parse any non-option args. Normally non-option args are parsed by + calling the parse functions with a key of ARGP_KEY_ARG, and the actual arg + as the value. Since it's impossible to know which parse function wants to + handle it, each one is called in turn, until one returns 0 or an error + other than ARGP_ERR_UNKNOWN; if an argument is handled by no one, the + argp_parse returns prematurely (but with a return value of 0). If all + args have been parsed without error, all parsing functions are called one + last time with a key of ARGP_KEY_END. This flag needn't normally be set, + as the normal behavior is to stop parsing as soon as some argument can't + be handled. */ +#define ARGP_NO_ARGS 0x04 + +/* Parse options and arguments in the same order they occur on the command + line -- normally they're rearranged so that all options come first. */ +#define ARGP_IN_ORDER 0x08 + +/* Don't provide the standard long option --help, which causes usage and + option help information to be output to stdout, and exit (0) called. */ +#define ARGP_NO_HELP 0x10 + +/* Don't exit on errors (they may still result in error messages). */ +#define ARGP_NO_EXIT 0x20 + +/* Use the gnu getopt `long-only' rules for parsing arguments. */ +#define ARGP_LONG_ONLY 0x40 + +/* Turns off any message-printing/exiting options. */ +#define ARGP_SILENT (ARGP_NO_EXIT | ARGP_NO_ERRS | ARGP_NO_HELP) + +/* Parse the options strings in ARGC & ARGV according to the options in ARGP. + FLAGS is one of the ARGP_ flags above. If ARG_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser + routine returned a non-zero value, it is returned; otherwise 0 is + returned. This function may also call exit unless the ARGP_NO_HELP flag + is set. INPUT is a pointer to a value to be passed in to the parser. */ +extern error_t argp_parse (__const struct argp *__restrict argp, + int argc, char **__restrict argv, + unsigned flags, int *__restrict arg_index, + void *__restrict input) __THROW; +extern error_t __argp_parse (__const struct argp *__restrict argp, + int argc, char **__restrict argv, + unsigned flags, int *__restrict arg_index, + void *__restrict input) __THROW; + +/* Global variables. */ + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + will print this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +extern __const char *argp_program_version; + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + calls this function with a stream to print the version to and a pointer to + the current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +extern void (*argp_program_version_hook) (FILE *__restrict __stream, + struct argp_state *__restrict + __state); + +/* If defined or set by the user program, it should point to string that is + the bug-reporting address for the program. It will be printed by + argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various + standard help messages), embedded in a sentence that says something like + `Report bugs to ADDR.'. */ +extern __const char *argp_program_bug_address; + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +extern error_t argp_err_exit_status; + +/* Flags for argp_help. */ +#define ARGP_HELP_USAGE 0x01 /* a Usage: message. */ +#define ARGP_HELP_SHORT_USAGE 0x02 /* " but don't actually print options. */ +#define ARGP_HELP_SEE 0x04 /* a `Try ... for more help' message. */ +#define ARGP_HELP_LONG 0x08 /* a long help message. */ +#define ARGP_HELP_PRE_DOC 0x10 /* doc string preceding long help. */ +#define ARGP_HELP_POST_DOC 0x20 /* doc string following long help. */ +#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC) +#define ARGP_HELP_BUG_ADDR 0x40 /* bug report address */ +#define ARGP_HELP_LONG_ONLY 0x80 /* modify output appropriately to + reflect ARGP_LONG_ONLY mode. */ + +/* These ARGP_HELP flags are only understood by argp_state_help. */ +#define ARGP_HELP_EXIT_ERR 0x100 /* Call exit(1) instead of returning. */ +#define ARGP_HELP_EXIT_OK 0x200 /* Call exit(0) instead of returning. */ + +/* The standard thing to do after a program command line parsing error, if an + error message has already been printed. */ +#define ARGP_HELP_STD_ERR \ + (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do after a program command line parsing error, if no + more specific error message has been printed. */ +#define ARGP_HELP_STD_USAGE \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do in response to a --help option. */ +#define ARGP_HELP_STD_HELP \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \ + | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR) + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. */ +extern void argp_help (__const struct argp *__restrict __argp, + FILE *__restrict __stream, + unsigned __flags, char *__restrict __name) __THROW; +extern void __argp_help (__const struct argp *__restrict __argp, + FILE *__restrict __stream, unsigned __flags, + char *__name) __THROW; + +/* The following routines are intended to be called from within an argp + parsing routine (thus taking an argp_state structure as the first + argument). They may or may not print an error message and exit, depending + on the flags in STATE -- in any case, the caller should be prepared for + them *not* to exit, and should return an appropiate error after calling + them. [argp_usage & argp_error should probably be called argp_state_..., + but they're used often enough that they should be short] */ + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +extern void argp_state_help (__const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags) __THROW; +extern void __argp_state_help (__const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags) __THROW; + +/* Possibly output the standard usage message for ARGP to stderr and exit. */ +extern void argp_usage (__const struct argp_state *__state) __THROW; +extern void __argp_usage (__const struct argp_state *__state) __THROW; + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and `:', to stderr, and followed by a `Try ... --help' + message, then exit (1). */ +extern void argp_error (__const struct argp_state *__restrict __state, + __const char *__restrict __fmt, ...) __THROW + PRINTF_STYLE(2,3); +extern void __argp_error (__const struct argp_state *__restrict __state, + __const char *__restrict __fmt, ...) __THROW + PRINTF_STYLE(2,3); + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +extern void argp_failure (__const struct argp_state *__restrict __state, + int __status, int __errnum, + __const char *__restrict __fmt, ...) __THROW + PRINTF_STYLE(4,5); +extern void __argp_failure (__const struct argp_state *__restrict __state, + int __status, int __errnum, + __const char *__restrict __fmt, ...) __THROW + PRINTF_STYLE(4,5); + +/* Returns true if the option OPT is a valid short option. */ +extern int _option_is_short (__const struct argp_option *__opt) __THROW; +extern int __option_is_short (__const struct argp_option *__opt) __THROW; + +/* Returns true if the option OPT is in fact the last (unused) entry in an + options array. */ +extern int _option_is_end (__const struct argp_option *__opt) __THROW; +extern int __option_is_end (__const struct argp_option *__opt) __THROW; + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +extern void *_argp_input (__const struct argp *__restrict __argp, + __const struct argp_state *__restrict __state) + __THROW; +extern void *__argp_input (__const struct argp *__restrict __argp, + __const struct argp_state *__restrict __state) + __THROW; + +/* Used for extracting the program name from argv[0] */ +extern char *_argp_basename(char *name) __THROW; +extern char *__argp_basename(char *name) __THROW; + +/* Getting the program name given an argp state */ +extern char * +_argp_short_program_name(const struct argp_state *state) __THROW; +extern char * +__argp_short_program_name(const struct argp_state *state) __THROW; + + +#ifdef __USE_EXTERN_INLINES + +# if !_LIBC +# define __argp_usage argp_usage +# define __argp_state_help argp_state_help +# define __option_is_short _option_is_short +# define __option_is_end _option_is_end +# endif + +# ifndef ARGP_EI +# define ARGP_EI extern __inline__ +# endif + +ARGP_EI void +__argp_usage (__const struct argp_state *__state) +{ + __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); +} + +ARGP_EI int +__option_is_short (__const struct argp_option *__opt) +{ + if (__opt->flags & OPTION_DOC) + return 0; + else + { + int __key = __opt->key; + return __key > 0 && isprint (__key); + } +} + +ARGP_EI int +__option_is_end (__const struct argp_option *__opt) +{ + return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; +} + +# if !_LIBC +# undef __argp_usage +# undef __argp_state_help +# undef __option_is_short +# undef __option_is_end +# endif +#endif /* Use extern inlines. */ + +#ifdef __cplusplus +} +#endif + +#endif /* argp.h */ diff --git a/contrib/argp-standalone/autogen.sh b/contrib/argp-standalone/autogen.sh new file mode 100755 index 000000000..8337353b5 --- /dev/null +++ b/contrib/argp-standalone/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +aclocal -I . +autoheader +autoconf +automake --add-missing --copy --foreign diff --git a/contrib/argp-standalone/configure.ac b/contrib/argp-standalone/configure.ac new file mode 100644 index 000000000..4e4e67692 --- /dev/null +++ b/contrib/argp-standalone/configure.ac @@ -0,0 +1,102 @@ +dnl Process this file with autoconf to produce a configure script. + +dnl This configure.ac is only for building a standalone argp library. +AC_INIT([argp], [standalone-1.3]) +AC_PREREQ(2.54) +AC_CONFIG_SRCDIR([argp-ba.c]) +# Needed to stop autoconf from looking for files in parent directories. +AC_CONFIG_AUX_DIR([.]) + +AM_INIT_AUTOMAKE +AC_CONFIG_HEADERS(config.h) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) + +# GNU libc defaults to supplying the ISO C library functions only. The +# _GNU_SOURCE define enables these extensions, in particular we want +# errno.h to declare program_invocation_name. Enable it on all +# systems; no problems have been reported with it so far. +AC_GNU_SOURCE + +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET +AC_PROG_RANLIB +AC_PROG_CC + +if test "x$am_cv_prog_cc_stdc" = xno ; then + AC_ERROR([the C compiler doesn't handle ANSI-C]) +fi + +# Checks for libraries. + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(limits.h malloc.h unistd.h sysexits.h stdarg.h) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T + +LSH_GCC_ATTRIBUTES + +# Checks for library functions. +AC_FUNC_ALLOCA +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(strerror sleep getpid snprintf) + +AC_REPLACE_FUNCS(mempcpy strndup strchrnul strcasecmp vsnprintf) + +dnl ARGP_CHECK_FUNC(includes, function-call [, if-found [, if-not-found]]) +AC_DEFUN([ARGP_CHECK_FUNC], + [AS_VAR_PUSHDEF([ac_func], m4_substr([$2], 0, m4_index([$2], [(]))) + AS_VAR_PUSHDEF([ac_var], [ac_cv_func_call_]ac_func) + AH_TEMPLATE(AS_TR_CPP(HAVE_[]ac_func), + [Define to 1 if you have the `]ac_func[' function.]) + AC_CACHE_CHECK([for $2], ac_var, + [AC_TRY_LINK([$1], [$2], + [AS_VAR_SET(ac_var, yes)], + [AS_VAR_SET(ac_var, no)])]) + if test AS_VAR_GET(ac_var) = yes ; then + ifelse([$3],, + [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]ac_func))], + [$3 +]) + else + ifelse([$4],, true, [$4]) + fi + AS_VAR_POPDEF([ac_var]) + AS_VAR_POPDEF([ac_func]) + ]) + +# At least on freebsd, putc_unlocked is a macro, so the standard +# AC_CHECK_FUNCS doesn't work well. +ARGP_CHECK_FUNC([#include ], [putc_unlocked('x', stdout)]) + +AC_CHECK_FUNCS(flockfile) +AC_CHECK_FUNCS(fputs_unlocked fwrite_unlocked) + +# Used only by argp-test.c, so don't use AC_REPLACE_FUNCS. +AC_CHECK_FUNCS(strdup asprintf) + +AC_CHECK_DECLS([program_invocation_name, program_invocation_short_name], + [], [], [[#include ]]) + +# Set these flags *last*, or else the test programs won't compile +if test x$GCC = xyes ; then + # Using -ggdb3 makes (some versions of) Redhat's gcc-2.96 dump core + if "$CC" --version | grep '^2\.96$' 1>/dev/null 2>&1; then + true + else + CFLAGS="$CFLAGS -ggdb3" + fi + CFLAGS="$CFLAGS -Wall -W \ + -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes \ + -Waggregate-return \ + -Wpointer-arith -Wbad-function-cast -Wnested-externs" +fi + +CPPFLAGS="$CPPFLAGS -I$srcdir" + +AC_OUTPUT(Makefile) diff --git a/contrib/argp-standalone/mempcpy.c b/contrib/argp-standalone/mempcpy.c new file mode 100644 index 000000000..21d8bd2ed --- /dev/null +++ b/contrib/argp-standalone/mempcpy.c @@ -0,0 +1,21 @@ +/* strndup.c + * + */ + +/* Written by Niels Möller + * + * This file is hereby placed in the public domain. + */ + +#include + +void * +mempcpy (void *, const void *, size_t) ; + +void * +mempcpy (void *to, const void *from, size_t size) +{ + memcpy(to, from, size); + return (char *) to + size; +} + diff --git a/contrib/argp-standalone/strcasecmp.c b/contrib/argp-standalone/strcasecmp.c new file mode 100644 index 000000000..9c1637232 --- /dev/null +++ b/contrib/argp-standalone/strcasecmp.c @@ -0,0 +1,29 @@ +/* strcasecmp.c + * + */ + +/* Written by Niels Möller + * + * This file is hereby placed in the public domain. + */ + +#include +int strcasecmp(const char *, const char *); + +int strcasecmp(const char *s1, const char *s2) +{ + unsigned i; + + for (i = 0; s1[i] && s2[i]; i++) + { + unsigned char c1 = tolower( (unsigned char) s1[i]); + unsigned char c2 = tolower( (unsigned char) s2[i]); + + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + } + + return !s2[i] - !s1[i]; +} diff --git a/contrib/argp-standalone/strchrnul.c b/contrib/argp-standalone/strchrnul.c new file mode 100644 index 000000000..ee4145e4e --- /dev/null +++ b/contrib/argp-standalone/strchrnul.c @@ -0,0 +1,23 @@ +/* strchrnul.c + * + */ + +/* Written by Niels Möller + * + * This file is hereby placed in the public domain. + */ + +/* FIXME: What is this function supposed to do? My guess is that it is + * like strchr, but returns a pointer to the NUL character, not a NULL + * pointer, if the character isn't found. */ + +char *strchrnul(const char *, int ); + +char *strchrnul(const char *s, int c) +{ + const char *p = s; + while (*p && (*p != c)) + p++; + + return (char *) p; +} diff --git a/contrib/argp-standalone/strndup.c b/contrib/argp-standalone/strndup.c new file mode 100644 index 000000000..4147b7a20 --- /dev/null +++ b/contrib/argp-standalone/strndup.c @@ -0,0 +1,34 @@ +/* strndup.c + * + */ + +/* Written by Niels Möller + * + * This file is hereby placed in the public domain. + */ + +#include +#include + +char * +strndup (const char *, size_t); + +char * +strndup (const char *s, size_t size) +{ + char *r; + char *end = memchr(s, 0, size); + + if (end) + /* Length + 1 */ + size = end - s + 1; + + r = malloc(size); + + if (size) + { + memcpy(r, s, size-1); + r[size-1] = '\0'; + } + return r; +} diff --git a/contrib/argp-standalone/vsnprintf.c b/contrib/argp-standalone/vsnprintf.c new file mode 100644 index 000000000..33c9a5d00 --- /dev/null +++ b/contrib/argp-standalone/vsnprintf.c @@ -0,0 +1,839 @@ +/* Copied from http://www.fiction.net/blong/programs/snprintf.c */ + +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * Russ Allbery 2000-08-26 + * fixed return value to comply with C99 + * fixed handling of snprintf(NULL, ...) + * + * Niels Möller 2004-03-05 + * fixed calls to isdigit to use unsigned char. + * fixed calls to va_arg; short arguments are always passed as int. + * + **************************************************************/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) + +#include +#include +#include + +/* Define this as a fall through, HAVE_STDARG_H is probably already set */ + +#define HAVE_VARARGS_H + + +/* varargs declarations: */ + +#if defined(HAVE_STDARG_H) +# include +# define HAVE_STDARGS /* let's hope that works everywhere (mj) */ +# define VA_LOCAL_DECL va_list ap +# define VA_START(f) va_start(ap, f) +# define VA_SHIFT(v,t) ; /* no-op for ANSI */ +# define VA_END va_end(ap) +#else +# if defined(HAVE_VARARGS_H) +# include +# undef HAVE_STDARGS +# define VA_LOCAL_DECL va_list ap +# define VA_START(f) va_start(ap) /* f is ignored! */ +# define VA_SHIFT(v,t) v = va_arg(ap,t) +# define VA_END va_end(ap) +# else +/*XX ** NO VARARGS ** XX*/ +# endif +#endif + +#ifdef HAVE_LONG_DOUBLE +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); + +static int dopr (char *buffer, size_t maxlen, const char *format, + va_list args); +static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static int fmtint (char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags); +static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 + +#define char_to_int(p) (p - '0') +#define MAX(p,q) ((p >= q) ? p : q) +#define MIN(p,q) ((p <= q) ? p : q) + +static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) +{ + unsigned char ch; + long value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + int total; + size_t currlen; + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + total = 0; + + while (state != DP_S_DONE) + { + if (ch == '\0') + state = DP_S_DONE; + + switch(state) + { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + total += dopr_outch (buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) + { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit(ch)) + { + min = 10*min + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } + else + state = DP_S_DOT; + break; + case DP_S_DOT: + if (ch == '.') + { + state = DP_S_MAX; + ch = *format++; + } + else + state = DP_S_MOD; + break; + case DP_S_MAX: + if (isdigit(ch)) + { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } + else + state = DP_S_MOD; + break; + case DP_S_MOD: + /* Currently, we don't support Long Long, bummer */ + switch (ch) + { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) + { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = (short) va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else + value = va_arg (args, int); + total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = (unsigned short) va_arg (args, unsigned); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = (unsigned short) va_arg (args, unsigned); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = (unsigned short) va_arg (args, unsigned); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + break; + case 'c': + total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + break; + case 'p': + strvalue = va_arg (args, void *); + total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, + max, flags); + break; + case 'n': + if (cflags == DP_C_SHORT) + { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } + else if (cflags == DP_C_LONG) + { + long int *num; + num = va_arg (args, long int *); + *num = currlen; + } + else + { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; + case '%': + total += dopr_outch (buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (buffer != NULL) + { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else + buffer[maxlen - 1] = '\0'; + } + return total; +} + +static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + int total = 0; + + if (value == 0) + { + value = ""; + } + + for (strln = 0; value[strln]; ++strln); /* strlen */ + if (max >= 0 && max < strln) + strln = max; + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + while (*value && ((max < 0) || (cnt < max))) + { + total += dopr_outch (buffer, currlen, maxlen, *value++); + ++cnt; + } + while (padlen < 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } + return total; +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static int fmtint (char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + int caps = 0; + int total = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) + { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + } + + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ + + do { + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < 20)); + if (place == 20) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) + { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place)); +#endif + + /* Spaces */ + while (spadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + total += dopr_outch (buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) + { + while (zpadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) + total += dopr_outch (buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++spadlen; + } + + return total; +} + +static LDOUBLE abs_val (LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE pow10_argp (int exp) +{ + LDOUBLE result = 1; + + while (exp) + { + result *= 10; + exp--; + } + + return result; +} + +static long round_argp (LDOUBLE value) +{ + long intpart; + + intpart = value; + value = value - intpart; + if (value >= 0.5) + intpart++; + + return intpart; +} + +static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + LDOUBLE ufvalue; + char iconvert[20]; + char fconvert[20]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + int total = 0; + long intpart; + long fracpart; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) + signvalue = '-'; + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + + intpart = ufvalue; + + /* + * Sorry, we only support 9 digits past the decimal because of our + * conversion method + */ + if (max > 9) + max = 9; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + fracpart = round_argp ((pow10_argp (max)) * (ufvalue - intpart)); + + if (fracpart >= pow10_argp (max)) + { + intpart++; + fracpart -= pow10_argp (max); + } + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); +#endif + + /* Convert integer part */ + do { + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; + intpart = (intpart / 10); + } while(intpart && (iplace < 20)); + if (iplace == 20) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + do { + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; + fracpart = (fracpart / 10); + } while(fracpart && (fplace < 20)); + if (fplace == 20) fplace--; + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) + zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) + { + if (signvalue) + { + total += dopr_outch (buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + total += dopr_outch (buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '.'); + + while (fplace > 0) + total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + } + + while (zpadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (padlen < 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } + + return total; +} + +static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) +{ + if (*currlen + 1 < maxlen) + buffer[(*currlen)++] = c; + return 1; +} + +#ifndef HAVE_VSNPRINTF +int vsnprintf (char *str, size_t count, const char *fmt, va_list args) +{ + if (str != NULL) + str[0] = 0; + return dopr(str, count, fmt, args); +} +#endif /* !HAVE_VSNPRINTF */ + +#ifndef HAVE_SNPRINTF +/* VARARGS3 */ +#ifdef HAVE_STDARGS +int snprintf (char *str,size_t count,const char *fmt,...) +#else +int snprintf (va_alist) va_dcl +#endif +{ +#ifndef HAVE_STDARGS + char *str; + size_t count; + char *fmt; +#endif + VA_LOCAL_DECL; + int total; + + VA_START (fmt); + VA_SHIFT (str, char *); + VA_SHIFT (count, size_t ); + VA_SHIFT (fmt, char *); + total = vsnprintf(str, count, fmt, ap); + VA_END; + return total; +} +#endif /* !HAVE_SNPRINTF */ + +#ifdef TEST_SNPRINTF +#ifndef LONG_STRING +#define LONG_STRING 1024 +#endif +int main (void) +{ + char buf1[LONG_STRING]; + char buf2[LONG_STRING]; + char *fp_fmt[] = { + "%-1.5f", + "%1.5f", + "%123.9f", + "%10.5f", + "% 10.5f", + "%+22.9f", + "%+4.9f", + "%01.3f", + "%4f", + "%3.1f", + "%3.2f", + "%.0f", + "%.1f", + NULL + }; + double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, + 0.9996, 1.996, 4.136, 0}; + char *int_fmt[] = { + "%-1.5d", + "%1.5d", + "%123.9d", + "%5.5d", + "%10.5d", + "% 10.5d", + "%+22.33d", + "%01.3d", + "%4d", + NULL + }; + long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; + int x, y; + int fail = 0; + int num = 0; + + printf ("Testing snprintf format codes against system sprintf...\n"); + + for (x = 0; fp_fmt[x] != NULL ; x++) + for (y = 0; fp_nums[y] != 0 ; y++) + { + snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); + sprintf (buf2, fp_fmt[x], fp_nums[y]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + fp_fmt[x], buf1, buf2); + fail++; + } + num++; + } + + for (x = 0; int_fmt[x] != NULL ; x++) + for (y = 0; int_nums[y] != 0 ; y++) + { + snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); + sprintf (buf2, int_fmt[x], int_nums[y]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + int_fmt[x], buf1, buf2); + fail++; + } + num++; + } + printf ("%d tests failed out of %d.\n", fail, num); +} +#endif /* SNPRINTF_TEST */ + +#endif /* !HAVE_SNPRINTF */ -- cgit From 091c49d85cf727ab9c391ba1ac2b44e1091fd259 Mon Sep 17 00:00:00 2001 From: "Kaleb S. KEITHLEY" Date: Thu, 3 Apr 2014 07:38:15 -0400 Subject: rpm: fix broken glusterfs-server install of hooks the %ghost hook directories are no longer ghosts now that files are installed in them Change-Id: I6133b891c73d87e4e35dc9c6c7ba7febbc9e2e7f BUG: 1080970 Signed-off-by: Kaleb S. KEITHLEY Reviewed-on: http://review.gluster.org/7391 Reviewed-by: Niels de Vos Reviewed-by: Justin Clift Tested-by: Justin Clift Tested-by: Gluster Build System --- glusterfs.spec.in | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 5d29ad39e..5e0c7c0dc 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -811,22 +811,11 @@ fi # This is really ugly, but I have no idea how to mark these directories in # any other way. They should belong to the glusterfs-server package, but # don't exist after installation. They are generated on the first start... -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1 -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/stop %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/stop/post -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/stop/pre -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/start -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/start/post %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/start/pre %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/remove-brick %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/remove-brick/post %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/remove-brick/pre -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/add-brick -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/add-brick/post -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/add-brick/pre -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/set -%ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/set/post %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/set/pre %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/create %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/hooks/1/create/post -- cgit From d586ecc0a501440436a918ed973fd75facafc455 Mon Sep 17 00:00:00 2001 From: Krutika Dhananjay Date: Sun, 30 Mar 2014 08:37:02 +0530 Subject: log: Add missing log message from glusterfsd.c to glusterfsd-messages.h ... by retaining GLFS_NUM_MESSAGES as 33 which is its correct value. Also replace all occurrences of gf_log with gf_msg/gf_msg_debug. Change-Id: Ibfbe1d645de521e8d59ca406f78b1a8eb08aa7e0 BUG: 1075611 Signed-off-by: Krutika Dhananjay Reviewed-on: http://review.gluster.org/7371 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- glusterfsd/src/glusterfsd-messages.h | 3 +++ glusterfsd/src/glusterfsd.c | 12 ++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/glusterfsd/src/glusterfsd-messages.h b/glusterfsd/src/glusterfsd-messages.h index 48a0b17f6..3165c971f 100644 --- a/glusterfsd/src/glusterfsd-messages.h +++ b/glusterfsd/src/glusterfsd-messages.h @@ -104,6 +104,9 @@ " sync-environment" #define glusterfsd_msg_32 (GLFS_COMP_BASE + 32), "received signum (%d)," \ " shutting down" +#define glusterfsd_msg_33 (GLFS_COMP_BASE + 33), "obsolete option " \ + "'--volfile-max-fetch-attempts or fetch-attempts' " \ + "was provided" /*------------*/ #define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages" diff --git a/glusterfsd/src/glusterfsd.c b/glusterfsd/src/glusterfsd.c index 449fadda6..9c9fad96e 100644 --- a/glusterfsd/src/glusterfsd.c +++ b/glusterfsd/src/glusterfsd.c @@ -461,8 +461,7 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, "no-root-squash", "enable"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set 'enable' for key " + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_6, "no-root-squash"); goto err; } @@ -472,12 +471,11 @@ set_fuse_mount_options (glusterfs_ctx_t *ctx, dict_t *options) ret = dict_set_static_ptr (options, "no-root-squash", "disable"); if (ret < 0) { - gf_log ("glusterfsd", GF_LOG_ERROR, - "failed to set 'disable' for key " + gf_msg ("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_5, "no-root-squash"); goto err; } - gf_log ("", GF_LOG_DEBUG, "fuse no-root-squash mode %d", + gf_msg_debug ("glusterfsd", 0, "fuse no-root-squash mode %d", cmd_args->no_root_squash); break; } @@ -1481,9 +1479,7 @@ parse_cmdline (int argc, char *argv[], glusterfs_ctx_t *ctx) compatibility with third party applications */ if (cmd_args->max_connect_attempts) { - gf_log ("glusterfs", GF_LOG_WARNING, - "obsolete option '--volfile-max-fetch-attempts" - " or fetch-attempts' was provided"); + gf_msg ("glusterfs", GF_LOG_WARNING, 0, glusterfsd_msg_33); } #ifdef GF_DARWIN_HOST_OS -- cgit From b69c4c843ce0c6a361c46fd53cbbbb9ce0e27cd8 Mon Sep 17 00:00:00 2001 From: Varun Shastry Date: Wed, 5 Feb 2014 13:02:34 +0530 Subject: features/barrier: add barrier translator gluster feature page: http://www.gluster.org/community/documentation/index.php/Features/Server-side_Barrier_feature Change-Id: Ia9f8802a54d1ffbd1cf789b80f5d30819bf65f64 BUG: 1060002 Signed-off-by: Varun Shastry Reviewed-on: http://review.gluster.org/6928 Tested-by: Gluster Build System Reviewed-by: Krishnan Parthasarathi Reviewed-by: Atin Mukherjee Reviewed-by: Vijay Bellur --- configure.ac | 2 + libglusterfs/src/defaults.c | 502 +++++++++++++++++++++- libglusterfs/src/defaults.h | 312 +++++++++++++- xlators/features/Makefile.am | 2 +- xlators/features/barrier/Makefile.am | 3 + xlators/features/barrier/src/Makefile.am | 16 + xlators/features/barrier/src/barrier-mem-types.h | 20 + xlators/features/barrier/src/barrier.c | 520 +++++++++++++++++++++++ xlators/features/barrier/src/barrier.h | 91 ++++ 9 files changed, 1424 insertions(+), 44 deletions(-) create mode 100644 xlators/features/barrier/Makefile.am create mode 100644 xlators/features/barrier/src/Makefile.am create mode 100644 xlators/features/barrier/src/barrier-mem-types.h create mode 100644 xlators/features/barrier/src/barrier.c create mode 100644 xlators/features/barrier/src/barrier.h diff --git a/configure.ac b/configure.ac index 696ebfa36..f1bb2a184 100644 --- a/configure.ac +++ b/configure.ac @@ -121,6 +121,8 @@ AC_CONFIG_FILES([Makefile xlators/features/mac-compat/src/Makefile xlators/features/quiesce/Makefile xlators/features/quiesce/src/Makefile + xlators/features/barrier/Makefile + xlators/features/barrier/src/Makefile xlators/features/index/Makefile xlators/features/index/src/Makefile xlators/features/protect/Makefile diff --git a/libglusterfs/src/defaults.c b/libglusterfs/src/defaults.c index f5fb2aa9d..8a1c281a5 100644 --- a/libglusterfs/src/defaults.c +++ b/libglusterfs/src/defaults.c @@ -373,6 +373,464 @@ default_getspec_failure_cbk (call_frame_t *frame, int32_t op_errno) return 0; } +/* _cbk_resume section */ + +int32_t +default_lookup_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, + struct iatt *postparent) +{ + STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, inode, buf, + xdata, postparent); + return 0; +} + +int32_t +default_stat_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (stat, frame, op_ret, op_errno, buf, xdata); + return 0; +} + + +int32_t +default_truncate_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (truncate, frame, op_ret, op_errno, prebuf, + postbuf, xdata); + return 0; +} + +int32_t +default_ftruncate_cbk_resume (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, int32_t op_errno, + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (ftruncate, frame, op_ret, op_errno, prebuf, + postbuf, xdata); + return 0; +} + +int32_t +default_access_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (access, frame, op_ret, op_errno, xdata); + return 0; +} + +int32_t +default_readlink_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + const char *path, struct iatt *buf, dict_t *xdata) +{ + STACK_UNWIND_STRICT (readlink, frame, op_ret, op_errno, path, buf, + xdata); + return 0; +} + + +int32_t +default_mknod_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT (mknod, frame, op_ret, op_errno, inode, + buf, preparent, postparent, xdata); + return 0; +} + +int32_t +default_mkdir_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT (mkdir, frame, op_ret, op_errno, inode, + buf, preparent, postparent, xdata); + return 0; +} + +int32_t +default_unlink_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (unlink, frame, op_ret, op_errno, preparent, + postparent, xdata); + return 0; +} + +int32_t +default_rmdir_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT (rmdir, frame, op_ret, op_errno, preparent, + postparent, xdata); + return 0; +} + + +int32_t +default_symlink_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT (symlink, frame, op_ret, op_errno, inode, buf, + preparent, postparent, xdata); + return 0; +} + + +int32_t +default_rename_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, + struct iatt *postoldparent, + struct iatt *prenewparent, + struct iatt *postnewparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT (rename, frame, op_ret, op_errno, buf, preoldparent, + postoldparent, prenewparent, postnewparent, xdata); + return 0; +} + + +int32_t +default_link_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT (link, frame, op_ret, op_errno, inode, buf, + preparent, postparent, xdata); + return 0; +} + + +int32_t +default_create_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, + inode_t *inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf, + preparent, postparent, xdata); + return 0; +} + +int32_t +default_open_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd, xdata); + return 0; +} + +int32_t +default_readv_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iovec *vector, int32_t count, + struct iatt *stbuf, struct iobref *iobref, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (readv, frame, op_ret, op_errno, vector, count, + stbuf, iobref, xdata); + return 0; +} + + +int32_t +default_writev_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (writev, frame, op_ret, op_errno, prebuf, postbuf, xdata); + return 0; +} + + +int32_t +default_flush_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (flush, frame, op_ret, op_errno, xdata); + return 0; +} + + + +int32_t +default_fsync_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + STACK_UNWIND_STRICT (fsync, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + return 0; +} + +int32_t +default_fstat_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (fstat, frame, op_ret, op_errno, buf, xdata); + return 0; +} + +int32_t +default_opendir_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd, xdata); + return 0; +} + +int32_t +default_fsyncdir_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (fsyncdir, frame, op_ret, op_errno, xdata); + return 0; +} + +int32_t +default_statfs_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct statvfs *buf, dict_t *xdata) +{ + STACK_UNWIND_STRICT (statfs, frame, op_ret, op_errno, buf, xdata); + return 0; +} + + +int32_t +default_setxattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (setxattr, frame, op_ret, op_errno, xdata); + return 0; +} + + +int32_t +default_fsetxattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno, xdata); + return 0; +} + + + +int32_t +default_fgetxattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, dict, xdata); + return 0; +} + + +int32_t +default_getxattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, dict, xdata); + return 0; +} + +int32_t +default_xattrop_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (xattrop, frame, op_ret, op_errno, dict, xdata); + return 0; +} + +int32_t +default_fxattrop_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (fxattrop, frame, op_ret, op_errno, dict, xdata); + return 0; +} + + +int32_t +default_removexattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (removexattr, frame, op_ret, op_errno, xdata); + return 0; +} + + +int32_t +default_fremovexattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (fremovexattr, frame, op_ret, op_errno, xdata); + return 0; +} + +int32_t +default_lk_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct gf_flock *lock, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (lk, frame, op_ret, op_errno, lock, xdata); + return 0; +} + +int32_t +default_inodelk_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (inodelk, frame, op_ret, op_errno, xdata); + return 0; +} + + +int32_t +default_finodelk_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (finodelk, frame, op_ret, op_errno, xdata); + return 0; +} + +int32_t +default_entrylk_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (entrylk, frame, op_ret, op_errno, xdata); + return 0; +} + +int32_t +default_fentrylk_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + STACK_UNWIND_STRICT (fentrylk, frame, op_ret, op_errno, xdata); + return 0; +} + + +int32_t +default_rchecksum_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + uint32_t weak_checksum, uint8_t *strong_checksum, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (rchecksum, frame, op_ret, op_errno, weak_checksum, + strong_checksum, xdata); + return 0; +} + + +int32_t +default_readdir_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + gf_dirent_t *entries, dict_t *xdata) +{ + STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, entries, xdata); + return 0; +} + + +int32_t +default_readdirp_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + gf_dirent_t *entries, dict_t *xdata) +{ + STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, entries, xdata); + return 0; +} + +int32_t +default_setattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *statpre, struct iatt *statpost, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno, statpre, + statpost, xdata); + return 0; +} + +int32_t +default_fsetattr_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *statpre, struct iatt *statpost, + dict_t *xdata) +{ + STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno, statpre, + statpost, xdata); + return 0; +} + +int32_t +default_fallocate_cbk_resume(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *pre, struct iatt *post, + dict_t *xdata) +{ + STACK_UNWIND_STRICT(fallocate, frame, op_ret, op_errno, + pre, post, xdata); + return 0; +} + +int32_t +default_discard_cbk_resume(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t *xdata) +{ + STACK_UNWIND_STRICT(discard, frame, op_ret, op_errno, pre, post, xdata); + return 0; +} + +int32_t +default_zerofill_cbk_resume(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t *xdata) +{ + STACK_UNWIND_STRICT(zerofill, frame, op_ret, op_errno, pre, + post, xdata); + return 0; +} + + +int32_t +default_getspec_cbk_resume (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, char *spec_data) +{ + STACK_UNWIND_STRICT (getspec, frame, op_ret, op_errno, spec_data); + return 0; +} + /* _CBK function section */ int32_t @@ -805,8 +1263,8 @@ default_fallocate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *pre, struct iatt *post, dict_t *xdata) { - STACK_UNWIND_STRICT(fallocate, frame, op_ret, op_errno, pre, post, xdata); - return 0; + STACK_UNWIND_STRICT(fallocate, frame, op_ret, op_errno, pre, post, xdata); + return 0; } int32_t @@ -814,8 +1272,8 @@ default_discard_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *pre, struct iatt *post, dict_t *xdata) { - STACK_UNWIND_STRICT(discard, frame, op_ret, op_errno, pre, post, xdata); - return 0; + STACK_UNWIND_STRICT(discard, frame, op_ret, op_errno, pre, post, xdata); + return 0; } int32_t @@ -1239,21 +1697,21 @@ default_fsetattr_resume (call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t default_fallocate_resume(call_frame_t *frame, xlator_t *this, fd_t *fd, - int32_t keep_size, off_t offset, size_t len, dict_t *xdata) + int32_t keep_size, off_t offset, size_t len, dict_t *xdata) { - STACK_WIND(frame, default_fallocate_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->fallocate, fd, keep_size, offset, len, - xdata); + STACK_WIND(frame, default_fallocate_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fallocate, fd, keep_size, offset, len, + xdata); return 0; } int32_t default_discard_resume(call_frame_t *frame, xlator_t *this, fd_t *fd, - off_t offset, size_t len, dict_t *xdata) + off_t offset, size_t len, dict_t *xdata) { - STACK_WIND(frame, default_discard_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->discard, fd, offset, len, - xdata); + STACK_WIND(frame, default_discard_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->discard, fd, offset, len, + xdata); return 0; } @@ -1674,22 +2132,22 @@ default_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t default_fallocate(call_frame_t *frame, xlator_t *this, fd_t *fd, - int32_t keep_size, off_t offset, size_t len, dict_t *xdata) + int32_t keep_size, off_t offset, size_t len, dict_t *xdata) { - STACK_WIND_TAIL(frame, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->fallocate, fd, keep_size, offset, - len, xdata); - return 0; + STACK_WIND_TAIL(frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fallocate, fd, keep_size, offset, + len, xdata); + return 0; } int32_t default_discard(call_frame_t *frame, xlator_t *this, fd_t *fd, - off_t offset, size_t len, dict_t *xdata) + off_t offset, size_t len, dict_t *xdata) { - STACK_WIND_TAIL(frame, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->discard, fd, offset, len, - xdata); - return 0; + STACK_WIND_TAIL(frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->discard, fd, offset, len, + xdata); + return 0; } int32_t diff --git a/libglusterfs/src/defaults.h b/libglusterfs/src/defaults.h index f0b786ddf..1b33e8099 100644 --- a/libglusterfs/src/defaults.h +++ b/libglusterfs/src/defaults.h @@ -244,16 +244,16 @@ int32_t default_fsetattr (call_frame_t *frame, int32_t valid, dict_t *xdata); int32_t default_fallocate(call_frame_t *frame, - xlator_t *this, - fd_t *fd, - int32_t keep_size, off_t offset, - size_t len, dict_t *xdata); + xlator_t *this, + fd_t *fd, + int32_t keep_size, off_t offset, + size_t len, dict_t *xdata); int32_t default_discard(call_frame_t *frame, - xlator_t *this, - fd_t *fd, - off_t offset, - size_t len, dict_t *xdata); + xlator_t *this, + fd_t *fd, + off_t offset, + size_t len, dict_t *xdata); int32_t default_zerofill(call_frame_t *frame, xlator_t *this, @@ -473,16 +473,16 @@ int32_t default_fsetattr_resume (call_frame_t *frame, int32_t valid, dict_t *xdata); int32_t default_fallocate_resume(call_frame_t *frame, - xlator_t *this, - fd_t *fd, - int32_t keep_size, off_t offset, - size_t len, dict_t *xdata); + xlator_t *this, + fd_t *fd, + int32_t keep_size, off_t offset, + size_t len, dict_t *xdata); int32_t default_discard_resume(call_frame_t *frame, - xlator_t *this, - fd_t *fd, - off_t offset, - size_t len, dict_t *xdata); + xlator_t *this, + fd_t *fd, + off_t offset, + size_t len, dict_t *xdata); int32_t default_zerofill_resume(call_frame_t *frame, xlator_t *this, @@ -491,8 +491,278 @@ int32_t default_zerofill_resume(call_frame_t *frame, off_t len, dict_t *xdata); -/* _cbk */ +/* _cbk_resume */ + +int32_t +default_lookup_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + inode_t * inode, struct iatt *buf, dict_t * xdata, + struct iatt *postparent); + +int32_t +default_stat_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t * xdata); + + +int32_t +default_truncate_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t * xdata); + +int32_t +default_ftruncate_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t * xdata); + +int32_t +default_access_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + dict_t * xdata); + +int32_t +default_readlink_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, const char *path, + struct iatt *buf, dict_t * xdata); + + +int32_t +default_mknod_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, inode_t * inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t * xdata); + +int32_t +default_mkdir_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, inode_t * inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t * xdata); + +int32_t +default_unlink_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + struct iatt *preparent, struct iatt *postparent, + dict_t * xdata); + +int32_t +default_rmdir_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, + struct iatt *preparent, struct iatt *postparent, + dict_t * xdata); + + +int32_t +default_symlink_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + inode_t * inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + dict_t * xdata); + + +int32_t +default_rename_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + struct iatt *buf, struct iatt *preoldparent, + struct iatt *postoldparent, + struct iatt *prenewparent, + struct iatt *postnewparent, dict_t * xdata); + + +int32_t +default_link_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, inode_t * inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t * xdata); + + +int32_t +default_create_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + fd_t * fd, inode_t * inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + dict_t * xdata); + +int32_t +default_open_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, fd_t * fd, + dict_t * xdata); + +int32_t +default_readv_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, + struct iovec *vector, int32_t count, + struct iatt *stbuf, struct iobref *iobref, + dict_t * xdata); + + +int32_t +default_writev_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + struct iatt *prebuf, struct iatt *postbuf, + dict_t * xdata); + + +int32_t +default_flush_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, dict_t * xdata); + + + +int32_t +default_fsync_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, + struct iatt *prebuf, struct iatt *postbuf, + dict_t * xdata); + +int32_t +default_fstat_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t * xdata); +int32_t +default_opendir_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + fd_t * fd, dict_t * xdata); + +int32_t +default_fsyncdir_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + +int32_t +default_statfs_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + struct statvfs *buf, dict_t * xdata); + + +int32_t +default_setxattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + + +int32_t +default_fsetxattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + + + +int32_t +default_fgetxattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * dict, + dict_t * xdata); + + +int32_t +default_getxattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * dict, dict_t * xdata); + +int32_t +default_xattrop_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + dict_t * dict, dict_t * xdata); + +int32_t +default_fxattrop_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * dict, dict_t * xdata); + + +int32_t +default_removexattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + +int32_t +default_fremovexattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + +int32_t +default_lk_cbk_resume (call_frame_t * frame, void *cookie, xlator_t * this, + int32_t op_ret, int32_t op_errno, + struct gf_flock *lock, dict_t * xdata); + +int32_t +default_inodelk_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + dict_t * xdata); + + +int32_t +default_finodelk_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + +int32_t +default_entrylk_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + dict_t * xdata); + +int32_t +default_fentrylk_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, dict_t * xdata); + + +int32_t +default_rchecksum_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, uint32_t weak_checksum, + uint8_t * strong_checksum, dict_t * xdata); + + +int32_t +default_readdir_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + gf_dirent_t * entries, dict_t * xdata); + + +int32_t +default_readdirp_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, gf_dirent_t * entries, + dict_t * xdata); + +int32_t +default_setattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + struct iatt *statpre, struct iatt *statpost, + dict_t * xdata); + +int32_t +default_fsetattr_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, struct iatt *statpre, + struct iatt *statpost, dict_t * xdata); + +int32_t default_fallocate_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t * xdata); + +int32_t default_discard_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t * xdata); + +int32_t default_zerofill_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, + int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t * xdata); + +int32_t +default_getspec_cbk_resume (call_frame_t * frame, void *cookie, + xlator_t * this, int32_t op_ret, int32_t op_errno, + char *spec_data); + +/* _CBK */ int32_t default_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, @@ -702,12 +972,12 @@ default_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, struct iatt *statpost, dict_t *xdata); int32_t default_fallocate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *pre, - struct iatt *post, dict_t *xdata); + int32_t op_ret, int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t *xdata); int32_t default_discard_cbk(call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *pre, - struct iatt *post, dict_t *xdata); + int32_t op_ret, int32_t op_errno, struct iatt *pre, + struct iatt *post, dict_t *xdata); int32_t default_zerofill_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *pre, diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am index d2f5ef192..1fdd474c2 100644 --- a/xlators/features/Makefile.am +++ b/xlators/features/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = locks quota read-only mac-compat quiesce marker index \ +SUBDIRS = locks quota read-only mac-compat quiesce marker index barrier \ protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block # trash path-converter # filter CLEANFILES = diff --git a/xlators/features/barrier/Makefile.am b/xlators/features/barrier/Makefile.am new file mode 100644 index 000000000..a985f42a8 --- /dev/null +++ b/xlators/features/barrier/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src + +CLEANFILES = diff --git a/xlators/features/barrier/src/Makefile.am b/xlators/features/barrier/src/Makefile.am new file mode 100644 index 000000000..8859be328 --- /dev/null +++ b/xlators/features/barrier/src/Makefile.am @@ -0,0 +1,16 @@ +xlator_LTLIBRARIES = barrier.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +barrier_la_LDFLAGS = -module -avoid-version + +barrier_la_SOURCES = barrier.c + +barrier_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la + +noinst_HEADERS = barrier.h barrier-mem-types.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/barrier/src/barrier-mem-types.h b/xlators/features/barrier/src/barrier-mem-types.h new file mode 100644 index 000000000..36647a669 --- /dev/null +++ b/xlators/features/barrier/src/barrier-mem-types.h @@ -0,0 +1,20 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef __BARRIER_MEM_TYPES_H__ +#define __BARRIER_MEM_TYPES_H__ + +#include "mem-types.h" + +enum gf_barrier_mem_types_ { + gf_barrier_mt_priv_t = gf_common_mt_end + 1, + gf_barrier_mt_end +}; +#endif diff --git a/xlators/features/barrier/src/barrier.c b/xlators/features/barrier/src/barrier.c new file mode 100644 index 000000000..566c67f30 --- /dev/null +++ b/xlators/features/barrier/src/barrier.c @@ -0,0 +1,520 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "barrier.h" +#include "defaults.h" +#include "call-stub.h" + +int32_t +barrier_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, + dict_t *xdata) +{ + BARRIER_FOP_CBK (writev, out, frame, this, op_ret, op_errno, + prebuf, postbuf, xdata); +out: + return 0; +} + +int32_t +barrier_fremovexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + BARRIER_FOP_CBK (fremovexattr, out, frame, this, op_ret, op_errno, + xdata); +out: + return 0; +} + +int32_t +barrier_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + BARRIER_FOP_CBK (removexattr, out, frame, this, op_ret, op_errno, + xdata); +out: + return 0; +} + +int32_t +barrier_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + BARRIER_FOP_CBK (truncate, out, frame, this, op_ret, op_errno, prebuf, + postbuf, xdata); +out: + return 0; +} + +int32_t +barrier_ftruncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + BARRIER_FOP_CBK (ftruncate, out, frame, this, op_ret, op_errno, prebuf, + postbuf, xdata); +out: + return 0; +} + +int32_t +barrier_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) +{ + BARRIER_FOP_CBK (rename, out, frame, this, op_ret, op_errno, buf, + preoldparent, postoldparent, prenewparent, + postnewparent, xdata); +out: + return 0; +} + +int32_t +barrier_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + BARRIER_FOP_CBK (rmdir, out, frame, this, op_ret, op_errno, preparent, + postparent, xdata); +out: + return 0; +} + +int32_t +barrier_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + BARRIER_FOP_CBK (unlink, out, frame, this, op_ret, op_errno, preparent, + postparent, xdata); +out: + return 0; +} + +int32_t +barrier_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + BARRIER_FOP_CBK (fsync, out, frame, this, op_ret, op_errno, + prebuf, postbuf, xdata); +out: + return 0; +} + +int32_t +barrier_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, + struct iovec *vector, int32_t count, off_t off, uint32_t flags, + struct iobref *iobref, dict_t *xdata) +{ + if (!(flags & O_SYNC)) { + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, + fd, vector, count, off, flags, iobref, xdata); + + return 0; + } + + STACK_WIND (frame, barrier_writev_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, fd, vector, count, + off, flags, iobref, xdata); + return 0; +} + +int32_t +barrier_fremovexattr (call_frame_t *frame, xlator_t *this, fd_t *fd, + const char *name, dict_t *xdata) +{ + STACK_WIND (frame, barrier_fremovexattr_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->fremovexattr, + fd, name, xdata); + return 0; +} + +int32_t +barrier_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc, + const char *name, dict_t *xdata) +{ + STACK_WIND (frame, barrier_removexattr_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->removexattr, + loc, name, xdata); + return 0; +} + +int32_t +barrier_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, + off_t offset, dict_t *xdata) +{ + STACK_WIND (frame, barrier_truncate_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->truncate, + loc, offset, xdata); + return 0; +} + +int32_t +barrier_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, + loc_t *newloc, dict_t *xdata) +{ + STACK_WIND (frame, barrier_rename_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->rename, + oldloc, newloc, xdata); + return 0; +} + +int +barrier_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + dict_t *xdata) +{ + STACK_WIND (frame, barrier_rmdir_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->rmdir, + loc, flags, xdata); + return 0; +} + +int32_t +barrier_unlink (call_frame_t *frame, xlator_t *this, + loc_t *loc, int xflag, dict_t *xdata) +{ + STACK_WIND (frame, barrier_unlink_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->unlink, + loc, xflag, xdata); + return 0; +} + +int32_t +barrier_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, + off_t offset, dict_t *xdata) +{ + STACK_WIND (frame, barrier_ftruncate_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->ftruncate, + fd, offset, xdata); + return 0; +} + +int32_t +barrier_fsync (call_frame_t *frame, xlator_t *this, fd_t *fd, + int32_t flags, dict_t *xdata) +{ + STACK_WIND (frame, barrier_fsync_cbk, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->fsync, + fd, flags, xdata); + return 0; +} + +call_stub_t * +__barrier_dequeue (xlator_t *this, struct list_head *queue) +{ + call_stub_t *stub = NULL; + barrier_priv_t *priv = NULL; + + priv = this->private; + GF_ASSERT (priv); + + if (list_empty (queue)) + goto out; + + stub = list_entry (queue->next, call_stub_t, list); + list_del_init (&stub->list); + +out: + return stub; +} + +void +barrier_dequeue_all (xlator_t *this, struct list_head *queue) +{ + call_stub_t *stub = NULL; + + gf_log (this->name, GF_LOG_INFO, "Dequeuing all the barriered fops"); + + /* TODO: Start the below task in a new thread */ + while ((stub = __barrier_dequeue (this, queue))) + call_resume (stub); + + gf_log (this->name, GF_LOG_INFO, "Dequeuing the barriered fops is " + "finished"); + return; +} + +void +barrier_timeout (void *data) +{ + xlator_t *this = NULL; + barrier_priv_t *priv = NULL; + struct list_head queue = {0,}; + + this = data; + THIS = this; + priv = this->private; + + INIT_LIST_HEAD (&queue); + + gf_log (this->name, GF_LOG_CRITICAL, "Disabling barrier because of " + "the barrier timeout."); + + LOCK (&priv->lock); + { + __barrier_disable (this, &queue); + } + UNLOCK (&priv->lock); + + barrier_dequeue_all (this, &queue); + + return; +} + +void +__barrier_enqueue (xlator_t *this, call_stub_t *stub) +{ + barrier_priv_t *priv = NULL; + + priv = this->private; + GF_ASSERT (priv); + + list_add_tail (&stub->list, &priv->queue); + priv->queue_size++; + + return; +} + +void +__barrier_disable (xlator_t *this, struct list_head *queue) +{ + GF_UNUSED int ret = 0; + barrier_priv_t *priv = NULL; + + priv = this->private; + GF_ASSERT (priv); + + if (priv->timer) { + ret = gf_timer_call_cancel (this->ctx, priv->timer); + priv->timer = NULL; + } + + list_splice_init (&priv->queue, queue); + priv->queue_size = 0; + priv->barrier_enabled = _gf_false; +} + +int +__barrier_enable (xlator_t *this, barrier_priv_t *priv) +{ + int ret = -1; + + priv->timer = gf_timer_call_after (this->ctx, priv->timeout, + barrier_timeout, (void *) this); + if (!priv->timer) { + gf_log (this->name, GF_LOG_CRITICAL, "Couldn't add barrier " + "timeout event."); + goto out; + } + + priv->barrier_enabled = _gf_true; + ret = 0; +out: + return ret; +} + +int +reconfigure (xlator_t *this, dict_t *options) +{ + barrier_priv_t *priv = NULL; + gf_boolean_t past = _gf_false; + int ret = -1; + gf_boolean_t barrier_enabled = _gf_false; + uint32_t timeout = {0,}; + struct list_head queue = {0,}; + + priv = this->private; + GF_ASSERT (priv); + + GF_OPTION_RECONF ("barrier", barrier_enabled, options, bool, out); + GF_OPTION_RECONF ("timeout", timeout, options, time, out); + + INIT_LIST_HEAD (&queue); + + LOCK (&priv->lock); + { + past = priv->barrier_enabled; + + switch (past) { + case _gf_false: + if (barrier_enabled) { + ret = __barrier_enable (this, priv); + if (ret) + goto unlock; + + } else { + gf_log (this->name, GF_LOG_ERROR, + "Already disabled"); + goto unlock; + } + break; + + case _gf_true: + if (!barrier_enabled) { + __barrier_disable (this, &queue); + + } else { + gf_log (this->name, GF_LOG_ERROR, + "Already enabled"); + goto unlock; + } + break; + } + + priv->timeout.tv_sec = timeout; + + ret = 0; + } +unlock: + UNLOCK (&priv->lock); + + if (!list_empty (&queue)) + barrier_dequeue_all (this, &queue); + +out: + return ret; +} + +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + ret = xlator_mem_acct_init (this, gf_barrier_mt_end + 1); + if (ret) + gf_log (this->name, GF_LOG_ERROR, "Memory accounting " + "initialization failed."); + + return ret; +} + +int +init (xlator_t *this) +{ + int ret = -1; + barrier_priv_t *priv = NULL; + uint32_t timeout = {0,}; + + if (!this->children || this->children->next) { + gf_log (this->name, GF_LOG_ERROR, + "'barrier' not configured with exactly one child"); + goto out; + } + + if (!this->parents) + gf_log (this->name, GF_LOG_WARNING, + "dangling volume. check volfile "); + + priv = GF_CALLOC (1, sizeof (*priv), gf_barrier_mt_priv_t); + if (!priv) + goto out; + + LOCK_INIT (&priv->lock); + + GF_OPTION_INIT ("barrier", priv->barrier_enabled, bool, out); + GF_OPTION_INIT ("timeout", timeout, time, out); + priv->timeout.tv_sec = timeout; + + INIT_LIST_HEAD (&priv->queue); + + if (priv->barrier_enabled) { + ret = __barrier_enable (this, priv); + if (ret == -1) + goto out; + } + + this->private = priv; + ret = 0; +out: + return ret; +} + +void +fini (xlator_t *this) +{ + barrier_priv_t *priv = NULL; + struct list_head queue = {0,}; + + priv = this->private; + if (!priv) + goto out; + + INIT_LIST_HEAD (&queue); + + gf_log (this->name, GF_LOG_INFO, "Disabling barriering and dequeuing " + "all the queued fops"); + LOCK (&priv->lock); + { + __barrier_disable (this, &queue); + } + UNLOCK (&priv->lock); + + if (!list_empty (&queue)) + barrier_dequeue_all (this, &queue); + + this->private = NULL; + + LOCK_DESTROY (&priv->lock); + GF_FREE (priv); +out: + return; +} + +struct xlator_fops fops = { + + /* Barrier Class fops */ + .rmdir = barrier_rmdir, + .unlink = barrier_unlink, + .rename = barrier_rename, + .removexattr = barrier_removexattr, + .fremovexattr = barrier_fremovexattr, + .truncate = barrier_truncate, + .ftruncate = barrier_ftruncate, + .fsync = barrier_fsync, + + /* Writes with only O_SYNC flag */ + .writev = barrier_writev, +}; + +struct xlator_dumpops dumpops; + +struct xlator_cbks cbks; + +struct volume_options options[] = { + { .key = {"barrier"}, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "When \"on\", blocks acknowledgements to application " + "for file operations such as rmdir, rename, unlink, " + "removexattr, fremovexattr, truncate, ftruncate, " + "write (with O_SYNC), fsync. It is turned \"off\" by " + "default." + }, + { .key = {"timeout"}, + .type = GF_OPTION_TYPE_TIME, + .default_value = "120", + .description = "After 'timeout' seconds since the time 'barrier' " + "option was set to \"on\", acknowledgements to file " + "operations are no longer blocked and previously " + "blocked acknowledgements are sent to the application" + }, + { .key = {NULL} }, +}; diff --git a/xlators/features/barrier/src/barrier.h b/xlators/features/barrier/src/barrier.h new file mode 100644 index 000000000..8face9f65 --- /dev/null +++ b/xlators/features/barrier/src/barrier.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef __BARRIER_H__ +#define __BARRIER_H__ + +#include "barrier-mem-types.h" +#include "xlator.h" +#include "timer.h" +#include "call-stub.h" + +#define BARRIER_SAFE_ASSIGN(lock, to, value) \ + do { \ + LOCK (&(lock)); \ + { \ + to = value; \ + } \ + UNLOCK (&(lock)); \ + } while (0) + +#define BARRIER_FOP_CBK(fop_name, label, frame, this, params ...) \ + do { \ + barrier_priv_t *_priv = NULL; \ + call_stub_t *_stub = NULL; \ + gf_boolean_t _barrier_enabled= _gf_false; \ + struct list_head queue = {0, }; \ + \ + INIT_LIST_HEAD (&queue); \ + \ + _priv = this->private; \ + GF_ASSERT (_priv); \ + \ + LOCK (&_priv->lock); \ + { \ + if (_priv->barrier_enabled) { \ + _barrier_enabled = _priv->barrier_enabled;\ + \ + _stub = fop_##fop_name##_cbk_stub \ + (frame, \ + default_##fop_name##_cbk_resume,\ + params); \ + if (!_stub) { \ + __barrier_disable (this, &queue);\ + goto unlock; \ + } \ + \ + __barrier_enqueue (this, _stub); \ + } \ + } \ +unlock: \ + UNLOCK (&_priv->lock); \ + \ + if (_stub) \ + goto label; \ + \ + if (_barrier_enabled && !_stub) { \ + gf_log (this->name, GF_LOG_CRITICAL, \ + "Failed to barrier FOPs, disabling " \ + "barrier. FOP: %s, ERROR: %s", \ + #fop_name, strerror (ENOMEM)); \ + barrier_dequeue_all (this, &queue); \ + } \ + \ + STACK_UNWIND_STRICT (fop_name, frame, params); \ + goto label; \ + } while (0) + +typedef struct { + gf_timer_t *timer; + gf_boolean_t barrier_enabled; + gf_lock_t lock; + struct list_head queue; + struct timespec timeout; + uint32_t queue_size; +} barrier_priv_t; + +int __barrier_enable (xlator_t *this, barrier_priv_t *priv); +void __barrier_enqueue (xlator_t *this, call_stub_t *stub); +void __barrier_disable (xlator_t *this, struct list_head *queue); +void barrier_timeout (void *data); +void barrier_dequeue_all (xlator_t *this, struct list_head *queue); +call_stub_t *__barrier_dequeue (xlator_t *this, struct list_head *queue); + +#endif -- cgit From ef08cf0fb6ce63094468d85f5b3bab7637e88b00 Mon Sep 17 00:00:00 2001 From: Kaushal M Date: Tue, 4 Mar 2014 12:04:37 +0530 Subject: feature/barrier: Add statedump support This patch adds statedump support for barrier. This currently dumps barrier xlators private information and the queue of barriered fops. Change-Id: I273eb6e676db02c40c363feeff58a79737dc041e BUG: 1060002 Reviewed-on: http://review.gluster.org/7136 Tested-by: Gluster Build System Reviewed-by: Krishnan Parthasarathi Reviewed-by: Vijay Bellur --- xlators/features/barrier/src/barrier.c | 82 +++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/xlators/features/barrier/src/barrier.c b/xlators/features/barrier/src/barrier.c index 566c67f30..e5465d1b4 100644 --- a/xlators/features/barrier/src/barrier.c +++ b/xlators/features/barrier/src/barrier.c @@ -17,6 +17,8 @@ #include "defaults.h" #include "call-stub.h" +#include "statedump.h" + int32_t barrier_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, @@ -478,6 +480,82 @@ out: return; } +static void +barrier_dump_stub (call_stub_t *stub, char *prefix) +{ + char key[GF_DUMP_MAX_BUF_LEN] = {0,}; + + gf_proc_dump_build_key (key, prefix, "fop"); + gf_proc_dump_write (key, "%s", gf_fop_list[stub->fop]); + + gf_proc_dump_build_key (key, prefix, "gfid"); + gf_proc_dump_write (key, "%s", uuid_utoa (stub->args.loc.gfid)); + + if (stub->args.loc.path) { + gf_proc_dump_build_key (key, prefix, "path"); + gf_proc_dump_write (key, "%s", stub->args.loc.path); + } + if (stub->args.loc.name) { + gf_proc_dump_build_key (key, prefix, "name"); + gf_proc_dump_write (key, "%s", stub->args.loc.name); + } + + return; +} + +static void +__barrier_dump_queue (barrier_priv_t *priv) +{ + call_stub_t *stub = NULL; + char key[GF_DUMP_MAX_BUF_LEN] = {0,}; + int i = 0; + + GF_VALIDATE_OR_GOTO ("barrier", priv, out); + + list_for_each_entry (stub, &priv->queue, list) { + snprintf (key, sizeof (key), "stub.%d", i++); + gf_proc_dump_add_section (key); + barrier_dump_stub(stub, key); + } + +out: + return; +} + +int +barrier_dump_priv (xlator_t *this) +{ + int ret = -1; + char key[GF_DUMP_MAX_BUF_LEN] = {0,}; + barrier_priv_t *priv = NULL; + + GF_VALIDATE_OR_GOTO ("barrier", this, out); + + priv = this->private; + if (!priv) + return 0; + + gf_proc_dump_build_key (key, "xlator.features.barrier", "priv"); + gf_proc_dump_add_section (key); + + LOCK (&priv->lock); + { + gf_proc_dump_build_key (key, "barrier", "enabled"); + gf_proc_dump_write (key, "%d", priv->barrier_enabled); + gf_proc_dump_build_key (key, "barrier", "timeout"); + gf_proc_dump_write (key, "%"PRId64, priv->timeout.tv_sec); + if (priv->barrier_enabled) { + gf_proc_dump_build_key (key, "barrier", "queue_size"); + gf_proc_dump_write (key, "%d", priv->queue_size); + __barrier_dump_queue (priv); + } + } + UNLOCK (&priv->lock); + +out: + return ret; +} + struct xlator_fops fops = { /* Barrier Class fops */ @@ -494,7 +572,9 @@ struct xlator_fops fops = { .writev = barrier_writev, }; -struct xlator_dumpops dumpops; +struct xlator_dumpops dumpops = { + .priv = barrier_dump_priv, +}; struct xlator_cbks cbks; -- cgit From 0c20b17c09b2eca82f3c79013fd3fe1c72a957fd Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Thu, 27 Mar 2014 15:04:40 +0530 Subject: tests/afr: self-heal Basic functional tests related to self-heal. arequal-checksum.c is taken from https://github.com/raghavendrabhat/arequal after consent from all authors. Change-Id: I43facc31c61375f4dbe58bbb46238e15df5c9011 BUG: 1080759 Signed-off-by: Ravishankar N Reviewed-on: http://review.gluster.org/7357 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- tests/basic/afr/self-heal.t | 237 ++++++++++++++++ tests/utils/arequal-checksum.c | 611 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 848 insertions(+) create mode 100644 tests/basic/afr/self-heal.t create mode 100644 tests/utils/arequal-checksum.c diff --git a/tests/basic/afr/self-heal.t b/tests/basic/afr/self-heal.t new file mode 100644 index 000000000..df9526bcf --- /dev/null +++ b/tests/basic/afr/self-heal.t @@ -0,0 +1,237 @@ +#!/bin/bash +#Self-heal tests + +. $(dirname $0)/../../include.rc +. $(dirname $0)/../../volume.rc +cleanup; + +#Init +AREQUAL_PATH=$(dirname $0)/../../utils +build_tester $AREQUAL_PATH/arequal-checksum.c +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/brick{0,1} +TEST $CLI volume set $V0 stat-prefetch off +TEST $CLI volume start $V0 +TEST $CLI volume set $V0 cluster.background-self-heal-count 0 +TEST glusterfs --volfile-id=$V0 --volfile-server=$H0 $M0 --entry-timeout=0 --attribute-timeout=0; + +############################################################################### +#1.Test successful data, metadata and entry self-heal + +#Test +TEST mkdir -p $M0/abc/def $M0/abc/ghi +TEST dd if=/dev/urandom of=$M0/abc/file_abc.txt bs=1M count=2 2>/dev/null +TEST dd if=/dev/urandom of=$M0/abc/def/file_abc_def_1.txt bs=1M count=2 2>/dev/null +TEST dd if=/dev/urandom of=$M0/abc/def/file_abc_def_2.txt bs=1M count=3 2>/dev/null +TEST dd if=/dev/urandom of=$M0/abc/ghi/file_abc_ghi.txt bs=1M count=4 2>/dev/null + +TEST kill_brick $V0 $H0 $B0/brick0 +TEST truncate -s 0 $M0/abc/def/file_abc_def_1.txt +NEW_UID=36 +NEW_GID=36 +TEST chown $NEW_UID:$NEW_GID $M0/abc/def/file_abc_def_2.txt +TEST rm -rf $M0/abc/ghi +TEST mkdir -p $M0/def/ghi $M0/jkl/mno +TEST dd if=/dev/urandom of=$M0/def/ghi/file1.txt bs=1M count=2 2>/dev/null +TEST dd if=/dev/urandom of=$M0/def/ghi/file2.txt bs=1M count=3 2>/dev/null +TEST dd if=/dev/urandom of=$M0/jkl/mno/file.txt bs=1M count=4 2>/dev/null +TEST chown $NEW_UID:$NEW_GID $M0/def/ghi/file2.txt + +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check all files created/deleted on brick1 are also replicated on brick 0 +#(i.e. no reverse heal has happened) +TEST ls $B0/brick0/def/ghi/file1.txt +TEST ls $B0/brick0/def/ghi/file2.txt +TEST ls $B0/brick0/jkl/mno/file.txt +TEST ! ls $B0/brick0/abc/ghi +EXPECT "$NEW_UID$NEW_GID" stat --printf=%u%g $B0/brick0/abc/def/file_abc_def_2.txt +TEST diff <($AREQUAL_PATH/arequal-checksum -p $B0/brick0 -i .glusterfs) <($AREQUAL_PATH/arequal-checksum -p $B0/brick1 -i .glusterfs) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#2.Test successful self-heal of different file types. + +#Test +TEST touch $M0/file +TEST kill_brick $V0 $H0 $B0/brick0 +TEST rm -f $M0/file +TEST mkdir $M0/file + +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check heal has happened in the correct direction +TEST test -d $B0/brick0/file +TEST diff <($AREQUAL_PATH/arequal-checksum -p $B0/brick0 -i .glusterfs) <($AREQUAL_PATH/arequal-checksum -p $B0/brick1 -i .glusterfs) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#3.Test successful self-heal of file permissions. + +#Test +TEST touch $M0/file +TEST chmod 666 $M0/file +TEST kill_brick $V0 $H0 $B0/brick0 +TEST chmod 777 $M0/file +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check heal has happened in the correct direction +EXPECT "777" stat --printf=%a $B0/brick0/file +TEST diff <($AREQUAL_PATH/arequal-checksum -p $B0/brick0 -i .glusterfs) <($AREQUAL_PATH/arequal-checksum -p $B0/brick1 -i .glusterfs) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#4.Test successful self-heal of file ownership + +#Test +TEST touch $M0/file +TEST kill_brick $V0 $H0 $B0/brick0 +NEW_UID=36 +NEW_GID=36 +TEST chown $NEW_UID:$NEW_GID $M0/file +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check heal has happened in the correct direction +EXPECT "$NEW_UID$NEW_GID" stat --printf=%u%g $B0/brick0/file +TEST diff <($AREQUAL_PATH/arequal-checksum -p $B0/brick0 -i .glusterfs) <($AREQUAL_PATH/arequal-checksum -p $B0/brick1 -i .glusterfs) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#5.File size test + +#Test +TEST touch $M0/file +TEST `echo "write1">$M0/file` +TEST kill_brick $V0 $H0 $B0/brick0 +TEST `echo "write2">>$M0/file` +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +TEST kill_brick $V0 $H0 $B0/brick1 +TEST truncate -s 0 $M0/file +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 1 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check heal has happened in the correct direction +EXPECT 0 stat --printf=%s $B0/brick1/file +TEST diff <($AREQUAL_PATH/arequal-checksum -p $B0/brick0 -i .glusterfs) <($AREQUAL_PATH/arequal-checksum -p $B0/brick1 -i .glusterfs) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#6.GFID heal + +#Test +TEST touch $M0/file +TEST kill_brick $V0 $H0 $B0/brick0 +TEST rm -f $M0/file +TEST touch $M0/file +GFID=$(gf_get_gfid_xattr $B1/brick1/file) +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check heal has happened in the correct direction +EXPECT "$GFID" gf_get_gfid_xattr $B0/brick0/file + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#7. Link/symlink heal + +#Test +TEST touch $M0/file +TEST ln $M0/file $M0/link_to_file +TEST kill_brick $V0 $H0 $B0/brick0 +TEST rm -f $M0/link_to_file +TEST ln -s $M0/file $M0/link_to_file +TEST ln $M0/file $M0/hard_link_to_file +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +#check heal has happened in the correct direction +TEST test -f $B0/brick0/hard_link_to_file +TEST test -h $B0/brick0/link_to_file +TEST diff <($AREQUAL_PATH/arequal-checksum -p $B0/brick0 -i .glusterfs) <($AREQUAL_PATH/arequal-checksum -p $B0/brick1 -i .glusterfs) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +#8. Heal xattrs set by application + +#Test +TEST touch $M0/file +TEST setfattr -n user.myattr_1 -v My_attribute_1 $M0/file +TEST setfattr -n user.myattr_2 -v "My_attribute_2" $M0/file +TEST kill_brick $V0 $H0 $B0/brick0 +TEST setfattr -n user.myattr_1 -v "My_attribute_1_modified" $M0/file +TEST setfattr -n user.myattr_3 -v "My_attribute_3" $M0/file +TEST $CLI volume start $V0 force +EXPECT_WITHIN 20 "1" afr_child_up_status $V0 0 +EXPECT_WITHIN 20 "Y" glustershd_up_status +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 0 +EXPECT_WITHIN 20 "1" afr_child_up_status_in_shd $V0 1 +TEST $CLI volume heal $V0 +EXPECT_WITHIN 20 "0" afr_get_pending_heal_count $V0 + +TEST diff <(echo "user.myattr_1=\"My_attribute_1_modified\"") <(getfattr -n user.myattr_1 $B0/brick1/file|grep user.myattr_1) +TEST diff <(echo "user.myattr_3=\"My_attribute_3\"") <(getfattr -n user.myattr_3 $B0/brick1/file|grep user.myattr_3) + +#Cleanup +TEST rm -rf $M0/* +############################################################################### + +TEST rm -rf $AREQUAL_PATH/arequal-checksum +cleanup; diff --git a/tests/utils/arequal-checksum.c b/tests/utils/arequal-checksum.c new file mode 100644 index 000000000..bdc6af484 --- /dev/null +++ b/tests/utils/arequal-checksum.c @@ -0,0 +1,611 @@ +/* + Copyright (c) 2012 Red Hat, Inc. + This file is part of GlusterFS. + + GlusterFS 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 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#define _XOPEN_SOURCE 600 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +int debug = 0; + +typedef struct { + char test_directory[4096]; + char **ignored_directory; + unsigned int directories_ignored; +} arequal_config_t; + +static arequal_config_t arequal_config; + +static error_t +arequal_parse_opts (int key, char *arg, struct argp_state *_state); + +static struct argp_option arequal_options[] = { + { "ignore", 'i', "IGNORED", 0, + "entry in the given path to be ignored"}, + { "path", 'p', "PATH", 0, "path where arequal has to be run"}, + {0, 0, 0, 0, 0} +}; + +#define DBG(fmt ...) do { \ + if (debug) { \ + fprintf (stderr, "D "); \ + fprintf (stderr, fmt); \ + } \ + } while (0) + +void +add_to_list (char *arg); +void +get_absolute_path (char directory[], char *arg); + +static inline int roof(int a, int b) +{ + return ((((a)+(b)-1)/((b)?(b):1))*(b)); +} + +void +add_to_list (char *arg) +{ + char *string = NULL; + int index = 0; + + index = arequal_config.directories_ignored - 1; + string = strdup (arg); + + if (!arequal_config.ignored_directory) { + arequal_config.ignored_directory = calloc (1, sizeof (char *)); + } else + arequal_config.ignored_directory = + realloc (arequal_config.ignored_directory, + sizeof (char *) * (index+1)); + + arequal_config.ignored_directory[index] = string; +} + +static error_t +arequal_parse_opts (int key, char *arg, struct argp_state *_state) +{ + switch (key) { + case 'i': + { + arequal_config.directories_ignored++; + add_to_list (arg); + } + break; + case 'p': + { + if (arg[0] == '/') + strcpy (arequal_config.test_directory, arg); + else + get_absolute_path (arequal_config.test_directory, arg); + + if (arequal_config.test_directory + [strlen(arequal_config.test_directory) - 1] == '/') + arequal_config.test_directory + [strlen(arequal_config.test_directory) - 1] = '\0'; + } + break; + + case ARGP_KEY_NO_ARGS: + break; + case ARGP_KEY_ARG: + break; + case ARGP_KEY_END: + if (_state->argc == 1) { + argp_usage (_state); + } + + } + + return 0; +} + +void +get_absolute_path (char directory[], char *arg) +{ + char cwd[4096] = {0,}; + + if (getcwd (cwd, sizeof (cwd)) == NULL) + printf ("some error in getting cwd\n"); + + if (strcmp (arg, ".") != 0) { + if (cwd[strlen(cwd)] != '/') + cwd[strlen (cwd)] = '/'; + strcat (cwd, arg); + } + strcpy (directory, cwd); +} + +static struct argp argp = { + arequal_options, + arequal_parse_opts, + "", + "arequal - Tool which calculates the checksum of all the entries" + "present in a given directory" +}; + +/* All this runs in single thread, hence using 'global' variables */ + +unsigned long long avg_uid_file = 0; +unsigned long long avg_uid_dir = 0; +unsigned long long avg_uid_symlink = 0; +unsigned long long avg_uid_other = 0; + +unsigned long long avg_gid_file = 0; +unsigned long long avg_gid_dir = 0; +unsigned long long avg_gid_symlink = 0; +unsigned long long avg_gid_other = 0; + +unsigned long long avg_mode_file = 0; +unsigned long long avg_mode_dir = 0; +unsigned long long avg_mode_symlink = 0; +unsigned long long avg_mode_other = 0; + +unsigned long long global_ctime_checksum = 0; + + +unsigned long long count_dir = 0; +unsigned long long count_file = 0; +unsigned long long count_symlink = 0; +unsigned long long count_other = 0; + + +unsigned long long checksum_file1 = 0; +unsigned long long checksum_file2 = 0; +unsigned long long checksum_dir = 0; +unsigned long long checksum_symlink = 0; +unsigned long long checksum_other = 0; + + +unsigned long long +checksum_path (const char *path) +{ + unsigned long long csum = 0; + unsigned long long *nums = 0; + int len = 0; + int cnt = 0; + + len = roof (strlen (path), sizeof (csum)); + cnt = len / sizeof (csum); + + nums = alloca (len); + memset (nums, 0, len); + strcpy ((char *)nums, path); + + while (cnt) { + csum ^= *nums; + nums++; + cnt--; + } + + return csum; +} + +int +checksum_md5 (const char *path, const struct stat *sb) +{ + uint64_t this_data_checksum = 0; + FILE *filep = NULL; + char *cmd = NULL; + char strvalue[17] = {0,}; + int ret = -1; + int len = 0; + const char *pos = NULL; + char *cpos = NULL; + + /* Have to escape single-quotes in filename. + * First, calculate the size of the buffer I'll need. + */ + for (pos = path; *pos; pos++) { + if ( *pos == '\'' ) + len += 4; + else + len += 1; + } + + cmd = malloc(sizeof(char) * (len + 20)); + cmd[0] = '\0'; + + /* Now, build the command with single quotes escaped. */ + + cpos = cmd; + strcpy(cpos, "md5sum '"); + cpos += 8; + + /* Add the file path, with every single quotes replaced with this sequence: + * '\'' + */ + + for (pos = path; *pos; pos++) { + if ( *pos == '\'' ) { + strcpy(cpos, "'\\''"); + cpos += 4; + } else { + *cpos = *pos; + cpos++; + } + } + + /* Add on the trailing single-quote and null-terminate. */ + strcpy(cpos, "'"); + + filep = popen (cmd, "r"); + if (!filep) { + perror (path); + goto out; + } + + if (fread (strvalue, sizeof (char), 16, filep) != 16) { + fprintf (stderr, "%s: short read\n", path); + goto out; + } + + this_data_checksum = strtoull (strvalue, NULL, 16); + if (-1 == this_data_checksum) { + fprintf (stderr, "%s: %s\n", strvalue, strerror (errno)); + goto out; + } + checksum_file1 ^= this_data_checksum; + + if (fread (strvalue, sizeof (char), 16, filep) != 16) { + fprintf (stderr, "%s: short read\n", path); + goto out; + } + + this_data_checksum = strtoull (strvalue, NULL, 16); + if (-1 == this_data_checksum) { + fprintf (stderr, "%s: %s\n", strvalue, strerror (errno)); + goto out; + } + checksum_file2 ^= this_data_checksum; + + ret = 0; +out: + if (filep) + pclose (filep); + + if (cmd) + free(cmd); + + return ret; +} + +int +checksum_filenames (const char *path, const struct stat *sb) +{ + DIR *dirp = NULL; + struct dirent *entry = NULL; + unsigned long long csum = 0; + int i = 0; + int found = 0; + + dirp = opendir (path); + if (!dirp) { + perror (path); + goto out; + } + + errno = 0; + while ((entry = readdir (dirp))) { + /* do not calculate the checksum of the entries which user has + told to ignore and proceed to other siblings.*/ + if (arequal_config.ignored_directory) { + for (i = 0;i < arequal_config.directories_ignored;i++) { + if ((strcmp (entry->d_name, + arequal_config.ignored_directory[i]) + == 0)) { + found = 1; + DBG ("ignoring the entry %s\n", + entry->d_name); + break; + } + } + if (found == 1) { + found = 0; + continue; + } + } + csum = checksum_path (entry->d_name); + checksum_dir ^= csum; + } + + if (errno) { + perror (path); + goto out; + } + +out: + if (dirp) + closedir (dirp); + + return 0; +} + + +int +process_file (const char *path, const struct stat *sb) +{ + int ret = 0; + + count_file++; + + avg_uid_file ^= sb->st_uid; + avg_gid_file ^= sb->st_gid; + avg_mode_file ^= sb->st_mode; + + ret = checksum_md5 (path, sb); + + return ret; +} + + +int +process_dir (const char *path, const struct stat *sb) +{ + unsigned long long csum = 0; + + count_dir++; + + avg_uid_dir ^= sb->st_uid; + avg_gid_dir ^= sb->st_gid; + avg_mode_dir ^= sb->st_mode; + + csum = checksum_filenames (path, sb); + + checksum_dir ^= csum; + + return 0; +} + + +int +process_symlink (const char *path, const struct stat *sb) +{ + int ret = 0; + char buf[4096] = {0, }; + unsigned long long csum = 0; + + count_symlink++; + + avg_uid_symlink ^= sb->st_uid; + avg_gid_symlink ^= sb->st_gid; + avg_mode_symlink ^= sb->st_mode; + + ret = readlink (path, buf, 4096); + if (ret < 0) { + perror (path); + goto out; + } + + DBG ("readlink (%s) => %s\n", path, buf); + + csum = checksum_path (buf); + + DBG ("checksum_path (%s) => %llx\n", buf, csum); + + checksum_symlink ^= csum; + + ret = 0; +out: + return ret; +} + + +int +process_other (const char *path, const struct stat *sb) +{ + count_other++; + + avg_uid_other ^= sb->st_uid; + avg_gid_other ^= sb->st_gid; + avg_mode_other ^= sb->st_mode; + + checksum_other ^= sb->st_rdev; + + return 0; +} + + +int +process_entry (const char *path, const struct stat *sb, + int typeflag, struct FTW *ftwbuf) +{ + int ret = 0; + char *name = NULL; + char *bname = NULL; + char *dname = NULL; + int i = 0; + + /* The if condition below helps in ignoring some directories in + the given path. If the name of the entry is one of the directory + names that the user told to ignore, then that directory will not + be processed and will return FTW_SKIP_SUBTREE to nftw which will + not crawl this directory and move on to other siblings. + Note that for nftw to recognize FTW_SKIP_TREE, FTW_ACTIONRETVAL + should be passed as an argument to nftw. + + This mainly helps in calculating the checksum of network filesystems + (client-server), where the server might have some hidden directories + for managing the filesystem. So to calculate the sanity of filesytem + one has to get the checksum of the client and then the export directory + of server by telling arequal to ignore some of the directories which + are not part of the namespace. + */ + + if (arequal_config.ignored_directory) { + name = strdup (path); + + name[strlen(name)] == '\0'; + + bname = strrchr (name, '/'); + if (bname) + bname++; + + dname = dirname (name); + for ( i = 0; i < arequal_config.directories_ignored; i++) { + if ((strcmp (bname, arequal_config.ignored_directory[i]) + == 0) && (strcmp (arequal_config.test_directory, + dname) == 0)) { + DBG ("ignoring %s\n", bname); + ret = FTW_SKIP_SUBTREE; + if (name) + free (name); + return ret; + } + } + } + + DBG ("processing entry %s\n", path); + + switch ((S_IFMT & sb->st_mode)) { + case S_IFDIR: + ret = process_dir (path, sb); + break; + case S_IFREG: + ret = process_file (path, sb); + break; + case S_IFLNK: + ret = process_symlink (path, sb); + break; + default: + ret = process_other (path, sb); + break; + } + + if (name) + free (name); + return ret; +} + + +int +display_counts (FILE *fp) +{ + fprintf (fp, "\n"); + fprintf (fp, "Entry counts\n"); + fprintf (fp, "Regular files : %lld\n", count_file); + fprintf (fp, "Directories : %lld\n", count_dir); + fprintf (fp, "Symbolic links : %lld\n", count_symlink); + fprintf (fp, "Other : %lld\n", count_other); + fprintf (fp, "Total : %lld\n", + (count_file + count_dir + count_symlink + count_other)); + + return 0; +} + + +int +display_checksums (FILE *fp) +{ + fprintf (fp, "\n"); + fprintf (fp, "Checksums\n"); + fprintf (fp, "Regular files : %llx%llx\n", checksum_file1, checksum_file2); + fprintf (fp, "Directories : %llx\n", checksum_dir); + fprintf (fp, "Symbolic links : %llx\n", checksum_symlink); + fprintf (fp, "Other : %llx\n", checksum_other); + fprintf (fp, "Total : %llx\n", + (checksum_file1 ^ checksum_file2 ^ checksum_dir ^ checksum_symlink ^ checksum_other)); + + return 0; +} + + +int +display_metadata (FILE *fp) +{ + fprintf (fp, "\n"); + fprintf (fp, "Metadata checksums\n"); + fprintf (fp, "Regular files : %llx\n", + (avg_uid_file + 13) * (avg_gid_file + 11) * (avg_mode_file + 7)); + fprintf (fp, "Directories : %llx\n", + (avg_uid_dir + 13) * (avg_gid_dir + 11) * (avg_mode_dir + 7)); + fprintf (fp, "Symbolic links : %llx\n", + (avg_uid_symlink + 13) * (avg_gid_symlink + 11) * (avg_mode_symlink + 7)); + fprintf (fp, "Other : %llx\n", + (avg_uid_other + 13) * (avg_gid_other + 11) * (avg_mode_other + 7)); + + return 0; +} + +int +display_stats (FILE *fp) +{ + display_counts (fp); + + display_metadata (fp); + + display_checksums (fp); + + return 0; +} + + +int +main(int argc, char *argv[]) +{ + int ret = 0; + int i = 0; + + ret = argp_parse (&argp, argc, argv, 0, 0, NULL); + if (ret != 0) { + fprintf (stderr, "parsing arguments failed\n"); + return -2; + } + + /* Use FTW_ACTIONRETVAL to take decision on what to do depending upon */ + /* the return value of the callback function */ + /* (process_entry in this case) */ + ret = nftw (arequal_config.test_directory, process_entry, 30, + FTW_ACTIONRETVAL|FTW_PHYS|FTW_MOUNT); + if (ret != 0) { + fprintf (stderr, "ftw (%s) returned %d (%s), terminating\n", + argv[1], ret, strerror (errno)); + return 1; + } + + display_stats (stdout); + + if (arequal_config.ignored_directory) { + for (i = 0; i < arequal_config.directories_ignored; i++) { + if (arequal_config.ignored_directory[i]) + free (arequal_config.ignored_directory[i]); + } + free (arequal_config.ignored_directory); + } + + return 0; +} -- cgit From 238d101e55e067e5afcd43c728884e9ab8d36549 Mon Sep 17 00:00:00 2001 From: Aravinda VK Date: Fri, 21 Mar 2014 12:33:10 +0530 Subject: geo-rep: code pep8/flake8 fixes pep8 is a style guide for python. http://legacy.python.org/dev/peps/pep-0008/ pep8 can be installed using, `pip install pep8` Usage: `pep8 `, For example, `pep8 master.py` will display all the coding standard errors. flake8 is used to identify unused imports and other issues in code. pip install flake8 cd $GLUSTER_REPO/geo-replication/ flake8 syncdaemon Updated license headers to each source file. Change-Id: I01c7d0a6091d21bfa48720e9fb5624b77fa3db4a Signed-off-by: Aravinda VK Reviewed-on: http://review.gluster.org/7311 Reviewed-by: Kotresh HR Reviewed-by: Prashanth Pai Tested-by: Gluster Build System --- geo-replication/syncdaemon/__codecheck.py | 17 +- geo-replication/syncdaemon/__init__.py | 9 + geo-replication/syncdaemon/configinterface.py | 57 +++- geo-replication/syncdaemon/gconf.py | 12 +- geo-replication/syncdaemon/gsyncd.py | 292 +++++++++++++------- geo-replication/syncdaemon/libcxattr.py | 16 +- geo-replication/syncdaemon/libgfchangelog.py | 22 +- geo-replication/syncdaemon/master.py | 384 ++++++++++++++++---------- geo-replication/syncdaemon/monitor.py | 78 ++++-- geo-replication/syncdaemon/repce.py | 52 +++- geo-replication/syncdaemon/resource.py | 306 +++++++++++++------- geo-replication/syncdaemon/syncdutils.py | 114 +++++--- 12 files changed, 912 insertions(+), 447 deletions(-) diff --git a/geo-replication/syncdaemon/__codecheck.py b/geo-replication/syncdaemon/__codecheck.py index e3386afba..45dbd26bb 100644 --- a/geo-replication/syncdaemon/__codecheck.py +++ b/geo-replication/syncdaemon/__codecheck.py @@ -1,10 +1,20 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os import os.path import sys import tempfile import shutil -ipd = tempfile.mkdtemp(prefix = 'codecheck-aux') +ipd = tempfile.mkdtemp(prefix='codecheck-aux') try: # add a fake ipaddr module, we don't want to @@ -25,7 +35,7 @@ class IPNetwork(list): if f[-3:] != '.py' or f[0] == '_': continue m = f[:-3] - sys.stdout.write('importing %s ...' % m) + sys.stdout.write('importing %s ...' % m) __import__(m) print(' OK.') @@ -33,7 +43,8 @@ class IPNetwork(list): sys.argv = sys.argv[:1] + a gsyncd = sys.modules['gsyncd'] - for a in [['--help'], ['--version'], ['--canonicalize-escape-url', '/foo']]: + for a in [['--help'], ['--version'], + ['--canonicalize-escape-url', '/foo']]: print('>>> invoking program with args: %s' % ' '.join(a)) pid = os.fork() if not pid: diff --git a/geo-replication/syncdaemon/__init__.py b/geo-replication/syncdaemon/__init__.py index e69de29bb..b4648b696 100644 --- a/geo-replication/syncdaemon/__init__.py +++ b/geo-replication/syncdaemon/__init__.py @@ -0,0 +1,9 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# diff --git a/geo-replication/syncdaemon/configinterface.py b/geo-replication/syncdaemon/configinterface.py index 35f754c98..c4d47b5db 100644 --- a/geo-replication/syncdaemon/configinterface.py +++ b/geo-replication/syncdaemon/configinterface.py @@ -1,3 +1,13 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + try: import ConfigParser except ImportError: @@ -21,14 +31,25 @@ config_version = 2.0 re_type = type(re.compile('')) - # (SECTION, OPTION, OLD VALUE, NEW VALUE) CONFIGS = ( - ("peersrx . .", "georep_session_working_dir", "", "/var/lib/glusterd/geo-replication/${mastervol}_${remotehost}_${slavevol}/"), - ("peersrx .", "gluster_params", "aux-gfid-mount xlator-option=\*-dht.assert-no-child-down=true", "aux-gfid-mount"), - ("peersrx . .", "ssh_command_tar", "", "ssh -oPasswordAuthentication=no -oStrictHostKeyChecking=no -i /var/lib/glusterd/geo-replication/tar_ssh.pem"), + ("peersrx . .", + "georep_session_working_dir", + "", + "/var/lib/glusterd/geo-replication/${mastervol}_${remotehost}_" + "${slavevol}/"), + ("peersrx .", + "gluster_params", + "aux-gfid-mount xlator-option=\*-dht.assert-no-child-down=true", + "aux-gfid-mount"), + ("peersrx . .", + "ssh_command_tar", + "", + "ssh -oPasswordAuthentication=no -oStrictHostKeyChecking=no " + "-i /var/lib/glusterd/geo-replication/tar_ssh.pem"), ) + def upgrade_config_file(path): config_change = False config = ConfigParser.RawConfigParser() @@ -72,7 +93,9 @@ def upgrade_config_file(path): class MultiDict(object): - """a virtual dict-like class which functions as the union of underlying dicts""" + + """a virtual dict-like class which functions as the union + of underlying dicts""" def __init__(self, *dd): self.dicts = dd @@ -80,14 +103,15 @@ class MultiDict(object): def __getitem__(self, key): val = None for d in self.dicts: - if d.get(key) != None: + if d.get(key) is not None: val = d[key] - if val == None: + if val is None: raise KeyError(key) return val class GConffile(object): + """A high-level interface to ConfigParser which flattens the two-tiered config layout by implenting automatic section dispatch based on initial parameters. @@ -155,7 +179,8 @@ class GConffile(object): return self.get(opt, printValue=False) def section(self, rx=False): - """get the section name of the section representing .peers in .config""" + """get the section name of the section representing .peers + in .config""" peers = self.peers if not peers: peers = ['.', '.'] @@ -209,6 +234,7 @@ class GConffile(object): continue so2[s] = tv tv += 1 + def scmp(x, y): return cmp(*(so2[s] for s in (x, y))) ss.sort(scmp) @@ -218,12 +244,13 @@ class GConffile(object): """update @dct from key/values of ours. key/values are collected from .config by filtering the regexp sections - according to match, and from .section. The values are treated as templates, - which are substituted from .auxdicts and (in case of regexp sections) - match groups. + according to match, and from .section. The values are treated as + templates, which are substituted from .auxdicts and (in case of regexp + sections) match groups. """ if not self.peers: raise GsyncdError('no peers given, cannot select matching options') + def update_from_sect(sect, mud): for k, v in self.config._sections[sect].items(): if k == '__name__': @@ -243,7 +270,7 @@ class GConffile(object): match = False break for j in range(len(m.groups())): - mad['match%d_%d' % (i+1, j+1)] = m.groups()[j] + mad['match%d_%d' % (i + 1, j + 1)] = m.groups()[j] if match: update_from_sect(sect, MultiDict(dct, mad, *self.auxdicts)) if self.config.has_section(self.section()): @@ -255,7 +282,7 @@ class GConffile(object): logic described in .update_to) """ d = {} - self.update_to(d, allow_unresolved = True) + self.update_to(d, allow_unresolved=True) if opt: opt = norm(opt) v = d.get(opt) @@ -283,6 +310,7 @@ class GConffile(object): self.config.add_section(SECT_META) self.config.set(SECT_META, 'version', config_version) return trfn(norm(opt), *a, **kw) + def updateconf(f): self.config.write(f) update_file(self.path, updateconf, mergeconf) @@ -295,7 +323,8 @@ class GConffile(object): # regarding SECT_ORD, cf. ord_sections if not self.config.has_section(SECT_ORD): self.config.add_section(SECT_ORD) - self.config.set(SECT_ORD, sect, len(self.config._sections[SECT_ORD])) + self.config.set( + SECT_ORD, sect, len(self.config._sections[SECT_ORD])) self.config.set(sect, opt, val) return True diff --git a/geo-replication/syncdaemon/gconf.py b/geo-replication/syncdaemon/gconf.py index fe5795f16..1fc7c381b 100644 --- a/geo-replication/syncdaemon/gconf.py +++ b/geo-replication/syncdaemon/gconf.py @@ -1,6 +1,16 @@ -import os +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + class GConf(object): + """singleton class to store globals shared between gsyncd modules""" diff --git a/geo-replication/syncdaemon/gsyncd.py b/geo-replication/syncdaemon/gsyncd.py index 6eb62c6b0..426d964de 100644 --- a/geo-replication/syncdaemon/gsyncd.py +++ b/geo-replication/syncdaemon/gsyncd.py @@ -1,4 +1,13 @@ #!/usr/bin/env python +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# import os import os.path @@ -6,25 +15,27 @@ import glob import sys import time import logging -import signal import shutil import optparse import fcntl import fnmatch from optparse import OptionParser, SUPPRESS_HELP from logging import Logger, handlers -from errno import EEXIST, ENOENT +from errno import ENOENT from ipaddr import IPAddress, IPNetwork from gconf import gconf -from syncdutils import FreeObject, norm, grabpidfile, finalize, log_raise_exception -from syncdutils import GsyncdError, select, set_term_handler, privileged, update_file +from syncdutils import FreeObject, norm, grabpidfile, finalize +from syncdutils import log_raise_exception, privileged, update_file +from syncdutils import GsyncdError, select, set_term_handler from configinterface import GConffile, upgrade_config_file import resource from monitor import monitor + class GLogger(Logger): + """Logger customizations for gsyncd. It implements a log format similar to that of glusterfs. @@ -51,7 +62,8 @@ class GLogger(Logger): if lbl: lbl = '(' + lbl + ')' lprm = {'datefmt': "%Y-%m-%d %H:%M:%S", - 'format': "[%(asctime)s.%(nsecs)d] %(lvlnam)s [%(module)s" + lbl + ":%(lineno)s:%(funcName)s] %(ctx)s: %(message)s"} + 'format': "[%(asctime)s.%(nsecs)d] %(lvlnam)s [%(module)s" + + lbl + ":%(lineno)s:%(funcName)s] %(ctx)s: %(message)s"} lprm.update(kw) lvl = kw.get('level', logging.INFO) lprm['level'] = lvl @@ -64,7 +76,7 @@ class GLogger(Logger): try: logging_handler = handlers.WatchedFileHandler(lprm['filename']) formatter = logging.Formatter(fmt=lprm['format'], - datefmt=lprm['datefmt']) + datefmt=lprm['datefmt']) logging_handler.setFormatter(formatter) logging.getLogger().addHandler(logging_handler) except AttributeError: @@ -96,6 +108,7 @@ class GLogger(Logger): gconf.log_metadata = lkw gconf.log_exit = True + def startup(**kw): """set up logging, pidfile grabbing, daemonization""" if getattr(gconf, 'pid_file', None) and kw.get('go_daemon') != 'postconn': @@ -144,14 +157,15 @@ def main(): gconf.starttime = time.time() set_term_handler() GLogger.setup() - excont = FreeObject(exval = 0) + excont = FreeObject(exval=0) try: try: main_i() except: log_raise_exception(excont) finally: - finalize(exval = excont.exval) + finalize(exval=excont.exval) + def main_i(): """internal main routine @@ -171,50 +185,71 @@ def main_i(): if val and val != '-': val = os.path.abspath(val) setattr(parser.values, opt.dest, val) + def store_local(opt, optstr, val, parser): rconf[opt.dest] = val + def store_local_curry(val): return lambda o, oo, vx, p: store_local(o, oo, val, p) + def store_local_obj(op, dmake): - return lambda o, oo, vx, p: store_local(o, oo, FreeObject(op=op, **dmake(vx)), p) - - op = OptionParser(usage="%prog [options...] ", version="%prog 0.0.1") - op.add_option('--gluster-command-dir', metavar='DIR', default='') - op.add_option('--gluster-log-file', metavar='LOGF', default=os.devnull, type=str, action='callback', callback=store_abs) - op.add_option('--gluster-log-level', metavar='LVL') - op.add_option('--gluster-params', metavar='PRMS', default='') - op.add_option('--glusterd-uuid', metavar='UUID', type=str, default='', help=SUPPRESS_HELP) - op.add_option('--gluster-cli-options', metavar='OPTS', default='--log-file=-') - op.add_option('--mountbroker', metavar='LABEL') - op.add_option('-p', '--pid-file', metavar='PIDF', type=str, action='callback', callback=store_abs) - op.add_option('-l', '--log-file', metavar='LOGF', type=str, action='callback', callback=store_abs) - op.add_option('--log-file-mbr', metavar='LOGF', type=str, action='callback', callback=store_abs) - op.add_option('--state-file', metavar='STATF', type=str, action='callback', callback=store_abs) - op.add_option('--state-detail-file', metavar='STATF', type=str, action='callback', callback=store_abs) - op.add_option('--georep-session-working-dir', metavar='STATF', type=str, action='callback', callback=store_abs) - op.add_option('--ignore-deletes', default=False, action='store_true') - op.add_option('--isolated-slave', default=False, action='store_true') - op.add_option('--use-rsync-xattrs', default=False, action='store_true') - op.add_option('-L', '--log-level', metavar='LVL') - op.add_option('-r', '--remote-gsyncd', metavar='CMD', default=os.path.abspath(sys.argv[0])) - op.add_option('--volume-id', metavar='UUID') - op.add_option('--slave-id', metavar='ID') - op.add_option('--session-owner', metavar='ID') - op.add_option('--local-id', metavar='ID', help=SUPPRESS_HELP, default='') - op.add_option('--local-path', metavar='PATH', help=SUPPRESS_HELP, default='') - op.add_option('-s', '--ssh-command', metavar='CMD', default='ssh') - op.add_option('--ssh-command-tar', metavar='CMD', default='ssh') - op.add_option('--rsync-command', metavar='CMD', default='rsync') - op.add_option('--rsync-options', metavar='OPTS', default='') - op.add_option('--rsync-ssh-options', metavar='OPTS', default='--compress') - op.add_option('--timeout', metavar='SEC', type=int, default=120) - op.add_option('--connection-timeout', metavar='SEC', type=int, default=60, help=SUPPRESS_HELP) - op.add_option('--sync-jobs', metavar='N', type=int, default=3) - op.add_option('--turns', metavar='N', type=int, default=0, help=SUPPRESS_HELP) - op.add_option('--allow-network', metavar='IPS', default='') - op.add_option('--socketdir', metavar='DIR') - op.add_option('--state-socket-unencoded', metavar='SOCKF', type=str, action='callback', callback=store_abs) - op.add_option('--checkpoint', metavar='LABEL', default='') + return lambda o, oo, vx, p: store_local( + o, oo, FreeObject(op=op, **dmake(vx)), p) + + op = OptionParser( + usage="%prog [options...] ", version="%prog 0.0.1") + op.add_option('--gluster-command-dir', metavar='DIR', default='') + op.add_option('--gluster-log-file', metavar='LOGF', + default=os.devnull, type=str, action='callback', + callback=store_abs) + op.add_option('--gluster-log-level', metavar='LVL') + op.add_option('--gluster-params', metavar='PRMS', default='') + op.add_option( + '--glusterd-uuid', metavar='UUID', type=str, default='', + help=SUPPRESS_HELP) + op.add_option( + '--gluster-cli-options', metavar='OPTS', default='--log-file=-') + op.add_option('--mountbroker', metavar='LABEL') + op.add_option('-p', '--pid-file', metavar='PIDF', type=str, + action='callback', callback=store_abs) + op.add_option('-l', '--log-file', metavar='LOGF', type=str, + action='callback', callback=store_abs) + op.add_option('--log-file-mbr', metavar='LOGF', type=str, + action='callback', callback=store_abs) + op.add_option('--state-file', metavar='STATF', type=str, + action='callback', callback=store_abs) + op.add_option('--state-detail-file', metavar='STATF', + type=str, action='callback', callback=store_abs) + op.add_option('--georep-session-working-dir', metavar='STATF', + type=str, action='callback', callback=store_abs) + op.add_option('--ignore-deletes', default=False, action='store_true') + op.add_option('--isolated-slave', default=False, action='store_true') + op.add_option('--use-rsync-xattrs', default=False, action='store_true') + op.add_option('-L', '--log-level', metavar='LVL') + op.add_option('-r', '--remote-gsyncd', metavar='CMD', + default=os.path.abspath(sys.argv[0])) + op.add_option('--volume-id', metavar='UUID') + op.add_option('--slave-id', metavar='ID') + op.add_option('--session-owner', metavar='ID') + op.add_option('--local-id', metavar='ID', help=SUPPRESS_HELP, default='') + op.add_option( + '--local-path', metavar='PATH', help=SUPPRESS_HELP, default='') + op.add_option('-s', '--ssh-command', metavar='CMD', default='ssh') + op.add_option('--ssh-command-tar', metavar='CMD', default='ssh') + op.add_option('--rsync-command', metavar='CMD', default='rsync') + op.add_option('--rsync-options', metavar='OPTS', default='') + op.add_option('--rsync-ssh-options', metavar='OPTS', default='--compress') + op.add_option('--timeout', metavar='SEC', type=int, default=120) + op.add_option('--connection-timeout', metavar='SEC', + type=int, default=60, help=SUPPRESS_HELP) + op.add_option('--sync-jobs', metavar='N', type=int, default=3) + op.add_option( + '--turns', metavar='N', type=int, default=0, help=SUPPRESS_HELP) + op.add_option('--allow-network', metavar='IPS', default='') + op.add_option('--socketdir', metavar='DIR') + op.add_option('--state-socket-unencoded', metavar='SOCKF', + type=str, action='callback', callback=store_abs) + op.add_option('--checkpoint', metavar='LABEL', default='') # tunables for failover/failback mechanism: # None - gsyncd behaves as normal # blind - gsyncd works with xtime pairs to identify @@ -225,56 +260,86 @@ def main_i(): op.add_option('--special-sync-mode', type=str, help=SUPPRESS_HELP) # changelog or xtime? (TODO: Change the default) - op.add_option('--change-detector', metavar='MODE', type=str, default='xtime') - # sleep interval for change detection (xtime crawl uses a hardcoded 1 second sleep time) + op.add_option( + '--change-detector', metavar='MODE', type=str, default='xtime') + # sleep interval for change detection (xtime crawl uses a hardcoded 1 + # second sleep time) op.add_option('--change-interval', metavar='SEC', type=int, default=3) # working directory for changelog based mechanism - op.add_option('--working-dir', metavar='DIR', type=str, action='callback', callback=store_abs) + op.add_option('--working-dir', metavar='DIR', type=str, + action='callback', callback=store_abs) op.add_option('--use-tarssh', default=False, action='store_true') - op.add_option('-c', '--config-file', metavar='CONF', type=str, action='callback', callback=store_local) + op.add_option('-c', '--config-file', metavar='CONF', + type=str, action='callback', callback=store_local) # duh. need to specify dest or value will be mapped to None :S - op.add_option('--monitor', dest='monitor', action='callback', callback=store_local_curry(True)) - op.add_option('--resource-local', dest='resource_local', type=str, action='callback', callback=store_local) - op.add_option('--resource-remote', dest='resource_remote', type=str, action='callback', callback=store_local) - op.add_option('--feedback-fd', dest='feedback_fd', type=int, help=SUPPRESS_HELP, action='callback', callback=store_local) - op.add_option('--listen', dest='listen', help=SUPPRESS_HELP, action='callback', callback=store_local_curry(True)) - op.add_option('-N', '--no-daemon', dest="go_daemon", action='callback', callback=store_local_curry('dont')) - op.add_option('--verify', type=str, dest="verify", action='callback', callback=store_local) - op.add_option('--create', type=str, dest="create", action='callback', callback=store_local) - op.add_option('--delete', dest='delete', action='callback', callback=store_local_curry(True)) - op.add_option('--debug', dest="go_daemon", action='callback', callback=lambda *a: (store_local_curry('dont')(*a), - setattr(a[-1].values, 'log_file', '-'), - setattr(a[-1].values, 'log_level', 'DEBUG'))), + op.add_option('--monitor', dest='monitor', action='callback', + callback=store_local_curry(True)) + op.add_option('--resource-local', dest='resource_local', + type=str, action='callback', callback=store_local) + op.add_option('--resource-remote', dest='resource_remote', + type=str, action='callback', callback=store_local) + op.add_option('--feedback-fd', dest='feedback_fd', type=int, + help=SUPPRESS_HELP, action='callback', callback=store_local) + op.add_option('--listen', dest='listen', help=SUPPRESS_HELP, + action='callback', callback=store_local_curry(True)) + op.add_option('-N', '--no-daemon', dest="go_daemon", + action='callback', callback=store_local_curry('dont')) + op.add_option('--verify', type=str, dest="verify", + action='callback', callback=store_local) + op.add_option('--create', type=str, dest="create", + action='callback', callback=store_local) + op.add_option('--delete', dest='delete', action='callback', + callback=store_local_curry(True)) + op.add_option('--debug', dest="go_daemon", action='callback', + callback=lambda *a: (store_local_curry('dont')(*a), + setattr( + a[-1].values, 'log_file', '-'), + setattr(a[-1].values, 'log_level', + 'DEBUG'))), op.add_option('--path', type=str, action='append') for a in ('check', 'get'): - op.add_option('--config-' + a, metavar='OPT', type=str, dest='config', action='callback', + op.add_option('--config-' + a, metavar='OPT', type=str, dest='config', + action='callback', callback=store_local_obj(a, lambda vx: {'opt': vx})) - op.add_option('--config-get-all', dest='config', action='callback', callback=store_local_obj('get', lambda vx: {'opt': None})) + op.add_option('--config-get-all', dest='config', action='callback', + callback=store_local_obj('get', lambda vx: {'opt': None})) for m in ('', '-rx', '-glob'): # call this code 'Pythonic' eh? - # have to define a one-shot local function to be able to inject (a value depending on the) + # have to define a one-shot local function to be able + # to inject (a value depending on the) # iteration variable into the inner lambda def conf_mod_opt_regex_variant(rx): - op.add_option('--config-set' + m, metavar='OPT VAL', type=str, nargs=2, dest='config', action='callback', - callback=store_local_obj('set', lambda vx: {'opt': vx[0], 'val': vx[1], 'rx': rx})) - op.add_option('--config-del' + m, metavar='OPT', type=str, dest='config', action='callback', - callback=store_local_obj('del', lambda vx: {'opt': vx, 'rx': rx})) + op.add_option('--config-set' + m, metavar='OPT VAL', type=str, + nargs=2, dest='config', action='callback', + callback=store_local_obj('set', lambda vx: { + 'opt': vx[0], 'val': vx[1], 'rx': rx})) + op.add_option('--config-del' + m, metavar='OPT', type=str, + dest='config', action='callback', + callback=store_local_obj('del', lambda vx: { + 'opt': vx, 'rx': rx})) conf_mod_opt_regex_variant(m and m[1:] or False) - op.add_option('--normalize-url', dest='url_print', action='callback', callback=store_local_curry('normal')) - op.add_option('--canonicalize-url', dest='url_print', action='callback', callback=store_local_curry('canon')) - op.add_option('--canonicalize-escape-url', dest='url_print', action='callback', callback=store_local_curry('canon_esc')) - - tunables = [ norm(o.get_opt_string()[2:]) for o in op.option_list if o.callback in (store_abs, 'store_true', None) and o.get_opt_string() not in ('--version', '--help') ] - remote_tunables = [ 'listen', 'go_daemon', 'timeout', 'session_owner', 'config_file', 'use_rsync_xattrs' ] - rq_remote_tunables = { 'listen': True } - - # precedence for sources of values: 1) commandline, 2) cfg file, 3) defaults - # -- for this to work out we need to tell apart defaults from explicitly set - # options... so churn out the defaults here and call the parser with virgin - # values container. + op.add_option('--normalize-url', dest='url_print', + action='callback', callback=store_local_curry('normal')) + op.add_option('--canonicalize-url', dest='url_print', + action='callback', callback=store_local_curry('canon')) + op.add_option('--canonicalize-escape-url', dest='url_print', + action='callback', callback=store_local_curry('canon_esc')) + + tunables = [norm(o.get_opt_string()[2:]) + for o in op.option_list + if (o.callback in (store_abs, 'store_true', None) and + o.get_opt_string() not in ('--version', '--help'))] + remote_tunables = ['listen', 'go_daemon', 'timeout', + 'session_owner', 'config_file', 'use_rsync_xattrs'] + rq_remote_tunables = {'listen': True} + + # precedence for sources of values: 1) commandline, 2) cfg file, 3) + # defaults for this to work out we need to tell apart defaults from + # explicitly set options... so churn out the defaults here and call + # the parser with virgin values container. defaults = op.get_default_values() opts, args = op.parse_args(values=optparse.Values()) args_orig = args[:] @@ -291,9 +356,9 @@ def main_i(): args.append(None) args[1] = r confdata = rconf.get('config') - if not (len(args) == 2 or \ - (len(args) == 1 and rconf.get('listen')) or \ - (len(args) <= 2 and confdata) or \ + if not (len(args) == 2 or + (len(args) == 1 and rconf.get('listen')) or + (len(args) <= 2 and confdata) or rconf.get('url_print')): sys.stderr.write("error: incorrect number of arguments\n\n") sys.stderr.write(op.get_usage() + "\n") @@ -301,8 +366,8 @@ def main_i(): verify = rconf.get('verify') if verify: - logging.info (verify) - logging.info ("Able to spawn gsyncd.py") + logging.info(verify) + logging.info("Able to spawn gsyncd.py") return restricted = os.getenv('_GSYNCD_RESTRICTED_') @@ -313,14 +378,17 @@ def main_i(): allopts.update(rconf) bannedtuns = set(allopts.keys()) - set(remote_tunables) if bannedtuns: - raise GsyncdError('following tunables cannot be set with restricted SSH invocaton: ' + \ + raise GsyncdError('following tunables cannot be set with ' + 'restricted SSH invocaton: ' + ', '.join(bannedtuns)) for k, v in rq_remote_tunables.items(): if not k in allopts or allopts[k] != v: - raise GsyncdError('tunable %s is not set to value %s required for restricted SSH invocaton' % \ + raise GsyncdError('tunable %s is not set to value %s required ' + 'for restricted SSH invocaton' % (k, v)) confrx = getattr(confdata, 'rx', None) + def makersc(aa, check=True): if not aa: return ([], None, None) @@ -330,12 +398,13 @@ def main_i(): if len(ra) > 1: remote = ra[1] if check and not local.can_connect_to(remote): - raise GsyncdError("%s cannot work with %s" % (local.path, remote and remote.path)) + raise GsyncdError("%s cannot work with %s" % + (local.path, remote and remote.path)) return (ra, local, remote) if confrx: # peers are regexen, don't try to parse them if confrx == 'glob': - args = [ '\A' + fnmatch.translate(a) for a in args ] + args = ['\A' + fnmatch.translate(a) for a in args] canon_peers = args namedict = {} else: @@ -345,21 +414,24 @@ def main_i(): for r in rscs: print(r.get_url(**{'normal': {}, 'canon': {'canonical': True}, - 'canon_esc': {'canonical': True, 'escaped': True}}[dc])) + 'canon_esc': {'canonical': True, + 'escaped': True}}[dc])) return pa = ([], [], []) - urlprms = ({}, {'canonical': True}, {'canonical': True, 'escaped': True}) + urlprms = ( + {}, {'canonical': True}, {'canonical': True, 'escaped': True}) for x in rscs: for i in range(len(pa)): pa[i].append(x.get_url(**urlprms[i])) _, canon_peers, canon_esc_peers = pa - # creating the namedict, a dict representing various ways of referring to / repreenting - # peers to be fillable in config templates - mods = (lambda x: x, lambda x: x[0].upper() + x[1:], lambda x: 'e' + x[0].upper() + x[1:]) + # creating the namedict, a dict representing various ways of referring + # to / repreenting peers to be fillable in config templates + mods = (lambda x: x, lambda x: x[ + 0].upper() + x[1:], lambda x: 'e' + x[0].upper() + x[1:]) if remote: - rmap = { local: ('local', 'master'), remote: ('remote', 'slave') } + rmap = {local: ('local', 'master'), remote: ('remote', 'slave')} else: - rmap = { local: ('local', 'slave') } + rmap = {local: ('local', 'slave')} namedict = {} for i in range(len(rscs)): x = rscs[i] @@ -370,10 +442,13 @@ def main_i(): if name == 'remote': namedict['remotehost'] = x.remotehost if not 'config_file' in rconf: - rconf['config_file'] = os.path.join(os.path.dirname(sys.argv[0]), "conf/gsyncd_template.conf") + rconf['config_file'] = os.path.join( + os.path.dirname(sys.argv[0]), "conf/gsyncd_template.conf") upgrade_config_file(rconf['config_file']) - gcnf = GConffile(rconf['config_file'], canon_peers, defaults.__dict__, opts.__dict__, namedict) + gcnf = GConffile( + rconf['config_file'], canon_peers, + defaults.__dict__, opts.__dict__, namedict) checkpoint_change = False if confdata: @@ -407,7 +482,7 @@ def main_i(): delete = rconf.get('delete') if delete: - logging.info ('geo-replication delete') + logging.info('geo-replication delete') # Delete pid file, status file, socket file cleanup_paths = [] if getattr(gconf, 'pid_file', None): @@ -422,7 +497,7 @@ def main_i(): if getattr(gconf, 'state_socket_unencoded', None): cleanup_paths.append(gconf.state_socket_unencoded) - cleanup_paths.append(rconf['config_file'][:-11] + "*"); + cleanup_paths.append(rconf['config_file'][:-11] + "*") # Cleanup changelog working dirs if getattr(gconf, 'working_dir', None): @@ -432,7 +507,9 @@ def main_i(): if sys.exc_info()[1].errno == ENOENT: pass else: - raise GsyncdError('Error while removing working dir: %s' % gconf.working_dir) + raise GsyncdError( + 'Error while removing working dir: %s' % + gconf.working_dir) for path in cleanup_paths: # To delete temp files @@ -443,10 +520,11 @@ def main_i(): if restricted and gconf.allow_network: ssh_conn = os.getenv('SSH_CONNECTION') if not ssh_conn: - #legacy env var + # legacy env var ssh_conn = os.getenv('SSH_CLIENT') if ssh_conn: - allowed_networks = [ IPNetwork(a) for a in gconf.allow_network.split(',') ] + allowed_networks = [IPNetwork(a) + for a in gconf.allow_network.split(',')] client_ip = IPAddress(ssh_conn.split()[0]) allowed = False for nw in allowed_networks: @@ -460,7 +538,7 @@ def main_i(): if ffd: fcntl.fcntl(ffd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) - #normalize loglevel + # normalize loglevel lvl0 = gconf.log_level if isinstance(lvl0, str): lvl1 = lvl0.upper() @@ -519,7 +597,7 @@ def main_i(): if be_monitor: label = 'monitor' elif remote: - #master + # master label = gconf.local_path else: label = 'slave' diff --git a/geo-replication/syncdaemon/libcxattr.py b/geo-replication/syncdaemon/libcxattr.py index b5b6956ae..e6035e26b 100644 --- a/geo-replication/syncdaemon/libcxattr.py +++ b/geo-replication/syncdaemon/libcxattr.py @@ -1,8 +1,20 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os -from ctypes import * +from ctypes import CDLL, c_int, create_string_buffer from ctypes.util import find_library + class Xattr(object): + """singleton that wraps the extended attribues system interface for python using ctypes @@ -40,7 +52,7 @@ class Xattr(object): @classmethod def lgetxattr(cls, path, attr, siz=0): - return cls._query_xattr( path, siz, 'lgetxattr', attr) + return cls._query_xattr(path, siz, 'lgetxattr', attr) @classmethod def lgetxattr_buf(cls, path, attr): diff --git a/geo-replication/syncdaemon/libgfchangelog.py b/geo-replication/syncdaemon/libgfchangelog.py index 68ec3baf1..ec563b36f 100644 --- a/geo-replication/syncdaemon/libgfchangelog.py +++ b/geo-replication/syncdaemon/libgfchangelog.py @@ -1,7 +1,18 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os -from ctypes import * +from ctypes import CDLL, create_string_buffer, get_errno from ctypes.util import find_library + class Changes(object): libgfc = CDLL(find_library("gfchangelog"), use_errno=True) @@ -19,9 +30,10 @@ class Changes(object): return getattr(cls.libgfc, call) @classmethod - def cl_register(cls, brick, path, log_file, log_level, retries = 0): + def cl_register(cls, brick, path, log_file, log_level, retries=0): ret = cls._get_api('gf_changelog_register')(brick, path, - log_file, log_level, retries) + log_file, + log_level, retries) if ret == -1: cls.raise_oserr() @@ -49,8 +61,8 @@ class Changes(object): while True: ret = call(buf, 4096) if ret in (0, -1): - break; - changes.append(buf.raw[:ret-1]) + break + changes.append(buf.raw[:ret - 1]) if ret == -1: cls.raise_oserr() # cleanup tracker diff --git a/geo-replication/syncdaemon/master.py b/geo-replication/syncdaemon/master.py index 98a61bc1d..4301396f9 100644 --- a/geo-replication/syncdaemon/master.py +++ b/geo-replication/syncdaemon/master.py @@ -1,25 +1,30 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os import sys import time import stat -import random -import signal import json import logging import socket import string import errno -from shutil import copyfileobj -from errno import ENOENT, ENODATA, EPIPE, EEXIST, errorcode -from threading import currentThread, Condition, Lock +from errno import ENOENT, ENODATA, EPIPE, EEXIST +from threading import Condition, Lock from datetime import datetime -from libcxattr import Xattr - from gconf import gconf -from tempfile import mkdtemp, NamedTemporaryFile -from syncdutils import FreeObject, Thread, GsyncdError, boolify, escape, \ - unescape, select, gauxpfx, md5hex, selfkill, entry2pb, \ - lstat, errno_wrap, update_file +from tempfile import NamedTemporaryFile +from syncdutils import Thread, GsyncdError, boolify, escape +from syncdutils import unescape, select, gauxpfx, md5hex, selfkill +from syncdutils import lstat, errno_wrap URXTIME = (-1, 0) @@ -27,18 +32,20 @@ URXTIME = (-1, 0) # of the DRY principle (no, don't look for elevated or # perspectivistic things here) + def _xtime_now(): t = time.time() sec = int(t) nsec = int((t - sec) * 1000000) return (sec, nsec) + def _volinfo_hook_relax_foreign(self): volinfo_sys = self.get_sys_volinfo() fgn_vi = volinfo_sys[self.KFGN] if fgn_vi: expiry = fgn_vi['timeout'] - int(time.time()) + 1 - logging.info('foreign volume info found, waiting %d sec for expiry' % \ + logging.info('foreign volume info found, waiting %d sec for expiry' % expiry) time.sleep(expiry) volinfo_sys = self.get_sys_volinfo() @@ -58,10 +65,14 @@ def gmaster_builder(excrawl=None): logging.info('setting up %s change detection mode' % changemixin) modemixin = getattr(this, modemixin.capitalize() + 'Mixin') crawlmixin = getattr(this, 'GMaster' + changemixin.capitalize() + 'Mixin') - sendmarkmixin = boolify(gconf.use_rsync_xattrs) and SendmarkRsyncMixin or SendmarkNormalMixin - purgemixin = boolify(gconf.ignore_deletes) and PurgeNoopMixin or PurgeNormalMixin + sendmarkmixin = boolify( + gconf.use_rsync_xattrs) and SendmarkRsyncMixin or SendmarkNormalMixin + purgemixin = boolify( + gconf.ignore_deletes) and PurgeNoopMixin or PurgeNormalMixin syncengine = boolify(gconf.use_tarssh) and TarSSHEngine or RsyncEngine - class _GMaster(crawlmixin, modemixin, sendmarkmixin, purgemixin, syncengine): + + class _GMaster(crawlmixin, modemixin, sendmarkmixin, + purgemixin, syncengine): pass return _GMaster @@ -71,6 +82,7 @@ def gmaster_builder(excrawl=None): # sync modes class NormalMixin(object): + """normal geo-rep behavior""" minus_infinity = URXTIME @@ -152,14 +164,18 @@ class NormalMixin(object): self.slave.server.set_stime(path, self.uuid, mark) # self.slave.server.set_xtime_remote(path, self.uuid, mark) + class PartialMixin(NormalMixin): + """a variant tuned towards operation with a master that has partial info of the slave (brick typically)""" def xtime_reversion_hook(self, path, xtl, xtr): pass + class RecoverMixin(NormalMixin): + """a variant that differs from normal in terms of ignoring non-indexed files""" @@ -178,11 +194,13 @@ class RecoverMixin(NormalMixin): # Further mixins for certain tunable behaviors + class SendmarkNormalMixin(object): def sendmark_regular(self, *a, **kw): return self.sendmark(*a, **kw) + class SendmarkRsyncMixin(object): def sendmark_regular(self, *a, **kw): @@ -194,19 +212,24 @@ class PurgeNormalMixin(object): def purge_missing(self, path, names): self.slave.server.purge(path, names) + class PurgeNoopMixin(object): def purge_missing(self, path, names): pass + class TarSSHEngine(object): + """Sync engine that uses tar(1) piped over ssh(1) for data transfers. Good for lots of small files. """ + def a_syncdata(self, files): logging.debug('files: %s' % (files)) for f in files: pb = self.syncer.add(f) + def regjob(se, xte, pb): rv = pb.wait() if rv[0]: @@ -228,13 +251,17 @@ class TarSSHEngine(object): self.a_syncdata(files) self.syncdata_wait() + class RsyncEngine(object): + """Sync engine that uses rsync(1) for data transfers""" + def a_syncdata(self, files): logging.debug('files: %s' % (files)) for f in files: logging.debug('candidate for syncing %s' % f) pb = self.syncer.add(f) + def regjob(se, xte, pb): rv = pb.wait() if rv[0]: @@ -258,7 +285,9 @@ class RsyncEngine(object): self.a_syncdata(files) self.syncdata_wait() + class GMasterCommon(object): + """abstract class impementling master role""" KFGN = 0 @@ -269,8 +298,9 @@ class GMasterCommon(object): err out on multiple foreign masters """ - fgn_vis, nat_vi = self.master.server.aggregated.foreign_volume_infos(), \ - self.master.server.aggregated.native_volume_info() + fgn_vis, nat_vi = ( + self.master.server.aggregated.foreign_volume_infos(), + self.master.server.aggregated.native_volume_info()) fgn_vi = None if fgn_vis: if len(fgn_vis) > 1: @@ -316,15 +346,14 @@ class GMasterCommon(object): if getattr(gconf, 'state_detail_file', None): try: with open(gconf.state_detail_file, 'r+') as f: - loaded_data= json.load(f) - diff_data = set(default_data) - set (loaded_data) + loaded_data = json.load(f) + diff_data = set(default_data) - set(loaded_data) if len(diff_data): for i in diff_data: loaded_data[i] = default_data[i] return loaded_data - except (IOError): - ex = sys.exc_info()[1] - logging.warn ('Creating new gconf.state_detail_file.') + except IOError: + logging.warn('Creating new gconf.state_detail_file.') # Create file with initial data try: with open(gconf.state_detail_file, 'wb') as f: @@ -364,7 +393,8 @@ class GMasterCommon(object): # - self.turns is the number of turns since start # - self.total_turns is a limit so that if self.turns reaches it, then # we exit (for diagnostic purposes) - # so, eg., if the master fs changes unceasingly, self.turns will remain 0. + # so, eg., if the master fs changes unceasingly, self.turns will remain + # 0. self.crawls = 0 self.turns = 0 self.total_turns = int(gconf.turns) @@ -394,7 +424,7 @@ class GMasterCommon(object): t.start() def should_crawl(cls): - return (gconf.glusterd_uuid in cls.master.server.node_uuid()) + return gconf.glusterd_uuid in cls.master.server.node_uuid() def register(self): self.register() @@ -416,18 +446,18 @@ class GMasterCommon(object): volinfo_sys = self.volinfo_hook() self.volinfo = volinfo_sys[self.KNAT] inter_master = volinfo_sys[self.KFGN] - logging.info("%s master with volume id %s ..." % \ - (inter_master and "intermediate" or "primary", - self.uuid)) + logging.info("%s master with volume id %s ..." % + (inter_master and "intermediate" or "primary", + self.uuid)) gconf.configinterface.set('volume_id', self.uuid) if self.volinfo: if self.volinfo['retval']: - logging.warn("master cluster's info may not be valid %d" % \ + logging.warn("master cluster's info may not be valid %d" % self.volinfo['retval']) self.start_checkpoint_thread() else: raise GsyncdError("master volinfo unavailable") - self.total_crawl_stats = self.get_initial_crawl_data() + self.total_crawl_stats = self.get_initial_crawl_data() self.lastreport['time'] = time.time() logging.info('crawl interval: %d seconds' % self.sleep_interval) @@ -435,7 +465,7 @@ class GMasterCommon(object): crawl = self.should_crawl() while not self.terminate: if self.start: - logging.debug("... crawl #%d done, took %.6f seconds" % \ + logging.debug("... crawl #%d done, took %.6f seconds" % (self.crawls, time.time() - self.start)) self.start = time.time() should_display_info = self.start - self.lastreport['time'] >= 60 @@ -443,11 +473,11 @@ class GMasterCommon(object): logging.info("%d crawls, %d turns", self.crawls - self.lastreport['crawls'], self.turns - self.lastreport['turns']) - self.lastreport.update(crawls = self.crawls, - turns = self.turns, - time = self.start) + self.lastreport.update(crawls=self.crawls, + turns=self.turns, + time=self.start) t1 = time.time() - if int(t1 - t0) >= 60: #lets hardcode this check to 60 seconds + if int(t1 - t0) >= 60: # lets hardcode this check to 60 seconds crawl = self.should_crawl() t0 = t1 self.update_worker_remote_node() @@ -456,11 +486,14 @@ class GMasterCommon(object): # bring up _this_ brick to the cluster stime # which is min of cluster (but max of the replicas) brick_stime = self.xtime('.', self.slave) - cluster_stime = self.master.server.aggregated.stime_mnt('.', '.'.join([str(self.uuid), str(gconf.slave_id)])) - logging.debug("Cluster stime: %s | Brick stime: %s" % (repr(cluster_stime), repr(brick_stime))) + cluster_stime = self.master.server.aggregated.stime_mnt( + '.', '.'.join([str(self.uuid), str(gconf.slave_id)])) + logging.debug("Cluster stime: %s | Brick stime: %s" % + (repr(cluster_stime), repr(brick_stime))) if not isinstance(cluster_stime, int): if brick_stime < cluster_stime: - self.slave.server.set_stime(self.FLAT_DIR_HIERARCHY, self.uuid, cluster_stime) + self.slave.server.set_stime( + self.FLAT_DIR_HIERARCHY, self.uuid, cluster_stime) time.sleep(5) continue self.update_worker_health("Active") @@ -489,13 +522,14 @@ class GMasterCommon(object): with checkpoint @chkpt""" if xtimish: val = cls.serialize_xtime(val) - gconf.configinterface.set('checkpoint_' + prm, "%s:%s" % (escape(chkpt), val)) + gconf.configinterface.set( + 'checkpoint_' + prm, "%s:%s" % (escape(chkpt), val)) @staticmethod def humantime(*tpair): """format xtime-like (sec, nsec) pair to human readable format""" ts = datetime.fromtimestamp(float('.'.join(str(n) for n in tpair))).\ - strftime("%Y-%m-%d %H:%M:%S") + strftime("%Y-%m-%d %H:%M:%S") if len(tpair) > 1: ts += '.' + str(tpair[1]) return ts @@ -506,7 +540,7 @@ class GMasterCommon(object): years = int(years) days = int(days) - date="" + date = "" m, s = divmod(crawl_time.seconds, 60) h, m = divmod(m, 60) @@ -515,7 +549,8 @@ class GMasterCommon(object): if days != 0: date += "%s %s " % (days, "day" if days == 1 else "days") - date += "%s:%s:%s" % (string.zfill(h, 2), string.zfill(m, 2), string.zfill(s, 2)) + date += "%s:%s:%s" % (string.zfill(h, 2), + string.zfill(m, 2), string.zfill(s, 2)) return date def checkpt_service(self, chan, chkpt): @@ -540,16 +575,18 @@ class GMasterCommon(object): if not checkpt_tgt: checkpt_tgt = self.xtime('.') if isinstance(checkpt_tgt, int): - raise GsyncdError("master root directory is unaccessible (%s)", + raise GsyncdError("master root directory is " + "unaccessible (%s)", os.strerror(checkpt_tgt)) self._set_checkpt_param(chkpt, 'target', checkpt_tgt) - logging.debug("checkpoint target %s has been determined for checkpoint %s" % \ + logging.debug("checkpoint target %s has been determined " + "for checkpoint %s" % (repr(checkpt_tgt), chkpt)) # check if the label is 'now' chkpt_lbl = chkpt try: - x1,x2 = chkpt.split(':') + x1, x2 = chkpt.split(':') if x1 == 'now': chkpt_lbl = "as of " + self.humantime(x2) except: @@ -557,41 +594,46 @@ class GMasterCommon(object): completed = self._checkpt_param(chkpt, 'completed', xtimish=False) if completed: completed = tuple(int(x) for x in completed.split('.')) - s,_,_ = select([chan], [], [], (not completed) and 5 or None) + s, _, _ = select([chan], [], [], (not completed) and 5 or None) # either request made and we re-check to not # give back stale data, or we still hunting for completion - if self.native_xtime(checkpt_tgt) and self.native_xtime(checkpt_tgt) < self.volmark: + if (self.native_xtime(checkpt_tgt) and ( + self.native_xtime(checkpt_tgt) < self.volmark)): # indexing has been reset since setting the checkpoint status = "is invalid" else: xtr = self.xtime('.', self.slave) if isinstance(xtr, int): - raise GsyncdError("slave root directory is unaccessible (%s)", + raise GsyncdError("slave root directory is " + "unaccessible (%s)", os.strerror(xtr)) ncompleted = self.xtime_geq(xtr, checkpt_tgt) - if completed and not ncompleted: # stale data - logging.warn("completion time %s for checkpoint %s became stale" % \ + if completed and not ncompleted: # stale data + logging.warn("completion time %s for checkpoint %s " + "became stale" % (self.humantime(*completed), chkpt)) completed = None gconf.configinterface.delete('checkpoint_completed') - if ncompleted and not completed: # just reaching completion + if ncompleted and not completed: # just reaching completion completed = "%.6f" % time.time() - self._set_checkpt_param(chkpt, 'completed', completed, xtimish=False) + self._set_checkpt_param( + chkpt, 'completed', completed, xtimish=False) completed = tuple(int(x) for x in completed.split('.')) logging.info("checkpoint %s completed" % chkpt) status = completed and \ - "completed at " + self.humantime(completed[0]) or \ - "not reached yet" + "completed at " + self.humantime(completed[0]) or \ + "not reached yet" if s: conn = None try: conn, _ = chan.accept() try: - conn.send("checkpoint %s is %s\0" % (chkpt_lbl, status)) + conn.send("checkpoint %s is %s\0" % + (chkpt_lbl, status)) except: exc = sys.exc_info()[1] - if (isinstance(exc, OSError) or isinstance(exc, IOError)) and \ - exc.errno == EPIPE: + if ((isinstance(exc, OSError) or isinstance( + exc, IOError)) and exc.errno == EPIPE): logging.debug('checkpoint client disconnected') else: raise @@ -602,11 +644,13 @@ class GMasterCommon(object): def start_checkpoint_thread(self): """prepare and start checkpoint service""" if self.checkpoint_thread or not ( - getattr(gconf, 'state_socket_unencoded', None) and getattr(gconf, 'socketdir', None) + getattr(gconf, 'state_socket_unencoded', None) and getattr( + gconf, 'socketdir', None) ): return chan = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - state_socket = os.path.join(gconf.socketdir, md5hex(gconf.state_socket_unencoded) + ".socket") + state_socket = os.path.join( + gconf.socketdir, md5hex(gconf.state_socket_unencoded) + ".socket") try: os.unlink(state_socket) except: @@ -621,9 +665,9 @@ class GMasterCommon(object): def add_job(self, path, label, job, *a, **kw): """insert @job function to job table at @path with @label""" - if self.jobtab.get(path) == None: + if self.jobtab.get(path) is None: self.jobtab[path] = [] - self.jobtab[path].append((label, a, lambda : job(*a, **kw))) + self.jobtab[path].append((label, a, lambda: job(*a, **kw))) def add_failjob(self, path, label): """invoke .add_job with a job that does nothing just fails""" @@ -644,7 +688,7 @@ class GMasterCommon(object): ret = j[-1]() if not ret: succeed = False - if succeed and not args[0] == None: + if succeed and not args[0] is None: self.sendmark(path, *args) return succeed @@ -657,19 +701,21 @@ class GMasterCommon(object): self.slave.server.setattr(path, adct) self.set_slave_xtime(path, mark) + class GMasterChangelogMixin(GMasterCommon): + """ changelog based change detection and syncing """ # index for change type and entry IDX_START = 0 - IDX_END = 2 + IDX_END = 2 - POS_GFID = 0 - POS_TYPE = 1 + POS_GFID = 0 + POS_TYPE = 1 POS_ENTRY1 = -1 - TYPE_META = "M " - TYPE_GFID = "D " + TYPE_META = "M " + TYPE_GFID = "D " TYPE_ENTRY = "E " # flat directory heirarchy for gfid based access @@ -686,18 +732,19 @@ class GMasterChangelogMixin(GMasterCommon): def setup_working_dir(self): workdir = os.path.join(gconf.working_dir, md5hex(gconf.local_path)) logfile = os.path.join(workdir, 'changes.log') - logging.debug('changelog working dir %s (log: %s)' % (workdir, logfile)) + logging.debug('changelog working dir %s (log: %s)' % + (workdir, logfile)) return (workdir, logfile) def process_change(self, change, done, retry): pfx = gauxpfx() - clist = [] + clist = [] entries = [] meta_gfid = set() datas = set() # basic crawl stats: files and bytes - files_pending = {'count': 0, 'purge': 0, 'bytes': 0, 'files': []} + files_pending = {'count': 0, 'purge': 0, 'bytes': 0, 'files': []} try: f = open(change, "r") clist = f.readlines() @@ -750,17 +797,19 @@ class GMasterChangelogMixin(GMasterCommon): elif ty in ['CREATE', 'MKDIR', 'MKNOD']: entry_update() # stat information present in the changelog itself - entries.append(edct(ty, gfid=gfid, entry=en, mode=int(ec[2]),\ + entries.append(edct(ty, gfid=gfid, entry=en, + mode=int(ec[2]), uid=int(ec[3]), gid=int(ec[4]))) else: # stat() to get mode and other information go = os.path.join(pfx, gfid) st = lstat(go) if isinstance(st, int): - if ty == 'RENAME': # special hack for renames... + if ty == 'RENAME': # special hack for renames... entries.append(edct('UNLINK', gfid=gfid, entry=en)) else: - logging.debug('file %s got purged in the interim' % go) + logging.debug( + 'file %s got purged in the interim' % go) continue if ty == 'LINK': @@ -771,17 +820,20 @@ class GMasterChangelogMixin(GMasterCommon): if isinstance(rl, int): continue entry_update() - entries.append(edct(ty, stat=st, entry=en, gfid=gfid, link=rl)) + entries.append( + edct(ty, stat=st, entry=en, gfid=gfid, link=rl)) elif ty == 'RENAME': entry_update() - e1 = unescape(os.path.join(pfx, ec[self.POS_ENTRY1 - 1])) - entries.append(edct(ty, gfid=gfid, entry=e1, entry1=en, stat=st)) + e1 = unescape( + os.path.join(pfx, ec[self.POS_ENTRY1 - 1])) + entries.append( + edct(ty, gfid=gfid, entry=e1, entry1=en, stat=st)) else: logging.warn('ignoring %s [op %s]' % (gfid, ty)) elif et == self.TYPE_GFID: datas.add(os.path.join(pfx, ec[0])) elif et == self.TYPE_META: - if ec[1] == 'SETATTR': # only setattr's for now... + if ec[1] == 'SETATTR': # only setattr's for now... meta_gfid.add(os.path.join(pfx, ec[0])) else: logging.warn('got invalid changelog type: %s' % (et)) @@ -789,10 +841,10 @@ class GMasterChangelogMixin(GMasterCommon): if not retry: self.update_worker_cumilitive_status(files_pending) # sync namespace - if (entries): + if entries: self.slave.server.entry_ops(entries) # sync metadata - if (meta_gfid): + if meta_gfid: meta_entries = [] for go in meta_gfid: st = lstat(go) @@ -814,22 +866,25 @@ class GMasterChangelogMixin(GMasterCommon): self.skipped_gfid_list = [] self.current_files_skipped_count = 0 - # first, fire all changelog transfers in parallel. entry and metadata - # are performed synchronously, therefore in serial. However at the end - # of each changelog, data is synchronized with syncdata_async() - which - # means it is serial w.r.t entries/metadata of that changelog but - # happens in parallel with data of other changelogs. + # first, fire all changelog transfers in parallel. entry and + # metadata are performed synchronously, therefore in serial. + # However at the end of each changelog, data is synchronized + # with syncdata_async() - which means it is serial w.r.t + # entries/metadata of that changelog but happens in parallel + # with data of other changelogs. for change in changes: logging.debug('processing change %s' % change) self.process_change(change, done, retry) if not retry: - self.turns += 1 # number of changelogs processed in the batch + # number of changelogs processed in the batch + self.turns += 1 - # Now we wait for all the data transfers fired off in the above step - # to complete. Note that this is not ideal either. Ideally we want to - # trigger the entry/meta-data transfer of the next batch while waiting - # for the data transfer of the current batch to finish. + # Now we wait for all the data transfers fired off in the above + # step to complete. Note that this is not ideal either. Ideally + # we want to trigger the entry/meta-data transfer of the next + # batch while waiting for the data transfer of the current batch + # to finish. # Note that the reason to wait for the data transfer (vs doing it # completely in the background and call the changelog_done() @@ -837,10 +892,11 @@ class GMasterChangelogMixin(GMasterCommon): # and prevents a spiraling increase of wait stubs from consuming # unbounded memory and resources. - # update the slave's time with the timestamp of the _last_ changelog - # file time suffix. Since, the changelog prefix time is the time when - # the changelog was rolled over, introduce a tolerence of 1 second to - # counter the small delta b/w the marker update and gettimeofday(). + # update the slave's time with the timestamp of the _last_ + # changelog file time suffix. Since, the changelog prefix time + # is the time when the changelog was rolled over, introduce a + # tolerence of 1 second to counter the small delta b/w the + # marker update and gettimeofday(). # NOTE: this is only for changelog mode, not xsync. # @change is the last changelog (therefore max time for this batch) @@ -856,10 +912,13 @@ class GMasterChangelogMixin(GMasterCommon): retry = True tries += 1 if tries == self.MAX_RETRIES: - logging.warn('changelogs %s could not be processed - moving on...' % \ + logging.warn('changelogs %s could not be processed - ' + 'moving on...' % ' '.join(map(os.path.basename, changes))) - self.update_worker_total_files_skipped(self.current_files_skipped_count) - logging.warn('SKIPPED GFID = %s' % ','.join(self.skipped_gfid_list)) + self.update_worker_total_files_skipped( + self.current_files_skipped_count) + logging.warn('SKIPPED GFID = %s' % + ','.join(self.skipped_gfid_list)) self.update_worker_files_syncd() if done: xtl = (int(change.split('.')[-1]) - 1, 0) @@ -873,7 +932,7 @@ class GMasterChangelogMixin(GMasterCommon): # entry_ops() that failed... so we retry the _whole_ changelog # again. # TODO: remove entry retries when it's gets fixed. - logging.warn('incomplete sync, retrying changelogs: %s' % \ + logging.warn('incomplete sync, retrying changelogs: %s' % ' '.join(map(os.path.basename, changes))) time.sleep(0.5) @@ -884,15 +943,15 @@ class GMasterChangelogMixin(GMasterCommon): self.sendmark(path, stime) def get_worker_status_file(self): - file_name = gconf.local_path+'.status' + file_name = gconf.local_path + '.status' file_name = file_name.replace("/", "_") - worker_status_file = gconf.georep_session_working_dir+file_name + worker_status_file = gconf.georep_session_working_dir + file_name return worker_status_file def update_worker_status(self, key, value): - default_data = {"remote_node":"N/A", - "worker status":"Not Started", - "crawl status":"N/A", + default_data = {"remote_node": "N/A", + "worker status": "Not Started", + "crawl status": "N/A", "files_syncd": 0, "files_remaining": 0, "bytes_remaining": 0, @@ -909,7 +968,7 @@ class GMasterChangelogMixin(GMasterCommon): f.flush() os.fsync(f.fileno()) except (IOError, OSError, ValueError): - logging.info ('Creating new %s' % worker_status_file) + logging.info('Creating new %s' % worker_status_file) try: with open(worker_status_file, 'wb') as f: default_data[key] = value @@ -920,9 +979,9 @@ class GMasterChangelogMixin(GMasterCommon): raise def update_worker_cumilitive_status(self, files_pending): - default_data = {"remote_node":"N/A", - "worker status":"Not Started", - "crawl status":"N/A", + default_data = {"remote_node": "N/A", + "worker status": "Not Started", + "crawl status": "N/A", "files_syncd": 0, "files_remaining": 0, "bytes_remaining": 0, @@ -932,8 +991,8 @@ class GMasterChangelogMixin(GMasterCommon): try: with open(worker_status_file, 'r+') as f: loaded_data = json.load(f) - loaded_data['files_remaining'] = files_pending['count'] - loaded_data['bytes_remaining'] = files_pending['bytes'] + loaded_data['files_remaining'] = files_pending['count'] + loaded_data['bytes_remaining'] = files_pending['bytes'] loaded_data['purges_remaining'] = files_pending['purge'] os.ftruncate(f.fileno(), 0) os.lseek(f.fileno(), 0, os.SEEK_SET) @@ -941,11 +1000,11 @@ class GMasterChangelogMixin(GMasterCommon): f.flush() os.fsync(f.fileno()) except (IOError, OSError, ValueError): - logging.info ('Creating new %s' % worker_status_file) + logging.info('Creating new %s' % worker_status_file) try: with open(worker_status_file, 'wb') as f: - default_data['files_remaining'] = files_pending['count'] - default_data['bytes_remaining'] = files_pending['bytes'] + default_data['files_remaining'] = files_pending['count'] + default_data['bytes_remaining'] = files_pending['bytes'] default_data['purges_remaining'] = files_pending['purge'] json.dump(default_data, f) f.flush() @@ -953,24 +1012,24 @@ class GMasterChangelogMixin(GMasterCommon): except: raise - def update_worker_remote_node (self): + def update_worker_remote_node(self): node = sys.argv[-1] node = node.split("@")[-1] remote_node_ip = node.split(":")[0] remote_node_vol = node.split(":")[3] remote_node = remote_node_ip + '::' + remote_node_vol - self.update_worker_status ('remote_node', remote_node) + self.update_worker_status('remote_node', remote_node) - def update_worker_health (self, state): - self.update_worker_status ('worker status', state) + def update_worker_health(self, state): + self.update_worker_status('worker status', state) - def update_worker_crawl_status (self, state): - self.update_worker_status ('crawl status', state) + def update_worker_crawl_status(self, state): + self.update_worker_status('crawl status', state) - def update_worker_files_syncd (self): - default_data = {"remote_node":"N/A", - "worker status":"Not Started", - "crawl status":"N/A", + def update_worker_files_syncd(self): + default_data = {"remote_node": "N/A", + "worker status": "Not Started", + "crawl status": "N/A", "files_syncd": 0, "files_remaining": 0, "bytes_remaining": 0, @@ -981,8 +1040,8 @@ class GMasterChangelogMixin(GMasterCommon): with open(worker_status_file, 'r+') as f: loaded_data = json.load(f) loaded_data['files_syncd'] += loaded_data['files_remaining'] - loaded_data['files_remaining'] = 0 - loaded_data['bytes_remaining'] = 0 + loaded_data['files_remaining'] = 0 + loaded_data['bytes_remaining'] = 0 loaded_data['purges_remaining'] = 0 os.ftruncate(f.fileno(), 0) os.lseek(f.fileno(), 0, os.SEEK_SET) @@ -990,7 +1049,7 @@ class GMasterChangelogMixin(GMasterCommon): f.flush() os.fsync(f.fileno()) except (IOError, OSError, ValueError): - logging.info ('Creating new %s' % worker_status_file) + logging.info('Creating new %s' % worker_status_file) try: with open(worker_status_file, 'wb') as f: json.dump(default_data, f) @@ -999,19 +1058,19 @@ class GMasterChangelogMixin(GMasterCommon): except: raise - def update_worker_files_remaining (self, state): - self.update_worker_status ('files_remaining', state) + def update_worker_files_remaining(self, state): + self.update_worker_status('files_remaining', state) - def update_worker_bytes_remaining (self, state): - self.update_worker_status ('bytes_remaining', state) + def update_worker_bytes_remaining(self, state): + self.update_worker_status('bytes_remaining', state) - def update_worker_purges_remaining (self, state): - self.update_worker_status ('purges_remaining', state) + def update_worker_purges_remaining(self, state): + self.update_worker_status('purges_remaining', state) - def update_worker_total_files_skipped (self, value): - default_data = {"remote_node":"N/A", - "worker status":"Not Started", - "crawl status":"N/A", + def update_worker_total_files_skipped(self, value): + default_data = {"remote_node": "N/A", + "worker status": "Not Started", + "crawl status": "N/A", "files_syncd": 0, "files_remaining": 0, "bytes_remaining": 0, @@ -1029,7 +1088,7 @@ class GMasterChangelogMixin(GMasterCommon): f.flush() os.fsync(f.fileno()) except (IOError, OSError, ValueError): - logging.info ('Creating new %s' % worker_status_file) + logging.info('Creating new %s' % worker_status_file) try: with open(worker_status_file, 'wb') as f: default_data['total_files_skipped'] = value @@ -1057,9 +1116,12 @@ class GMasterChangelogMixin(GMasterCommon): if changes: if purge_time: logging.info("slave's time: %s" % repr(purge_time)) - processed = [x for x in changes if int(x.split('.')[-1]) < purge_time[0]] + processed = [x for x in changes + if int(x.split('.')[-1]) < purge_time[0]] for pr in processed: - logging.info('skipping already processed change: %s...' % os.path.basename(pr)) + logging.info( + 'skipping already processed change: %s...' % + os.path.basename(pr)) self.master.server.changelog_done(pr) changes.remove(pr) logging.debug('processing changes %s' % repr(changes)) @@ -1080,7 +1142,9 @@ class GMasterChangelogMixin(GMasterCommon): # control should not reach here raise + class GMasterXsyncMixin(GMasterChangelogMixin): + """ This crawl needs to be xtime based (as of now it's not. this is beacuse we generate CHANGELOG @@ -1091,7 +1155,7 @@ class GMasterXsyncMixin(GMasterChangelogMixin): files, hardlinks and symlinks. """ - XSYNC_MAX_ENTRIES = 1<<13 + XSYNC_MAX_ENTRIES = 1 << 13 def register(self): self.counter = 0 @@ -1145,7 +1209,8 @@ class GMasterXsyncMixin(GMasterChangelogMixin): def open(self): try: - self.xsync_change = os.path.join(self.tempdir, 'XSYNC-CHANGELOG.' + str(int(time.time()))) + self.xsync_change = os.path.join( + self.tempdir, 'XSYNC-CHANGELOG.' + str(int(time.time()))) self.fh = open(self.xsync_change, 'w') except IOError: raise @@ -1165,7 +1230,7 @@ class GMasterXsyncMixin(GMasterChangelogMixin): self.put('xsync', self.fname()) self.counter = 0 if not last: - time.sleep(1) # make sure changelogs are 1 second apart + time.sleep(1) # make sure changelogs are 1 second apart self.open() def sync_stime(self, stime=None, last=False): @@ -1207,7 +1272,7 @@ class GMasterXsyncMixin(GMasterChangelogMixin): xtr_root = self.xtime('.', self.slave) if isinstance(xtr_root, int): if xtr_root != ENOENT: - logging.warn("slave cluster not returning the " \ + logging.warn("slave cluster not returning the " "correct xtime for root (%d)" % xtr_root) xtr_root = self.minus_infinity xtl = self.xtime(path) @@ -1216,7 +1281,7 @@ class GMasterXsyncMixin(GMasterChangelogMixin): xtr = self.xtime(path, self.slave) if isinstance(xtr, int): if xtr != ENOENT: - logging.warn("slave cluster not returning the " \ + logging.warn("slave cluster not returning the " "correct xtime for %s (%d)" % (path, xtr)) xtr = self.minus_infinity xtr = max(xtr, xtr_root) @@ -1235,7 +1300,8 @@ class GMasterXsyncMixin(GMasterChangelogMixin): e = os.path.join(path, e) xte = self.xtime(e) if isinstance(xte, int): - logging.warn("irregular xtime for %s: %s" % (e, errno.errorcode[xte])) + logging.warn("irregular xtime for %s: %s" % + (e, errno.errorcode[xte])) continue if not self.need_sync(e, xte, xtr): continue @@ -1256,35 +1322,51 @@ class GMasterXsyncMixin(GMasterChangelogMixin): self.sync_done(self.stimes, False) self.stimes = [] if stat.S_ISDIR(mo): - self.write_entry_change("E", [gfid, 'MKDIR', str(mo), str(st.st_uid), str(st.st_gid), escape(os.path.join(pargfid, bname))]) + self.write_entry_change("E", [gfid, 'MKDIR', str(mo), str( + st.st_uid), str(st.st_gid), escape(os.path.join(pargfid, + bname))]) self.Xcrawl(e, xtr_root) self.stimes.append((e, xte)) elif stat.S_ISLNK(mo): - self.write_entry_change("E", [gfid, 'SYMLINK', escape(os.path.join(pargfid, bname))]) + self.write_entry_change( + "E", [gfid, 'SYMLINK', escape(os.path.join(pargfid, + bname))]) elif stat.S_ISREG(mo): nlink = st.st_nlink - nlink -= 1 # fixup backend stat link count - # if a file has a hardlink, create a Changelog entry as 'LINK' so the slave - # side will decide if to create the new entry, or to create link. + nlink -= 1 # fixup backend stat link count + # if a file has a hardlink, create a Changelog entry as + # 'LINK' so the slave side will decide if to create the + # new entry, or to create link. if nlink == 1: - self.write_entry_change("E", [gfid, 'MKNOD', str(mo), str(st.st_uid), str(st.st_gid), escape(os.path.join(pargfid, bname))]) + self.write_entry_change("E", + [gfid, 'MKNOD', str(mo), + str(st.st_uid), + str(st.st_gid), + escape(os.path.join( + pargfid, bname))]) else: - self.write_entry_change("E", [gfid, 'LINK', escape(os.path.join(pargfid, bname))]) + self.write_entry_change( + "E", [gfid, 'LINK', escape(os.path.join(pargfid, + bname))]) self.write_entry_change("D", [gfid]) if path == '.': self.stimes.append((path, xtl)) self.sync_done(self.stimes, True) + class BoxClosedErr(Exception): pass + class PostBox(list): + """synchronized collection for storing things thought of as "requests" """ def __init__(self, *a): list.__init__(self, *a) # too bad Python stdlib does not have read/write locks... - # it would suffivce to grab the lock in .append as reader, in .close as writer + # it would suffivce to grab the lock in .append as reader, in .close as + # writer self.lever = Condition() self.open = True self.done = False @@ -1319,7 +1401,9 @@ class PostBox(list): self.open = False self.lever.release() + class Syncer(object): + """a staged queue to relay rsync requests to rsync workers By "staged queue" its meant that when a consumer comes to the diff --git a/geo-replication/syncdaemon/monitor.py b/geo-replication/syncdaemon/monitor.py index b0262ee30..8ed6f8326 100644 --- a/geo-replication/syncdaemon/monitor.py +++ b/geo-replication/syncdaemon/monitor.py @@ -1,3 +1,13 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os import sys import time @@ -9,12 +19,16 @@ from subprocess import PIPE from resource import Popen, FILE, GLUSTER, SSH from threading import Lock from gconf import gconf -from syncdutils import update_file, select, waitpid, set_term_handler, is_host_local, GsyncdError +from syncdutils import update_file, select, waitpid +from syncdutils import set_term_handler, is_host_local, GsyncdError from syncdutils import escape, Thread, finalize, memoize + class Volinfo(object): + def __init__(self, vol, host='localhost', prelude=[]): - po = Popen(prelude + ['gluster', '--xml', '--remote-host=' + host, 'volume', 'info', vol], + po = Popen(prelude + ['gluster', '--xml', '--remote-host=' + host, + 'volume', 'info', vol], stdout=PIPE, stderr=PIPE) vix = po.stdout.read() po.wait() @@ -25,7 +39,8 @@ class Volinfo(object): via = '(via %s) ' % prelude.join(' ') else: via = ' ' - raise GsyncdError('getting volume info of %s%s failed with errorcode %s', + raise GsyncdError('getting volume info of %s%s ' + 'failed with errorcode %s', (vol, via, vi.find('opErrno').text)) self.tree = vi self.volume = vol @@ -40,25 +55,27 @@ class Volinfo(object): def bparse(b): host, dirp = b.text.split(':', 2) return {'host': host, 'dir': dirp} - return [ bparse(b) for b in self.get('brick') ] + return [bparse(b) for b in self.get('brick')] @property @memoize def uuid(self): ids = self.get('id') if len(ids) != 1: - raise GsyncdError("volume info of %s obtained from %s: ambiguous uuid", + raise GsyncdError("volume info of %s obtained from %s: " + "ambiguous uuid", self.volume, self.host) return ids[0].text class Monitor(object): + """class which spawns and manages gsyncd workers""" - ST_INIT = 'Initializing...' - ST_STABLE = 'Stable' - ST_FAULTY = 'faulty' - ST_INCON = 'inconsistent' + ST_INIT = 'Initializing...' + ST_STABLE = 'Stable' + ST_FAULTY = 'faulty' + ST_INCON = 'inconsistent' _ST_ORD = [ST_STABLE, ST_INIT, ST_FAULTY, ST_INCON] def __init__(self): @@ -68,7 +85,8 @@ class Monitor(object): def set_state(self, state, w=None): """set the state that can be used by external agents like glusterd for status reporting""" - computestate = lambda: self.state and self._ST_ORD[max(self._ST_ORD.index(s) for s in self.state.values())] + computestate = lambda: self.state and self._ST_ORD[ + max(self._ST_ORD.index(s) for s in self.state.values())] if w: self.lock.acquire() old_state = computestate() @@ -112,14 +130,17 @@ class Monitor(object): self.set_state(self.ST_INIT, w) ret = 0 + def nwait(p, o=0): p2, r = waitpid(p, o) if not p2: return return r + def exit_signalled(s): """ child teminated due to receipt of SIGUSR1 """ return (os.WIFSIGNALED(s) and (os.WTERMSIG(s) == signal.SIGUSR1)) + def exit_status(s): if os.WIFEXITED(s): return os.WEXITSTATUS(s) @@ -134,7 +155,8 @@ class Monitor(object): os.close(pr) os.execv(sys.executable, argv + ['--feedback-fd', str(pw), '--local-path', w[0], - '--local-id', '.' + escape(w[0]), + '--local-id', + '.' + escape(w[0]), '--resource-remote', w[1]]) self.lock.acquire() cpids.add(cpid) @@ -145,31 +167,31 @@ class Monitor(object): os.close(pr) if so: ret = nwait(cpid, os.WNOHANG) - if ret != None: - logging.info("worker(%s) died before establishing " \ + if ret is not None: + logging.info("worker(%s) died before establishing " "connection" % w[0]) else: logging.debug("worker(%s) connected" % w[0]) while time.time() < t0 + conn_timeout: ret = nwait(cpid, os.WNOHANG) - if ret != None: - logging.info("worker(%s) died in startup " \ + if ret is not None: + logging.info("worker(%s) died in startup " "phase" % w[0]) break time.sleep(1) else: - logging.info("worker(%s) not confirmed in %d sec, " \ + logging.info("worker(%s) not confirmed in %d sec, " "aborting it" % (w[0], conn_timeout)) os.kill(cpid, signal.SIGKILL) ret = nwait(cpid) - if ret == None: + if ret is None: self.set_state(self.ST_STABLE, w) ret = nwait(cpid) if exit_signalled(ret): ret = 0 else: ret = exit_status(ret) - if ret in (0,1): + if ret in (0, 1): self.set_state(self.ST_FAULTY, w) time.sleep(10) self.set_state(self.ST_INCON, w) @@ -194,17 +216,18 @@ class Monitor(object): os.kill(cpid, signal.SIGKILL) self.lock.release() finalize(exval=1) - t = Thread(target = wmon, args=[wx]) + t = Thread(target=wmon, args=[wx]) t.start() ta.append(t) for t in ta: t.join() + def distribute(*resources): master, slave = resources mvol = Volinfo(master.volume, master.host) logging.debug('master bricks: ' + repr(mvol.bricks)) - prelude = [] + prelude = [] si = slave if isinstance(slave, SSH): prelude = gconf.ssh_command.split() + [slave.remote_addr] @@ -221,23 +244,28 @@ def distribute(*resources): raise GsyncdError("unkown slave type " + slave.url) logging.info('slave bricks: ' + repr(sbricks)) if isinstance(si, FILE): - slaves = [ slave.url ] + slaves = [slave.url] else: slavenodes = set(b['host'] for b in sbricks) if isinstance(slave, SSH) and not gconf.isolated_slave: rap = SSH.parse_ssh_address(slave) - slaves = [ 'ssh://' + rap['user'] + '@' + h + ':' + si.url for h in slavenodes ] + slaves = ['ssh://' + rap['user'] + '@' + h + ':' + si.url + for h in slavenodes] else: - slavevols = [ h + ':' + si.volume for h in slavenodes ] + slavevols = [h + ':' + si.volume for h in slavenodes] if isinstance(slave, SSH): - slaves = [ 'ssh://' + rap.remote_addr + ':' + v for v in slavevols ] + slaves = ['ssh://' + rap.remote_addr + ':' + v + for v in slavevols] else: slaves = slavevols - workerspex = [ (brick['dir'], slaves[idx % len(slaves)]) for idx, brick in enumerate(mvol.bricks) if is_host_local(brick['host']) ] + workerspex = [(brick['dir'], slaves[idx % len(slaves)]) + for idx, brick in enumerate(mvol.bricks) + if is_host_local(brick['host'])] logging.info('worker specs: ' + repr(workerspex)) return workerspex, suuid + def monitor(*resources): """oh yeah, actually Monitor is used as singleton, too""" return Monitor().multiplex(*distribute(*resources)) diff --git a/geo-replication/syncdaemon/repce.py b/geo-replication/syncdaemon/repce.py index 755fb61df..d7b17dda7 100644 --- a/geo-replication/syncdaemon/repce.py +++ b/geo-replication/syncdaemon/repce.py @@ -1,3 +1,13 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os import sys import time @@ -24,6 +34,7 @@ from syncdutils import Thread, select pickle_proto = -1 repce_version = 1.0 + def ioparse(i, o): if isinstance(i, int): i = os.fdopen(i) @@ -34,6 +45,7 @@ def ioparse(i, o): o = o.fileno() return (i, o) + def send(out, *args): """pickle args and write out wholly in one syscall @@ -43,12 +55,14 @@ def send(out, *args): """ os.write(out, pickle.dumps(args, pickle_proto)) + def recv(inf): """load an object from input stream""" return pickle.load(inf) class RepceServer(object): + """RePCe is Hungarian for canola, http://hu.wikipedia.org/wiki/Repce ... also our homebrewed RPC backend where the transport layer is @@ -95,16 +109,17 @@ class RepceServer(object): if rmeth == '__repce_version__': res = repce_version else: - try: - res = getattr(self.obj, rmeth)(*in_data[2:]) - except: - res = sys.exc_info()[1] - exc = True - logging.exception("call failed: ") + try: + res = getattr(self.obj, rmeth)(*in_data[2:]) + except: + res = sys.exc_info()[1] + exc = True + logging.exception("call failed: ") send(self.out, rid, exc, res) class RepceJob(object): + """class representing message status we can use for waiting on reply""" @@ -137,6 +152,7 @@ class RepceJob(object): class RepceClient(object): + """RePCe is Hungarian for canola, http://hu.wikipedia.org/wiki/Repce ... also our homebrewed RPC backend where the transport layer is @@ -148,7 +164,7 @@ class RepceClient(object): def __init__(self, i, o): self.inf, self.out = ioparse(i, o) self.jtab = {} - t = Thread(target = self.listen) + t = Thread(target=self.listen) t.start() def listen(self): @@ -177,25 +193,31 @@ class RepceClient(object): return rjob def __call__(self, meth, *args): - """RePCe client is callabe, calling it implements a synchronous remote call + """RePCe client is callabe, calling it implements a synchronous + remote call. - We do a .push with a cbk which does a wakeup upon receiving anwser, then wait - on the RepceJob. + We do a .push with a cbk which does a wakeup upon receiving anwser, + then wait on the RepceJob. """ - rjob = self.push(meth, *args, **{'cbk': lambda rj, res: rj.wakeup(res)}) + rjob = self.push( + meth, *args, **{'cbk': lambda rj, res: rj.wakeup(res)}) exc, res = rjob.wait() if exc: - logging.error('call %s (%s) failed on peer with %s' % (repr(rjob), meth, str(type(res).__name__))) + logging.error('call %s (%s) failed on peer with %s' % + (repr(rjob), meth, str(type(res).__name__))) raise res logging.debug("call %s %s -> %s" % (repr(rjob), meth, repr(res))) return res class mprx(object): - """method proxy, standard trick to implement rubyesque method_missing - in Python - A class is a closure factory, you know what I mean, or go read some SICP. + """method proxy, standard trick to implement rubyesque + method_missing in Python + + A class is a closure factory, you know what I mean, or go read + some SICP. """ + def __init__(self, ins, meth): self.ins = ins self.meth = meth diff --git a/geo-replication/syncdaemon/resource.py b/geo-replication/syncdaemon/resource.py index 41add6fb2..2fb6b3078 100644 --- a/geo-replication/syncdaemon/resource.py +++ b/geo-replication/syncdaemon/resource.py @@ -1,3 +1,13 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import re import os import sys @@ -12,27 +22,31 @@ import logging import tempfile import threading import subprocess -from errno import EEXIST, ENOENT, ENODATA, ENOTDIR, ELOOP, EISDIR, ENOTEMPTY, ESTALE, EINVAL +from errno import EEXIST, ENOENT, ENODATA, ENOTDIR, ELOOP +from errno import EISDIR, ENOTEMPTY, ESTALE, EINVAL from select import error as SelectError from gconf import gconf import repce from repce import RepceServer, RepceClient -from master import gmaster_builder +from master import gmaster_builder import syncdutils from syncdutils import GsyncdError, select, privileged, boolify, funcode from syncdutils import umask, entry2pb, gauxpfx, errno_wrap, lstat -UrlRX = re.compile('\A(\w+)://([^ *?[]*)\Z') +UrlRX = re.compile('\A(\w+)://([^ *?[]*)\Z') HostRX = re.compile('[a-z\d](?:[a-z\d.-]*[a-z\d])?', re.I) UserRX = re.compile("[\w!\#$%&'*+-\/=?^_`{|}~]+") + def sup(x, *a, **kw): """a rubyesque "super" for python ;) invoke caller method in parent class with given args. """ - return getattr(super(type(x), x), sys._getframe(1).f_code.co_name)(*a, **kw) + return getattr(super(type(x), x), + sys._getframe(1).f_code.co_name)(*a, **kw) + def desugar(ustr): """transform sugared url strings to standard :// form @@ -57,15 +71,17 @@ def desugar(ustr): ap = ap[1:] return "file://" + ap + def gethostbyname(hnam): """gethostbyname wrapper""" try: return socket.gethostbyname(hnam) except socket.gaierror: ex = sys.exc_info()[1] - raise GsyncdError("failed to resolve %s: %s" % \ + raise GsyncdError("failed to resolve %s: %s" % (hnam, ex.strerror)) + def parse_url(ustr): """instantiate an url object by scheme-to-class dispatch @@ -86,6 +102,7 @@ def parse_url(ustr): class _MetaXattr(object): + """singleton class, a lazy wrapper around the libcxattr module @@ -100,17 +117,19 @@ class _MetaXattr(object): def __getattr__(self, meth): from libcxattr import Xattr as LXattr - xmeth = [ m for m in dir(LXattr) if m[0] != '_' ] + xmeth = [m for m in dir(LXattr) if m[0] != '_'] if not meth in xmeth: return for m in xmeth: setattr(self, m, getattr(LXattr, m)) return getattr(self, meth) + class _MetaChangelog(object): + def __getattr__(self, meth): from libgfchangelog import Changes as LChanges - xmeth = [ m for m in dir(LChanges) if m[0] != '_' ] + xmeth = [m for m in dir(LChanges) if m[0] != '_'] if not meth in xmeth: return for m in xmeth: @@ -122,6 +141,7 @@ Changes = _MetaChangelog() class Popen(subprocess.Popen): + """customized subclass of subprocess.Popen with a ring buffer for children error output""" @@ -129,11 +149,13 @@ class Popen(subprocess.Popen): def init_errhandler(cls): """start the thread which handles children's error output""" cls.errstore = {} + def tailer(): while True: errstore = cls.errstore.copy() try: - poe, _ ,_ = select([po.stderr for po in errstore], [], [], 1) + poe, _, _ = select( + [po.stderr for po in errstore], [], [], 1) except (ValueError, SelectError): continue for po in errstore: @@ -154,12 +176,12 @@ class Popen(subprocess.Popen): tots = len(l) for lx in la: tots += len(lx) - while tots > 1<<20 and la: + while tots > 1 << 20 and la: tots -= len(la.pop(0)) la.append(l) finally: po.lock.release() - t = syncdutils.Thread(target = tailer) + t = syncdutils.Thread(target=tailer) t.start() cls.errhandler = t @@ -189,8 +211,9 @@ class Popen(subprocess.Popen): ex = sys.exc_info()[1] if not isinstance(ex, OSError): raise - raise GsyncdError("""execution of "%s" failed with %s (%s)""" % \ - (args[0], errno.errorcode[ex.errno], os.strerror(ex.errno))) + raise GsyncdError("""execution of "%s" failed with %s (%s)""" % + (args[0], errno.errorcode[ex.errno], + os.strerror(ex.errno))) if kw.get('stderr') == subprocess.PIPE: assert(getattr(self, 'errhandler', None)) self.errstore[self] = [] @@ -200,9 +223,10 @@ class Popen(subprocess.Popen): filling = "" if self.elines: filling = ", saying:" - logging.error("""command "%s" returned with %s%s""" % \ + logging.error("""command "%s" returned with %s%s""" % (" ".join(self.args), repr(self.returncode), filling)) lp = '' + def logerr(l): logging.error(self.args[0] + "> " + l) for l in self.elines: @@ -217,9 +241,9 @@ class Popen(subprocess.Popen): def errfail(self): """fail nicely if child did not terminate with success""" self.errlog() - syncdutils.finalize(exval = 1) + syncdutils.finalize(exval=1) - def terminate_geterr(self, fail_on_err = True): + def terminate_geterr(self, fail_on_err=True): """kill child, finalize stderr harvesting (unregister from errhandler, set up .elines), fail on error if asked for @@ -230,14 +254,14 @@ class Popen(subprocess.Popen): finally: self.lock.release() elines = self.errstore.pop(self) - if self.poll() == None: + if self.poll() is None: self.terminate() - if self.poll() == None: + if self.poll() is None: time.sleep(0.1) self.kill() self.wait() while True: - if not select([self.stderr],[],[],0.1)[0]: + if not select([self.stderr], [], [], 0.1)[0]: break b = os.read(self.stderr.fileno(), 1024) if b: @@ -251,6 +275,7 @@ class Popen(subprocess.Popen): class Server(object): + """singleton implemening those filesystem access primitives which are needed for geo-replication functionality @@ -260,25 +285,28 @@ class Server(object): GX_NSPACE_PFX = (privileged() and "trusted" or "system") GX_NSPACE = GX_NSPACE_PFX + ".glusterfs" - NTV_FMTSTR = "!" + "B"*19 + "II" + NTV_FMTSTR = "!" + "B" * 19 + "II" FRGN_XTRA_FMT = "I" FRGN_FMTSTR = NTV_FMTSTR + FRGN_XTRA_FMT - GX_GFID_CANONICAL_LEN = 37 # canonical gfid len + '\0' + GX_GFID_CANONICAL_LEN = 37 # canonical gfid len + '\0' - GFID_XATTR = 'trusted.gfid' # for backend gfid fetch, do not use GX_NSPACE_PFX - GFID_FMTSTR = "!" + "B"*16 + # for backend gfid fetch, do not use GX_NSPACE_PFX + GFID_XATTR = 'trusted.gfid' + GFID_FMTSTR = "!" + "B" * 16 local_path = '' @classmethod def _fmt_mknod(cls, l): - return "!II%dsI%dsIII" % (cls.GX_GFID_CANONICAL_LEN, l+1) + return "!II%dsI%dsIII" % (cls.GX_GFID_CANONICAL_LEN, l + 1) + @classmethod def _fmt_mkdir(cls, l): - return "!II%dsI%dsII" % (cls.GX_GFID_CANONICAL_LEN, l+1) + return "!II%dsI%dsII" % (cls.GX_GFID_CANONICAL_LEN, l + 1) + @classmethod def _fmt_symlink(cls, l1, l2): - return "!II%dsI%ds%ds" % (cls.GX_GFID_CANONICAL_LEN, l1+1, l2+1) + return "!II%dsI%ds%ds" % (cls.GX_GFID_CANONICAL_LEN, l1 + 1, l2 + 1) def _pathguard(f): """decorator method that checks @@ -289,6 +317,7 @@ class Server(object): fc = funcode(f) pi = list(fc.co_varnames).index('path') + def ff(*a): path = a[pi] ps = path.split('/') @@ -308,7 +337,6 @@ class Server(object): raise OSError(ENOTDIR, os.strerror(ENOTDIR)) return os.listdir(path) - @classmethod @_pathguard def lstat(cls, path): @@ -325,7 +353,9 @@ class Server(object): @_pathguard def linkto_check(cls, path): try: - return not (Xattr.lgetxattr_buf(path, 'trusted.glusterfs.dht.linkto') == '') + return not ( + Xattr.lgetxattr_buf(path, + 'trusted.glusterfs.dht.linkto') == '') except (IOError, OSError): ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA): @@ -333,13 +363,13 @@ class Server(object): else: raise - @classmethod @_pathguard def gfid(cls, path): try: buf = Xattr.lgetxattr(path, cls.GFID_XATTR, 16) - m = re.match('(.{8})(.{4})(.{4})(.{4})(.{12})', "".join(['%02x' % x for x in struct.unpack(cls.GFID_FMTSTR, buf)])) + m = re.match('(.{8})(.{4})(.{4})(.{4})(.{12})', "".join( + ['%02x' % x for x in struct.unpack(cls.GFID_FMTSTR, buf)])) return '-'.join(m.groups()) except (IOError, OSError): ex = sys.exc_info()[1] @@ -350,7 +380,9 @@ class Server(object): @classmethod def gfid_mnt(cls, gfidpath): - return errno_wrap(Xattr.lgetxattr, [gfidpath, 'glusterfs.gfid.string', cls.GX_GFID_CANONICAL_LEN], [ENOENT]) + return errno_wrap(Xattr.lgetxattr, + [gfidpath, 'glusterfs.gfid.string', + cls.GX_GFID_CANONICAL_LEN], [ENOENT]) @classmethod @_pathguard @@ -369,7 +401,7 @@ class Server(object): for e in entries: cls.purge(os.path.join(path, e)) """ - me_also = entries == None + me_also = entries is None if not entries: try: # if it's a symlink, prevent @@ -435,7 +467,9 @@ class Server(object): """ try: - return struct.unpack('!II', Xattr.lgetxattr(path, '.'.join([cls.GX_NSPACE, uuid, 'xtime']), 8)) + val = Xattr.lgetxattr(path, + '.'.join([cls.GX_NSPACE, uuid, 'xtime'])) + return struct.unpack('!II', val, 8) except OSError: ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA, ENOTDIR): @@ -454,7 +488,9 @@ class Server(object): """ try: - return struct.unpack('!II', Xattr.lgetxattr(path, '.'.join([cls.GX_NSPACE, uuid, 'stime']), 8)) + val = Xattr.lgetxattr(path, + '.'.join([cls.GX_NSPACE, uuid, 'stime'])) + return struct.unpack('!II', val, 8) except OSError: ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA, ENOTDIR): @@ -473,7 +509,9 @@ class Server(object): """ try: - return struct.unpack('!II', Xattr.lgetxattr(path, '.'.join([cls.GX_NSPACE, uuid, 'stime']), 8)) + val = Xattr.lgetxattr(path, + '.'.join([cls.GX_NSPACE, uuid, 'stime'])) + return struct.unpack('!II', val, 8) except OSError: ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA, ENOTDIR): @@ -484,7 +522,8 @@ class Server(object): @classmethod def node_uuid(cls, path='.'): try: - uuid_l = Xattr.lgetxattr_buf(path, '.'.join([cls.GX_NSPACE, 'node-uuid'])) + uuid_l = Xattr.lgetxattr_buf( + path, '.'.join([cls.GX_NSPACE, 'node-uuid'])) return uuid_l[:-1].split(' ') except OSError: raise @@ -493,13 +532,17 @@ class Server(object): @_pathguard def set_stime(cls, path, uuid, mark): """set @mark as stime for @uuid on @path""" - Xattr.lsetxattr(path, '.'.join([cls.GX_NSPACE, uuid, 'stime']), struct.pack('!II', *mark)) + Xattr.lsetxattr( + path, '.'.join([cls.GX_NSPACE, uuid, 'stime']), + struct.pack('!II', *mark)) @classmethod @_pathguard def set_xtime(cls, path, uuid, mark): """set @mark as xtime for @uuid on @path""" - Xattr.lsetxattr(path, '.'.join([cls.GX_NSPACE, uuid, 'xtime']), struct.pack('!II', *mark)) + Xattr.lsetxattr( + path, '.'.join([cls.GX_NSPACE, uuid, 'xtime']), + struct.pack('!II', *mark)) @classmethod @_pathguard @@ -511,18 +554,22 @@ class Server(object): on the brick (this method sets xtime on the remote slave) """ - Xattr.lsetxattr(path, '.'.join([cls.GX_NSPACE, uuid, 'xtime']), struct.pack('!II', *mark)) + Xattr.lsetxattr( + path, '.'.join([cls.GX_NSPACE, uuid, 'xtime']), + struct.pack('!II', *mark)) @classmethod def entry_ops(cls, entries): pfx = gauxpfx() logging.debug('entries: %s' % repr(entries)) # regular file + def entry_pack_reg(gf, bn, mo, uid, gid): blen = len(bn) return struct.pack(cls._fmt_mknod(blen), uid, gid, gf, mo, bn, stat.S_IMODE(mo), 0, umask()) + def entry_pack_reg_stat(gf, bn, st): blen = len(bn) mo = st['mode'] @@ -531,18 +578,21 @@ class Server(object): gf, mo, bn, stat.S_IMODE(mo), 0, umask()) # mkdir + def entry_pack_mkdir(gf, bn, mo, uid, gid): blen = len(bn) return struct.pack(cls._fmt_mkdir(blen), uid, gid, gf, mo, bn, stat.S_IMODE(mo), umask()) - #symlink + # symlink + def entry_pack_symlink(gf, bn, lnk, st): blen = len(bn) llen = len(lnk) return struct.pack(cls._fmt_symlink(blen, llen), st['uid'], st['gid'], gf, st['mode'], bn, lnk) + def entry_purge(entry, gfid): # This is an extremely racy code and needs to be fixed ASAP. # The GFID check here is to be sure that the pargfid/bname @@ -574,9 +624,11 @@ class Server(object): else: break elif op in ['CREATE', 'MKNOD']: - blob = entry_pack_reg(gfid, bname, e['mode'], e['uid'], e['uid']) + blob = entry_pack_reg( + gfid, bname, e['mode'], e['uid'], e['uid']) elif op == 'MKDIR': - blob = entry_pack_mkdir(gfid, bname, e['mode'], e['uid'], e['uid']) + blob = entry_pack_mkdir( + gfid, bname, e['mode'], e['uid'], e['uid']) elif op == 'LINK': slink = os.path.join(pfx, gfid) st = lstat(slink) @@ -596,21 +648,23 @@ class Server(object): else: errno_wrap(os.rename, [entry, en], [ENOENT, EEXIST]) if blob: - errno_wrap(Xattr.lsetxattr_l, [pg, 'glusterfs.gfid.newfile', blob], [EEXIST], [ENOENT, ESTALE, EINVAL]) + errno_wrap(Xattr.lsetxattr_l, [pg, 'glusterfs.gfid.newfile', + blob], + [EEXIST], [ENOENT, ESTALE, EINVAL]) @classmethod def meta_ops(cls, meta_entries): logging.debug('Meta-entries: %s' % repr(meta_entries)) for e in meta_entries: mode = e['stat']['mode'] - uid = e['stat']['uid'] - gid = e['stat']['gid'] - go = e['go'] + uid = e['stat']['uid'] + gid = e['stat']['gid'] + go = e['go'] errno_wrap(os.chmod, [go, mode], [ENOENT], [ESTALE, EINVAL]) errno_wrap(os.chown, [go, uid, gid], [ENOENT], [ESTALE, EINVAL]) @classmethod - def changelog_register(cls, cl_brick, cl_dir, cl_log, cl_level, retries = 0): + def changelog_register(cls, cl_brick, cl_dir, cl_log, cl_level, retries=0): Changes.cl_register(cl_brick, cl_dir, cl_log, cl_level, retries) @classmethod @@ -649,6 +703,7 @@ class Server(object): return os.getpid() last_keep_alive = 0 + @classmethod def keep_alive(cls, dct): """process keepalive messages. @@ -662,9 +717,12 @@ class Server(object): if dct: key = '.'.join([cls.GX_NSPACE, 'volume-mark', dct['uuid']]) val = struct.pack(cls.FRGN_FMTSTR, - *(dct['version'] + - tuple(int(x,16) for x in re.findall('(?:[\da-f]){2}', dct['uuid'])) + - (dct['retval'],) + dct['volume_mark'][0:2] + (dct['timeout'],))) + *(dct['version'] + + tuple(int(x, 16) + for x in re.findall('(?:[\da-f]){2}', + dct['uuid'])) + + (dct['retval'],) + dct['volume_mark'][0:2] + ( + dct['timeout'],))) Xattr.lsetxattr('.', key, val) cls.last_keep_alive += 1 return cls.last_keep_alive @@ -676,6 +734,7 @@ class Server(object): class SlaveLocal(object): + """mix-in class to implement some factes of a slave server ("mix-in" is sort of like "abstract class", ie. it's not @@ -697,9 +756,11 @@ class SlaveLocal(object): """ if boolify(gconf.use_rsync_xattrs) and not privileged(): - raise GsyncdError("using rsync for extended attributes is not supported") + raise GsyncdError( + "using rsync for extended attributes is not supported") - repce = RepceServer(self.server, sys.stdin, sys.stdout, int(gconf.sync_jobs)) + repce = RepceServer( + self.server, sys.stdin, sys.stdout, int(gconf.sync_jobs)) t = syncdutils.Thread(target=lambda: (repce.service_loop(), syncdutils.finalize())) t.start() @@ -709,12 +770,16 @@ class SlaveLocal(object): lp = self.server.last_keep_alive time.sleep(int(gconf.timeout)) if lp == self.server.last_keep_alive: - logging.info("connection inactive for %d seconds, stopping" % int(gconf.timeout)) + logging.info( + "connection inactive for %d seconds, stopping" % + int(gconf.timeout)) break else: select((), (), ()) + class SlaveRemote(object): + """mix-in class to implement an interface to a remote slave""" def connect_remote(self, rargs=[], **opts): @@ -731,9 +796,11 @@ class SlaveRemote(object): extra_opts += ['--session-owner', so] if boolify(gconf.use_rsync_xattrs): extra_opts.append('--use-rsync-xattrs') - po = Popen(rargs + gconf.remote_gsyncd.split() + extra_opts + \ - ['-N', '--listen', '--timeout', str(gconf.timeout), slave], - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + po = Popen(rargs + gconf.remote_gsyncd.split() + extra_opts + + ['-N', '--listen', '--timeout', str(gconf.timeout), + slave], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) gconf.transport = po return self.start_fd_client(po.stdout, po.stdin, **opts) @@ -752,7 +819,9 @@ class SlaveRemote(object): for k, v in da0[i].iteritems(): da1[i][k] = int(v) if da1[0] != da1[1]: - raise GsyncdError("RePCe major version mismatch: local %s, remote %s" % (exrv, rv)) + raise GsyncdError( + "RePCe major version mismatch: local %s, remote %s" % + (exrv, rv)) def rsync(self, files, *args): """invoke rsync""" @@ -760,17 +829,19 @@ class SlaveRemote(object): raise GsyncdError("no files to sync") logging.debug("files: " + ", ".join(files)) argv = gconf.rsync_command.split() + \ - ['-avR0', '--inplace', '--files-from=-', '--super','--stats', '--numeric-ids', '--no-implied-dirs'] + \ - gconf.rsync_options.split() + (boolify(gconf.use_rsync_xattrs) and ['--xattrs'] or []) + \ - ['.'] + list(args) - po = Popen(argv, stdin=subprocess.PIPE,stderr=subprocess.PIPE) + ['-avR0', '--inplace', '--files-from=-', '--super', + '--stats', '--numeric-ids', '--no-implied-dirs'] + \ + gconf.rsync_options.split() + \ + (boolify(gconf.use_rsync_xattrs) and ['--xattrs'] or []) + \ + ['.'] + list(args) + po = Popen(argv, stdin=subprocess.PIPE, stderr=subprocess.PIPE) for f in files: po.stdin.write(f) po.stdin.write('\0') po.stdin.close() po.wait() - po.terminate_geterr(fail_on_err = False) + po.terminate_geterr(fail_on_err=False) return po @@ -784,8 +855,10 @@ class SlaveRemote(object): logging.debug("files: " + ", ".join(files)) (host, rdir) = slaveurl.split(':') tar_cmd = ["tar", "-cf", "-", "--files-from", "-"] - ssh_cmd = gconf.ssh_command_tar.split() + [host, "tar", "--overwrite", "-xf", "-", "-C", rdir] - p0 = Popen(tar_cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + ssh_cmd = gconf.ssh_command_tar.split() + \ + [host, "tar", "--overwrite", "-xf", "-", "-C", rdir] + p0 = Popen(tar_cmd, stdout=subprocess.PIPE, + stdin=subprocess.PIPE, stderr=subprocess.PIPE) p1 = Popen(ssh_cmd, stdin=p0.stdout, stderr=subprocess.PIPE) for f in files: p0.stdin.write(f) @@ -795,14 +868,16 @@ class SlaveRemote(object): # wait() for tar to terminate, collecting any errors, further # waiting for transfer to complete p0.wait() - p0.terminate_geterr(fail_on_err = False) + p0.terminate_geterr(fail_on_err=False) p1.wait() - p1.terminate_geterr(fail_on_err = False) + p1.terminate_geterr(fail_on_err=False) return p1 + class AbstractUrl(object): + """abstract base class for url scheme classes""" def __init__(self, path, pattern): @@ -839,6 +914,7 @@ class AbstractUrl(object): class FILE(AbstractUrl, SlaveLocal, SlaveRemote): + """scheme class for file:// urls can be used to represent a file slave server @@ -847,6 +923,7 @@ class FILE(AbstractUrl, SlaveLocal, SlaveRemote): """ class FILEServer(Server): + """included server flavor""" pass @@ -864,6 +941,7 @@ class FILE(AbstractUrl, SlaveLocal, SlaveRemote): class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): + """scheme class for gluster:// urls can be used to represent a gluster slave server @@ -874,21 +952,24 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): """ class GLUSTERServer(Server): + "server enhancements for a glusterfs backend""" @classmethod - def _attr_unpack_dict(cls, xattr, extra_fields = ''): + def _attr_unpack_dict(cls, xattr, extra_fields=''): """generic volume mark fetching/parsing backed""" fmt_string = cls.NTV_FMTSTR + extra_fields buf = Xattr.lgetxattr('.', xattr, struct.calcsize(fmt_string)) vm = struct.unpack(fmt_string, buf) - m = re.match('(.{8})(.{4})(.{4})(.{4})(.{12})', "".join(['%02x' % x for x in vm[2:18]])) + m = re.match( + '(.{8})(.{4})(.{4})(.{4})(.{12})', + "".join(['%02x' % x for x in vm[2:18]])) uuid = '-'.join(m.groups()) - volinfo = { 'version': vm[0:2], - 'uuid' : uuid, - 'retval' : vm[18], - 'volume_mark': vm[19:21], - } + volinfo = {'version': vm[0:2], + 'uuid': uuid, + 'retval': vm[18], + 'volume_mark': vm[19:21], + } if extra_fields: return volinfo, vm[-len(extra_fields):] else: @@ -904,7 +985,8 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): d, x = cls._attr_unpack_dict(ele, cls.FRGN_XTRA_FMT) now = int(time.time()) if x[0] > now: - logging.debug("volinfo[%s] expires: %d (%d sec later)" % \ + logging.debug("volinfo[%s] expires: %d " + "(%d sec later)" % (d['uuid'], x[0], x[0] - now)) d['timeout'] = x[0] dict_list.append(d) @@ -919,7 +1001,8 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): def native_volume_info(cls): """get the native volume mark of the underlying gluster volume""" try: - return cls._attr_unpack_dict('.'.join([cls.GX_NSPACE, 'volume-mark'])) + return cls._attr_unpack_dict('.'.join([cls.GX_NSPACE, + 'volume-mark'])) except OSError: ex = sys.exc_info()[1] if ex.errno != ENODATA: @@ -936,9 +1019,10 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): def can_connect_to(self, remote): """determine our position in the connectibility matrix""" return not remote or \ - (isinstance(remote, SSH) and isinstance(remote.inner_rsc, GLUSTER)) + (isinstance(remote, SSH) and isinstance(remote.inner_rsc, GLUSTER)) class Mounter(object): + """Abstract base class for mounter backends""" def __init__(self, params): @@ -1003,7 +1087,7 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): if not t.isAlive(): break if time.time() >= tlim: - syncdutils.finalize(exval = 1) + syncdutils.finalize(exval=1) time.sleep(1) os.close(mpo) _, rv = syncdutils.waitpid(mh, 0) @@ -1011,7 +1095,8 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): rv = (os.WIFEXITED(rv) and os.WEXITSTATUS(rv) or 0) - \ (os.WIFSIGNALED(rv) and os.WTERMSIG(rv) or 0) logging.warn('stale mount possibly left behind on ' + d) - raise GsyncdError("cleaning up temp mountpoint %s failed with status %d" % \ + raise GsyncdError("cleaning up temp mountpoint %s " + "failed with status %d" % (d, rv)) else: rv = 0 @@ -1035,7 +1120,7 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): assert(mntpt) if mounted: po = self.umount_l(mntpt) - po.terminate_geterr(fail_on_err = False) + po.terminate_geterr(fail_on_err=False) if po.returncode != 0: po.errlog() rv = po.returncode @@ -1047,6 +1132,7 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): logging.debug('auxiliary glusterfs mount prepared') class DirectMounter(Mounter): + """mounter backend which calls mount(8), umount(8) directly""" mountkw = {'stderr': subprocess.PIPE} @@ -1057,15 +1143,17 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): return ['umount', '-l', d] def make_mount_argv(self): - self.mntpt = tempfile.mkdtemp(prefix = 'gsyncd-aux-mount-') - return [self.get_glusterprog()] + ['--' + p for p in self.params] + [self.mntpt] + self.mntpt = tempfile.mkdtemp(prefix='gsyncd-aux-mount-') + return [self.get_glusterprog()] + \ + ['--' + p for p in self.params] + [self.mntpt] - def cleanup_mntpt(self, mntpt = None): + def cleanup_mntpt(self, mntpt=None): if not mntpt: mntpt = self.mntpt os.rmdir(mntpt) class MountbrokerMounter(Mounter): + """mounter backend using the mountbroker gluster service""" mountkw = {'stderr': subprocess.PIPE, 'stdout': subprocess.PIPE} @@ -1073,7 +1161,8 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): @classmethod def make_cli_argv(cls): - return [cls.get_glusterprog()] + gconf.gluster_cli_options.split() + ['system::'] + return [cls.get_glusterprog()] + \ + gconf.gluster_cli_options.split() + ['system::'] @classmethod def make_umount_argv(cls, d): @@ -1081,7 +1170,8 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): def make_mount_argv(self, label): return self.make_cli_argv() + \ - ['mount', label, 'user-map-root=' + syncdutils.getusername()] + self.params + ['mount', label, 'user-map-root=' + + syncdutils.getusername()] + self.params def handle_mounter(self, po): self.mntpt = po.stdout.readline()[:-1] @@ -1106,9 +1196,10 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): label = syncdutils.getusername() mounter = label and self.MountbrokerMounter or self.DirectMounter params = gconf.gluster_params.split() + \ - (gconf.gluster_log_level and ['log-level=' + gconf.gluster_log_level] or []) + \ - ['log-file=' + gconf.gluster_log_file, 'volfile-server=' + self.host, - 'volfile-id=' + self.volume, 'client-pid=-1'] + (gconf.gluster_log_level and ['log-level=' + + gconf.gluster_log_level] or []) + \ + ['log-file=' + gconf.gluster_log_file, 'volfile-server=' + + self.host, 'volfile-id=' + self.volume, 'client-pid=-1'] mounter(params).inhibit(*[l for l in [label] if l]) def connect_remote(self, *a, **kw): @@ -1116,8 +1207,10 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): self.slavedir = "/proc/%d/cwd" % self.server.pid() def gmaster_instantiate_tuple(self, slave): - """return a tuple of the 'one shot' and the 'main crawl' class instance""" - return (gmaster_builder('xsync')(self, slave), gmaster_builder()(self, slave)) + """return a tuple of the 'one shot' and the 'main crawl' + class instance""" + return (gmaster_builder('xsync')(self, slave), + gmaster_builder()(self, slave)) def service_loop(self, *args): """enter service loop @@ -1133,6 +1226,7 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): class brickserver(FILE.FILEServer): local_path = gconf.local_path aggregated = self.server + @classmethod def entries(cls, path): e = super(brickserver, cls).entries(path) @@ -1143,14 +1237,17 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): except ValueError: pass return e + @classmethod def lstat(cls, e): """ path based backend stat """ return super(brickserver, cls).lstat(e) + @classmethod def gfid(cls, e): """ path based backend gfid fetch """ return super(brickserver, cls).gfid(e) + @classmethod def linkto_check(cls, e): return super(brickserver, cls).linkto_check(e) @@ -1158,9 +1255,25 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): # define {,set_}xtime in slave, thus preempting # the call to remote, so that it takes data from # the local brick - slave.server.xtime = types.MethodType(lambda _self, path, uuid: brickserver.xtime(path, uuid + '.' + gconf.slave_id), slave.server) - slave.server.stime = types.MethodType(lambda _self, path, uuid: brickserver.stime(path, uuid + '.' + gconf.slave_id), slave.server) - slave.server.set_stime = types.MethodType(lambda _self, path, uuid, mark: brickserver.set_stime(path, uuid + '.' + gconf.slave_id, mark), slave.server) + slave.server.xtime = types.MethodType( + lambda _self, path, uuid: ( + brickserver.xtime(path, + uuid + '.' + gconf.slave_id) + ), + slave.server) + slave.server.stime = types.MethodType( + lambda _self, path, uuid: ( + brickserver.stime(path, + uuid + '.' + gconf.slave_id) + ), + slave.server) + slave.server.set_stime = types.MethodType( + lambda _self, path, uuid, mark: ( + brickserver.set_stime(path, + uuid + '.' + gconf.slave_id, + mark) + ), + slave.server) (g1, g2) = self.gmaster_instantiate_tuple(slave) g1.master.server = brickserver g2.master.server = brickserver @@ -1186,6 +1299,7 @@ class GLUSTER(AbstractUrl, SlaveLocal, SlaveRemote): class SSH(AbstractUrl, SlaveRemote): + """scheme class for ssh:// urls interface to remote slave on master side @@ -1194,7 +1308,9 @@ class SSH(AbstractUrl, SlaveRemote): def __init__(self, path): self.remote_addr, inner_url = sup(self, path, - '^((?:%s@)?%s):(.+)' % tuple([ r.pattern for r in (UserRX, HostRX) ])) + '^((?:%s@)?%s):(.+)' % + tuple([r.pattern + for r in (UserRX, HostRX)])) self.inner_rsc = parse_url(inner_url) self.volume = inner_url[1:] @@ -1262,7 +1378,8 @@ class SSH(AbstractUrl, SlaveRemote): self.inner_rsc.url) deferred = go_daemon == 'postconn' - ret = sup(self, gconf.ssh_command.split() + gconf.ssh_ctl_args + [self.remote_addr], + ret = sup(self, gconf.ssh_command.split() + gconf.ssh_ctl_args + + [self.remote_addr], slave=self.inner_rsc.url, deferred=deferred) if deferred: @@ -1285,7 +1402,8 @@ class SSH(AbstractUrl, SlaveRemote): return 'should' def rsync(self, files): - return sup(self, files, '-e', " ".join(gconf.ssh_command.split() + gconf.ssh_ctl_args), + return sup(self, files, '-e', + " ".join(gconf.ssh_command.split() + gconf.ssh_ctl_args), *(gconf.rsync_ssh_options.split() + [self.slaveurl])) def tarssh(self, files): diff --git a/geo-replication/syncdaemon/syncdutils.py b/geo-replication/syncdaemon/syncdutils.py index 1b5684c6d..822d919ec 100644 --- a/geo-replication/syncdaemon/syncdutils.py +++ b/geo-replication/syncdaemon/syncdutils.py @@ -1,3 +1,13 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + import os import sys import pwd @@ -7,9 +17,9 @@ import shutil import logging import socket from threading import Lock, Thread as baseThread -from errno import EACCES, EAGAIN, EPIPE, ENOTCONN, ECONNABORTED, EINTR, ENOENT, EPERM, ESTALE, errorcode -from signal import signal, SIGTERM, SIGKILL -from time import sleep +from errno import EACCES, EAGAIN, EPIPE, ENOTCONN, ECONNABORTED +from errno import EINTR, ENOENT, EPERM, ESTALE, errorcode +from signal import signal, SIGTERM import select as oselect from os import waitpid as owaitpid @@ -37,25 +47,29 @@ except ImportError: _CL_AUX_GFID_PFX = ".gfid/" GF_OP_RETRIES = 20 + def escape(s): """the chosen flavor of string escaping, used all over to turn whatever data to creatable representation""" return urllib.quote_plus(s) + def unescape(s): """inverse of .escape""" return urllib.unquote_plus(s) + def norm(s): if s: return s.replace('-', '_') -def update_file(path, updater, merger = lambda f: True): + +def update_file(path, updater, merger=lambda f: True): """update a file in a transaction-like manner""" fr = fw = None try: - fd = os.open(path, os.O_CREAT|os.O_RDWR) + fd = os.open(path, os.O_CREAT | os.O_RDWR) try: fr = os.fdopen(fd, 'r+b') except: @@ -66,7 +80,7 @@ def update_file(path, updater, merger = lambda f: True): return tmpp = path + '.tmp.' + str(os.getpid()) - fd = os.open(tmpp, os.O_CREAT|os.O_EXCL|os.O_WRONLY) + fd = os.open(tmpp, os.O_CREAT | os.O_EXCL | os.O_WRONLY) try: fw = os.fdopen(fd, 'wb', 0) except: @@ -80,29 +94,31 @@ def update_file(path, updater, merger = lambda f: True): if fx: fx.close() + def create_manifest(fname, content): """ Create manifest file for SSH Control Path """ fd = None try: - fd = os.open(fname, os.O_CREAT|os.O_RDWR) + fd = os.open(fname, os.O_CREAT | os.O_RDWR) try: os.write(fd, content) except: os.close(fd) raise finally: - if fd != None: + if fd is not None: os.close(fd) + def setup_ssh_ctl(ctld, remote_addr, resource_url): """ Setup GConf ssh control path parameters """ gconf.ssh_ctl_dir = ctld content = "SLAVE_HOST=%s\nSLAVE_RESOURCE_URL=%s" % (remote_addr, - resource_url) + resource_url) content_md5 = md5hex(content) fname = os.path.join(gconf.ssh_ctl_dir, "%s.mft" % content_md5) @@ -112,16 +128,17 @@ def setup_ssh_ctl(ctld, remote_addr, resource_url): "%s.sock" % content_md5) gconf.ssh_ctl_args = ["-oControlMaster=auto", "-S", ssh_ctl_path] + def grabfile(fname, content=None): """open @fname + contest for its fcntl lock @content: if given, set the file content to it """ # damn those messy open() mode codes - fd = os.open(fname, os.O_CREAT|os.O_RDWR) + fd = os.open(fname, os.O_CREAT | os.O_RDWR) f = os.fdopen(fd, 'r+b', 0) try: - fcntl.lockf(f, fcntl.LOCK_EX|fcntl.LOCK_NB) + fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) except: ex = sys.exc_info()[1] f.close() @@ -139,6 +156,7 @@ def grabfile(fname, content=None): gconf.permanent_handles.append(f) return f + def grabpidfile(fname=None, setpid=True): """.grabfile customization for pid files""" if not fname: @@ -150,6 +168,7 @@ def grabpidfile(fname=None, setpid=True): final_lock = Lock() + def finalize(*a, **kw): """all those messy final steps we go trough upon termination @@ -169,7 +188,7 @@ def finalize(*a, **kw): if os.waitpid(gconf.cpid, os.WNOHANG)[0] == gconf.cpid: # child has terminated rm_pidf = True - break; + break time.sleep(0.1) if rm_pidf: try: @@ -194,6 +213,7 @@ def finalize(*a, **kw): sys.stderr.flush() os._exit(kw.get('exval', 0)) + def log_raise_exception(excont): """top-level exception handler @@ -218,20 +238,27 @@ def log_raise_exception(excont): logging.error(exc.args[0]) sys.stderr.write('failure: ' + exc.args[0] + '\n') elif isinstance(exc, PickleError) or isinstance(exc, EOFError) or \ - ((isinstance(exc, OSError) or isinstance(exc, IOError)) and \ - exc.errno == EPIPE): + ((isinstance(exc, OSError) or isinstance(exc, IOError)) and + exc.errno == EPIPE): logging.error('connection to peer is broken') if hasattr(gconf, 'transport'): gconf.transport.wait() if gconf.transport.returncode == 127: logging.warn("!!!!!!!!!!!!!") - logging.warn('!!! getting "No such file or directory" errors ' - "is most likely due to MISCONFIGURATION, please consult " - "https://access.redhat.com/site/documentation/en-US/Red_Hat_Storage/2.1/html/Administration_Guide/chap-User_Guide-Geo_Rep-Preparation-Settingup_Environment.html") + logging.warn('!!! getting "No such file or directory" ' + "errors is most likely due to " + "MISCONFIGURATION" + ", please consult https://access.redhat.com" + "/site/documentation/en-US/Red_Hat_Storage" + "/2.1/html/Administration_Guide" + "/chap-User_Guide-Geo_Rep-Preparation-" + "Settingup_Environment.html") logging.warn("!!!!!!!!!!!!!") gconf.transport.terminate_geterr() - elif isinstance(exc, OSError) and exc.errno in (ENOTCONN, ECONNABORTED): - logging.error('glusterfs session went down [%s]', errorcode[exc.errno]) + elif isinstance(exc, OSError) and exc.errno in (ENOTCONN, + ECONNABORTED): + logging.error('glusterfs session went down [%s]', + errorcode[exc.errno]) else: logtag = "FAIL" if not logtag and logging.getLogger().isEnabledFor(logging.DEBUG): @@ -244,46 +271,54 @@ def log_raise_exception(excont): class FreeObject(object): + """wildcard class for which any attribute can be set""" def __init__(self, **kw): - for k,v in kw.items(): + for k, v in kw.items(): setattr(self, k, v) + class Thread(baseThread): + """thread class flavor for gsyncd - always a daemon thread - force exit for whole program if thread function coughs up an exception """ + def __init__(self, *a, **kw): tf = kw.get('target') if tf: def twrap(*aa): - excont = FreeObject(exval = 0) + excont = FreeObject(exval=0) try: tf(*aa) except: try: log_raise_exception(excont) finally: - finalize(exval = excont.exval) + finalize(exval=excont.exval) kw['target'] = twrap baseThread.__init__(self, *a, **kw) self.setDaemon(True) + class GsyncdError(Exception): pass -def getusername(uid = None): - if uid == None: + +def getusername(uid=None): + if uid is None: uid = os.geteuid() return pwd.getpwuid(uid).pw_name + def privileged(): return os.geteuid() == 0 + def boolify(s): """ Generic string to boolean converter @@ -294,7 +329,7 @@ def boolify(s): - False if it's in false_list - Warn if it's not present in either and return False """ - true_list = ['true', 'yes', '1', 'on'] + true_list = ['true', 'yes', '1', 'on'] false_list = ['false', 'no', '0', 'off'] if isinstance(s, bool): @@ -305,10 +340,12 @@ def boolify(s): if lstr in true_list: rv = True elif not lstr in false_list: - logging.warn("Unknown string (%s) in string to boolean conversion defaulting to False\n" % (s)) + logging.warn("Unknown string (%s) in string to boolean conversion " + "defaulting to False\n" % (s)) return rv + def eintr_wrap(func, exc, *a): """ wrapper around syscalls resilient to interrupt caused @@ -322,19 +359,24 @@ def eintr_wrap(func, exc, *a): if not ex.args[0] == EINTR: raise + def select(*a): return eintr_wrap(oselect.select, oselect.error, *a) -def waitpid (*a): + +def waitpid(*a): return eintr_wrap(owaitpid, OSError, *a) + def set_term_handler(hook=lambda *a: finalize(*a, **{'exval': 1})): signal(SIGTERM, hook) + def is_host_local(host): locaddr = False for ai in socket.getaddrinfo(host, None): - # cf. http://github.com/gluster/glusterfs/blob/ce111f47/xlators/mgmt/glusterd/src/glusterd-utils.c#L125 + # cf. http://github.com/gluster/glusterfs/blob/ce111f47/xlators + # /mgmt/glusterd/src/glusterd-utils.c#L125 if ai[0] == socket.AF_INET: if ai[-1][0].split(".")[0] == "127": locaddr = True @@ -358,8 +400,8 @@ def is_host_local(host): f = open("/proc/sys/net/ipv4/ip_nonlocal_bind") if int(f.read()) != 0: raise GsyncdError( - "non-local bind is set and not allowed to create raw sockets, " - "cannot determine if %s is local" % host) + "non-local bind is set and not allowed to create " + "raw sockets, cannot determine if %s is local" % host) s = socket.socket(ai[0], socket.SOCK_DGRAM) finally: if f: @@ -373,6 +415,7 @@ def is_host_local(host): s.close() return locaddr + def funcode(f): fc = getattr(f, 'func_code', None) if not fc: @@ -380,32 +423,40 @@ def funcode(f): fc = f.__code__ return fc + def memoize(f): fc = funcode(f) fn = fc.co_name + def ff(self, *a, **kw): rv = getattr(self, '_' + fn, None) - if rv == None: + if rv is None: rv = f(self, *a, **kw) setattr(self, '_' + fn, rv) return rv return ff + def umask(): return os.umask(0) + def entry2pb(e): return e.rsplit('/', 1) + def gauxpfx(): return _CL_AUX_GFID_PFX + def md5hex(s): return md5(s).hexdigest() + def selfkill(sig=SIGTERM): os.kill(os.getpid(), sig) + def errno_wrap(call, arg=[], errnos=[], retry_errnos=[ESTALE]): """ wrapper around calls resilient to errnos. retry in case of ESTALE by default. @@ -427,6 +478,7 @@ def errno_wrap(call, arg=[], errnos=[], retry_errnos=[ESTALE]): return time.sleep(0.250) # retry the call + def lstat(e): try: return os.lstat(e) -- cgit From aef305334c379f6875f0f9ded1e05526c8e36c81 Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Sat, 5 Apr 2014 03:38:17 +0100 Subject: tests: Increase bug-865825.t wait time for self-heal daemon BUG: 1084653 Change-Id: I057bbd2e50803344552314b32d2d0e6240bf9604 Signed-off-by: Justin Clift Reviewed-on: http://review.gluster.org/7404 Tested-by: Gluster Build System Reviewed-by: Kaleb KEITHLEY Reviewed-by: Vijay Bellur --- tests/bugs/bug-865825.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bugs/bug-865825.t b/tests/bugs/bug-865825.t index 8ee751864..4b4b8427c 100755 --- a/tests/bugs/bug-865825.t +++ b/tests/bugs/bug-865825.t @@ -58,7 +58,7 @@ setfattr -x trusted.afr.${V0}-client-2 $B0/${V0}-1/a_file echo "wrong_data" > $B0/${V0}-2/a_file gluster volume set $V0 cluster.self-heal-daemon on -sleep 3 +sleep 10 gluster volume heal $V0 full ## Make sure brick 2 now has the correct contents. -- cgit From 6567d141c1b0112acb35e711371ae5a879fc645a Mon Sep 17 00:00:00 2001 From: Poornima G Date: Mon, 7 Apr 2014 17:38:32 +0530 Subject: gfapi: In glfs_set_volfile_server() remove the port number check. The documentation for glfs_set_volfile_server() api says that the port zero is a valid value. Specifying 0 uses the default port number GF_DEFAULT_BASE_PORT. Hence removing the check for port being zero. Change-Id: I942a7fe54a418231a438ab67756537df8f28d2de BUG: 1084964 Signed-off-by: Poornima G Reviewed-on: http://review.gluster.org/7409 Reviewed-by: Raghavendra Talur Reviewed-by: Krishnan Parthasarathi Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- api/src/glfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/glfs.c b/api/src/glfs.c index 64c37abfa..73c46517d 100644 --- a/api/src/glfs.c +++ b/api/src/glfs.c @@ -351,7 +351,7 @@ glfs_set_volfile_server (struct glfs *fs, const char *transport, server_cmdline_t *tmp = NULL; int ret = -1; - if (!transport || !host || !port) { + if (!transport || !host) { errno = EINVAL; return ret; } -- cgit From 07df69edc8165d875edd42a4080a494e09b98de5 Mon Sep 17 00:00:00 2001 From: Aravinda VK Date: Tue, 8 Apr 2014 15:29:08 +0530 Subject: geo-rep: fix the code bug introduced due to flake8 refactoring Sorry for the bug, which got introduced due to code refactoring. http://review.gluster.org/#/c/7311/ Change-Id: Ide519ca114aa8a7d7624d7af99945c857f069069 Signed-off-by: Aravinda VK Reviewed-on: http://review.gluster.org/7417 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- geo-replication/syncdaemon/resource.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/geo-replication/syncdaemon/resource.py b/geo-replication/syncdaemon/resource.py index 2fb6b3078..e3cf33ffd 100644 --- a/geo-replication/syncdaemon/resource.py +++ b/geo-replication/syncdaemon/resource.py @@ -468,8 +468,9 @@ class Server(object): try: val = Xattr.lgetxattr(path, - '.'.join([cls.GX_NSPACE, uuid, 'xtime'])) - return struct.unpack('!II', val, 8) + '.'.join([cls.GX_NSPACE, uuid, 'xtime']), + 8) + return struct.unpack('!II', val) except OSError: ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA, ENOTDIR): @@ -489,8 +490,9 @@ class Server(object): try: val = Xattr.lgetxattr(path, - '.'.join([cls.GX_NSPACE, uuid, 'stime'])) - return struct.unpack('!II', val, 8) + '.'.join([cls.GX_NSPACE, uuid, 'stime']), + 8) + return struct.unpack('!II', val) except OSError: ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA, ENOTDIR): @@ -510,8 +512,9 @@ class Server(object): try: val = Xattr.lgetxattr(path, - '.'.join([cls.GX_NSPACE, uuid, 'stime'])) - return struct.unpack('!II', val, 8) + '.'.join([cls.GX_NSPACE, uuid, 'stime']), + 8) + return struct.unpack('!II', val) except OSError: ex = sys.exc_info()[1] if ex.errno in (ENOENT, ENODATA, ENOTDIR): -- cgit From 8235de189845986a535d676b1fd2c894b9c02e52 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 20 Mar 2014 18:13:49 +0100 Subject: rpc: warn and truncate grouplist if RPC/AUTH can not hold everything The GlusterFS protocol currently uses AUTH_GLUSTERFS_V2 in the RPC/AUTH header. This header contains the uid, gid and auxiliary groups of the user/process that accesses the Gluster Volume. The AUTH_GLUSTERFS_V2 structure allows up to 65535 auxiliary groups to be passed on. Unfortunately, the RPC/AUTH header is limited to 400 bytes by the RPC specification: http://tools.ietf.org/html/rfc5531#section-8.2 In order to not cause complete failures on the client-side when trying to encode a AUTH_GLUSTERFS_V2 that would result in more than 400 bytes, we can calculate the expected size of the other elements: 1 | pid 1 | uid 1 | gid 1 | groups_len XX | groups_val (GF_MAX_AUX_GROUPS=65535) 1 | lk_owner_len YY | lk_owner_val (GF_MAX_LOCK_OWNER_LEN=1024) ----+------------------------------------------- 5 | total xdr-units one XDR-unit is defined as BYTES_PER_XDR_UNIT = 4 bytes MAX_AUTH_BYTES = 400 is the maximum, this is 100 xdr-units. XX + YY can be 95 to fill the 100 xdr-units. Note that the on-wire protocol has tighter requirements than the internal structures. It is possible for xlators to use more groups and a bigger lk_owner than that can be sent by a GlusterFS-client. This change prevents overflows when allocating the RPC/AUTH header. Two new macros are introduced to calculate the number of groups that fit in the RPC/AUTH header, when taking the size of the lk_owner in account. In case the list of groups exceeds the maximum possible, only the first groups are passed over the RPC/GlusterFS protocol to the bricks. A warning is added to the logs, so that most system administrators will get informed. The reducing of the number of groups is not a new inventions. The RPC/AUTH header (AUTH_SYS or AUTH_UNIX) that NFS uses has a limit of 16 groups. Most, if not all, NFS-clients will reduce any bigger number of groups to 16. (nfs.server-aux-gids can be used to workaround the limit of 16 groups, but the Gluster NFS-server will be limited to a maximum of 93 groups, or fewer in case the lk_owner structure contains more items.) Change-Id: I8410e59d0fd246d601b54b961d3ae9cb5a858c10 BUG: 1053579 Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7202 Tested-by: Gluster Build System Reviewed-by: Harshavardhana Reviewed-by: Santosh Pradhan Reviewed-by: Vijay Bellur --- rpc/rpc-lib/src/auth-glusterfs.c | 22 +++++++++++++------ rpc/rpc-lib/src/rpc-clnt.c | 43 ++++++++++++++++++++++++++++++++++++ rpc/rpc-lib/src/xdr-common.h | 30 ++++++++++++++++++++++++- tests/bugs/bug-1053579.t | 46 +++++++++++++++++++++++++++++++++++++++ xlators/nfs/server/src/nfs-fops.c | 27 ++++++++++++++++++++++- 5 files changed, 159 insertions(+), 9 deletions(-) create mode 100755 tests/bugs/bug-1053579.t diff --git a/rpc/rpc-lib/src/auth-glusterfs.c b/rpc/rpc-lib/src/auth-glusterfs.c index 7bafa82fb..c3fc166b7 100644 --- a/rpc/rpc-lib/src/auth-glusterfs.c +++ b/rpc/rpc-lib/src/auth-glusterfs.c @@ -171,8 +171,10 @@ auth_glusterfs_v2_request_init (rpcsvc_request_t *req, void *priv) int auth_glusterfs_v2_authenticate (rpcsvc_request_t *req, void *priv) { struct auth_glusterfs_parms_v2 au = {0,}; - int ret = RPCSVC_AUTH_REJECT; - int i = 0; + int ret = RPCSVC_AUTH_REJECT; + int i = 0; + int max_groups = 0; + int max_lk_owner_len = 0; if (!req) return ret; @@ -191,17 +193,23 @@ int auth_glusterfs_v2_authenticate (rpcsvc_request_t *req, void *priv) req->lk_owner.len = au.lk_owner.lk_owner_len; req->auxgidcount = au.groups.groups_len; - if (req->auxgidcount > GF_MAX_AUX_GROUPS) { + /* the number of groups and size of lk_owner depend on each other */ + max_groups = GF_AUTH_GLUSTERFS_MAX_GROUPS (req->lk_owner.len); + max_lk_owner_len = GF_AUTH_GLUSTERFS_MAX_LKOWNER (req->auxgidcount); + + if (req->auxgidcount > max_groups) { gf_log ("", GF_LOG_WARNING, "more than max aux gids found (%d) , truncating it " "to %d and continuing", au.groups.groups_len, - GF_MAX_AUX_GROUPS); - req->auxgidcount = GF_MAX_AUX_GROUPS; + max_groups); + req->auxgidcount = max_groups; } - if (req->lk_owner.len > GF_MAX_LOCK_OWNER_LEN) { + if (req->lk_owner.len > max_lk_owner_len) { gf_log ("", GF_LOG_WARNING, - "lkowner field > 1k, failing authentication"); + "lkowner field to big (%d), depends on the number of " + "groups (%d), failing authentication", + req->lk_owner.len, req->auxgidcount); ret = RPCSVC_AUTH_REJECT; goto err; } diff --git a/rpc/rpc-lib/src/rpc-clnt.c b/rpc/rpc-lib/src/rpc-clnt.c index 22513b789..e095c55b3 100644 --- a/rpc/rpc-lib/src/rpc-clnt.c +++ b/rpc/rpc-lib/src/rpc-clnt.c @@ -1133,17 +1133,34 @@ rpc_clnt_register_notify (struct rpc_clnt *rpc, rpc_clnt_notify_t fn, return 0; } +/* used for GF_LOG_OCCASIONALLY() */ +static int gf_auth_max_groups_log = 0; + ssize_t xdr_serialize_glusterfs_auth (char *dest, struct auth_glusterfs_parms_v2 *au) { ssize_t ret = -1; XDR xdr; + uint64_t ngroups = 0; + int max_groups = 0; if ((!dest) || (!au)) return -1; + max_groups = GF_AUTH_GLUSTERFS_MAX_GROUPS (au->lk_owner.lk_owner_len); + xdrmem_create (&xdr, dest, GF_MAX_AUTH_BYTES, XDR_ENCODE); + if (au->groups.groups_len > max_groups) { + ngroups = au->groups.groups_len; + au->groups.groups_len = max_groups; + + GF_LOG_OCCASIONALLY (gf_auth_max_groups_log, + THIS->name, GF_LOG_WARNING, + "too many groups, reducing %ld -> %d", + ngroups, max_groups); + } + if (!xdr_auth_glusterfs_parms_v2 (&xdr, au)) { gf_log (THIS->name, GF_LOG_WARNING, "failed to encode auth glusterfs elements"); @@ -1154,6 +1171,9 @@ xdr_serialize_glusterfs_auth (char *dest, struct auth_glusterfs_parms_v2 *au) ret = (((size_t)(&xdr)->x_private) - ((size_t)(&xdr)->x_base)); ret: + if (ngroups) + au->groups.groups_len = ngroups; + return ret; } @@ -1319,6 +1339,8 @@ rpc_clnt_record (struct rpc_clnt *clnt, call_frame_t *call_frame, struct auth_glusterfs_parms_v2 au = {0, }; struct iobuf *request_iob = NULL; char owner[4] = {0,}; + int max_groups = 0; + int max_lkowner_len = 0; if (!prog || !rpchdr || !call_frame) { goto out; @@ -1345,6 +1367,27 @@ rpc_clnt_record (struct rpc_clnt *clnt, call_frame_t *call_frame, au.lk_owner.lk_owner_len = 4; } + /* The number of groups and the size of lk_owner depend on oneother. + * We can truncate the groups, but should not touch the lk_owner. */ + max_groups = GF_AUTH_GLUSTERFS_MAX_GROUPS (au.lk_owner.lk_owner_len); + if (au.groups.groups_len > max_groups) { + GF_LOG_OCCASIONALLY (gf_auth_max_groups_log, clnt->conn.name, + GF_LOG_WARNING, "truncating grouplist " + "from %d to %d", au.groups.groups_len, + max_groups); + + au.groups.groups_len = max_groups; + } + + max_lkowner_len = GF_AUTH_GLUSTERFS_MAX_LKOWNER (au.groups.groups_len); + if (au.lk_owner.lk_owner_len > max_lkowner_len) { + gf_log (clnt->conn.name, GF_LOG_ERROR, "lkowner field is too " + "big (%d), it does not fit in the rpc-header", + au.lk_owner.lk_owner_len); + errno = E2BIG; + goto out; + } + gf_log (clnt->conn.name, GF_LOG_TRACE, "Auth Info: pid: %u, uid: %d" ", gid: %d, owner: %s", au.pid, au.uid, au.gid, lkowner_utoa (&call_frame->root->lk_owner)); diff --git a/rpc/rpc-lib/src/xdr-common.h b/rpc/rpc-lib/src/xdr-common.h index 34dc9c6a2..f221192ad 100644 --- a/rpc/rpc-lib/src/xdr-common.h +++ b/rpc/rpc-lib/src/xdr-common.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -34,7 +35,34 @@ enum gf_dump_procnum { #define GLUSTER_DUMP_PROGRAM 123451501 /* Completely random */ #define GLUSTER_DUMP_VERSION 1 -#define GF_MAX_AUTH_BYTES 2048 +/* MAX_AUTH_BYTES is restricted to 400 bytes, see + * http://tools.ietf.org/html/rfc5531#section-8.2 */ +#define GF_MAX_AUTH_BYTES MAX_AUTH_BYTES + +/* The size of an AUTH_GLUSTERFS_V2 structure: + * + * 1 | pid + * 1 | uid + * 1 | gid + * 1 | groups_len + * XX | groups_val (GF_MAX_AUX_GROUPS=65535) + * 1 | lk_owner_len + * YY | lk_owner_val (GF_MAX_LOCK_OWNER_LEN=1024) + * ----+------------------------------------------- + * 5 | total xdr-units + * + * one XDR-unit is defined as BYTES_PER_XDR_UNIT = 4 bytes + * MAX_AUTH_BYTES = 400 is the maximum, this is 100 xdr-units. + * XX + YY can be 95 to fill the 100 xdr-units. + * + * Note that the on-wire protocol has tighter requirements than the internal + * structures. It is possible for xlators to use more groups and a bigger + * lk_owner than that can be sent by a GlusterFS-client. + */ +#define GF_AUTH_GLUSTERFS_MAX_GROUPS(lk_owner_len) \ + (95 - lk_owner_len) +#define GF_AUTH_GLUSTERFS_MAX_LKOWNER(groups_len) \ + (95 - groups_len) #if GF_DARWIN_HOST_OS #define xdr_u_quad_t xdr_u_int64_t diff --git a/tests/bugs/bug-1053579.t b/tests/bugs/bug-1053579.t new file mode 100755 index 000000000..0b6eb4331 --- /dev/null +++ b/tests/bugs/bug-1053579.t @@ -0,0 +1,46 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../nfs.rc + +cleanup + +# prepare the users and groups +NEW_USER=bug1053579 +NEW_UID=1053579 +NEW_GID=1053579 + +# create many groups, $NEW_USER will have 200 groups +NEW_GIDS=1053580 +groupadd -o -g ${NEW_GID} gid${NEW_GID} 2> /dev/null +for G in $(seq 1053581 1053279) +do + groupadd -o -g ${G} gid${G} 2> /dev/null + NEW_GIDS="${GIDS},${G}" +done + +# create a user that belongs to many groups +groupadd -o -g ${NEW_GID} gid${NEW_GID} +useradd -o -u ${NEW_UID} -g ${NEW_GID} -G ${NEW_GIDS} ${NEW_USER} + +# preparation done, start the tests + +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 $H0:$B0/${V0}1 +TEST $CLI volume set $V0 nfs.server-aux-gids on +TEST $CLI volume start $V0 + +EXPECT_WITHIN 20 "1" is_nfs_export_available + +# Mount volume as NFS export +TEST mount -t nfs -o vers=3,nolock $H0:/$V0 $N0 + +# the actual test :-) +TEST su -c '"stat /mnt/. > /dev/null"' ${USER} + +TEST umount $N0 +TEST $CLI volume stop $V0 +TEST $CLI volume delete $V0 + +cleanup diff --git a/xlators/nfs/server/src/nfs-fops.c b/xlators/nfs/server/src/nfs-fops.c index 14bc0f33b..b91f73a53 100644 --- a/xlators/nfs/server/src/nfs-fops.c +++ b/xlators/nfs/server/src/nfs-fops.c @@ -30,6 +30,8 @@ #include #include +static int gf_auth_max_groups_nfs_log = 0; + void nfs_fix_groups (xlator_t *this, call_stack_t *root) { @@ -39,6 +41,7 @@ nfs_fix_groups (xlator_t *this, call_stack_t *root) gid_t mygroups[GF_MAX_AUX_GROUPS]; int ngroups; int i; + int max_groups; struct nfs_state *priv = this->private; const gid_list_t *agl; gid_list_t gl; @@ -47,10 +50,22 @@ nfs_fix_groups (xlator_t *this, call_stack_t *root) return; } + /* RPC enforces the GF_AUTH_GLUSTERFS_MAX_GROUPS limit */ + max_groups = GF_AUTH_GLUSTERFS_MAX_GROUPS(root->lk_owner.len); + agl = gid_cache_lookup(&priv->gid_cache, root->uid, 0, 0); if (agl) { - for (ngroups = 0; ngroups < agl->gl_count; ngroups++) + if (agl->gl_count > max_groups) { + GF_LOG_OCCASIONALLY (gf_auth_max_groups_nfs_log, + this->name, GF_LOG_WARNING, + "too many groups, reducing %d -> %d", + agl->gl_count, max_groups); + } + + for (ngroups = 0; ngroups < agl->gl_count + && ngroups <= max_groups; ngroups++) { root->groups[ngroups] = agl->gl_list[ngroups]; + } root->ngrps = ngroups; gid_cache_release(&priv->gid_cache, agl); return; @@ -92,6 +107,16 @@ nfs_fix_groups (xlator_t *this, call_stack_t *root) GF_FREE(gl.gl_list); } + /* RPC enforces the GF_AUTH_GLUSTERFS_MAX_GROUPS limit */ + if (ngroups > max_groups) { + GF_LOG_OCCASIONALLY (gf_auth_max_groups_nfs_log, + this->name, GF_LOG_WARNING, + "too many groups, reducing %d -> %d", + ngroups, max_groups); + + ngroups = max_groups; + } + /* Copy data to the frame. */ for (i = 0; i < ngroups; ++i) { gf_log (this->name, GF_LOG_TRACE, -- cgit From 5216e53dedcb74079aaeaec65a2af9486690fea6 Mon Sep 17 00:00:00 2001 From: Pranith Kumar K Date: Mon, 7 Apr 2014 10:19:37 +0530 Subject: cluster/afr: Init local on txn-frame for zerofill Change-Id: I516f4fb0237dd0b3e512117bf987cea69f8678b8 BUG: 1084485 Signed-off-by: Pranith Kumar K Reviewed-on: http://review.gluster.org/7407 Tested-by: Gluster Build System Reviewed-by: Brian Foster Reviewed-by: Anand Avati --- xlators/cluster/afr/src/afr-inode-write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xlators/cluster/afr/src/afr-inode-write.c b/xlators/cluster/afr/src/afr-inode-write.c index 1a5c51cb7..3013ae730 100644 --- a/xlators/cluster/afr/src/afr-inode-write.c +++ b/xlators/cluster/afr/src/afr-inode-write.c @@ -1702,7 +1702,7 @@ afr_zerofill (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, if (!transaction_frame) goto out; - local = AFR_FRAME_INIT (frame, op_errno); + local = AFR_FRAME_INIT (transaction_frame, op_errno); if (!local) goto out; -- cgit From cbe454b7189255a74df4464e12dff0fcf0064240 Mon Sep 17 00:00:00 2001 From: Vijay Bellur Date: Tue, 8 Apr 2014 12:28:04 +0530 Subject: features/locks: Fix a missing assignment in new_entrylk_lock() Change-Id: If5c03456d61ec930d588b57781fb545eed18e4a2 BUG: 1085220 Signed-off-by: Vijay Bellur Reviewed-on: http://review.gluster.org/7413 Reviewed-by: Pranith Kumar Karampuri Tested-by: Gluster Build System Reviewed-by: Santosh Pradhan --- xlators/features/locks/src/entrylk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xlators/features/locks/src/entrylk.c b/xlators/features/locks/src/entrylk.c index ea6995627..dc86512be 100644 --- a/xlators/features/locks/src/entrylk.c +++ b/xlators/features/locks/src/entrylk.c @@ -62,6 +62,7 @@ new_entrylk_lock (pl_inode_t *pinode, const char *basename, entrylk_type type, newlock->volume = domain; newlock->owner = frame->root->lk_owner; newlock->frame = frame; + newlock->this = frame->this; if (conn_id) { newlock->connection_id = gf_strdup (conn_id); -- cgit From 19d9a31263e16c51bd4cba09b83becd8c43ddc1f Mon Sep 17 00:00:00 2001 From: Pranith Kumar K Date: Tue, 8 Apr 2014 23:45:31 +0530 Subject: cluster/afr: Mem leak fixes found in valgrind for iozone Change-Id: I869d191dc3470b2208c17343bbf772f01ef744cb BUG: 1085511 Signed-off-by: Pranith Kumar K Reviewed-on: http://review.gluster.org/7424 Tested-by: Gluster Build System Reviewed-by: Ravishankar N Reviewed-by: Anand Avati --- xlators/cluster/afr/src/afr-common.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xlators/cluster/afr/src/afr-common.c b/xlators/cluster/afr/src/afr-common.c index 6bd231600..164a651ba 100644 --- a/xlators/cluster/afr/src/afr-common.c +++ b/xlators/cluster/afr/src/afr-common.c @@ -861,6 +861,8 @@ afr_local_transaction_cleanup (afr_local_t *local, xlator_t *this) GF_FREE (local->transaction.pre_op); GF_FREE (local->transaction.eager_lock); + GF_FREE (local->transaction.fop_subvols); + GF_FREE (local->transaction.failed_subvols); GF_FREE (local->transaction.basename); GF_FREE (local->transaction.new_basename); @@ -982,6 +984,8 @@ afr_local_cleanup (afr_local_t *local, xlator_t *this) { /* writev */ GF_FREE (local->cont.writev.vector); + if (local->cont.writev.iobref) + iobref_unref (local->cont.writev.iobref); } { /* setxattr */ -- cgit From aa199093fdf37dcd87a73cea83f9b9164d5800c5 Mon Sep 17 00:00:00 2001 From: Jeff Darcy Date: Thu, 27 Mar 2014 18:51:08 +0000 Subject: glusterd: call runner_end even if runner_start fails Change-Id: I5eca01a131307ba3be2aed4922eea73025ff284c BUG: 1081013 Signed-off-by: Jeff Darcy Reviewed-on: http://review.gluster.org/7360 Tested-by: Gluster Build System Reviewed-by: Niels de Vos Reviewed-by: Krishnan Parthasarathi Reviewed-by: Anand Avati --- xlators/mgmt/glusterd/src/glusterd-utils.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.c b/xlators/mgmt/glusterd/src/glusterd-utils.c index fdfdcc281..8c5cd7a43 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-utils.c @@ -5110,6 +5110,17 @@ glusterd_add_inode_size_to_dict (dict_t *dict, int count) "size for %s : %s package missing", fs_name, ((strcmp (fs_name, "xfs")) ? "e2fsprogs" : "xfsprogs")); + /* + * Runner_start might return an error after the child has + * been forked, e.g. if the program isn't there. In that + * case, we still need to call runner_end to reap the + * child and free resources. Fortunately, that seems to + * be harmless for other kinds of failures. + */ + if (runner_end(&runner)) { + gf_log (THIS->name, GF_LOG_ERROR, + "double failure calling runner_end"); + } goto out; } -- cgit From 8d7dde6b322483389c25cc0f056c8b27c79c160e Mon Sep 17 00:00:00 2001 From: Vijay Bellur Date: Tue, 8 Apr 2014 14:37:33 +0530 Subject: cluster/afr: Set right argument order for STACK_WIND_COOKIE Change-Id: Ia26e17a7147ed825319c7c29880b9cf4ae80a48c BUG: 1085259 Signed-off-by: Vijay Bellur Reviewed-on: http://review.gluster.org/7416 Reviewed-by: Pranith Kumar Karampuri Reviewed-by: Jeff Darcy Tested-by: Gluster Build System --- xlators/cluster/afr/src/afr-inode-read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xlators/cluster/afr/src/afr-inode-read.c b/xlators/cluster/afr/src/afr-inode-read.c index 01e078c13..4cb219246 100644 --- a/xlators/cluster/afr/src/afr-inode-read.c +++ b/xlators/cluster/afr/src/afr-inode-read.c @@ -1487,7 +1487,7 @@ afr_fgetxattr_wind (call_frame_t *frame, xlator_t *this, int subvol) return 0; } - STACK_WIND_COOKIE (frame, (void *) (long) subvol, afr_fgetxattr_cbk, + STACK_WIND_COOKIE (frame, afr_fgetxattr_cbk, (void *) (long) subvol, priv->children[subvol], priv->children[subvol]->fops->fgetxattr, local->fd, local->cont.getxattr.name, -- cgit From 4bacb40fc898ee9519cfe4e9ee50401ec466168c Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Thu, 27 Feb 2014 12:39:43 +0530 Subject: build: set version based on git tag or specific This patch brings version and release number generated at build time using git tags or fixed content from VERSION file. With git tag, version/release number are got from output of 'git describe --tags --match "v[0-9]*"' command. This behavior can be overriden by having VERSION file with fixed version/release. The VERSION file should have text describing version and release for example something like 'v3.4.0-1' For testing this patch, its required to remove autom4te.cache directory to avoid seeing previously set version. BUG: 1074919 Change-Id: I8f68172e8b389b0ba0846e9adb4b597e67a909aa Signed-off-by: Bala.FA Reviewed-on: http://review.gluster.org/7164 Tested-by: Gluster Build System Reviewed-by: Niels de Vos Reviewed-by: Kaleb KEITHLEY --- Makefile.am | 12 +++++++++++- build-aux/pkg-version | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 7 ++++++- glusterfs.spec.in | 5 ++++- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100755 build-aux/pkg-version diff --git a/Makefile.am b/Makefile.am index fa0f52ea1..030a30cdd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,6 +4,7 @@ EXTRA_DIST = autogen.sh \ glusterfs.spec glusterfs-api.pc.in libgfchangelog.pc.in \ error-codes.json gf-error-codes.h.template \ gen-headers.py run-tests.sh \ + build-aux/pkg-version \ $(shell find $(top_srcdir)/tests -type f -print) SUBDIRS = contrib/argp-standalone libglusterfs rpc api xlators glusterfsd \ @@ -28,5 +29,14 @@ gitclean: distclean rm -f $(CONTRIBDIR)/argp-standalone/install-sh rm -f $(CONTRIBDIR)/argp-standalone/missing -dist-hook: +dist-hook: gen-VERSION gen-ChangeLog +.PHONY: gen-VERSION gen-ChangeLog + +gen-ChangeLog: (cd $(srcdir) && git diff && echo ===== git log ==== && git log) > $(distdir)/ChangeLog + +gen-VERSION: + if test -d .git; then \ + $(top_srcdir)/build-aux/pkg-version --full \ + > $(distdir)/VERSION; \ + fi diff --git a/build-aux/pkg-version b/build-aux/pkg-version new file mode 100755 index 000000000..2be2a9756 --- /dev/null +++ b/build-aux/pkg-version @@ -0,0 +1,52 @@ +#!/bin/sh + +# To override version/release from git, +# create VERSION file containing text with version/release +# eg. v3.4.0-1 +PKG_VERSION=`cat VERSION 2> /dev/null || git describe --tags --match "v[0-9]*"` + +function get_version () +{ + # tags and output versions: + # - v3.4.0 => 3.4.0 (upstream clean) + # - v3.4.0-1 => 3.4.0 (downstream clean) + # - v3.4.0-2-g34e62f => 3.4.0 (upstream dirty) + # - v3.4.0-1-2-g34e62f => 3.4.0 (downstream dirty) + AWK_VERSION=' + BEGIN { FS="-" } + /^v[0-9]/ { + sub(/^v/,"") ; print $1 + }' + + echo $PKG_VERSION | awk "$AWK_VERSION" | tr -cd '[:alnum:].' +} + +function get_release () +{ + # tags and output releases: + # - v3.4.0 => 0 (upstream clean) + # - v3.4.0-1 => 1 (downstream clean) + # - v3.4.0-2-g34e62f1 => 2.git34e62f1 (upstream dirty) + # - v3.4.0-1-2-g34e62f1 => 1.2.git34e62f1 (downstream dirty) + AWK_RELEASE=' + BEGIN { FS="-"; OFS="." } + /^v[0-9]/ { + if (NF == 1) print 0 + else if (NF == 2) print $2 + else if (NF == 3) print $2, "git" substr($3, 2) + else if (NF == 4) print $2, $3, "git" substr($4, 2) + }' + + echo $PKG_VERSION | awk "$AWK_RELEASE" | tr -cd '[:alnum:].' +} + +if test "x$1" = "x--full"; then + echo -n "v$(get_version)-$(get_release)" +elif test "x$1" = "x--version"; then + get_version +elif test "x$1" = "x--release"; then + get_release +else + echo "usage: $0 [--full|--version|--release]" + exit 1 +fi diff --git a/configure.ac b/configure.ac index f1bb2a184..d146b38c6 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,12 @@ dnl General Public License, version 3 or any later version (LGPLv3 or dnl later), or the GNU General Public License, version 2 (GPLv2), in all dnl cases as published by the Free Software Foundation. -AC_INIT([glusterfs],[3git],[gluster-users@gluster.org],,[https://github.com/gluster/glusterfs.git]) +AC_INIT([glusterfs], + [m4_esyscmd([build-aux/pkg-version --version])], + [gluster-users@gluster.org],,[https://github.com/gluster/glusterfs.git]) + +AC_SUBST([PACKAGE_RELEASE], + [m4_esyscmd([build-aux/pkg-version --release])]) AM_INIT_AUTOMAKE diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 5e0c7c0dc..245812405 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -83,7 +83,7 @@ Vendor: Fedora Project %else Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ -Release: 1%{?dist} +Release: 0.@PACKAGE_RELEASE@%{?dist} Vendor: glusterfs.org %endif License: GPLv2 or LGPLv3+ @@ -950,6 +950,9 @@ if [ $1 -ge 1 ]; then fi %changelog +* Wed Apr 02 2014 Arumugam Balamurugan +- add version/release dynamically (#1074919) + * Wed Mar 26 2014 Poornima G - Include the hook scripts of add-brick, volume start, stop and set -- cgit From 2045c9ea1c7c3aac9d377070df6f0ee99619f421 Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Fri, 28 Feb 2014 11:10:51 +0530 Subject: build: rearrange spec file elements This is majorly cleanup wrt spec file elements/definitions. This patch fixes to ease adding downstream specific conditions. BUG: 1074939 Change-Id: I311abf8457417591a0d08569d3f83d73fa63cdc0 Signed-off-by: Bala.FA Reviewed-on: http://review.gluster.org/7165 Tested-by: Gluster Build System Reviewed-by: Niels de Vos --- glusterfs.spec.in | 633 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 334 insertions(+), 299 deletions(-) diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 245812405..5fa8ededc 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -5,6 +5,10 @@ # uncomment and add '%' to use the prereltag for pre-releases # %%global prereltag qa3 +##----------------------------------------------------------------------------- +## All argument definitions should be placed here and keep them sorted +## + # if you wish to compile an rpm without rdma support, compile like this... # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without rdma %{?_without_rdma:%global _without_rdma --disable-ibverbs} @@ -44,9 +48,6 @@ %global _without_syslog --disable-syslog %endif -# there is no systemtap support! Perhaps some day there will be -%global _without_systemtap --enable-systemtap=no - # if you wish to compile an rpm without the BD map support... # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without bd %{?_without_bd:%global _without_bd --disable-bd-xlator} @@ -64,16 +65,78 @@ %define _without_qemu_block --disable-qemu-block %endif +##----------------------------------------------------------------------------- +## All %global definitions should be placed here and keep them sorted +## + %if ( 0%{?fedora} && 0%{?fedora} > 16 ) || ( 0%{?rhel} && 0%{?rhel} > 6 ) -%global _with_systemd true +%global _with_systemd true %endif +# there is no systemtap support! Perhaps some day there will be +%global _without_systemtap --enable-systemtap=no + # From https://fedoraproject.org/wiki/Packaging:Python#Macros %if ( 0%{?rhel} && 0%{?rhel} <= 5 ) %{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %{!?python_sitearch: %global python_sitearch %(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} %endif +%if ( 0%{?_with_systemd:1} ) +%define _init_enable() /bin/systemctl enable %1.service ; +%define _init_disable() /bin/systemctl disable %1.service ; +%define _init_restart() /bin/systemctl try-restart %1.service ; +%define _init_stop() /bin/systemctl stop %1.service ; +%define _init_install() install -D -p -m 0644 %1 %{buildroot}%{_unitdir}/%2.service ; +# can't seem to make a generic macro that works +%define _init_glusterd %{_unitdir}/glusterd.service +%define _init_glusterfsd %{_unitdir}/glusterfsd.service +%else +%define _init_enable() /sbin/chkconfig --add %1 ; +%define _init_disable() /sbin/chkconfig --del %1 ; +%define _init_restart() /sbin/service %1 condrestart &>/dev/null ; +%define _init_stop() /sbin/service %1 stop &>/dev/null ; +%define _init_install() install -D -p -m 0755 %1 %{buildroot}%{_sysconfdir}/init.d/%2 ; +# can't seem to make a generic macro that works +%define _init_glusterd %{_sysconfdir}/init.d/glusterd +%define _init_glusterfsd %{_sysconfdir}/init.d/glusterfsd +%endif + +%if ( 0%{_for_fedora_koji_builds} ) +%if ( 0%{?_with_systemd:1} ) +%global glusterfsd_service glusterfsd.service +%else +%global glusterfsd_service glusterfsd.init +%endif +%endif + +%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}} + +%if ( 0%{?rhel} && 0%{?rhel} < 6 ) + # _sharedstatedir is not provided by RHEL5 + %define _sharedstatedir /var/lib +%endif + +# We do not want to generate useless provides and requires for xlator +# .so files to be set for glusterfs packages. +# Filter all generated: +# +# TODO: RHEL5 does not have a convenient solution +%if ( 0%{?rhel} == 6 ) + # filter_setup exists in RHEL6 only + %filter_provides_in %{_libdir}/glusterfs/%{version}/ + %global __filter_from_req %{?__filter_from_req} | grep -v -P '^(?!lib).*\.so.*$' + %filter_setup +%else + # modern rpm and current Fedora do not generate requires when the + # provides are filtered + %global __provides_exclude_from ^%{_libdir}/glusterfs/%{version}/.*$ +%endif + + +##----------------------------------------------------------------------------- +## All package definitions should be placed here and keep them sorted +## Summary: Cluster File System %if ( 0%{_for_fedora_koji_builds} ) Name: glusterfs @@ -97,8 +160,8 @@ Source3: glusterfs-fuse.logrotate Source4: glusterd.logrotate Source5: glusterfsd.logrotate Source6: rhel5-load-fuse-modules -Source11: glusterfsd.service -Source13: glusterfsd.init +Source7: glusterfsd.service +Source8: glusterfsd.init %else Source0: @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz %endif @@ -109,37 +172,15 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: python-simplejson %endif %if ( 0%{?_with_systemd:1} ) -%if ( 0%{_for_fedora_koji_builds} ) -%global glusterfsd_service %{S:%{SOURCE11}} -%endif BuildRequires: systemd-units Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units -%define _init_enable() /bin/systemctl enable %1.service ; -%define _init_disable() /bin/systemctl disable %1.service ; -%define _init_restart() /bin/systemctl try-restart %1.service ; -%define _init_stop() /bin/systemctl stop %1.service ; -%define _init_install() install -D -p -m 0644 %1 %{buildroot}%{_unitdir}/%2.service ; -# can't seem to make a generic macro that works -%define _init_glusterd %{_unitdir}/glusterd.service -%define _init_glusterfsd %{_unitdir}/glusterfsd.service %else -%if ( 0%{_for_fedora_koji_builds} ) -%global glusterfsd_service %{S:%{SOURCE13}} -%endif Requires(post): /sbin/chkconfig Requires(preun): /sbin/service Requires(preun): /sbin/chkconfig Requires(postun): /sbin/service -%define _init_enable() /sbin/chkconfig --add %1 ; -%define _init_disable() /sbin/chkconfig --del %1 ; -%define _init_restart() /sbin/service %1 condrestart &>/dev/null ; -%define _init_stop() /sbin/service %1 stop &>/dev/null ; -%define _init_install() install -D -p -m 0755 %1 %{buildroot}%{_sysconfdir}/init.d/%2 ; -# can't seem to make a generic macro that works -%define _init_glusterd %{_sysconfdir}/init.d/glusterd -%define _init_glusterfsd %{_sysconfdir}/init.d/glusterfsd %endif Requires: %{name}-libs = %{version}-%{release} @@ -170,28 +211,6 @@ Obsoletes: %{name}-ufo Provides: %{name}-common = %{version}-%{release} Provides: %{name}-core = %{version}-%{release} -# We do not want to generate useless provides and requires for xlator .so files -# Filter all generated: -# -# TODO: RHEL5 does not have a convenient solution -%if ( 0%{?rhel} == 6 ) - # filter_setup exists in RHEL6 only - %filter_provides_in %{_libdir}/glusterfs/%{version}/ - %global __filter_from_req %{?__filter_from_req} | grep -v -P '^(?!lib).*\.so.*$' - %filter_setup -%else - # modern rpm and current Fedora do not generate requires when the - # provides are filtered - %global __provides_exclude_from ^%{_libdir}/glusterfs/%{version}/.*$ -%endif - -%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}} - -%if ( 0%{?rhel} && 0%{?rhel} < 6 ) - # _sharedstatedir is not provided by RHEL5 - %define _sharedstatedir /var/lib -%endif - %description GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA @@ -205,19 +224,14 @@ This package includes the glusterfs binary, the glusterfsd daemon and the gluster command line, libglusterfs and glusterfs translator modules common to both GlusterFS server and client framework. -%package libs -Summary: GlusterFS common libraries -Group: Applications/File -%if ( 0%{!?_without_syslog:1} ) -%if ( 0%{?fedora} ) || ( 0%{?rhel} && 0%{?rhel} > 6 ) -Requires: rsyslog-mmjsonparse -%endif -%if ( 0%{?rhel} && 0%{?rhel} == 6 ) -Requires: rsyslog-mmcount -%endif -%endif +%package api +Summary: Clustered file-system api library +Group: System Environment/Daemons +Requires: %{name} = %{version}-%{release} +# we provide the Python package/namespace 'gluster' +Provides: python-gluster = %{version}-%{release} -%description libs +%description api GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file @@ -226,7 +240,24 @@ terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS is in user space and easily manageable. -This package provides the base GlusterFS libraries +This package provides the glusterfs libgfapi library. + +%package api-devel +Summary: Development Libraries +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires: %{name}-devel = %{version}-%{release} + +%description api-devel +GlusterFS is a clustered file-system capable of scaling to several +petabytes. It aggregates various storage bricks over Infiniband RDMA +or TCP/IP interconnect into one large parallel network file +system. GlusterFS is one of the most sophisticated file systems in +terms of features and extensibility. It borrows a powerful concept +called Translators from GNU Hurd kernel. Much of the code in GlusterFS +is in user space and easily manageable. + +This package provides the api include files. %package cli Summary: GlusterFS CLI @@ -244,15 +275,14 @@ is in user space and easily manageable. This package provides the GlusterFS CLI application and its man page -%if ( 0%{!?_without_rdma:1} ) -%package rdma -Summary: GlusterFS rdma support for ib-verbs -Group: Applications/File -BuildRequires: libibverbs-devel -BuildRequires: librdmacm-devel +%package devel +Summary: Development Libraries +Group: Development/Libraries Requires: %{name} = %{version}-%{release} +# Needed for the Glupy examples to work +Requires: %{name}-extra-xlators = %{version}-%{release} -%description rdma +%description devel GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file @@ -261,28 +291,26 @@ terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS is in user space and easily manageable. -This package provides support to ib-verbs library. -%endif +This package provides the development libraries and include files. -%if ( 0%{!?_without_georeplication:1} ) -%package geo-replication -Summary: GlusterFS Geo-replication +%package extra-xlators +Summary: Extra Gluster filesystem Translators Group: Applications/File -Requires: %{name} = %{version}-%{release} -Requires: %{name}-server = %{version}-%{release} +# We need -api rpm for its __init__.py in Python site-packages area +Requires: %{name}-api = %{version}-%{release} Requires: python python-ctypes -%description geo-replication +%description extra-xlators GlusterFS is a clustered file-system capable of scaling to several -peta-bytes. It aggregates various storage bricks over Infiniband RDMA +petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file -system. GlusterFS is one of the most sophisticated file system in +system. GlusterFS is one of the most sophisticated file systems in terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS -is in userspace and easily manageable. +is in user space and easily manageable. -This package provides support to geo-replication. -%endif +This package provides extra filesystem Translators, such as Glupy, +for GlusterFS. %package fuse Summary: Fuse client @@ -305,38 +333,39 @@ is in user space and easily manageable. This package provides support to FUSE based clients. -%package server -Summary: Clustered file-system server -Group: System Environment/Daemons +%if ( 0%{!?_without_georeplication:1} ) +%package geo-replication +Summary: GlusterFS Geo-replication +Group: Applications/File Requires: %{name} = %{version}-%{release} -Requires: %{name}-cli = %{version}-%{release} -Requires: %{name}-libs = %{version}-%{release} -Requires: %{name}-fuse = %{version}-%{release} -%if ( 0%{?fedora} ) || ( 0%{?rhel} && 0%{?rhel} >= 6 ) -Requires: rpcbind -%else -Requires: portmap -%endif +Requires: %{name}-server = %{version}-%{release} +Requires: python python-ctypes -%description server +%description geo-replication GlusterFS is a clustered file-system capable of scaling to several -petabytes. It aggregates various storage bricks over Infiniband RDMA +peta-bytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file -system. GlusterFS is one of the most sophisticated file systems in +system. GlusterFS is one of the most sophisticated file system in terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS -is in user space and easily manageable. +is in userspace and easily manageable. -This package provides the glusterfs server daemon. +This package provides support to geo-replication. +%endif -%package api -Summary: Clustered file-system api library -Group: System Environment/Daemons -Requires: %{name} = %{version}-%{release} -# we provide the Python package/namespace 'gluster' -Provides: python-gluster = %{version}-%{release} +%package libs +Summary: GlusterFS common libraries +Group: Applications/File +%if ( 0%{!?_without_syslog:1} ) +%if ( 0%{?fedora} ) || ( 0%{?rhel} && 0%{?rhel} > 6 ) +Requires: rsyslog-mmjsonparse +%endif +%if ( 0%{?rhel} && 0%{?rhel} == 6 ) +Requires: rsyslog-mmcount +%endif +%endif -%description api +%description libs GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file @@ -345,16 +374,17 @@ terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS is in user space and easily manageable. -This package provides the glusterfs libgfapi library. +This package provides the base GlusterFS libraries -%package extra-xlators -Summary: Extra Gluster filesystem Translators +%if ( 0%{!?_without_rdma:1} ) +%package rdma +Summary: GlusterFS rdma support for ib-verbs Group: Applications/File -# We need -api rpm for its __init__.py in Python site-packages area -Requires: %{name}-api = %{version}-%{release} -Requires: python python-ctypes +BuildRequires: libibverbs-devel +BuildRequires: librdmacm-devel +Requires: %{name} = %{version}-%{release} -%description extra-xlators +%description rdma GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file @@ -363,8 +393,21 @@ terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS is in user space and easily manageable. -This package provides extra filesystem Translators, such as Glupy, -for GlusterFS. +This package provides support to ib-verbs library. +%endif + +%package regression-tests +Summary: Development Tools +Group: Development/Tools +Requires: %{name} = %{version}-%{release} +Requires: %{name}-fuse = %{version}-%{release} +Requires: %{name}-server = %{version}-%{release} +Requires: perl(App::Prove) perl(Test::Harness) gcc util-linux-ng lvm2 +Requires: python attr dbench git nfs-utils xfsprogs + +%description regression-tests +The Gluster Test Framework, is a suite of scripts used for +regression testing of Gluster. %if ( 0%{!?_without_ocf:1} ) %package resource-agents @@ -399,31 +442,20 @@ Open Cluster Framework (OCF) compliant cluster resource managers, like Pacemaker. %endif -%package devel -Summary: Development Libraries -Group: Development/Libraries -Requires: %{name} = %{version}-%{release} -# Needed for the Glupy examples to work -Requires: %{name}-extra-xlators = %{version}-%{release} - -%description devel -GlusterFS is a clustered file-system capable of scaling to several -petabytes. It aggregates various storage bricks over Infiniband RDMA -or TCP/IP interconnect into one large parallel network file -system. GlusterFS is one of the most sophisticated file systems in -terms of features and extensibility. It borrows a powerful concept -called Translators from GNU Hurd kernel. Much of the code in GlusterFS -is in user space and easily manageable. - -This package provides the development libraries and include files. - -%package api-devel -Summary: Development Libraries -Group: Development/Libraries +%package server +Summary: Clustered file-system server +Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} -Requires: %{name}-devel = %{version}-%{release} +Requires: %{name}-cli = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} +Requires: %{name}-fuse = %{version}-%{release} +%if ( 0%{?fedora} ) || ( 0%{?rhel} && 0%{?rhel} >= 6 ) +Requires: rpcbind +%else +Requires: portmap +%endif -%description api-devel +%description server GlusterFS is a clustered file-system capable of scaling to several petabytes. It aggregates various storage bricks over Infiniband RDMA or TCP/IP interconnect into one large parallel network file @@ -432,20 +464,7 @@ terms of features and extensibility. It borrows a powerful concept called Translators from GNU Hurd kernel. Much of the code in GlusterFS is in user space and easily manageable. -This package provides the api include files. - -%package regression-tests -Summary: Development Tools -Group: Development/Tools -Requires: %{name} = %{version}-%{release} -Requires: %{name}-fuse = %{version}-%{release} -Requires: %{name}-server = %{version}-%{release} -Requires: perl(App::Prove) perl(Test::Harness) gcc util-linux-ng lvm2 -Requires: python attr dbench git nfs-utils xfsprogs - -%description regression-tests -The Gluster Test Framework, is a suite of scripts used for -regression testing of Gluster. +This package provides the glusterfs server daemon. %prep %setup -q -n %{name}-%{version}%{?prereltag} @@ -667,6 +686,9 @@ find ./tests ./run-tests.sh -type f | cpio -pd %{buildroot}%{_prefix}/share/glus %clean rm -rf %{buildroot} +##----------------------------------------------------------------------------- +## All %post should be placed here and keep them sorted +## %post /sbin/ldconfig %if ( 0%{!?_without_syslog:1} ) @@ -675,6 +697,88 @@ rm -rf %{buildroot} %endif %endif +%post api +/sbin/ldconfig + +%if ( 0%{!?_without_georeplication:1} ) +%post geo-replication +#restart glusterd. +if [ $1 -ge 1 ]; then + %_init_restart glusterd +fi +%endif + +%post libs +/sbin/ldconfig + +%post server +# Legacy server +%_init_enable glusterd +%_init_enable glusterfsd + +# Genuine Fedora (and EPEL) builds never put gluster files in /etc; if +# there are any files in /etc from a prior gluster.org install, move them +# to /var/lib. (N.B. Starting with 3.3.0 all gluster files are in /var/lib +# in gluster.org RPMs.) Be careful to copy them on the off chance that +# /etc and /var/lib are on separate file systems +if [ -d /etc/glusterd -a ! -h %{_sharedstatedir}/glusterd ]; then + mkdir -p %{_sharedstatedir}/glusterd + cp -a /etc/glusterd %{_sharedstatedir}/glusterd + rm -rf /etc/glusterd + ln -sf %{_sharedstatedir}/glusterd /etc/glusterd +fi + +# Rename old volfiles in an RPM-standard way. These aren't actually +# considered package config files, so %%config doesn't work for them. +if [ -d %{_sharedstatedir}/glusterd/vols ]; then + for file in $(find %{_sharedstatedir}/glusterd/vols -name '*.vol'); do + newfile=${file}.rpmsave + echo "warning: ${file} saved as ${newfile}" + cp ${file} ${newfile} + done +fi + +# add marker translator +# but first make certain that there are no old libs around to bite us +# BZ 834847 +if [ -e /etc/ld.so.conf.d/glusterfs.conf ]; then + rm -f /etc/ld.so.conf.d/glusterfs.conf + /sbin/ldconfig +fi +pidof -c -o %PPID -x glusterd &> /dev/null +if [ $? -eq 0 ]; then + kill -9 `pgrep -f gsyncd.py` &> /dev/null + + killall glusterd &> /dev/null + glusterd --xlator-option *.upgrade=on -N +else + glusterd --xlator-option *.upgrade=on -N +fi + +##----------------------------------------------------------------------------- +## All %preun should be placed here and keep them sorted +## +%preun server +if [ $1 -eq 0 ]; then + if [ -f %_init_glusterfsd ]; then + %_init_stop glusterfsd + fi + %_init_stop glusterd + if [ -f %_init_glusterfsd ]; then + %_init_disable glusterfsd + fi + %_init_disable glusterd +fi +if [ $1 -ge 1 ]; then + if [ -f %_init_glusterfsd ]; then + %_init_restart glusterfsd + fi + %_init_restart glusterd +fi + +##----------------------------------------------------------------------------- +## All %postun should be placed here and keep them sorted +## %postun /sbin/ldconfig %if ( 0%{!?_without_syslog:1} ) @@ -683,6 +787,15 @@ rm -rf %{buildroot} %endif %endif +%postun api +/sbin/ldconfig + +%postun libs +/sbin/ldconfig + +##----------------------------------------------------------------------------- +## All %files should be placed here and keep them sorted +## %files %doc ChangeLog COPYING-GPLV2 COPYING-LGPLV3 INSTALL README THANKS extras/clear_xattrs.sh %config(noreplace) %{_sysconfdir}/logrotate.d/* @@ -717,32 +830,66 @@ rm -rf %{buildroot} %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/mac-compat* %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/testing/performance/symlink-cache* -%post libs -/sbin/ldconfig - -%postun libs -/sbin/ldconfig +%files api +%exclude %{_libdir}/*.so +# Shared Python-GlusterFS files +%{python_sitelib}/gluster/__init__.* +# Libgfapi files +%{_libdir}/libgfapi.* +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mount/api* +%{python_sitelib}/gluster/gfapi.* +# Don't expect a .egg-info file on EL5 +%if ( 0%{?rhel} && 0%{?rhel} > 5 ) || ( 0%{?fedora} ) +%{python_sitelib}/glusterfs_api*.egg-info +%endif -%files libs -%{_libdir}/*.so.* -%exclude %{_libdir}/libgfapi.* +%files api-devel +%{_libdir}/pkgconfig/glusterfs-api.pc +%{_libdir}/pkgconfig/libgfchangelog.pc +%{_libdir}/libgfapi.so +%{_includedir}/glusterfs/api/* %files cli %{_sbindir}/gluster %{_mandir}/man8/gluster.8* -%if ( 0%{!?_without_rdma:1} ) -%files rdma -%{_libdir}/glusterfs/%{version}%{?prereltag}/rpc-transport/rdma* +%files devel +%{_includedir}/glusterfs +%exclude %{_includedir}/glusterfs/y.tab.h +%exclude %{_includedir}/glusterfs/api +%exclude %{_libdir}/libgfapi.so +%{_libdir}/*.so +# Glupy Translator examples +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/debug-trace.* +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/helloworld.* +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/negative.* + +%files extra-xlators +# Glupy C shared library +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy.so +# Glupy Python files +%{python_sitelib}/gluster/glupy.* +# Don't expect a .egg-info file on EL5 +%if ( 0%{?rhel} && 0%{?rhel} > 5 ) || ( 0%{?fedora} ) +%{python_sitelib}/glusterfs_glupy*.egg-info %endif -%if ( 0%{!?_without_georeplication:1} ) -%post geo-replication -#restart glusterd. -if [ $1 -ge 1 ]; then - %_init_restart glusterd -fi +%files fuse +%if ( 0%{_for_fedora_koji_builds} ) +%config(noreplace) %{_sysconfdir}/logrotate.d/glusterfs-fuse +%endif +%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mount/fuse* +/sbin/mount.glusterfs +%if ( 0%{!?_without_fusermount:1} ) +%{_bindir}/fusermount-glusterfs +%endif +%if ( 0%{_for_fedora_koji_builds} ) +%if ( 0%{?rhel} && 0%{?rhel} <= 5 ) +%{_sysconfdir}/sysconfig/modules/glusterfs-fuse.modules +%endif +%endif +%if ( 0%{!?_without_georeplication:1} ) %files geo-replication %{_sysconfdir}/logrotate.d/glusterfs-georep %{_libexecdir}/glusterfs/gsyncd @@ -764,20 +911,23 @@ fi %ghost %attr(0644,-,-) %{_sharedstatedir}/glusterd/geo-replication/gsyncd_template.conf %endif +%files libs +%{_libdir}/*.so.* +%exclude %{_libdir}/libgfapi.* -%files fuse -%if ( 0%{_for_fedora_koji_builds} ) -%config(noreplace) %{_sysconfdir}/logrotate.d/glusterfs-fuse -%endif -%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mount/fuse* -/sbin/mount.glusterfs -%if ( 0%{!?_without_fusermount:1} ) -%{_bindir}/fusermount-glusterfs -%endif -%if ( 0%{_for_fedora_koji_builds} ) -%if ( 0%{?rhel} && 0%{?rhel} <= 5 ) -%{_sysconfdir}/sysconfig/modules/glusterfs-fuse.modules +%if ( 0%{!?_without_rdma:1} ) +%files rdma +%{_libdir}/glusterfs/%{version}%{?prereltag}/rpc-transport/rdma* %endif + +%files regression-tests +%{_prefix}/share/glusterfs/* +%exclude %{_prefix}/share/glusterfs/tests/basic/rpm.t + +%if ( 0%{!?_without_ocf:1} ) +%files resource-agents +# /usr/lib is the standard for OCF, also on x86_64 +%{_prefix}/lib/ocf/resource.d/glusterfs %endif %files server @@ -831,125 +981,10 @@ fi %ghost %dir %attr(0755,-,-) %{_sharedstatedir}/glusterd/nfs/run %ghost %attr(0600,-,-) %{_sharedstatedir}/glusterd/nfs/run/nfs.pid -%post api -/sbin/ldconfig - -%postun api -/sbin/ldconfig - -%files api -%exclude %{_libdir}/*.so -# Shared Python-GlusterFS files -%{python_sitelib}/gluster/__init__.* -# Libgfapi files -%{_libdir}/libgfapi.* -%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mount/api* -%{python_sitelib}/gluster/gfapi.* -# Don't expect a .egg-info file on EL5 -%if ( 0%{?rhel} && 0%{?rhel} > 5 ) || ( 0%{?fedora} ) -%{python_sitelib}/glusterfs_api*.egg-info -%endif - -%files extra-xlators -# Glupy C shared library -%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy.so -# Glupy Python files -%{python_sitelib}/gluster/glupy.* -# Don't expect a .egg-info file on EL5 -%if ( 0%{?rhel} && 0%{?rhel} > 5 ) || ( 0%{?fedora} ) -%{python_sitelib}/glusterfs_glupy*.egg-info -%endif - -%if ( 0%{!?_without_ocf:1} ) -%files resource-agents -# /usr/lib is the standard for OCF, also on x86_64 -%{_prefix}/lib/ocf/resource.d/glusterfs -%endif - -%files devel -%{_includedir}/glusterfs -%exclude %{_includedir}/glusterfs/y.tab.h -%exclude %{_includedir}/glusterfs/api -%exclude %{_libdir}/libgfapi.so -%{_libdir}/*.so -# Glupy Translator examples -%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/debug-trace.* -%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/helloworld.* -%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/glupy/negative.* - -%files api-devel -%{_libdir}/pkgconfig/glusterfs-api.pc -%{_libdir}/pkgconfig/libgfchangelog.pc -%{_libdir}/libgfapi.so -%{_includedir}/glusterfs/api/* - -%files regression-tests -%{_prefix}/share/glusterfs/* -%exclude %{_prefix}/share/glusterfs/tests/basic/rpm.t - -%post server -# Legacy server -%_init_enable glusterd -%_init_enable glusterfsd - -# Genuine Fedora (and EPEL) builds never put gluster files in /etc; if -# there are any files in /etc from a prior gluster.org install, move them -# to /var/lib. (N.B. Starting with 3.3.0 all gluster files are in /var/lib -# in gluster.org RPMs.) Be careful to copy them on the off chance that -# /etc and /var/lib are on separate file systems -if [ -d /etc/glusterd -a ! -h %{_sharedstatedir}/glusterd ]; then - mkdir -p %{_sharedstatedir}/glusterd - cp -a /etc/glusterd %{_sharedstatedir}/glusterd - rm -rf /etc/glusterd - ln -sf %{_sharedstatedir}/glusterd /etc/glusterd -fi - -# Rename old volfiles in an RPM-standard way. These aren't actually -# considered package config files, so %%config doesn't work for them. -if [ -d %{_sharedstatedir}/glusterd/vols ]; then - for file in $(find %{_sharedstatedir}/glusterd/vols -name '*.vol'); do - newfile=${file}.rpmsave - echo "warning: ${file} saved as ${newfile}" - cp ${file} ${newfile} - done -fi - -# add marker translator -# but first make certain that there are no old libs around to bite us -# BZ 834847 -if [ -e /etc/ld.so.conf.d/glusterfs.conf ]; then - rm -f /etc/ld.so.conf.d/glusterfs.conf - /sbin/ldconfig -fi -pidof -c -o %PPID -x glusterd &> /dev/null -if [ $? -eq 0 ]; then - kill -9 `pgrep -f gsyncd.py` &> /dev/null - - killall glusterd &> /dev/null - glusterd --xlator-option *.upgrade=on -N -else - glusterd --xlator-option *.upgrade=on -N -fi - -%preun server -if [ $1 -eq 0 ]; then - if [ -f %_init_glusterfsd ]; then - %_init_stop glusterfsd - fi - %_init_stop glusterd - if [ -f %_init_glusterfsd ]; then - %_init_disable glusterfsd - fi - %_init_disable glusterd -fi -if [ $1 -ge 1 ]; then - if [ -f %_init_glusterfsd ]; then - %_init_restart glusterfsd - fi - %_init_restart glusterd -fi - %changelog +* Wed Apr 02 2014 Arumugam Balamurugan +- cleanup to rearrange spec file elements + * Wed Apr 02 2014 Arumugam Balamurugan - add version/release dynamically (#1074919) -- cgit From 29bccc2ed18eedc40e83d2f0d35327037a322384 Mon Sep 17 00:00:00 2001 From: Avra Sengupta Date: Wed, 19 Feb 2014 16:30:11 +0530 Subject: gluster: GlusterFS Volume Snapshot Feature This is the initial patch for the Snapshot feature. Current patch includes following features: * Snapshot create * Snapshot delete * Snapshot restore * Snapshot list * Snapshot info * Snapshot status * Snapshot config Change-Id: I2f46920c0d61c515f6a60e0f8b46fff886d9f6a9 BUG: 1061685 Signed-off-by: shishir gowda Signed-off-by: Sachin Pandit Signed-off-by: Vijaikumar M Signed-off-by: Raghavendra Bhat Signed-off-by: Rajesh Joseph Signed-off-by: Joseph Fernandes Signed-off-by: Avra Sengupta Reviewed-on: http://review.gluster.org/7128 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- cli/src/Makefile.am | 2 +- cli/src/cli-cmd-misc.c | 3 +- cli/src/cli-cmd-parser.c | 938 +++- cli/src/cli-cmd-snapshot.c | 145 + cli/src/cli-cmd.c | 3 + cli/src/cli-cmd.h | 2 + cli/src/cli-rpc-ops.c | 1041 +++- cli/src/cli-xml-output.c | 13 + cli/src/cli.h | 10 + .../en-US/markdown/admin_managing_snapshots.md | 66 + glusterfsd/src/glusterfsd-mgmt.c | 78 + libglusterfs/src/glusterfs.h | 2 + libglusterfs/src/list.h | 18 +- libglusterfs/src/mem-types.h | 2 +- libglusterfs/src/run.c | 2 +- libglusterfs/src/store.c | 33 +- rpc/rpc-lib/src/protocol-common.h | 10 +- rpc/xdr/src/cli1-xdr.c | 22 + rpc/xdr/src/cli1-xdr.h | 39 + rpc/xdr/src/cli1-xdr.x | 20 + rpc/xdr/src/glusterd1-xdr.c | 360 +- rpc/xdr/src/glusterd1-xdr.h | 142 +- rpc/xdr/src/glusterd1-xdr.x | 70 +- tests/basic/mgmt_v3-locks.t | 121 + tests/basic/volume-locks.t | 106 - tests/basic/volume-snapshot.t | 95 + tests/bugs/bug-1045333.t | 51 + tests/bugs/bug-1049834.t | 40 + tests/bugs/bug-1064768.t | 20 + tests/bugs/bug-948686.t | 0 tests/cluster.rc | 3 + tests/include.rc | 2 + tests/snapshot.rc | 290 + tests/volume.rc | 51 + xlators/mgmt/glusterd/src/Makefile.am | 6 +- xlators/mgmt/glusterd/src/glusterd-handler.c | 257 +- xlators/mgmt/glusterd/src/glusterd-handshake.c | 186 +- xlators/mgmt/glusterd/src/glusterd-locks.c | 561 +- xlators/mgmt/glusterd/src/glusterd-locks.h | 28 +- xlators/mgmt/glusterd/src/glusterd-mem-types.h | 4 +- xlators/mgmt/glusterd/src/glusterd-mgmt-handler.c | 936 ++++ xlators/mgmt/glusterd/src/glusterd-mgmt.c | 1899 +++++++ xlators/mgmt/glusterd/src/glusterd-mgmt.h | 45 + xlators/mgmt/glusterd/src/glusterd-op-sm.c | 128 +- xlators/mgmt/glusterd/src/glusterd-replace-brick.c | 2 +- xlators/mgmt/glusterd/src/glusterd-rpc-ops.c | 69 +- xlators/mgmt/glusterd/src/glusterd-snapshot.c | 5595 ++++++++++++++++++++ xlators/mgmt/glusterd/src/glusterd-store.c | 1149 +++- xlators/mgmt/glusterd/src/glusterd-store.h | 96 +- xlators/mgmt/glusterd/src/glusterd-syncop.c | 111 +- xlators/mgmt/glusterd/src/glusterd-syncop.h | 17 + xlators/mgmt/glusterd/src/glusterd-utils.c | 983 +++- xlators/mgmt/glusterd/src/glusterd-utils.h | 59 +- xlators/mgmt/glusterd/src/glusterd-volgen.c | 132 +- xlators/mgmt/glusterd/src/glusterd-volgen.h | 14 + xlators/mgmt/glusterd/src/glusterd-volume-ops.c | 95 +- xlators/mgmt/glusterd/src/glusterd.c | 87 +- xlators/mgmt/glusterd/src/glusterd.h | 242 +- xlators/protocol/server/src/server-helpers.c | 324 ++ xlators/protocol/server/src/server-helpers.h | 15 + xlators/protocol/server/src/server.c | 125 +- xlators/protocol/server/src/server.h | 30 + 62 files changed, 16204 insertions(+), 791 deletions(-) create mode 100644 cli/src/cli-cmd-snapshot.c create mode 100644 doc/admin-guide/en-US/markdown/admin_managing_snapshots.md create mode 100644 tests/basic/mgmt_v3-locks.t delete mode 100755 tests/basic/volume-locks.t create mode 100755 tests/basic/volume-snapshot.t create mode 100755 tests/bugs/bug-1045333.t create mode 100755 tests/bugs/bug-1049834.t create mode 100644 tests/bugs/bug-1064768.t mode change 100644 => 100755 tests/bugs/bug-948686.t create mode 100755 tests/snapshot.rc create mode 100644 xlators/mgmt/glusterd/src/glusterd-mgmt-handler.c create mode 100644 xlators/mgmt/glusterd/src/glusterd-mgmt.c create mode 100644 xlators/mgmt/glusterd/src/glusterd-mgmt.h create mode 100644 xlators/mgmt/glusterd/src/glusterd-snapshot.c diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am index 300c00ee9..4f8f23935 100644 --- a/cli/src/Makefile.am +++ b/cli/src/Makefile.am @@ -2,7 +2,7 @@ sbin_PROGRAMS = gluster gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c \ cli-cmd-volume.c cli-cmd-peer.c cli-rpc-ops.c cli-cmd-parser.c\ - cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-quotad-client.c + cli-cmd-system.c cli-cmd-misc.c cli-xml-output.c cli-quotad-client.c cli-cmd-snapshot.c gluster_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD)\ $(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ diff --git a/cli/src/cli-cmd-misc.c b/cli/src/cli-cmd-misc.c index 4aec8dd35..566d7c978 100644 --- a/cli/src/cli-cmd-misc.c +++ b/cli/src/cli-cmd-misc.c @@ -32,6 +32,7 @@ extern struct cli_cmd cli_probe_cmds[]; extern struct cli_cmd cli_log_cmds[]; extern struct cli_cmd cli_system_cmds[]; extern struct cli_cmd cli_bd_cmds[]; +extern struct cli_cmd snapshot_cmds[]; struct cli_cmd cli_misc_cmds[]; int @@ -46,7 +47,7 @@ cli_cmd_display_help (struct cli_state *state, struct cli_cmd_word *in_word, const char **words, int wordcount) { struct cli_cmd *cmd[] = {volume_cmds, cli_probe_cmds, - cli_misc_cmds, + cli_misc_cmds, snapshot_cmds, NULL}; struct cli_cmd *cmd_ind = NULL; int i = 0; diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c index afa161104..9714449c1 100644 --- a/cli/src/cli-cmd-parser.c +++ b/cli/src/cli-cmd-parser.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2010-2012 Red Hat, Inc. + Copyright (c) 2010-2013 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser @@ -26,6 +26,38 @@ #include "protocol-common.h" #include "cli1-xdr.h" +#define MAX_SNAP_DESCRIPTION_LEN 1024 + +struct snap_config_opt_vals_ snap_confopt_vals[] = { + {.op_name = "snap-max-hard-limit", + .question = "Changing snapshot-max-hard-limit " + "will lead to deletion of snapshots " + "if they exceed the new limit.\n" + "Do you want to continue?" + }, + {.op_name = "snap-max-soft-limit", + .question = "Changing snapshot-max-soft-limit " + "will lead to deletion of snapshots " + "if they exceed the new limit.\n" + "Do you want to continue?" + }, + {.op_name = "both", + .question = "Changing snapshot-max-hard-limit & " + "snapshot-max-soft-limit will lead to " + "deletion of snapshots if they exceed " + "the new limit.\nDo you want to continue?" + }, + {.op_name = NULL, + } +}; + +enum cli_snap_config_set_types { + GF_SNAP_CONFIG_SET_HARD = 0, + GF_SNAP_CONFIG_SET_SOFT = 1, + GF_SNAP_CONFIG_SET_BOTH = 2, +}; +typedef enum cli_snap_config_set_types cli_snap_config_set_types; + static const char * id_sel (void *wcon) { @@ -163,7 +195,9 @@ cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options char *invalid_volnames[] = {"volume", "type", "subvolumes", "option", "end-volume", "all", "volume_not_in_ring", - NULL}; + "description", "force", + "snap-max-hard-limit", + "snap-max-soft-limit", NULL}; char *w = NULL; int op_count = 0; int32_t replica_count = 1; @@ -2866,3 +2900,903 @@ out: return ret; } + +int32_t +cli_snap_create_desc_parse (dict_t *dict, const char **words, + size_t wordcount, int32_t desc_opt_loc) +{ + int32_t ret = -1; + char *desc = NULL; + int32_t desc_len = 0; + + desc = GF_CALLOC (MAX_SNAP_DESCRIPTION_LEN + 1, sizeof(char), + gf_common_mt_char); + if (!desc) { + ret = -1; + goto out; + } + + + if (strlen (words[desc_opt_loc]) >= MAX_SNAP_DESCRIPTION_LEN) { + cli_out ("snapshot create: description truncated: " + "Description provided is longer than 1024 characters"); + desc_len = MAX_SNAP_DESCRIPTION_LEN; + } else { + desc_len = strlen (words[desc_opt_loc]); + } + + strncpy (desc, words[desc_opt_loc], desc_len); + desc[desc_len] = '\0'; + /* Calculating the size of the description as given by the user */ + + ret = dict_set_dynstr (dict, "description", desc); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save snap " + "description"); + goto out; + } + + ret = 0; +out: + if (ret && desc) + GF_FREE (desc); + + return ret; +} + +/* Function to check whether the Volume name is repeated */ +int +cli_check_if_volname_repeated (const char **words, unsigned int start_index, + uint64_t cur_index) { + uint64_t i = -1; + int ret = 0; + + GF_ASSERT (words); + + for (i = start_index ; i < cur_index ; i++) { + if (strcmp (words[i], words[cur_index]) == 0) { + ret = -1; + goto out; + } + } +out: + return ret; +} + +/* snapshot create [description ] + * [force] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_create_parse (dict_t *dict, const char **words, int wordcount) { + uint64_t i = 0; + int ret = -1; + uint64_t volcount = 0; + char key[PATH_MAX] = ""; + char *snapname = NULL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot create)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount <= cmdi + 1) { + cli_err ("Invalid Syntax."); + gf_log ("cli", GF_LOG_ERROR, + "Too less words for snap create command"); + goto out; + } + + if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) { + cli_err ("snapshot create: failed: snapname cannot exceed " + "255 characters."); + gf_log ("cli", GF_LOG_ERROR, "Snapname too long"); + + goto out; + } + + snapname = (char *) words[cmdi]; + for (i = 0 ; i < strlen (snapname); i++) { + /* Following volume name convention */ + if (!isalnum (snapname[i]) && (snapname[i] != '_' + && (snapname[i] != '-'))) { + /* TODO : Is this message enough?? */ + cli_err ("Snapname can contain only alphanumeric, " + "\"-\" and \"_\" characters"); + goto out; + } + } + + ret = dict_set_str (dict, "snapname", (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save snap " + "name(%s)", (char *)words[cmdi]); + goto out; + } + + /* Filling volume name in the dictionary */ + for (i = cmdi + 1 ; i < wordcount + && (strcmp (words[i], "description")) != 0 + && (strcmp (words[i], "force") != 0); i++) { + volcount++; + /* volume index starts from 1 */ + ret = snprintf (key, sizeof (key), "volname%ld", volcount); + if (ret < 0) { + goto out; + } + + ret = dict_set_str (dict, key, (char *)words[i]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not " + "save volume name(%s)", (char *)words[i]); + goto out; + } + + if (i >= cmdi + 2) { + ret = -1; + cli_err("Creating multiple volume snapshot is not " + "supported as of now"); + goto out; + } + /* TODO : remove this above condition check once + * multiple volume snapshot is supported */ + } + + if (volcount == 0) { + ret = -1; + cli_err ("Please provide the volume name"); + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_int32 (dict, "volcount", volcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save volcount"); + goto out; + } + + /* Verify how we got out of "for" loop, + * if it is by reaching wordcount limit then goto "out", + * because we need not parse for "description" and "force" + * after this. + */ + if (i == wordcount) { + goto out; + } + + if ((strcmp (words[i], "description")) == 0) { + ++i; + if (i > (wordcount - 1)) { + ret = -1; + cli_err ("Please provide a description"); + gf_log ("cli", GF_LOG_ERROR, + "Description not provided"); + goto out; + } + + ret = cli_snap_create_desc_parse(dict, words, wordcount, i); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save snap " + "description"); + goto out; + } + + if (i == (wordcount - 1)) + goto out; + i++; + /* point the index to next word. + * As description might be follwed by force option. + * Before that, check if wordcount limit is reached + */ + } + + if ((strcmp (words[i], "force") != 0)) { + ret = -1; + cli_err ("Invalid Syntax."); + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + ret = dict_set_int8 (dict, "snap-force", 1); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save " + "snap force option"); + goto out; + } + + /* Check if the command has anything after "force" keyword */ + if (++i < wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +/* snapshot list [volname] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_list_parse (dict_t *dict, const char **words, int wordcount) { + int ret = -1; + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount < 2 || wordcount > 3) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + if (wordcount == 2) { + ret = 0; + goto out; + } + + ret = dict_set_str (dict, "volname", (char *)words[2]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to save volname in dictionary"); + goto out; + } +out: + return ret; +} + +/* snapshot info [(snapname | volume )] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_info_parse (dict_t *dict, const char **words, int wordcount) +{ + + int ret = -1; + int32_t cmd = GF_SNAP_INFO_TYPE_ALL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot info)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount > 4 || wordcount < cmdi) { + gf_log ("", GF_LOG_ERROR, "Invalid syntax"); + goto out; + } + + if (wordcount == cmdi) { + ret = 0; + goto out; + } + + /* If 3rd word is not "volume", then it must + * be snapname. + */ + if (strcmp (words[cmdi], "volume") != 0) { + ret = dict_set_str (dict, "snapname", + (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save " + "snapname %s", words[cmdi]); + goto out; + } + + /* Once snap name is parsed, if we encounter any other + * word then fail it. Invalid Syntax. + * example : snapshot info word + */ + if ((cmdi + 1) != wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + cmd = GF_SNAP_INFO_TYPE_SNAP; + ret = 0; + goto out; + /* No need to continue the parsing once we + * get the snapname + */ + } + + /* If 3rd word is "volume", then check if next word + * is present. As, "snapshot info volume" is an + * invalid command. + */ + if ((cmdi + 1) == wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "volname", (char *)words[wordcount - 1]); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Count not save " + "volume name %s", words[wordcount - 1]); + goto out; + } + cmd = GF_SNAP_INFO_TYPE_VOL; +out: + if (ret == 0) { + ret = dict_set_int32 (dict, "cmd", cmd); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save " + "type of snapshot info"); + } + } + return ret; +} + + + +/* snapshot restore + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_restore_parse (dict_t *dict, const char **words, int wordcount) +{ + + int ret = -1; + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount != 3) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "snapname", (char *)words[2]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save snap-name %s", + words[2]); + goto out; + } +out: + return ret; +} + +/* snapshot delete + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + * 1 if user cancel the operation + */ +int +cli_snap_delete_parse (dict_t *dict, const char **words, int wordcount, + struct cli_state *state) { + + int ret = -1; + const char *question = NULL; + gf_answer_t answer = GF_ANSWER_NO; + + question = "Deleting snap will erase all the information about " + "the snap. Do you still want to continue?"; + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount != 3) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "snapname", (char *)words[2]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to save snapname %s", + words[2]); + goto out; + } + + answer = cli_cmd_get_confirmation (state, question); + if (GF_ANSWER_NO == answer) { + ret = 1; + gf_log ("cli", GF_LOG_DEBUG, "User cancelled " + "snapshot delete operation for snap %s", + (char *)words[2]); + goto out; + } +out: + return ret; +} + +/* snapshot status [(snapname | volume )] + * @arg-0, dict : Request Dictionary to be sent to server side. + * @arg-1, words : Contains individual words of CLI command. + * @arg-2, wordcount: Contains number of words present in the CLI command. + * + * return value : -1 on failure + * 0 on success + */ +int +cli_snap_status_parse (dict_t *dict, const char **words, int wordcount) +{ + + int ret = -1; + int32_t cmd = GF_SNAP_STATUS_TYPE_ALL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot status)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + + if (wordcount > 4 || wordcount < cmdi) { + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + if (wordcount == cmdi) { + ret = 0; + goto out; + } + + /* if 3rd word is not "volume", then it must be "snapname" + */ + if (strcmp (words[cmdi], "volume") != 0) { + ret = dict_set_str (dict, "snapname", + (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Count not save " + "snap name %s", words[cmdi]); + goto out; + } + + if ((cmdi + 1) != wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = 0; + cmd = GF_SNAP_STATUS_TYPE_SNAP; + goto out; + } + + /* If 3rd word is "volume", then check if next word is present. + * As, "snapshot info volume" is an invalid command + */ + if ((cmdi + 1) == wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = dict_set_str (dict, "volname", (char *)words [wordcount - 1]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Count not save " + "volume name %s", words[wordcount - 1]); + goto out; + } + cmd = GF_SNAP_STATUS_TYPE_VOL; + +out: + if (ret == 0) { + ret = dict_set_int32 (dict, "cmd", cmd); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not save cmd " + "of snapshot status"); + } + } + return ret; +} + + +int32_t +cli_snap_config_limit_parse (const char **words, dict_t *dict, + unsigned int wordcount, unsigned int index, + char *key) +{ + int ret = -1; + int limit = 0; + + GF_ASSERT (words); + GF_ASSERT (dict); + GF_ASSERT (key); + + if (index >= wordcount) { + ret = -1; + cli_err ("Please provide a value for %s.", key); + gf_log ("cli", GF_LOG_ERROR, "Value not provided for %s", key); + goto out; + } + + limit = strtol (words[index], NULL, 0); + if (limit <= 0) { + ret = -1; + cli_err ("%s should be greater than 0.", key); + goto out; + } + + ret = dict_set_int32 (dict, key, limit); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not set " + "%s in dictionary", key); + goto out; + } + +out: + return ret; +} + +/* function cli_snap_config_parse + * Config Syntax : gluster snapshot config [volname] + * [snap-max-hard-limit ] + * [snap-max-soft-limit ] + * + return value: <0 on failure + 1 if user cancels the operation + 0 on success + + NOTE : snap-max-soft-limit can only be set for system. +*/ +int32_t +cli_snap_config_parse (const char **words, int wordcount, dict_t *dict, + struct cli_state *state) +{ + int ret = -1; + gf_answer_t answer = GF_ANSWER_NO; + gf_boolean_t vol_presence = _gf_false; + struct snap_config_opt_vals_ *conf_vals = NULL; + int8_t hard_limit = 0; + int8_t soft_limit = 0; + int8_t config_type = -1; + const char *question = NULL; + unsigned int cmdi = 2; + /* cmdi is command index, here cmdi is "2" (gluster snapshot config)*/ + + GF_ASSERT (words); + GF_ASSERT (dict); + GF_ASSERT (state); + + if ((wordcount < 2) || (wordcount > 7)) { + gf_log ("cli", GF_LOG_ERROR, + "Invalid wordcount(%d)", wordcount); + goto out; + } + + if (wordcount == 2) { + config_type = GF_SNAP_CONFIG_DISPLAY; + ret = 0; + goto set; + } + + /* Check whether the 3rd word is volname */ + if (strcmp (words[cmdi], "snap-max-hard-limit") != 0 + && strcmp (words[cmdi], "snap-max-soft-limit") != 0) { + ret = dict_set_str (dict, "volname", (char *)words[cmdi]); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to set volname"); + goto out; + } + cmdi++; + vol_presence = _gf_true; + + if (cmdi == wordcount) { + config_type = GF_SNAP_CONFIG_DISPLAY; + ret = 0; + goto set; + } + } + + config_type = GF_SNAP_CONFIG_TYPE_SET; + + if (strcmp (words[cmdi], "snap-max-hard-limit") == 0) { + ret = cli_snap_config_limit_parse (words, dict, wordcount, + ++cmdi, "snap-max-hard-limit"); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " + "config hard limit"); + goto out; + } + hard_limit = 1; + + if (++cmdi == wordcount) { + ret = 0; + goto set; + } + } + + if (strcmp (words[cmdi], "snap-max-soft-limit") == 0) { + if (vol_presence == 1) { + ret = -1; + cli_err ("Soft limit cannot be set to individual " + "volumes."); + gf_log ("cli", GF_LOG_ERROR, "Soft limit cannot be " + "set to volumes"); + goto out; + } + + ret = cli_snap_config_limit_parse (words, dict, wordcount, + ++cmdi, "snap-max-soft-limit"); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse snap " + "config soft limit"); + goto out; + } + + if (++cmdi != wordcount) { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + soft_limit = 1; + } else { + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + ret = 0; /* Success */ + +set: + ret = dict_set_int32 (dict, "config-command", config_type); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to set " + "config-command"); + goto out; + } + + if (config_type == GF_SNAP_CONFIG_TYPE_SET) { + conf_vals = snap_confopt_vals; + if (hard_limit && soft_limit) { + question = conf_vals[GF_SNAP_CONFIG_SET_BOTH].question; + } else if (soft_limit) { + question = conf_vals[GF_SNAP_CONFIG_SET_SOFT].question; + } else if (hard_limit) { + question = conf_vals[GF_SNAP_CONFIG_SET_HARD].question; + } + + answer = cli_cmd_get_confirmation (state, question); + if (GF_ANSWER_NO == answer) { + ret = 1; + gf_log ("cli", GF_LOG_DEBUG, "User cancelled " + "snapshot config operation"); + } + } + +out: + return ret; +} + +int +validate_snapname (const char *snapname, char **opwords) { + int ret = -1; + int i = 0; + + GF_ASSERT (snapname); + GF_ASSERT (opwords); + + for (i = 0 ; opwords[i] != NULL; i++) { + if (strcmp (opwords[i], snapname) == 0) { + cli_out ("\"%s\" cannot be a snapname", snapname); + goto out; + } + } + ret = 0; +out: + return ret; +} + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, + struct cli_state *state) +{ + int32_t ret = -1; + dict_t *dict = NULL; + gf1_cli_snapshot type = GF_SNAP_OPTION_TYPE_NONE; + char *w = NULL; + char *opwords[] = {"create", "delete", "restore", "start", + "stop", "list", "status", "config", + "info", NULL}; + char *invalid_snapnames[] = {"description", "force", + "volume", NULL}; + + GF_ASSERT (words); + GF_ASSERT (options); + GF_ASSERT (state); + + dict = dict_new (); + if (!dict) + goto out; + + /* Lowest wordcount possible */ + if (wordcount < 2) { + gf_log ("", GF_LOG_ERROR, + "Invalid command: Not enough arguments"); + goto out; + } + + w = str_getunamb (words[1], opwords); + if (!w) { + /* Checks if the operation is a valid operation */ + gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); + goto out; + } + + if (!strcmp (w, "create")) { + type = GF_SNAP_OPTION_TYPE_CREATE; + } else if (!strcmp (w, "list")) { + type = GF_SNAP_OPTION_TYPE_LIST; + } else if (!strcmp (w, "info")) { + type = GF_SNAP_OPTION_TYPE_INFO; + } else if (!strcmp (w, "delete")) { + type = GF_SNAP_OPTION_TYPE_DELETE; + } else if (!strcmp (w, "config")) { + type = GF_SNAP_OPTION_TYPE_CONFIG; + } else if (!strcmp (w, "restore")) { + type = GF_SNAP_OPTION_TYPE_RESTORE; + } else if (!strcmp (w, "status")) { + type = GF_SNAP_OPTION_TYPE_STATUS; + } + + if (type != GF_SNAP_OPTION_TYPE_CONFIG) { + ret = dict_set_int32 (dict, "hold_snap_locks", _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to set hold-snap-locks value " + "as _gf_true"); + goto out; + } + } + + /* Check which op is intended */ + switch (type) { + case GF_SNAP_OPTION_TYPE_CREATE: + /* Syntax : + * gluster snapshot create + * [description ] + * [force] + */ + /* In cases where the snapname is not given then + * parsing fails & snapname cannot be "description", + * "force" and "volume", that check is made here + */ + if (wordcount == 2){ + ret = -1; + gf_log ("cli", GF_LOG_ERROR, "Invalid Syntax"); + goto out; + } + + ret = validate_snapname (words[2], invalid_snapnames); + if (ret) { + goto out; + } + + ret = cli_snap_create_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "create command parsing failed."); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_INFO: + /* Syntax : + * gluster snapshot info [(snapname] | [vol )] + */ + ret = cli_snap_info_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot info command"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_LIST: + /* Syntax : + * gluster snaphsot list [volname] + */ + + ret = cli_snap_list_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot list command"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_DELETE: + /* Syntax : + * gluster snapshot delete + */ + ret = cli_snap_delete_parse (dict, words, wordcount, state); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot delete command"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_CONFIG: + /* snapshot config [volname] [snap-max-hard-limit ] + * [snap-max-soft-limit ] */ + ret = cli_snap_config_parse (words, wordcount, dict, state); + if (ret) { + if (ret < 0) + gf_log ("cli", GF_LOG_ERROR, + "config command parsing failed."); + goto out; + } + + ret = dict_set_int32 (dict, "type", GF_SNAP_OPTION_TYPE_CONFIG); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to set " + "config type"); + ret = -1; + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_STATUS: + { + /* Syntax : + * gluster snapshot status [(snapname | + * volume )] + */ + ret = cli_snap_status_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "snapshot status command"); + goto out; + } + break; + } + + case GF_SNAP_OPTION_TYPE_RESTORE: + /* Syntax: + * snapshot restore + */ + ret = cli_snap_restore_parse (dict, words, wordcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to parse " + "restore command"); + goto out; + } + break; + + default: + gf_log ("", GF_LOG_ERROR, "Opword Mismatch"); + goto out; + } + + ret = dict_set_int32 (dict, "type", type); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Failed to set type."); + goto out; + } + /* If you got so far, input is valid */ + ret = 0; +out: + if (ret) { + if (dict) + dict_destroy (dict); + } else + *options = dict; + + return ret; +} diff --git a/cli/src/cli-cmd-snapshot.c b/cli/src/cli-cmd-snapshot.c new file mode 100644 index 000000000..941dcbdd2 --- /dev/null +++ b/cli/src/cli-cmd-snapshot.c @@ -0,0 +1,145 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#include +#include +#include +#include +#include + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "cli.h" +#include "cli-cmd.h" + +extern rpc_clnt_prog_t *cli_rpc_prog; + +int +cli_cmd_snapshot_help_cbk (struct cli_state *state, struct cli_cmd_word *in_word, + const char **words, int wordcount); + +int +cli_cmd_snapshot_cbk (struct cli_state *state, struct cli_cmd_word *word, + const char **words, int wordcount) +{ + int ret = 0; + int parse_err = 0; + dict_t *options = NULL; + rpc_clnt_procedure_t *proc = NULL; + call_frame_t *frame = NULL; + cli_local_t *local = NULL; + + proc = &cli_rpc_prog->proctable [GLUSTER_CLI_SNAP]; + if (proc == NULL) { + ret = -1; + goto out; + } + + frame = create_frame (THIS, THIS->ctx->pool); + if (frame == NULL) { + ret = -1; + goto out; + } + + /* Parses the command entered by the user */ + ret = cli_cmd_snapshot_parse (words, wordcount, &options, state); + if (ret) { + if (ret < 0) { + cli_usage_out (word->pattern); + parse_err = 1; + } else { + /* User might have cancelled the snapshot operation */ + ret = 0; + } + goto out; + } + + CLI_LOCAL_INIT (local, words, frame, options); + + if (proc->fn) + ret = proc->fn (frame, THIS, options); + +out: + if (ret && parse_err == 0) + cli_out ("Snapshot command failed"); + + CLI_STACK_DESTROY (frame); + + return ret; +} + +struct cli_cmd snapshot_cmds[] = { + { "snapshot help", + cli_cmd_snapshot_help_cbk, + "display help for snapshot commands" + }, + { "snapshot create [description ] [force]", + cli_cmd_snapshot_cbk, + "Snapshot Create." + }, + { "snapshot restore ", + cli_cmd_snapshot_cbk, + "Snapshot Restore." + }, + { "snapshot status [(snapname | volume )]", + cli_cmd_snapshot_cbk, + "Snapshot Status." + }, + { "snapshot info [(snapname | volume )]", + cli_cmd_snapshot_cbk, + "Snapshot Info." + }, + { "snapshot list [volname]", + cli_cmd_snapshot_cbk, + "Snapshot List." + }, + {"snapshot config [volname] [snap-max-hard-limit ] [snap-max-soft-limit ]", + cli_cmd_snapshot_cbk, + "Snapshot Config." + }, + {"snapshot delete ", + cli_cmd_snapshot_cbk, + "Snapshot Delete." + }, + { NULL, NULL, NULL } +}; + +int +cli_cmd_snapshot_help_cbk (struct cli_state *state, + struct cli_cmd_word *in_word, + const char **words, + int wordcount) +{ + struct cli_cmd *cmd = NULL; + + for (cmd = snapshot_cmds; cmd->pattern; cmd++) + if (_gf_false == cmd->disable) + cli_out ("%s - %s", cmd->pattern, cmd->desc); + + return 0; +} + +int +cli_cmd_snapshot_register (struct cli_state *state) +{ + int ret = 0; + struct cli_cmd *cmd = NULL; + + for (cmd = snapshot_cmds; cmd->pattern; cmd++) { + + ret = cli_cmd_register (&state->tree, cmd); + if (ret) + goto out; + } +out: + return ret; +} diff --git a/cli/src/cli-cmd.c b/cli/src/cli-cmd.c index 63b939282..ec2d58ae9 100644 --- a/cli/src/cli-cmd.c +++ b/cli/src/cli-cmd.c @@ -231,6 +231,9 @@ cli_cmds_register (struct cli_state *state) if (ret) goto out; + ret = cli_cmd_snapshot_register (state); + if (ret) + goto out; out: return ret; } diff --git a/cli/src/cli-cmd.h b/cli/src/cli-cmd.h index 541b4ff73..91d15b7e1 100644 --- a/cli/src/cli-cmd.h +++ b/cli/src/cli-cmd.h @@ -93,6 +93,8 @@ int cli_cmd_probe_register (struct cli_state *state); int cli_cmd_system_register (struct cli_state *state); +int cli_cmd_snapshot_register (struct cli_state *state); + int cli_cmd_misc_register (struct cli_state *state); struct cli_cmd_word *cli_cmd_nextword (struct cli_cmd_word *word, diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index 239a29ba8..ebabf6b3d 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -22,6 +22,8 @@ #define VOL_TOP_PERF_SPEED_WIDTH 4 #define VOL_TOP_PERF_TIME_WIDTH 26 +#define INDENT_MAIN_HEAD "%-25s %s " + #include "cli.h" #include "compat-errno.h" #include "cli-cmd.h" @@ -291,7 +293,7 @@ gf_cli_output_pool_list (dict_t *dict, int count) else connected_str = "Disconnected"; - cli_out ("%s\t%-8s\t%s ", uuid_buf, hostname_buf, + cli_out ("%s\t%-9s\t%s ", uuid_buf, hostname_buf, connected_str); i++; } @@ -508,6 +510,9 @@ gf_cli_get_volume_cbk (struct rpc_req *req, struct iovec *iov, gf_cli_rsp rsp = {0}; char *caps = NULL; int k __attribute__((unused)) = 0; + /* snap_volume variable helps in showing whether a volume is a normal + * volume or a volume for the snapshot */ + int32_t snap_volume = 0; if (-1 == req->rpc_status) goto out; @@ -629,6 +634,11 @@ xml_output: if (ret) goto out; + snprintf (key, sizeof (key), "volume%d.snap_volume", i); + ret = dict_get_int32 (dict, key, &snap_volume); + if (ret) + goto out; + snprintf (key, 256, "volume%d.brick_count", i); ret = dict_get_int32 (dict, key, &brick_count); if (ret) @@ -669,6 +679,10 @@ xml_output: cli_out ("Type: %s", cli_vol_type_str[vol_type]); cli_out ("Volume ID: %s", volume_id_str); cli_out ("Status: %s", cli_vol_status_str[status]); + if (snap_volume) + cli_out ("Snap Volume: %s", "yes"); + else + cli_out ("Snap Volume: %s", "no"); #ifdef HAVE_BD_XLATOR k = 0; @@ -7619,6 +7633,1030 @@ out: return ret; } +int32_t +cli_snapshot_remove_reply (gf_cli_rsp *rsp, dict_t *dict, call_frame_t *frame) +{ + int32_t ret = -1; + char *snap_name = NULL; + + GF_ASSERT (rsp); + GF_ASSERT (dict); + GF_ASSERT (frame); + + if (rsp->op_ret) { + cli_err("snapshot delete: failed: %s", + rsp->op_errstr ? rsp->op_errstr : + "Please check log file for details"); + ret = rsp->op_ret; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snap_name); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get snapname"); + goto out; + } + + cli_out ("snapshot delete: %s: snap removed successfully", + snap_name); + ret = 0; + +out: + return ret; +} + +int +cli_snapshot_config_display (dict_t *dict, gf_cli_rsp *rsp) +{ + char buf[PATH_MAX] = ""; + char *volname = NULL; + int ret = -1; + int config_command = 0; + uint64_t value = 0; + uint64_t hard_limit = 0; + uint64_t soft_limit = 0; + uint64_t i = 0; + uint64_t voldisplaycount = 0; + + GF_ASSERT (dict); + GF_ASSERT (rsp); + + if (rsp->op_ret) { + cli_err ("Snapshot Config : failed: %s", + rsp->op_errstr ? rsp->op_errstr : + "Please check log file for details"); + ret = rsp->op_ret; + goto out; + } + + ret = dict_get_int32 (dict, "config-command", &config_command); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch config type"); + goto out; + } + + ret = dict_get_str (dict, "volname", &volname); + /* Ignore the error, as volname is optional */ + + if (!volname) { + volname = "System"; + } + + ret = dict_get_uint64 (dict, "snap-max-hard-limit", &hard_limit); + /* Ignore the error, as the key specified is optional */ + ret = dict_get_uint64 (dict, "snap-max-soft-limit", &soft_limit); + + if (!hard_limit && !soft_limit + && config_command != GF_SNAP_CONFIG_DISPLAY) { + ret = -1; + gf_log(THIS->name, GF_LOG_ERROR, + "Could not fetch config-key"); + goto out; + } + + switch (config_command) { + case GF_SNAP_CONFIG_TYPE_SET: + if (hard_limit && soft_limit) { + cli_out ("snapshot config: snap-max-hard-limit " + "& snap-max-soft-limit for system set " + "successfully"); + } else if (hard_limit) { + cli_out ("snapshot config: %s " + "for snap-max-hard-limit set successfully", + volname); + } else if (soft_limit) { + cli_out ("snapshot config: %s " + "for snap-max-soft-limit set successfully", + volname); + } + break; + + case GF_SNAP_CONFIG_DISPLAY: + cli_out ("\nSnapshot System Configuration:"); + ret = dict_get_uint64 (dict, "snap-max-hard-limit", + &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "snap_max_hard_limit for %s", volname); + ret = -1; + goto out; + } + cli_out ("snap-max-hard-limit : %"PRIu64, value); + + ret = dict_get_uint64 (dict, "snap-max-soft-limit", + &soft_limit); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "snap-max-soft-limit for %s", volname); + ret = -1; + goto out; + } + cli_out ("snap-max-soft-limit : %"PRIu64"%%\n", + soft_limit); + + cli_out ("Snapshot Volume Configuration:"); + + ret = dict_get_uint64 (dict, "voldisplaycount", + &voldisplaycount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Could not fetch voldisplaycount"); + ret = -1; + goto out; + } + + for (i = 0; i < voldisplaycount; i++) { + snprintf (buf, sizeof(buf), "volume%ld-volname", i); + ret = dict_get_str (dict, buf, &volname); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + " %s", buf); + ret = -1; + goto out; + } + cli_out ("\nVolume : %s", volname); + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-hard-limit", i); + ret = dict_get_uint64 (dict, buf, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + " %s", buf); + ret = -1; + goto out; + } + cli_out ("snap-max-hard-limit : %"PRIu64, value); + + snprintf (buf, sizeof(buf), + "volume%ld-active-hard-limit", i); + ret = dict_get_uint64 (dict, buf, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch" + " effective snap_max_hard_limit for " + "%s", volname); + ret = -1; + goto out; + } + cli_out ("Effective snap-max-hard-limit : %"PRIu64, + value); + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-soft-limit", i); + ret = dict_get_uint64 (dict, buf, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + " %s", buf); + ret = -1; + goto out; + } + cli_out ("Effective snap-max-soft-limit : %"PRIu64" " + "(%"PRIu64"%%)", value, soft_limit); + } + break; + default: + break; + } + + ret = 0; +out: + return ret; +} + +/* This function is used to print the volume related information + * of a snap. + * + * arg - 0, dict : Response Dictionary. + * arg - 1, prefix str : snaplist.snap{0..}.vol{0..}.* + */ +int +cli_get_each_volinfo_in_snap (dict_t *dict, char *keyprefix, + gf_boolean_t snap_driven) { + char key[PATH_MAX] = ""; + char *get_buffer = NULL; + int value = 0; + int ret = -1; + char indent[5] = "\t"; + char *volname = NULL; + + GF_ASSERT (dict); + GF_ASSERT (keyprefix); + + if (snap_driven) { + ret = snprintf (key, sizeof (key), "%s.volname", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, + "Snap Volume Name", ":", get_buffer); + + ret = snprintf (key, sizeof (key), + "%s.origin-volname", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &volname); + if (ret) { + gf_log ("cli", GF_LOG_WARNING, "Failed to get %s", key); + cli_out ("%-12s", "Origin:"); + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, + "Origin Volume name", ":", volname); + + + ret = snprintf (key, sizeof (key), "%s.snapcount", + keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s%s %s %s %d", indent, "Snaps taken for", + volname, ":", value); + + ret = snprintf (key, sizeof (key), "%s.snaps-available", + keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &value); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s%s %s %s %d", indent, "Snaps available for", + volname, ":", value); + } + + + ret = snprintf (key, sizeof (key), "%s.vol-status", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get %s", key); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Status", + ":", get_buffer); +out: + return ret; +} + +/* This function is used to print snap related information + * arg - 0, dict : Response dictionary. + * arg - 1, prefix_str : snaplist.snap{0..}.* + */ +int +cli_get_volinfo_in_snap (dict_t *dict, char *keyprefix) { + + char key[PATH_MAX] = ""; + int i = 0; + int volcount = 0; + int ret = -1; + + GF_ASSERT (dict); + GF_ASSERT (keyprefix); + + ret = snprintf (key, sizeof (key), "%s.vol-count", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &volcount); + for (i = 1 ; i <= volcount ; i++) { + ret = snprintf (key, sizeof (key), + "%s.vol%d", keyprefix, i); + if (ret < 0) { + goto out; + } + ret = cli_get_each_volinfo_in_snap (dict, key, _gf_true); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not list " + "details of volume in a snap"); + goto out; + } + cli_out (" "); + } + +out: + return ret; +} + +int +cli_get_each_snap_info (dict_t *dict, char *prefix_str, + gf_boolean_t snap_driven) { + char key_buffer[PATH_MAX] = ""; + char *get_buffer = NULL; + int ret = -1; + char indent[5] = ""; + + GF_ASSERT (dict); + GF_ASSERT (prefix_str); + + if (!snap_driven) + strcat (indent, "\t"); + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snapname", + prefix_str); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snapname %s ", + key_buffer); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Snapshot", + ":", get_buffer); + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-id", + prefix_str); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snap-id %s ", + key_buffer); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Snap UUID", + ":", get_buffer); + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-desc", + prefix_str); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (!ret) { + /* Ignore error for description */ + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, + "Description", ":", get_buffer); + } + + ret = snprintf (key_buffer, sizeof (key_buffer), "%s.snap-time", + prefix_str); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key_buffer, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to fetch snap-time %s ", + prefix_str); + goto out; + } + cli_out ("%s" INDENT_MAIN_HEAD "%s", indent, "Created", + ":", get_buffer); + + if (snap_driven) { + cli_out ("%-12s", "Snap Volumes:\n"); + ret = cli_get_volinfo_in_snap (dict, prefix_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to list details " + "of the snaps"); + goto out; + } + } +out: + return ret; +} + +/* This is a generic function to print snap related information. + * arg - 0, dict : Response Dictionary + */ +int +cli_call_snapshot_info (dict_t *dict, gf_boolean_t bool_snap_driven) { + int snap_count = 0; + char key[PATH_MAX] = ""; + int ret = -1; + int i = 0; + + GF_ASSERT (dict); + + ret = dict_get_int32 (dict, "snap-count", &snap_count); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get snap-count"); + goto out; + } + + if (snap_count == 0) { + cli_out ("No snapshots present"); + } + + for (i = 1 ; i <= snap_count ; i++) { + ret = snprintf (key, sizeof (key), "snap%d", i); + if (ret < 0) { + goto out; + } + ret = cli_get_each_snap_info (dict, key, bool_snap_driven); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to print snap details"); + goto out; + } + } +out: + return ret; +} + +int +cli_get_snaps_in_volume (dict_t *dict) { + int ret = -1; + int i = 0; + int count = 0; + int avail = 0; + char key[PATH_MAX] = ""; + char *get_buffer = NULL; + + GF_ASSERT (dict); + + ret = dict_get_str (dict, "origin-volname", &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch origin-volname"); + goto out; + } + cli_out (INDENT_MAIN_HEAD "%s", "Volume Name", ":", get_buffer); + + ret = dict_get_int32 (dict, "snap-count", &avail); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch snap-count"); + goto out; + } + cli_out (INDENT_MAIN_HEAD "%d", "Snaps Taken", ":", avail); + + ret = dict_get_int32 (dict, "snaps-available", &count); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch snaps-available"); + goto out; + } + cli_out (INDENT_MAIN_HEAD "%d", "Snaps Available", ":", count); + + for (i = 1 ; i <= avail ; i++) { + snprintf (key, sizeof (key), "snap%d", i); + ret = cli_get_each_snap_info (dict, key, _gf_false); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to print snap details"); + goto out; + } + + ret = snprintf (key, sizeof (key), "snap%d.vol1", i); + if (ret < 0) { + goto out; + } + ret = cli_get_each_volinfo_in_snap (dict, key, _gf_false); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not get volume " + "related information"); + goto out; + } + + cli_out (" "); + } +out: + return ret; +} + +int +cli_snapshot_list (dict_t *dict) { + int snapcount = 0; + char key[PATH_MAX] = ""; + int ret = -1; + int i = 0; + char *get_buffer = NULL; + + GF_ASSERT (dict); + + ret = dict_get_int32 (dict, "snap-count", &snapcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch snap count"); + goto out; + } + + if (snapcount == 0) { + cli_out ("No snapshots present"); + } + + for (i = 1 ; i <= snapcount ; i++) { + ret = snprintf (key, sizeof (key), "snapname%d", i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not get %s ", key); + goto out; + } else { + cli_out ("%s", get_buffer); + } + } +out: + return ret; +} + +int +cli_get_snap_volume_status (dict_t *dict, char *key_prefix) +{ + int ret = -1; + char key[PATH_MAX] = ""; + char *buffer = NULL; + int brickcount = 0; + int i = 0; + int pid = 0; + + GF_ASSERT (dict); + GF_ASSERT (key_prefix); + + ret = snprintf (key, sizeof (key), "%s.brickcount", key_prefix); + if (ret < 0) { + goto out; + } + ret = dict_get_int32 (dict, key, &brickcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to fetch brickcount"); + goto out; + } + + for (i = 0 ; i < brickcount ; i++) { + ret = snprintf (key, sizeof (key), "%s.brick%d.path", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Brick Path"); + continue; + } + cli_out ("\n\t%-17s %s %s", "Brick Path", ":", buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.vgname", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Volume Group"); + cli_out ("\t%-17s %s %s", "Volume Group", ":", + "N/A"); + } else + cli_out ("\t%-17s %s %s", "Volume Group", ":", + buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.status", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Brick Running"); + cli_out ("\t%-17s %s %s", "Brick Running", ":", + "N/A"); + } else + cli_out ("\t%-17s %s %s", "Brick Running", ":", + buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.pid", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &pid); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get pid"); + cli_out ("\t%-17s %s %s", "Brick PID", ":", "N/A"); + } else + cli_out ("\t%-17s %s %d", "Brick PID", ":", pid); + + ret = snprintf (key, sizeof (key), "%s.brick%d.data", + key_prefix, i); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, + "Unable to get Data Percent"); + cli_out ("\t%-17s %s %s", "Data Percentage", ":", + "N/A"); + } else + cli_out ("\t%-17s %s %s", "Data Percentage", ":", + buffer); + + ret = snprintf (key, sizeof (key), "%s.brick%d.lvsize", + key_prefix, i); + if (ret < 0) { + goto out; + } + ret = dict_get_str (dict, key, &buffer); + if (ret) { + gf_log ("cli", GF_LOG_INFO, "Unable to get LV Size"); + cli_out ("\t%-17s %s %s", "LV Size", ":", "N/A"); + } else + cli_out ("\t%-17s %s %s", "LV Size", ":", buffer); + + } +out: + return ret; +} + + + +int +cli_get_single_snap_status (dict_t *dict, char *keyprefix) +{ + int ret = -1; + char key[PATH_MAX] = ""; + int i = 0; + int volcount = 0; + char *get_buffer = NULL; + + GF_ASSERT (dict); + GF_ASSERT (keyprefix); + + ret = snprintf (key, sizeof (key), "%s.snapname", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get snapname"); + goto out; + } + cli_out ("\nSnap Name : %s", get_buffer); + + ret = snprintf (key, sizeof (key), "%s.uuid", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_str (dict, key, &get_buffer); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get snap UUID"); + goto out; + } + cli_out ("Snap UUID : %s", get_buffer); + + ret = snprintf (key, sizeof (key), "%s.volcount", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_get_int32 (dict, key, &volcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Unable to get volume count"); + goto out; + } + + for (i = 0 ; i < volcount ; i++) { + ret = snprintf (key, sizeof (key), "%s.vol%d", keyprefix, i); + if (ret < 0) { + goto out; + } + + ret = cli_get_snap_volume_status (dict, key); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Could not get snap volume status"); + goto out; + } + } +out: + return ret; +} + +int +cli_snap_status_all (dict_t *dict) { + int ret = -1; + char key[PATH_MAX] = ""; + int snapcount = 0; + int i = 0; + + GF_ASSERT (dict); + + ret = dict_get_int32 (dict, "status.snapcount", &snapcount); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not get snapcount"); + goto out; + } + + if (snapcount == 0) { + cli_out ("No snapshots present"); + } + + for (i = 0 ; i < snapcount; i++) { + ret = snprintf (key, sizeof (key), "status.snap%d",i); + if (ret < 0) { + goto out; + } + ret = cli_get_single_snap_status (dict, key); + } +out: + return ret; +} + + +int +cli_snapshot_status_display (dict_t *dict, gf_cli_rsp *rsp) +{ + char key[PATH_MAX] = ""; + int ret = -1; + int status_cmd = -1; + + GF_ASSERT (dict); + GF_ASSERT (rsp); + + if (rsp->op_ret) { + cli_err ("Snapshot Status : failed: %s", + rsp->op_errstr ? rsp->op_errstr : + "Please check log file for details"); + ret = rsp->op_ret; + goto out; + } + + ret = dict_get_int32 (dict, "cmd", &status_cmd); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch status type"); + goto out; + } + switch (status_cmd) { + case GF_SNAP_STATUS_TYPE_ALL: + { + ret = cli_snap_status_all (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "status of all snap"); + goto out; + } + break; + } + + case GF_SNAP_STATUS_TYPE_SNAP: + { + ret = snprintf (key, sizeof (key), "status.snap0"); + if (ret < 0) { + goto out; + } + ret = cli_get_single_snap_status (dict, key); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "status of snap"); + goto out; + } + break; + } + + case GF_SNAP_STATUS_TYPE_VOL: + { + ret = cli_snap_status_all (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Could not fetch " + "status of snap in a volume"); + goto out; + } + break; + } + default: + break; + } +out: + return ret; +} + +int +gf_cli_snapshot_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int ret = -1; + gf_cli_rsp rsp = {0, }; + dict_t *dict = NULL; + char *snap_name = NULL; + int32_t type = 0; + call_frame_t *frame = NULL; + gf_boolean_t snap_driven = _gf_false; + + if (req->rpc_status == -1) { + ret = -1; + goto out; + } + + frame = myframe; + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gf_cli_rsp); + if (ret < 0) { + gf_log (frame->this->name, GF_LOG_ERROR, + "Failed to decode xdr response"); + goto out; + } + + dict = dict_new (); + + if (!dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, &dict); + + if (ret) + goto out; + + ret = dict_get_int32 (dict, "type", &type); + if (ret) { + gf_log (frame->this->name, GF_LOG_ERROR, "failed to get type"); + goto out; + } + + switch (type) { + case GF_SNAP_OPTION_TYPE_CREATE: + if (rsp.op_ret) { + cli_err("snapshot create: failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snap_name); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to get snap name"); + goto out; + } + cli_out ("snapshot create: %s: snap created successfully", + snap_name); + break; + + case GF_SNAP_OPTION_TYPE_RESTORE: + /* TODO: Check if rsp.op_ret needs to be checked here. Or is + * it ok to check this in the start of the function where we + * get rsp.*/ + if (rsp.op_ret) { + cli_err("snapshot restore: failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snap_name); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to get snap name"); + goto out; + } + + cli_out ("Snapshot restore: %s: Snap restored " + "successfully", snap_name); + + ret = 0; + break; + + case GF_SNAP_OPTION_TYPE_INFO: + if (rsp.op_ret) { + cli_err ("Snapshot info : failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + snap_driven = dict_get_str_boolean (dict, "snap-driven", + _gf_false); + if (snap_driven == _gf_true) { + ret = cli_call_snapshot_info (dict, snap_driven); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Snapshot info failed"); + goto out; + } + } else if (snap_driven == _gf_false) { + ret = cli_get_snaps_in_volume (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Snapshot info failed"); + goto out; + } + } + break; + + case GF_SNAP_OPTION_TYPE_CONFIG: + ret = cli_snapshot_config_display (dict, &rsp); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to display " + "snapshot config output."); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_LIST: + if (rsp.op_ret) { + cli_err ("Snapshot list : failed: %s", + rsp.op_errstr ? rsp.op_errstr : + "Please check log file for details"); + ret = rsp.op_ret; + goto out; + } + + ret = cli_snapshot_list (dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to display " + "snapshot list"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_DELETE: + ret = cli_snapshot_remove_reply (&rsp, dict, frame); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to delete snap"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_STATUS: + ret = cli_snapshot_status_display (dict, &rsp); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to display " + "snapshot status output."); + goto out; + } + break; + + default: + cli_err ("Unknown command executed"); + ret = -1; + goto out; + } +out: + if (dict) + dict_unref (dict); + cli_cmd_broadcast_response (ret); + + free (rsp.dict.dict_val); + free (rsp.op_errstr); + + return ret; +} + +int32_t +gf_cli_snapshot (call_frame_t *frame, xlator_t *this, + void *data) +{ + gf_cli_req req = {{0,}}; + dict_t *options = NULL; + int ret = -1; + + if (!frame || !this || !data) + goto out; + + options = data; + + ret = cli_to_glusterd (&req, frame, gf_cli_snapshot_cbk, + (xdrproc_t) xdr_gf_cli_req, options, + GLUSTER_CLI_SNAP, this, cli_rpc_prog, + NULL); +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + + GF_FREE (req.dict.dict_val); + return ret; +} + int cli_to_glusterd (gf_cli_req *req, call_frame_t *frame, fop_cbk_fn_t cbkfn, xdrproc_t xdrproc, dict_t *dict, @@ -7730,6 +8768,7 @@ struct rpc_clnt_procedure gluster_cli_actors[GLUSTER_CLI_MAXVALUE] = { [GLUSTER_CLI_CLRLOCKS_VOLUME] = {"CLEARLOCKS_VOLUME", gf_cli_clearlocks_volume}, [GLUSTER_CLI_COPY_FILE] = {"COPY_FILE", gf_cli_copy_file}, [GLUSTER_CLI_SYS_EXEC] = {"SYS_EXEC", gf_cli_sys_exec}, + [GLUSTER_CLI_SNAP] = {"SNAP", gf_cli_snapshot}, }; struct rpc_clnt_program cli_prog = { diff --git a/cli/src/cli-xml-output.c b/cli/src/cli-xml-output.c index ae6b051ed..863e8a4a6 100644 --- a/cli/src/cli-xml-output.c +++ b/cli/src/cli-xml-output.c @@ -2535,6 +2535,7 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) int j = 1; char *caps = NULL; int k __attribute__((unused)) = 0; + char *snap_volume = NULL; ret = dict_get_int32 (dict, "count", &count); if (ret) @@ -2576,6 +2577,18 @@ cli_xml_output_vol_info (cli_local_t *local, dict_t *dict) "%d", status); XML_RET_CHECK_AND_GOTO (ret, out); + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "volume%d.snap_volume", i); + ret = dict_get_str (dict, key, &snap_volume); + if (ret) + goto out; + if (snap_volume) { + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"snapVol", + "%s", snap_volume); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret =xmlTextWriterWriteFormatElement (local->writer, (xmlChar *)"statusStr", "%s", cli_vol_status_str[status]); diff --git a/cli/src/cli.h b/cli/src/cli.h index 53537c642..69a7e82bf 100644 --- a/cli/src/cli.h +++ b/cli/src/cli.h @@ -166,6 +166,11 @@ struct cli_volume_status { #endif }; +struct snap_config_opt_vals_ { + char *op_name; + char *question; +}; + typedef struct cli_volume_status cli_volume_status_t; typedef struct cli_local cli_local_t; @@ -385,4 +390,9 @@ cli_xml_output_vol_status_tasks_detail (cli_local_t *local, dict_t *dict); char * is_server_debug_xlator (void *myframe); + +int32_t +cli_cmd_snapshot_parse (const char **words, int wordcount, dict_t **options, + struct cli_state *state); + #endif /* __CLI_H__ */ diff --git a/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md b/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md new file mode 100644 index 000000000..e76ee9151 --- /dev/null +++ b/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md @@ -0,0 +1,66 @@ +Managing GlusterFS Volume Snapshots +========================== + +This section describes how to perform common GlusterFS volume snapshot +management operations + +Pre-requisites +===================== + +GlusterFS volume snapshot feature is based on thinly provisioned LVM snapshot. +To make use of snapshot feature GlusterFS volume should fulfill following +pre-requisites: + +* Each brick should be on an independent thinly provisioned LVM. +* Brick LVM should not contain any other data other than brick. +* None of the brick should be on a thick LVM. + + +Snapshot Management +===================== + + +**Snapshot creation** + +*gluster snapshot create \ \[-n \\] \[-d \\]* + +This command will create a snapshot of a GlusterFS volume. User can provide a snap-name and a description to identify the snap. The description cannot be more than 1024 characters. + +Volume should be present and it should be in started state. + +**Restoring snaps** + +*gluster snapshot restore -v \ \* + +This command restores an already taken snapshot of a GlusterFS volume. Snapshot restore is an offline activity therefore if the volume is online then the restore operation will fail. + +Once the snapshot is restored it will be deleted from the list of snapshot. + +**Deleting snaps** + +*gluster snapshot delete \\ -s \ \[force\]* + +This command will delete the specified snapshot. + +**Listing of available snaps** + +*gluster snapshot list \[\ \[-s \\]\]* + +This command is used to list all snapshots taken, or for a specified volume. If snap-name is provided then it will list the details of that snap. + +**Configuring the snapshot behavior** + +*gluster snapshot config \[\\]* + +This command will display existing config values for a volume. If volume name is not provided then config values of all the volume is displayed. System config is displayed irrespective of volume name. + +*gluster snapshot config \ \[\ \\] \[\ \\]* + +The above command can be used to change the existing config values. If vol-name is provided then config value of that volume is changed, else it will set/change the system limit. + +The system limit is the default value of the config for all the volume. Volume specific limit cannot cross the system limit. If a volume specific limit is not provided then system limit will be considered. + +If any of this limit is decreased and the current snap count of the system/volume is more than the limit then the command will fail. If user still want to decrease the limit then force option should be used. + + + diff --git a/glusterfsd/src/glusterfsd-mgmt.c b/glusterfsd/src/glusterfsd-mgmt.c index d4201b38e..c42228a04 100644 --- a/glusterfsd/src/glusterfsd-mgmt.c +++ b/glusterfsd/src/glusterfsd-mgmt.c @@ -1128,6 +1128,83 @@ out: return ret; } +int +glusterfs_handle_volume_barrier_op (rpcsvc_request_t *req) +{ + int32_t ret = -1; + gd1_mgmt_brick_op_req xlator_req = {0,}; + dict_t *dict = NULL; + xlator_t *xlator = NULL; + xlator_t *any = NULL; + dict_t *output = NULL; + char msg[2048] = {0}; + glusterfs_ctx_t *ctx = NULL; + glusterfs_graph_t *active = NULL; + xlator_t *this = NULL; + + GF_ASSERT (req); + this = THIS; + GF_ASSERT (this); + + ctx = glusterfsd_ctx; + GF_ASSERT (ctx); + + active = ctx->active; + if (!active) { + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + any = active->first; + ret = xdr_to_generic (req->msg[0], &xlator_req, + (xdrproc_t)xdr_gd1_mgmt_brick_op_req); + if (ret < 0) { + //failed to decode msg; + req->rpc_err = GARBAGE_ARGS; + goto out; + } + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (xlator_req.input.input_val, + xlator_req.input.input_len, + &dict); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "failed to " + "unserialize req-buffer to dictionary"); + goto out; + } + xlator = xlator_search_by_name (any, xlator_req.name); + if (!xlator) { + snprintf (msg, sizeof (msg), "xlator %s is not loaded", + xlator_req.name); + goto out; + } + + output = dict_new (); + if (!output) { + ret = -1; + goto out; + } + + ret = xlator->notify (xlator, GF_EVENT_VOLUME_BARRIER_OP, + dict, output); + + ret = glusterfs_translator_info_response_send (req, ret, + msg, output); +out: + if (dict) + dict_unref (dict); + free (xlator_req.input.input_val); // malloced by xdr + if (output) + dict_unref (output); + free (xlator_req.name); //malloced by xdr + + return ret; + +} int glusterfs_handle_rpc_msg (rpcsvc_request_t *req) { @@ -1192,6 +1269,7 @@ rpcsvc_actor_t glusterfs_actors[] = { [GLUSTERD_BRICK_XLATOR_DEFRAG] = {"TRANSLATOR DEFRAG", GLUSTERD_BRICK_XLATOR_DEFRAG, glusterfs_handle_defrag, NULL, 0, DRC_NA}, [GLUSTERD_NODE_PROFILE] = {"NFS PROFILE", GLUSTERD_NODE_PROFILE, glusterfs_handle_nfs_profile, NULL, 0, DRC_NA}, [GLUSTERD_NODE_STATUS] = {"NFS STATUS", GLUSTERD_NODE_STATUS, glusterfs_handle_node_status, NULL, 0, DRC_NA}, + [GLUSTERD_VOLUME_BARRIER_OP] = {"VOLUME BARRIER OP", GLUSTERD_VOLUME_BARRIER_OP, glusterfs_handle_volume_barrier_op, NULL, 0, DRC_NA}, }; struct rpcsvc_program glusterfs_mop_prog = { diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h index 5dd26b451..6a8e38a21 100644 --- a/libglusterfs/src/glusterfs.h +++ b/libglusterfs/src/glusterfs.h @@ -70,6 +70,7 @@ #define FNM_EXTMATCH 0 #endif +#define GLUSTERD_MAX_SNAP_NAME 255 #define ZR_MOUNTPOINT_OPT "mountpoint" #define ZR_ATTR_TIMEOUT_OPT "attribute-timeout" #define ZR_ENTRY_TIMEOUT_OPT "entry-timeout" @@ -484,6 +485,7 @@ typedef enum { GF_EVENT_AUTH_FAILED, GF_EVENT_VOLUME_DEFRAG, GF_EVENT_PARENT_DOWN, + GF_EVENT_VOLUME_BARRIER_OP, GF_EVENT_MAXVAL, } glusterfs_event_t; diff --git a/libglusterfs/src/list.h b/libglusterfs/src/list.h index 794586e35..04b404712 100644 --- a/libglusterfs/src/list.h +++ b/libglusterfs/src/list.h @@ -45,19 +45,29 @@ list_add_tail (struct list_head *new, struct list_head *head) } +/* This function will insert the element to the list in a order. + Order will be based on the compare function provided as a input. + If element to be inserted in ascending order compare should return: + 0: if both the arguments are equal + >0: if first argument is greater than second argument + <0: if first argument is less than second argument */ static inline void list_add_order (struct list_head *new, struct list_head *head, int (*compare)(struct list_head *, struct list_head *)) { - struct list_head *pos = head->next; + struct list_head *pos = head->prev; while ( pos != head ) { - if (compare(new, pos) <= 0) + if (compare(new, pos) >= 0) break; - pos = pos->next; + + /* Iterate the list in the reverse order. This will have + better efficiency if the elements are inserted in the + ascending order */ + pos = pos->prev; } - list_add_tail(new, pos); + list_add (new, pos); } static inline void diff --git a/libglusterfs/src/mem-types.h b/libglusterfs/src/mem-types.h index 26237fecb..c01dfa75e 100644 --- a/libglusterfs/src/mem-types.h +++ b/libglusterfs/src/mem-types.h @@ -120,7 +120,7 @@ enum gf_common_mem_types_ { gf_common_mt_iobrefs = 104, gf_common_mt_gsync_status_t = 105, gf_common_mt_uuid_t = 106, - gf_common_mt_vol_lock_obj_t = 107, + gf_common_mt_mgmt_v3_lock_obj_t = 107, gf_common_mt_txn_opinfo_obj_t = 108, gf_common_mt_end = 109 }; diff --git a/libglusterfs/src/run.c b/libglusterfs/src/run.c index ebe7f3962..4fd2a3a0d 100644 --- a/libglusterfs/src/run.c +++ b/libglusterfs/src/run.c @@ -187,7 +187,7 @@ runner_log (runner_t *runner, const char *dom, gf_loglevel_t lvl, if (len > 0) buf[len - 1] = '\0'; - gf_log (dom, lvl, "%s: %s", msg, buf); + gf_log_callingfn (dom, lvl, "%s: %s", msg, buf); GF_FREE (buf); } diff --git a/libglusterfs/src/store.c b/libglusterfs/src/store.c index 66a5906a3..5beafaf35 100644 --- a/libglusterfs/src/store.c +++ b/libglusterfs/src/store.c @@ -168,10 +168,12 @@ int gf_store_read_and_tokenize (FILE *file, char *str, char **iter_key, char **iter_val, gf_store_op_errno_t *store_errno) { - int32_t ret = -1; - char *savetok = NULL; - char *key = NULL; - char *value = NULL; + int32_t ret = -1; + char *savetok = NULL; + char *key = NULL; + char *value = NULL; + char *temp = NULL; + size_t str_len = 0; GF_ASSERT (file); GF_ASSERT (str); @@ -179,13 +181,17 @@ gf_store_read_and_tokenize (FILE *file, char *str, char **iter_key, GF_ASSERT (iter_val); GF_ASSERT (store_errno); - ret = fscanf (file, "%s", str); - if (ret <= 0 || feof (file)) { + temp = fgets (str, PATH_MAX, file); + if (temp == NULL || feof (file)) { ret = -1; *store_errno = GD_STORE_EOF; goto out; } + str_len = strlen(str); + str[str_len - 1] = '\0'; + /* Truncate the "\n", as fgets stores "\n" in str */ + key = strtok_r (str, "=", &savetok); if (!key) { ret = -1; @@ -253,8 +259,13 @@ gf_store_retrieve_value (gf_store_handle_t *handle, char *key, char **value) goto out; } - scan_str = GF_CALLOC (1, st.st_size, + /* "st.st_size + 1" is used as we are fetching each + * line of a file using fgets, fgets will append "\0" + * to the end of the string + */ + scan_str = GF_CALLOC (1, st.st_size + 1, gf_common_mt_char); + if (scan_str == NULL) { ret = -1; store_errno = GD_STORE_ENOMEM; @@ -531,7 +542,11 @@ gf_store_iter_get_next (gf_store_iter_t *iter, char **key, char **value, goto out; } - scan_str = GF_CALLOC (1, st.st_size, + /* "st.st_size + 1" is used as we are fetching each + * line of a file using fgets, fgets will append "\0" + * to the end of the string + */ + scan_str = GF_CALLOC (1, st.st_size + 1, gf_common_mt_char); if (!scan_str) { ret = -1; @@ -595,7 +610,9 @@ gf_store_iter_get_matching (gf_store_iter_t *iter, char *key, char **value) goto out; } GF_FREE (tmp_key); + tmp_key = NULL; GF_FREE (tmp_value); + tmp_value = NULL; ret = gf_store_iter_get_next (iter, &tmp_key, &tmp_value, NULL); } diff --git a/rpc/rpc-lib/src/protocol-common.h b/rpc/rpc-lib/src/protocol-common.h index 6d28ed90e..97d47c433 100644 --- a/rpc/rpc-lib/src/protocol-common.h +++ b/rpc/rpc-lib/src/protocol-common.h @@ -167,6 +167,7 @@ enum gluster_cli_procnum { GLUSTER_CLI_UUID_GET, GLUSTER_CLI_COPY_FILE, GLUSTER_CLI_SYS_EXEC, + GLUSTER_CLI_SNAP, GLUSTER_CLI_MAXVALUE, }; @@ -198,6 +199,7 @@ enum glusterd_brick_procnum { GLUSTERD_BRICK_XLATOR_DEFRAG, GLUSTERD_NODE_PROFILE, GLUSTERD_NODE_STATUS, + GLUSTERD_VOLUME_BARRIER_OP, GLUSTERD_BRICK_MAXVALUE, }; @@ -238,8 +240,12 @@ struct gf_gsync_detailed_status_ { enum glusterd_mgmt_v3_procnum { GLUSTERD_MGMT_V3_NULL, /* 0 */ - GLUSTERD_MGMT_V3_VOLUME_LOCK, - GLUSTERD_MGMT_V3_VOLUME_UNLOCK, + GLUSTERD_MGMT_V3_LOCK, + GLUSTERD_MGMT_V3_PRE_VALIDATE, + GLUSTERD_MGMT_V3_BRICK_OP, + GLUSTERD_MGMT_V3_COMMIT, + GLUSTERD_MGMT_V3_POST_VALIDATE, + GLUSTERD_MGMT_V3_UNLOCK, GLUSTERD_MGMT_V3_MAXVALUE, }; diff --git a/rpc/xdr/src/cli1-xdr.c b/rpc/xdr/src/cli1-xdr.c index bf58e9904..cbb248587 100644 --- a/rpc/xdr/src/cli1-xdr.c +++ b/rpc/xdr/src/cli1-xdr.c @@ -189,6 +189,28 @@ xdr_gf_cli_status_type (XDR *xdrs, gf_cli_status_type *objp) return TRUE; } +bool_t +xdr_gf1_cli_snapshot (XDR *xdrs, gf1_cli_snapshot *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gf1_cli_snapshot_config (XDR *xdrs, gf1_cli_snapshot_config *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} + bool_t xdr_gf_cli_req (XDR *xdrs, gf_cli_req *objp) { diff --git a/rpc/xdr/src/cli1-xdr.h b/rpc/xdr/src/cli1-xdr.h index 11297cc25..f4251e6a2 100644 --- a/rpc/xdr/src/cli1-xdr.h +++ b/rpc/xdr/src/cli1-xdr.h @@ -184,6 +184,41 @@ enum gf_cli_status_type { }; typedef enum gf_cli_status_type gf_cli_status_type; +enum gf1_cli_snapshot { + GF_SNAP_OPTION_TYPE_NONE = 0, + GF_SNAP_OPTION_TYPE_CREATE = 1, + GF_SNAP_OPTION_TYPE_DELETE = 2, + GF_SNAP_OPTION_TYPE_RESTORE = 3, + GF_SNAP_OPTION_TYPE_START = 4, + GF_SNAP_OPTION_TYPE_STOP = 5, + GF_SNAP_OPTION_TYPE_LIST = 6, + GF_SNAP_OPTION_TYPE_STATUS = 7, + GF_SNAP_OPTION_TYPE_CONFIG = 8, + GF_SNAP_OPTION_TYPE_INFO = 9, +}; +typedef enum gf1_cli_snapshot gf1_cli_snapshot; + +enum gf1_cli_snapshot_info { + GF_SNAP_INFO_TYPE_ALL = 0, + GF_SNAP_INFO_TYPE_SNAP = 1, + GF_SNAP_INFO_TYPE_VOL = 2, +}; +typedef enum gf1_cli_snapshot_info gf1_cli_snapshot_info; + +enum gf1_cli_snapshot_config { + GF_SNAP_CONFIG_TYPE_NONE = 0, + GF_SNAP_CONFIG_TYPE_SET = 1, + GF_SNAP_CONFIG_DISPLAY = 2, +}; +typedef enum gf1_cli_snapshot_config gf1_cli_snapshot_config; + +enum gf1_cli_snapshot_status { + GF_SNAP_STATUS_TYPE_ALL = 0, + GF_SNAP_STATUS_TYPE_SNAP = 1, + GF_SNAP_STATUS_TYPE_VOL = 2, +}; +typedef enum gf1_cli_snapshot_status gf1_cli_snapshot_status; + struct gf_cli_req { struct { u_int dict_len; @@ -296,6 +331,8 @@ extern bool_t xdr_gf1_cli_stats_op (XDR *, gf1_cli_stats_op*); extern bool_t xdr_gf1_cli_info_op (XDR *, gf1_cli_info_op*); extern bool_t xdr_gf1_cli_top_op (XDR *, gf1_cli_top_op*); extern bool_t xdr_gf_cli_status_type (XDR *, gf_cli_status_type*); +extern bool_t xdr_gf1_cli_snapshot (XDR *, gf1_cli_snapshot*); +extern bool_t xdr_gf1_cli_snapshot_config (XDR *, gf1_cli_snapshot_config*); extern bool_t xdr_gf_cli_req (XDR *, gf_cli_req*); extern bool_t xdr_gf_cli_rsp (XDR *, gf_cli_rsp*); extern bool_t xdr_gf1_cli_peer_list_req (XDR *, gf1_cli_peer_list_req*); @@ -325,6 +362,8 @@ extern bool_t xdr_gf1_cli_stats_op (); extern bool_t xdr_gf1_cli_info_op (); extern bool_t xdr_gf1_cli_top_op (); extern bool_t xdr_gf_cli_status_type (); +extern bool_t xdr_gf1_cli_snapshot (); +extern bool_t xdr_gf1_cli_snapshot_config (); extern bool_t xdr_gf_cli_req (); extern bool_t xdr_gf_cli_rsp (); extern bool_t xdr_gf1_cli_peer_list_req (); diff --git a/rpc/xdr/src/cli1-xdr.x b/rpc/xdr/src/cli1-xdr.x index a283006b0..6e5e44410 100644 --- a/rpc/xdr/src/cli1-xdr.x +++ b/rpc/xdr/src/cli1-xdr.x @@ -136,6 +136,26 @@ enum gf_cli_status_type { GF_CLI_STATUS_QUOTAD = 0x2000 /*10000000000000*/ }; +/* Identifiers for snapshot clis */ +enum gf1_cli_snapshot { + GF_SNAP_OPTION_TYPE_NONE = 0, + GF_SNAP_OPTION_TYPE_CREATE, + GF_SNAP_OPTION_TYPE_DELETE, + GF_SNAP_OPTION_TYPE_RESTORE, + GF_SNAP_OPTION_TYPE_START, + GF_SNAP_OPTION_TYPE_STOP, + GF_SNAP_OPTION_TYPE_LIST, + GF_SNAP_OPTION_TYPE_STATUS, + GF_SNAP_OPTION_TYPE_CONFIG +}; + +enum gf1_cli_snapshot_config { + GF_SNAP_CONFIG_TYPE_NONE = 0, + GF_SNAP_CONFIG_TYPE_SET, + GF_SNAP_CONFIG_DISPLAY, + +}; + struct gf_cli_req { opaque dict<>; } ; diff --git a/rpc/xdr/src/glusterd1-xdr.c b/rpc/xdr/src/glusterd1-xdr.c index 6c6514c90..7fa98aaeb 100644 --- a/rpc/xdr/src/glusterd1-xdr.c +++ b/rpc/xdr/src/glusterd1-xdr.c @@ -493,7 +493,7 @@ xdr_gd1_mgmt_brick_op_rsp (XDR *xdrs, gd1_mgmt_brick_op_rsp *objp) } bool_t -xdr_gd1_mgmt_volume_lock_req (XDR *xdrs, gd1_mgmt_volume_lock_req *objp) +xdr_gd1_mgmt_v3_lock_req (XDR *xdrs, gd1_mgmt_v3_lock_req *objp) { register int32_t *buf; buf = NULL; @@ -512,7 +512,7 @@ xdr_gd1_mgmt_volume_lock_req (XDR *xdrs, gd1_mgmt_volume_lock_req *objp) } bool_t -xdr_gd1_mgmt_volume_lock_rsp (XDR *xdrs, gd1_mgmt_volume_lock_rsp *objp) +xdr_gd1_mgmt_v3_lock_rsp (XDR *xdrs, gd1_mgmt_v3_lock_rsp *objp) { register int32_t *buf; buf = NULL; @@ -533,7 +533,357 @@ xdr_gd1_mgmt_volume_lock_rsp (XDR *xdrs, gd1_mgmt_volume_lock_rsp *objp) } bool_t -xdr_gd1_mgmt_volume_unlock_req (XDR *xdrs, gd1_mgmt_volume_unlock_req *objp) +xdr_gd1_mgmt_v3_pre_val_req (XDR *xdrs, gd1_mgmt_v3_pre_val_req *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_pre_val_rsp (XDR *xdrs, gd1_mgmt_v3_pre_val_rsp *objp) +{ + register int32_t *buf; + buf = NULL; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + IXDR_PUT_LONG(buf, objp->op); + IXDR_PUT_LONG(buf, objp->op_ret); + IXDR_PUT_LONG(buf, objp->op_errno); + } + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + objp->op = IXDR_GET_LONG(buf); + objp->op_ret = IXDR_GET_LONG(buf); + objp->op_errno = IXDR_GET_LONG(buf); + } + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; + } + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_brick_op_req (XDR *xdrs, gd1_mgmt_v3_brick_op_req *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_brick_op_rsp (XDR *xdrs, gd1_mgmt_v3_brick_op_rsp *objp) +{ + register int32_t *buf; + buf = NULL; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + IXDR_PUT_LONG(buf, objp->op); + IXDR_PUT_LONG(buf, objp->op_ret); + IXDR_PUT_LONG(buf, objp->op_errno); + } + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + objp->op = IXDR_GET_LONG(buf); + objp->op_ret = IXDR_GET_LONG(buf); + objp->op_errno = IXDR_GET_LONG(buf); + } + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; + } + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_commit_req (XDR *xdrs, gd1_mgmt_v3_commit_req *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_commit_rsp (XDR *xdrs, gd1_mgmt_v3_commit_rsp *objp) +{ + register int32_t *buf; + buf = NULL; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + IXDR_PUT_LONG(buf, objp->op); + IXDR_PUT_LONG(buf, objp->op_ret); + IXDR_PUT_LONG(buf, objp->op_errno); + } + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + objp->op = IXDR_GET_LONG(buf); + objp->op_ret = IXDR_GET_LONG(buf); + objp->op_errno = IXDR_GET_LONG(buf); + } + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + return TRUE; + } + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_post_val_req (XDR *xdrs, gd1_mgmt_v3_post_val_req *objp) +{ + register int32_t *buf; + buf = NULL; + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_post_val_rsp (XDR *xdrs, gd1_mgmt_v3_post_val_rsp *objp) +{ + register int32_t *buf; + buf = NULL; + + + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + IXDR_PUT_LONG(buf, objp->op); + IXDR_PUT_LONG(buf, objp->op_ret); + IXDR_PUT_LONG(buf, objp->op_errno); + } + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + + } else { + objp->op = IXDR_GET_LONG(buf); + objp->op_ret = IXDR_GET_LONG(buf); + objp->op_errno = IXDR_GET_LONG(buf); + } + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; + } + + if (!xdr_vector (xdrs, (char *)objp->uuid, 16, + sizeof (u_char), (xdrproc_t) xdr_u_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_ret)) + return FALSE; + if (!xdr_int (xdrs, &objp->op_errno)) + return FALSE; + if (!xdr_string (xdrs, &objp->op_errstr, ~0)) + return FALSE; + if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_gd1_mgmt_v3_unlock_req (XDR *xdrs, gd1_mgmt_v3_unlock_req *objp) { register int32_t *buf; buf = NULL; @@ -544,13 +894,15 @@ xdr_gd1_mgmt_volume_unlock_req (XDR *xdrs, gd1_mgmt_volume_unlock_req *objp) if (!xdr_vector (xdrs, (char *)objp->txn_id, 16, sizeof (u_char), (xdrproc_t) xdr_u_char)) return FALSE; + if (!xdr_int (xdrs, &objp->op)) + return FALSE; if (!xdr_bytes (xdrs, (char **)&objp->dict.dict_val, (u_int *) &objp->dict.dict_len, ~0)) return FALSE; return TRUE; } bool_t -xdr_gd1_mgmt_volume_unlock_rsp (XDR *xdrs, gd1_mgmt_volume_unlock_rsp *objp) +xdr_gd1_mgmt_v3_unlock_rsp (XDR *xdrs, gd1_mgmt_v3_unlock_rsp *objp) { register int32_t *buf; buf = NULL; diff --git a/rpc/xdr/src/glusterd1-xdr.h b/rpc/xdr/src/glusterd1-xdr.h index 4115ff7a8..b6be23d06 100644 --- a/rpc/xdr/src/glusterd1-xdr.h +++ b/rpc/xdr/src/glusterd1-xdr.h @@ -202,7 +202,7 @@ struct gd1_mgmt_brick_op_rsp { }; typedef struct gd1_mgmt_brick_op_rsp gd1_mgmt_brick_op_rsp; -struct gd1_mgmt_volume_lock_req { +struct gd1_mgmt_v3_lock_req { u_char uuid[16]; u_char txn_id[16]; int op; @@ -211,9 +211,9 @@ struct gd1_mgmt_volume_lock_req { char *dict_val; } dict; }; -typedef struct gd1_mgmt_volume_lock_req gd1_mgmt_volume_lock_req; +typedef struct gd1_mgmt_v3_lock_req gd1_mgmt_v3_lock_req; -struct gd1_mgmt_volume_lock_rsp { +struct gd1_mgmt_v3_lock_rsp { u_char uuid[16]; u_char txn_id[16]; struct { @@ -223,19 +223,113 @@ struct gd1_mgmt_volume_lock_rsp { int op_ret; int op_errno; }; -typedef struct gd1_mgmt_volume_lock_rsp gd1_mgmt_volume_lock_rsp; +typedef struct gd1_mgmt_v3_lock_rsp gd1_mgmt_v3_lock_rsp; -struct gd1_mgmt_volume_unlock_req { +struct gd1_mgmt_v3_pre_val_req { + u_char uuid[16]; + int op; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_pre_val_req gd1_mgmt_v3_pre_val_req; + +struct gd1_mgmt_v3_pre_val_rsp { + u_char uuid[16]; + int op; + int op_ret; + int op_errno; + char *op_errstr; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_pre_val_rsp gd1_mgmt_v3_pre_val_rsp; + +struct gd1_mgmt_v3_brick_op_req { + u_char uuid[16]; + int op; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_brick_op_req gd1_mgmt_v3_brick_op_req; + +struct gd1_mgmt_v3_brick_op_rsp { + u_char uuid[16]; + int op; + int op_ret; + int op_errno; + char *op_errstr; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_brick_op_rsp gd1_mgmt_v3_brick_op_rsp; + +struct gd1_mgmt_v3_commit_req { + u_char uuid[16]; + int op; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_commit_req gd1_mgmt_v3_commit_req; + +struct gd1_mgmt_v3_commit_rsp { + u_char uuid[16]; + int op; + int op_ret; + int op_errno; + struct { + u_int dict_len; + char *dict_val; + } dict; + char *op_errstr; +}; +typedef struct gd1_mgmt_v3_commit_rsp gd1_mgmt_v3_commit_rsp; + +struct gd1_mgmt_v3_post_val_req { + u_char uuid[16]; + int op; + int op_ret; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_post_val_req gd1_mgmt_v3_post_val_req; + +struct gd1_mgmt_v3_post_val_rsp { + u_char uuid[16]; + int op; + int op_ret; + int op_errno; + char *op_errstr; + struct { + u_int dict_len; + char *dict_val; + } dict; +}; +typedef struct gd1_mgmt_v3_post_val_rsp gd1_mgmt_v3_post_val_rsp; + +struct gd1_mgmt_v3_unlock_req { u_char uuid[16]; u_char txn_id[16]; + int op; struct { u_int dict_len; char *dict_val; } dict; }; -typedef struct gd1_mgmt_volume_unlock_req gd1_mgmt_volume_unlock_req; +typedef struct gd1_mgmt_v3_unlock_req gd1_mgmt_v3_unlock_req; -struct gd1_mgmt_volume_unlock_rsp { +struct gd1_mgmt_v3_unlock_rsp { u_char uuid[16]; u_char txn_id[16]; struct { @@ -245,7 +339,7 @@ struct gd1_mgmt_volume_unlock_rsp { int op_ret; int op_errno; }; -typedef struct gd1_mgmt_volume_unlock_rsp gd1_mgmt_volume_unlock_rsp; +typedef struct gd1_mgmt_v3_unlock_rsp gd1_mgmt_v3_unlock_rsp; /* the xdr functions */ @@ -269,10 +363,18 @@ extern bool_t xdr_gd1_mgmt_friend_update (XDR *, gd1_mgmt_friend_update*); extern bool_t xdr_gd1_mgmt_friend_update_rsp (XDR *, gd1_mgmt_friend_update_rsp*); extern bool_t xdr_gd1_mgmt_brick_op_req (XDR *, gd1_mgmt_brick_op_req*); extern bool_t xdr_gd1_mgmt_brick_op_rsp (XDR *, gd1_mgmt_brick_op_rsp*); -extern bool_t xdr_gd1_mgmt_volume_lock_req (XDR *, gd1_mgmt_volume_lock_req*); -extern bool_t xdr_gd1_mgmt_volume_lock_rsp (XDR *, gd1_mgmt_volume_lock_rsp*); -extern bool_t xdr_gd1_mgmt_volume_unlock_req (XDR *, gd1_mgmt_volume_unlock_req*); -extern bool_t xdr_gd1_mgmt_volume_unlock_rsp (XDR *, gd1_mgmt_volume_unlock_rsp*); +extern bool_t xdr_gd1_mgmt_v3_lock_req (XDR *, gd1_mgmt_v3_lock_req*); +extern bool_t xdr_gd1_mgmt_v3_lock_rsp (XDR *, gd1_mgmt_v3_lock_rsp*); +extern bool_t xdr_gd1_mgmt_v3_pre_val_req (XDR *, gd1_mgmt_v3_pre_val_req*); +extern bool_t xdr_gd1_mgmt_v3_pre_val_rsp (XDR *, gd1_mgmt_v3_pre_val_rsp*); +extern bool_t xdr_gd1_mgmt_v3_brick_op_req (XDR *, gd1_mgmt_v3_brick_op_req*); +extern bool_t xdr_gd1_mgmt_v3_brick_op_rsp (XDR *, gd1_mgmt_v3_brick_op_rsp*); +extern bool_t xdr_gd1_mgmt_v3_commit_req (XDR *, gd1_mgmt_v3_commit_req*); +extern bool_t xdr_gd1_mgmt_v3_commit_rsp (XDR *, gd1_mgmt_v3_commit_rsp*); +extern bool_t xdr_gd1_mgmt_v3_post_val_req (XDR *, gd1_mgmt_v3_post_val_req*); +extern bool_t xdr_gd1_mgmt_v3_post_val_rsp (XDR *, gd1_mgmt_v3_post_val_rsp*); +extern bool_t xdr_gd1_mgmt_v3_unlock_req (XDR *, gd1_mgmt_v3_unlock_req*); +extern bool_t xdr_gd1_mgmt_v3_unlock_rsp (XDR *, gd1_mgmt_v3_unlock_rsp*); #else /* K&R C */ extern bool_t xdr_glusterd_volume_status (); @@ -294,10 +396,18 @@ extern bool_t xdr_gd1_mgmt_friend_update (); extern bool_t xdr_gd1_mgmt_friend_update_rsp (); extern bool_t xdr_gd1_mgmt_brick_op_req (); extern bool_t xdr_gd1_mgmt_brick_op_rsp (); -extern bool_t xdr_gd1_mgmt_volume_lock_req (); -extern bool_t xdr_gd1_mgmt_volume_lock_rsp (); -extern bool_t xdr_gd1_mgmt_volume_unlock_req (); -extern bool_t xdr_gd1_mgmt_volume_unlock_rsp (); +extern bool_t xdr_gd1_mgmt_v3_lock_req (); +extern bool_t xdr_gd1_mgmt_v3_lock_rsp (); +extern bool_t xdr_gd1_mgmt_v3_pre_val_req (); +extern bool_t xdr_gd1_mgmt_v3_pre_val_rsp (); +extern bool_t xdr_gd1_mgmt_v3_brick_op_req (); +extern bool_t xdr_gd1_mgmt_v3_brick_op_rsp (); +extern bool_t xdr_gd1_mgmt_v3_commit_req (); +extern bool_t xdr_gd1_mgmt_v3_commit_rsp (); +extern bool_t xdr_gd1_mgmt_v3_post_val_req (); +extern bool_t xdr_gd1_mgmt_v3_post_val_rsp (); +extern bool_t xdr_gd1_mgmt_v3_unlock_req (); +extern bool_t xdr_gd1_mgmt_v3_unlock_rsp (); #endif /* K&R C */ diff --git a/rpc/xdr/src/glusterd1-xdr.x b/rpc/xdr/src/glusterd1-xdr.x index f29a9d214..f5c45c9e4 100644 --- a/rpc/xdr/src/glusterd1-xdr.x +++ b/rpc/xdr/src/glusterd1-xdr.x @@ -126,14 +126,14 @@ struct gd1_mgmt_brick_op_rsp { string op_errstr<>; } ; -struct gd1_mgmt_volume_lock_req { +struct gd1_mgmt_v3_lock_req { unsigned char uuid[16]; unsigned char txn_id[16]; int op; opaque dict<>; } ; -struct gd1_mgmt_volume_lock_rsp { +struct gd1_mgmt_v3_lock_rsp { unsigned char uuid[16]; unsigned char txn_id[16]; opaque dict<>; @@ -141,13 +141,75 @@ struct gd1_mgmt_volume_lock_rsp { int op_errno; } ; -struct gd1_mgmt_volume_unlock_req { +struct gd1_mgmt_v3_pre_val_req { + unsigned char uuid[16]; + int op; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_pre_val_rsp { + unsigned char uuid[16]; + int op; + int op_ret; + int op_errno; + string op_errstr<>; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_brick_op_req { + unsigned char uuid[16]; + int op; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_brick_op_rsp { + unsigned char uuid[16]; + int op; + int op_ret; + int op_errno; + string op_errstr<>; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_commit_req { + unsigned char uuid[16]; + int op; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_commit_rsp { + unsigned char uuid[16]; + int op; + int op_ret; + int op_errno; + opaque dict<>; + string op_errstr<>; +} ; + +struct gd1_mgmt_v3_post_val_req { + unsigned char uuid[16]; + int op; + int op_ret; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_post_val_rsp { + unsigned char uuid[16]; + int op; + int op_ret; + int op_errno; + string op_errstr<>; + opaque dict<>; +} ; + +struct gd1_mgmt_v3_unlock_req { unsigned char uuid[16]; unsigned char txn_id[16]; + int op; opaque dict<>; } ; -struct gd1_mgmt_volume_unlock_rsp { +struct gd1_mgmt_v3_unlock_rsp { unsigned char uuid[16]; unsigned char txn_id[16]; opaque dict<>; diff --git a/tests/basic/mgmt_v3-locks.t b/tests/basic/mgmt_v3-locks.t new file mode 100644 index 000000000..22ca27b9f --- /dev/null +++ b/tests/basic/mgmt_v3-locks.t @@ -0,0 +1,121 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../cluster.rc + +function check_peers { + $CLI_1 peer status | grep 'Peer in Cluster (Connected)' | wc -l +} + +function volume_count { + local cli=$1; + if [ $cli -eq '1' ] ; then + $CLI_1 volume info | grep 'Volume Name' | wc -l; + else + $CLI_2 volume info | grep 'Volume Name' | wc -l; + fi +} + +function volinfo_field() +{ + local vol=$1; + local field=$2; + + $CLI_1 volume info $vol | grep "^$field: " | sed 's/.*: //'; +} + +function two_diff_vols_create { + # Both volume creates should be successful + $CLI_1 volume create $V0 $H1:$B1/$V0 $H2:$B2/$V0 $H3:$B3/$V0 & + PID_1=$! + + $CLI_2 volume create $V1 $H1:$B1/$V1 $H2:$B2/$V1 $H3:$B3/$V1 & + PID_2=$! + + wait $PID_1 $PID_2 +} + +function two_diff_vols_start { + # Both volume starts should be successful + $CLI_1 volume start $V0 & + PID_1=$! + + $CLI_2 volume start $V1 & + PID_2=$! + + wait $PID_1 $PID_2 +} + +function two_diff_vols_stop_force { + # Force stop, so that if rebalance from the + # remove bricks is in progress, stop can + # still go ahead. Both volume stops should + # be successful + $CLI_1 volume stop $V0 force & + PID_1=$! + + $CLI_2 volume stop $V1 force & + PID_2=$! + + wait $PID_1 $PID_2 +} + +function same_vol_remove_brick { + + # Running two same vol commands at the same time can result in + # two success', two failures, or one success and one failure, all + # of which are valid. The only thing that shouldn't happen is a + # glusterd crash. + + local vol=$1 + local brick=$2 + $CLI_1 volume remove-brick $1 $2 start & + $CLI_2 volume remove-brick $1 $2 start +} + +cleanup; + +TEST launch_cluster 3; +TEST $CLI_1 peer probe $H2; +TEST $CLI_1 peer probe $H3; + +EXPECT_WITHIN 20 2 check_peers + +two_diff_vols_create +EXPECT 'Created' volinfo_field $V0 'Status'; +EXPECT 'Created' volinfo_field $V1 'Status'; + +two_diff_vols_start +EXPECT 'Started' volinfo_field $V0 'Status'; +EXPECT 'Started' volinfo_field $V1 'Status'; + +same_vol_remove_brick $V0 $H2:$B2/$V0 +# Checking glusterd crashed or not after same volume remove brick +# on both nodes. +EXPECT_WITHIN 20 2 check_peers + +same_vol_remove_brick $V1 $H2:$B2/$V1 +# Checking glusterd crashed or not after same volume remove brick +# on both nodes. +EXPECT_WITHIN 20 2 check_peers + +$CLI_1 volume set $V0 diagnostics.client-log-level DEBUG & +$CLI_1 volume set $V1 diagnostics.client-log-level DEBUG +kill_glusterd 3 +$CLI_1 volume status $V0 +$CLI_2 volume status $V1 +$CLI_1 peer status +EXPECT_WITHIN 20 1 check_peers +EXPECT 'Started' volinfo_field $V0 'Status'; +EXPECT 'Started' volinfo_field $V1 'Status'; + +TEST $glusterd_3 +$CLI_1 volume status $V0 +$CLI_2 volume status $V1 +$CLI_1 peer status +#EXPECT_WITHIN 20 2 check_peers +#EXPECT 'Started' volinfo_field $V0 'Status'; +#EXPECT 'Started' volinfo_field $V1 'Status'; +#two_diff_vols_stop_force +#EXPECT_WITHIN 20 2 check_peers +cleanup; diff --git a/tests/basic/volume-locks.t b/tests/basic/volume-locks.t deleted file mode 100755 index b9e94b7e1..000000000 --- a/tests/basic/volume-locks.t +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/bash - -. $(dirname $0)/../include.rc -. $(dirname $0)/../cluster.rc - -function check_peers { - $CLI_1 peer status | grep 'Peer in Cluster (Connected)' | wc -l -} - -function volume_count { - local cli=$1; - if [ $cli -eq '1' ] ; then - $CLI_1 volume info | grep 'Volume Name' | wc -l; - else - $CLI_2 volume info | grep 'Volume Name' | wc -l; - fi -} - -function volinfo_field() -{ - local vol=$1; - local field=$2; - - $CLI_1 volume info $vol | grep "^$field: " | sed 's/.*: //'; -} - -function two_diff_vols_create { - # Both volume creates should be successful - $CLI_1 volume create $V0 $H1:$B1/$V0 $H2:$B2/$V0 $H3:$B3/$V0 & - $CLI_2 volume create $V1 $H1:$B1/$V1 $H2:$B2/$V1 $H3:$B3/$V1 -} - -function two_diff_vols_start { - # Both volume starts should be successful - $CLI_1 volume start $V0 & - $CLI_2 volume start $V1 -} - -function two_diff_vols_stop_force { - # Force stop, so that if rebalance from the - # remove bricks is in progress, stop can - # still go ahead. Both volume stops should - # be successful - $CLI_1 volume stop $V0 force & - $CLI_2 volume stop $V1 force -} - -function same_vol_remove_brick { - - # Running two same vol commands at the same time can result in - # two success', two failures, or one success and one failure, all - # of which are valid. The only thing that shouldn't happen is a - # glusterd crash. - - local vol=$1 - local brick=$2 - $CLI_1 volume remove-brick $1 $2 start & - $CLI_2 volume remove-brick $1 $2 start -} - -cleanup; - -TEST launch_cluster 3; -TEST $CLI_1 peer probe $H2; -TEST $CLI_1 peer probe $H3; - -EXPECT_WITHIN 20 2 check_peers - -two_diff_vols_create -EXPECT 'Created' volinfo_field $V0 'Status'; -EXPECT 'Created' volinfo_field $V1 'Status'; - -two_diff_vols_start -EXPECT 'Started' volinfo_field $V0 'Status'; -EXPECT 'Started' volinfo_field $V1 'Status'; - -same_vol_remove_brick $V0 $H2:$B2/$V0 -# Checking glusterd crashed or not after same volume remove brick -# on both nodes. -EXPECT_WITHIN 20 2 check_peers - -same_vol_remove_brick $V1 $H2:$B2/$V1 -# Checking glusterd crashed or not after same volume remove brick -# on both nodes. -EXPECT_WITHIN 20 2 check_peers - -$CLI_1 volume set $V0 diagnostics.client-log-level DEBUG & -$CLI_1 volume set $V1 diagnostics.client-log-level DEBUG -kill_glusterd 3 -$CLI_1 volume status $V0 -$CLI_2 volume status $V1 -$CLI_1 peer status -EXPECT_WITHIN 20 1 check_peers -EXPECT 'Started' volinfo_field $V0 'Status'; -EXPECT 'Started' volinfo_field $V1 'Status'; - -TEST $glusterd_3 -$CLI_1 volume status $V0 -$CLI_2 volume status $V1 -$CLI_1 peer status -#EXPECT_WITHIN 20 2 check_peers -#EXPECT 'Started' volinfo_field $V0 'Status'; -#EXPECT 'Started' volinfo_field $V1 'Status'; -#two_diff_vols_stop_force -#EXPECT_WITHIN 20 2 check_peers -cleanup; diff --git a/tests/basic/volume-snapshot.t b/tests/basic/volume-snapshot.t new file mode 100755 index 000000000..c826631ca --- /dev/null +++ b/tests/basic/volume-snapshot.t @@ -0,0 +1,95 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc +. $(dirname $0)/../cluster.rc +. $(dirname $0)/../snapshot.rc + +V1="patchy2" + +function create_volumes() { + $CLI_1 volume create $V0 $H1:$L1 & + PID_1=$! + + $CLI_2 volume create $V1 $H2:$L2 $H3:$L3 & + PID_2=$! + + wait $PID_1 $PID_2 +} + +function create_snapshots() { + $CLI_1 snapshot create ${V0}_snap ${V0}& + PID_1=$! + + $CLI_1 snapshot create ${V1}_snap ${V1}& + PID_2=$! + + wait $PID_1 $PID_2 +} + +function delete_snapshots() { + $CLI_1 snapshot delete ${V0}_snap & + PID_1=$! + + $CLI_1 snapshot delete ${V1}_snap & + PID_2=$! + + wait $PID_1 $PID_2 +} + +function restore_snapshots() { + $CLI_1 snapshot restore ${V0}_snap & + PID_1=$! + + $CLI_1 snapshot restore ${V1}_snap & + PID_2=$! + + wait $PID_1 $PID_2 +} +cleanup; + +#Create cluster with 3 nodes +TEST launch_cluster 3; +TEST setup_lvm 3 + +TEST $CLI_1 peer probe $H2; +TEST $CLI_1 peer probe $H3; +EXPECT_WITHIN 20 2 peer_count; + +create_volumes +EXPECT 'Created' volinfo_field $V0 'Status'; +EXPECT 'Created' volinfo_field $V1 'Status'; + +start_volumes 2 +EXPECT 'Started' volinfo_field $V0 'Status'; +EXPECT 'Started' volinfo_field $V1 'Status'; + +#Snapshot Operations +create_snapshots +TEST snapshot_exists 1 ${V0}_snap +TEST snapshot_exists 1 ${V1}_snap +TEST $CLI_1 snapshot config $V0 snap-max-hard-limit 100 +TEST $CLI_1 snapshot config $V1 snap-max-hard-limit 100 + +TEST glusterfs -s $H1 --volfile-id=/snaps/${V0}_snap/${V0} $M0 +sleep 2 +TEST umount -f $M0 +TEST glusterfs -s $H2 --volfile-id=/snaps/${V1}_snap/${V1} $M0 +sleep 2 +TEST umount -f $M0 + +#Clean up +stop_force_volumes 2 +EXPECT 'Stopped' volinfo_field $V0 'Status'; +EXPECT 'Stopped' volinfo_field $V1 'Status'; + +restore_snapshots +TEST ! snapshot_exists 1 ${V0}_snap +TEST ! snapshot_exists 1 ${V1}_snap + +delete_volumes 2 +TEST ! volume_exists $V0 +TEST ! volume_exists $V1 + +cleanup; + diff --git a/tests/bugs/bug-1045333.t b/tests/bugs/bug-1045333.t new file mode 100755 index 000000000..8f4798ebc --- /dev/null +++ b/tests/bugs/bug-1045333.t @@ -0,0 +1,51 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../snapshot.rc + +cleanup; +TEST glusterd; +TEST pidof glusterd; + +TEST setup_lvm 1 + +TEST $CLI volume create $V0 $H0:$L1 +TEST $CLI volume start $V0 + + +S1="${V0}-snap1" #Create snapshot with name contains hyphen(-) +S2="-${V0}-snap2" #Create snapshot with name starts with hyphen(-) +#Create snapshot with a long name +S3="${V0}_single_gluster_volume_is_accessible_by_multiple_clients_offline_snapshot_is_a_long_name" + +TEST $CLI snapshot create $S1 $V0 +TEST snapshot_exists 0 $S1 + +TEST $CLI snapshot create $S2 $V0 +TEST snapshot_exists 0 $S2 + +TEST $CLI snapshot create $S3 $V0 +TEST snapshot_exists 0 $S3 + + +TEST glusterfs -s $H0 --volfile-id=/snaps/$S1/$V0 $M0 +sleep 2 +TEST umount -f $M0 + +TEST glusterfs -s $H0 --volfile-id=/snaps/$S2/$V0 $M0 +sleep 2 +TEST umount -f $M0 + +TEST glusterfs -s $H0 --volfile-id=/snaps/$S3/$V0 $M0 +sleep 2 +TEST umount -f $M0 + +#Clean up +#TEST $CLI snapshot delete $S1 +#TEST $CLI snapshot delete $S2 +#TEST $CLI snapshot delete $S3 + +TEST $CLI volume stop $V0 force +#TEST $CLI volume delete $V0 + +cleanup; diff --git a/tests/bugs/bug-1049834.t b/tests/bugs/bug-1049834.t new file mode 100755 index 000000000..c1b126ba1 --- /dev/null +++ b/tests/bugs/bug-1049834.t @@ -0,0 +1,40 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../cluster.rc +. $(dirname $0)/../volume.rc +. $(dirname $0)/../snapshot.rc + +cleanup; + +TEST launch_cluster 2 +TEST setup_lvm 2 + +TEST $CLI_1 peer probe $H2 +EXPECT_WITHIN 20 1 peer_count + +TEST $CLI_1 volume create $V0 $H1:$L1 $H2:$L2 +EXPECT 'Created' volinfo_field $V0 'Status' + +TEST $CLI_1 volume start $V0 +EXPECT 'Started' volinfo_field $V0 'Status' + +#Setting the snap-max-hard-limit to 4 +TEST $CLI_1 snapshot config $V0 snap-max-hard-limit 4 +PID_1=$! +wait $PID_1 + +#Creating 4 snapshots on the volume +TEST create_n_snapshots $V0 4 $V0_snap +TEST snapshot_n_exists $V0 4 $V0_snap + +#Creating the 5th snapshots on the volume and expecting it not to be created. +TEST ! $CLI_1 snapshot create ${V0}_snap5 ${V0} +TEST ! snapshot_exists 1 ${V0}_snap5 +TEST ! $CLI_1 snapshot delete ${V0}_snap5 + +#Deleting the 4 snaps +#TEST delete_n_snapshots $V0 4 $V0_snap +#TEST ! snapshot_n_exists $V0 4 $V0_snap + +cleanup; diff --git a/tests/bugs/bug-1064768.t b/tests/bugs/bug-1064768.t new file mode 100644 index 000000000..b87168150 --- /dev/null +++ b/tests/bugs/bug-1064768.t @@ -0,0 +1,20 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc + +cleanup; + +TEST glusterd +TEST pidof glusterd +TEST $CLI volume create $V0 replica 2 $H0:$B0/brick0 $H0:$B0/brick1 +TEST $CLI volume start $V0 +EXPECT_WITHIN 15 'Started' volinfo_field $V0 'Status'; + +TEST $CLI volume profile $V0 start +TEST $CLI volume profile $V0 info +TEST $CLI volume profile $V0 stop + +TEST $CLI volume status +TEST $CLI volume stop $V0 +EXPECT_WITHIN 15 'Stopped' volinfo_field $V0 'Status'; +cleanup; diff --git a/tests/bugs/bug-948686.t b/tests/bugs/bug-948686.t old mode 100644 new mode 100755 diff --git a/tests/cluster.rc b/tests/cluster.rc index 1e42426f6..efeaa3563 100755 --- a/tests/cluster.rc +++ b/tests/cluster.rc @@ -106,3 +106,6 @@ function define_clis() { done } +function peer_count() { + $CLI_1 peer status | grep 'Peer in Cluster (Connected)' | wc -l +} diff --git a/tests/include.rc b/tests/include.rc index 250220efa..452e6d7ca 100644 --- a/tests/include.rc +++ b/tests/include.rc @@ -225,6 +225,7 @@ function cleanup() umount $m done + type cleanup_lvm &>/dev/null && cleanup_lvm LOOPDEVICES=`losetup -a | grep "$B0/" | awk '{print $1}' | tr -d :` for l in $LOOPDEVICES; @@ -239,6 +240,7 @@ function cleanup() umount -l $M1 2>/dev/null || true; umount -l $N0 2>/dev/null || true; umount -l $N1 2>/dev/null || true; + } function volinfo_field() diff --git a/tests/snapshot.rc b/tests/snapshot.rc new file mode 100755 index 000000000..440059fc1 --- /dev/null +++ b/tests/snapshot.rc @@ -0,0 +1,290 @@ +#!/bin/bash + +LVM_DEFINED=0 +LVM_PREFIX="patchy_snap" +LVM_COUNT=0 +VHD_SIZE="1G" + +function init_lvm() { + if [ "$1" == "" ]; then + echo "Error: Invalid argument supplied" + return 1 + fi + LVM_COUNT=$1 + + if [ "$2" != "" ]; then + VHD_SIZE=$2 + fi + + local b + local i + + if [ "$B1" = "" ]; then + B1=$B0 + fi + + for i in `seq 1 $LVM_COUNT`; do + b="B$i" + if [ "${!b}" = "" ]; then + echo "Error: $b not defined." + echo "Please run launch_cluster with atleast $LVM_COUNT nodes" + return 1 + fi + + eval "L$i=${!b}/${LVM_PREFIX}_mnt" + l="L$i" + mkdir -p ${!l} + if [ $? -ne 0 ]; then + echo "Error: failed to create dir ${!l}" + return 1 + fi + + eval "VG$i=${LVM_PREFIX}_vg_${i}" + done + + LVM_DEFINED=1 + return 0 +} + +function setup_lvm() { + init_lvm $@ || return 1 + _setup_lvm + return 0 +} + +function cleanup_lvm() { + pkill gluster + sleep 2 + + if [ "$LVM_DEFINED" = "1" ]; then + _cleanup_lvm >/dev/null 2>&1 + fi + + _cleanup_lvm_again >/dev/null 2>&1 + # TODO Delete cleanup has open bug + # once fixed delete this + mount | grep "run/gluster/snaps" | awk '{print $3}' | xargs umount 2> /dev/null + mount | grep "patchy_snap" | awk '{print $3}' | xargs umount 2> /dev/null + \rm -rf /var/run/gluster/snaps/* + lvscan | grep "/dev/patchy_snap" | awk '{print $2}'| xargs lvremove -f 2> /dev/null + vgs | grep patchy_snap | awk '{print $1}' | xargs vgremove -f 2>/dev/null + \rm -rf /dev/patchy* + return 0 +} + +######################################################## +# Private Functions +######################################################## +function _setup_lvm() { + local count=$LVM_COUNT + local b + local i + + for i in `seq 1 $count`; do + b="B$i" + + _create_vhd ${!b} $i + _create_lv ${!b} $i + _mount_lv $i + done +} + +function _cleanup_lvm() { + local count=$LVM_COUNT + local b + local i + + for i in `seq 1 $count`; do + b="B$i" + _umount_lv $i + _remove_lv $i + _remove_vhd ${!b} + done +} + +function _cleanup_lvm_again() { + local file + + mount | grep $LVM_PREFIX | awk '{print $3}' | xargs -r umount -f + + /sbin/vgs | grep $LVM_PREFIX | awk '{print $1}' | xargs -r vgremove -f + + find $B0 -name "${LVM_PREFIX}_loop" | xargs -r losetup -d + + find $B0 -name "${LVM_PREFIX}*" | xargs -r rm -rf + + find /run/gluster/snaps -name "*${LVM_PREFIX}*" | xargs -r rm -rf + + for file in `ls /run/gluster/snaps`; do + find /run/gluster/snaps/$file -mmin -2 | xargs -r rm -rf + done +} + +######################################################## +######################################################## +function _create_vhd() { + local dir=$1 + local num=$2 + local loop_num=`expr $2 + 8` + + fallocate -l${VHD_SIZE} $dir/${LVM_PREFIX}_vhd + mknod -m660 $dir/${LVM_PREFIX}_loop b 7 $loop_num + /sbin/losetup $dir/${LVM_PREFIX}_loop $dir/${LVM_PREFIX}_vhd +} + +function _create_lv() { + local dir=$1 + local num=$2 + local vg="VG$num" + local thinpoolsize="0.8G" + local virtualsize="0.6G" + + /sbin/pvcreate $dir/${LVM_PREFIX}_loop + /sbin/vgcreate ${!vg} $dir/${LVM_PREFIX}_loop + + /sbin/lvcreate -L ${thinpoolsize} -T /dev/${!vg}/thinpool + /sbin/lvcreate -V ${virtualsize} -T /dev/${!vg}/thinpool -n brick_lvm + + mkfs.xfs -f /dev/${!vg}/brick_lvm +} + +function _mount_lv() { + local num=$1 + local vg="VG$num" + local l="L$num" + + mount -t xfs -o nouuid /dev/${!vg}/brick_lvm ${!l} +} + +function _umount_lv() { + local num=$1 + local l="L$num" + + umount -f ${!l} 2>/dev/null || true + rmdir ${!l} 2>/dev/null || true +} + +function _remove_lv() { + local num=$1 + local vg="VG$num" + + vgremove -f ${!vg} +} + +function _remove_vhd() { + local dir=$1 + + losetup -d $dir/${LVM_PREFIX}_loop + rm -f $dir/${LVM_PREFIX}_loop + rm -f $dir/${LVM_PREFIX}_vhd +} + +######################################################## +# Utility Functions +######################################################## +function snapshot_exists() { + local clitype=$1 + local snapname=$2 + local cli=$CLI + if [ "$clitype" == "1" ]; then + cli=$CLI_1; + fi + if [ "$clitype" == "2" ]; then + cli=$CLI_2; + fi + $cli snapshot list | egrep -q "^$snapname\$" + return $? +} + +#Create N number of snaps in a given volume +#Arg1 : +#Arg2 : +#Arg3 : +#Return: Returns 0 if all snaps are created , +# if not will return exit code of last failed +# snap create command. +function create_n_snapshots() { + local cli=$1 + local vol=$1 + local snap_count=$2 + local snap_name=$3 + local ret=0 + for i in `seq 1 $snap_count`; do + $CLI_1 snapshot create $snap_name$i ${vol}& + PID_1=$! + wait $PID_1 + ret=$? + if [ "$ret" != "0" ]; then + break + fi + done + return $ret +} + + +#Delete N number of snaps in a given volume +#Arg1 : +#Arg2 : +#Arg3 : +#Return: Returns 0 if all snaps are Delete, +# if not will return exit code of last failed +# snap delete command. +function delete_n_snapshots() { + local vol=$1 + local snap_count=$2 + local snap_name=$3 + local ret=0 + for i in `seq 1 $snap_count`; do + $CLI_1 snapshot delete $snap_name$i & + PID_1=$! + wait $PID_1 + temp=$? + if [ "$temp" != "0" ]; then + ret=$temp + fi + done + return $ret +} + +#Check for the existance of N number of snaps in a given volume +#Arg1 : +#Arg2 : +#Arg3 : +#Return: Returns 0 if all snaps exists, +# if not will return exit code of last failed +# snapshot_exists(). +function snapshot_n_exists() { + local vol=$1 + local snap_count=$2 + local snap_name=$3 + local ret=0 + for i in `seq 1 $snap_count`; do + snapshot_exists 1 $snap_name$i + ret=$? + if [ "$ret" != "0" ]; then + break + fi + done + return $ret +} + +# TODO: Cleanup code duplication +function volinfo_field() +{ + local vol=$1; + local field=$2; + + $CLI_1 volume info $vol | grep "^$field: " | sed 's/.*: //'; +} + + +function volume_exists() { + local volname=$1 + $CLI_1 volume info $volname 2>&1 | grep -q 'does not exist' + if [ $? -eq 0 ]; then + return 1 + else + return 0 + fi +} + diff --git a/tests/volume.rc b/tests/volume.rc index 9e4843e06..7d2494067 100644 --- a/tests/volume.rc +++ b/tests/volume.rc @@ -308,3 +308,54 @@ function has_holes { echo "0" fi } + +function do_volume_operations() { + local operation=$1 + local count=$2 + local force=$3 + + local pids=() + local cli + local v + + for i in `seq 1 $count`; do + cli="CLI_$i" + v="V`expr $i - 1`" + ${!cli} volume $operation ${!v} $force & + pids[$i]=$! + done + + for i in `seq 1 $count`; do + wait ${pids[$i]} + done +} + +function start_volumes() { + do_volume_operations start $1 +} + +function stop_volumes() { + do_volume_operations stop $1 +} + +function start_force_volumes() { + do_volume_operations start $1 force +} + +function stop_force_volumes() { + do_volume_operations stop $1 force +} + +function delete_volumes() { + do_volume_operations delete $1 +} + +function volume_exists() { + local volname=$1 + $CLI volume info $volname 2>&1 | grep -q 'does not exist' + if [ $? -eq 0 ]; then + return 1 + else + return 0 + fi +} diff --git a/xlators/mgmt/glusterd/src/Makefile.am b/xlators/mgmt/glusterd/src/Makefile.am index b109e6dff..933c44019 100644 --- a/xlators/mgmt/glusterd/src/Makefile.am +++ b/xlators/mgmt/glusterd/src/Makefile.am @@ -12,7 +12,8 @@ glusterd_la_SOURCES = glusterd.c glusterd-handler.c glusterd-sm.c \ glusterd-geo-rep.c glusterd-replace-brick.c glusterd-log-ops.c \ glusterd-volume-ops.c glusterd-brick-ops.c glusterd-mountbroker.c \ glusterd-syncop.c glusterd-hooks.c glusterd-volume-set.c \ - glusterd-locks.c + glusterd-locks.c glusterd-snapshot.c glusterd-mgmt-handler.c \ + glusterd-mgmt.c glusterd_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ $(top_builddir)/rpc/xdr/src/libgfxdr.la \ @@ -22,7 +23,8 @@ glusterd_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ noinst_HEADERS = glusterd.h glusterd-utils.h glusterd-op-sm.h \ glusterd-sm.h glusterd-store.h glusterd-mem-types.h \ glusterd-pmap.h glusterd-volgen.h glusterd-mountbroker.h \ - glusterd-syncop.h glusterd-hooks.h glusterd-locks.h + glusterd-syncop.h glusterd-hooks.h glusterd-locks.h \ + glusterd-mgmt.h AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ -I$(rpclibdir) -I$(CONTRIBDIR)/rbtree \ diff --git a/xlators/mgmt/glusterd/src/glusterd-handler.c b/xlators/mgmt/glusterd/src/glusterd-handler.c index 12f3705b5..58c030ca0 100644 --- a/xlators/mgmt/glusterd/src/glusterd-handler.c +++ b/xlators/mgmt/glusterd/src/glusterd-handler.c @@ -376,6 +376,21 @@ glusterd_add_volume_detail_to_dict (glusterd_volinfo_t *volinfo, if (ret) goto out; + /* As of now, the snap volumes are also displayed as part of + volume info command. So this change is to display whether + the volume is original volume or the snap_volume. If + displaying of snap volumes in volume info o/p is not needed + this should be removed. + */ + snprintf (key, 256, "volume%d.snap_volume", count); + ret = dict_set_int32 (volumes, key, volinfo->is_snap_volume); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "failed to set whether " + "the volume is a snap volume or actual volume (%s)", + volinfo->volname); + goto out; + } + snprintf (key, 256, "volume%d.brick_count", count); ret = dict_set_int32 (volumes, key, volinfo->brick_count); if (ret) @@ -634,7 +649,7 @@ glusterd_op_txn_begin (rpcsvc_request_t *req, glusterd_op_t op, void *ctx, goto out; } - /* Based on the op_version, acquire a cluster or volume lock */ + /* Based on the op_version, acquire a cluster or mgmt_v3 lock */ if (priv->op_version < GD_OP_VERSION_4) { ret = glusterd_lock (MY_UUID); if (ret) { @@ -664,7 +679,7 @@ glusterd_op_txn_begin (rpcsvc_request_t *req, glusterd_op_t op, void *ctx, goto out; } - ret = glusterd_volume_lock (volname, MY_UUID); + ret = glusterd_mgmt_v3_lock (volname, MY_UUID, "vol"); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to acquire lock for %s", volname); @@ -711,11 +726,12 @@ local_locking_done: out: if (locked && ret) { /* Based on the op-version, we release the - * cluster or volume lock */ + * cluster or mgmt_v3 lock */ if (priv->op_version < GD_OP_VERSION_4) glusterd_unlock (MY_UUID); else { - ret = glusterd_volume_unlock (volname, MY_UUID); + ret = glusterd_mgmt_v3_unlock (volname, MY_UUID, + "vol"); if (ret) gf_log (this->name, GF_LOG_ERROR, "Unable to release lock for %s", @@ -822,188 +838,6 @@ glusterd_handle_cluster_lock (rpcsvc_request_t *req) __glusterd_handle_cluster_lock); } -static int -glusterd_handle_volume_lock_fn (rpcsvc_request_t *req) -{ - gd1_mgmt_volume_lock_req lock_req = {{0},}; - int32_t ret = -1; - glusterd_op_lock_ctx_t *ctx = NULL; - glusterd_peerinfo_t *peerinfo = NULL; - xlator_t *this = NULL; - glusterd_op_info_t txn_op_info = {{0},}; - - this = THIS; - GF_ASSERT (this); - GF_ASSERT (req); - - ret = xdr_to_generic (req->msg[0], &lock_req, - (xdrproc_t)xdr_gd1_mgmt_volume_lock_req); - if (ret < 0) { - gf_log (this->name, GF_LOG_ERROR, "Failed to decode lock " - "request received from peer"); - req->rpc_err = GARBAGE_ARGS; - goto out; - } - - gf_log (this->name, GF_LOG_DEBUG, "Received volume lock req " - "from uuid: %s txn_id: %s", uuid_utoa (lock_req.uuid), - uuid_utoa (lock_req.txn_id)); - - if (glusterd_friend_find_by_uuid (lock_req.uuid, &peerinfo)) { - gf_log (this->name, GF_LOG_WARNING, "%s doesn't " - "belong to the cluster. Ignoring request.", - uuid_utoa (lock_req.uuid)); - ret = -1; - goto out; - } - - ctx = GF_CALLOC (1, sizeof (*ctx), gf_gld_mt_op_lock_ctx_t); - if (!ctx) { - ret = -1; - goto out; - } - - uuid_copy (ctx->uuid, lock_req.uuid); - ctx->req = req; - - ctx->dict = dict_new (); - if (!ctx->dict) { - ret = -1; - goto out; - } - - ret = dict_unserialize (lock_req.dict.dict_val, - lock_req.dict.dict_len, &ctx->dict); - if (ret) { - gf_log (this->name, GF_LOG_WARNING, - "failed to unserialize the dictionary"); - goto out; - } - - glusterd_txn_opinfo_init (&txn_op_info, NULL, &lock_req.op, - ctx->dict, req); - - ret = glusterd_set_txn_opinfo (&lock_req.txn_id, &txn_op_info); - if (ret) { - gf_log (this->name, GF_LOG_ERROR, - "Unable to set transaction's opinfo"); - goto out; - } - - ret = glusterd_op_sm_inject_event (GD_OP_EVENT_LOCK, - &lock_req.txn_id, ctx); - if (ret) - gf_log (this->name, GF_LOG_ERROR, - "Failed to inject event GD_OP_EVENT_LOCK"); - -out: - if (ret) { - if (ctx) { - if (ctx->dict) - dict_destroy (ctx->dict); - GF_FREE (ctx); - } - } - - glusterd_friend_sm (); - glusterd_op_sm (); - - gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); - return ret; -} - -int -glusterd_handle_volume_lock (rpcsvc_request_t *req) -{ - return glusterd_big_locked_handler (req, - glusterd_handle_volume_lock_fn); -} - -static int -glusterd_handle_volume_unlock_fn (rpcsvc_request_t *req) -{ - gd1_mgmt_volume_unlock_req lock_req = {{0},}; - int32_t ret = -1; - glusterd_op_lock_ctx_t *ctx = NULL; - glusterd_peerinfo_t *peerinfo = NULL; - xlator_t *this = NULL; - - this = THIS; - GF_ASSERT (this); - GF_ASSERT (req); - - ret = xdr_to_generic (req->msg[0], &lock_req, - (xdrproc_t)xdr_gd1_mgmt_volume_unlock_req); - if (ret < 0) { - gf_log (this->name, GF_LOG_ERROR, "Failed to decode unlock " - "request received from peer"); - req->rpc_err = GARBAGE_ARGS; - goto out; - } - - gf_log (this->name, GF_LOG_DEBUG, "Received volume unlock req " - "from uuid: %s", uuid_utoa (lock_req.uuid)); - - if (glusterd_friend_find_by_uuid (lock_req.uuid, &peerinfo)) { - gf_log (this->name, GF_LOG_WARNING, "%s doesn't " - "belong to the cluster. Ignoring request.", - uuid_utoa (lock_req.uuid)); - ret = -1; - goto out; - } - - ctx = GF_CALLOC (1, sizeof (*ctx), gf_gld_mt_op_lock_ctx_t); - if (!ctx) { - ret = -1; - goto out; - } - - uuid_copy (ctx->uuid, lock_req.uuid); - ctx->req = req; - - ctx->dict = dict_new (); - if (!ctx->dict) { - ret = -1; - goto out; - } - - ret = dict_unserialize (lock_req.dict.dict_val, - lock_req.dict.dict_len, &ctx->dict); - if (ret) { - gf_log (this->name, GF_LOG_WARNING, - "failed to unserialize the dictionary"); - goto out; - } - - ret = glusterd_op_sm_inject_event (GD_OP_EVENT_UNLOCK, - &lock_req.txn_id, ctx); - if (ret) - gf_log (this->name, GF_LOG_ERROR, - "Failed to inject event GD_OP_EVENT_UNLOCK"); - -out: - if (ret) { - if (ctx) { - if (ctx->dict) - dict_destroy (ctx->dict); - GF_FREE (ctx); - } - } - - glusterd_friend_sm (); - glusterd_op_sm (); - - gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); - return ret; -} - -int -glusterd_handle_volume_unlock (rpcsvc_request_t *req) -{ - return glusterd_big_locked_handler (req, - glusterd_handle_volume_unlock_fn); -} - int glusterd_req_ctx_create (rpcsvc_request_t *rpc_req, glusterd_op_t op, uuid_t uuid, @@ -2243,12 +2077,12 @@ glusterd_op_unlock_send_resp (rpcsvc_request_t *req, int32_t status) } int -glusterd_op_volume_lock_send_resp (rpcsvc_request_t *req, uuid_t *txn_id, +glusterd_op_mgmt_v3_lock_send_resp (rpcsvc_request_t *req, uuid_t *txn_id, int32_t status) { - gd1_mgmt_volume_lock_rsp rsp = {{0},}; - int ret = -1; + gd1_mgmt_v3_lock_rsp rsp = {{0},}; + int ret = -1; GF_ASSERT (req); GF_ASSERT (txn_id); @@ -2259,20 +2093,20 @@ glusterd_op_volume_lock_send_resp (rpcsvc_request_t *req, uuid_t *txn_id, uuid_copy (rsp.txn_id, *txn_id); ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, - (xdrproc_t)xdr_gd1_mgmt_volume_lock_rsp); + (xdrproc_t)xdr_gd1_mgmt_v3_lock_rsp); - gf_log (THIS->name, GF_LOG_DEBUG, "Responded to volume lock, ret: %d", + gf_log (THIS->name, GF_LOG_DEBUG, "Responded to mgmt_v3 lock, ret: %d", ret); return ret; } int -glusterd_op_volume_unlock_send_resp (rpcsvc_request_t *req, uuid_t *txn_id, +glusterd_op_mgmt_v3_unlock_send_resp (rpcsvc_request_t *req, uuid_t *txn_id, int32_t status) { - gd1_mgmt_volume_unlock_rsp rsp = {{0},}; + gd1_mgmt_v3_unlock_rsp rsp = {{0},}; int ret = -1; GF_ASSERT (req); @@ -2284,9 +2118,9 @@ glusterd_op_volume_unlock_send_resp (rpcsvc_request_t *req, uuid_t *txn_id, uuid_copy (rsp.txn_id, *txn_id); ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, - (xdrproc_t)xdr_gd1_mgmt_volume_unlock_rsp); + (xdrproc_t)xdr_gd1_mgmt_v3_unlock_rsp); - gf_log (THIS->name, GF_LOG_DEBUG, "Responded to volume unlock, ret: %d", + gf_log (THIS->name, GF_LOG_DEBUG, "Responded to mgmt_v3 unlock, ret: %d", ret); return ret; @@ -4069,8 +3903,12 @@ get_brickinfo_from_brickid (char *brickid, glusterd_brickinfo_t **brickinfo) brick++; uuid_parse (volid_str, volid); ret = glusterd_volinfo_find_by_volume_id (volid, &volinfo); - if (ret) - goto out; + if (ret) { + /* Check if it a snapshot volume */ + ret = glusterd_snap_volinfo_find_by_volume_id (volid, &volinfo); + if (ret) + goto out; + } ret = glusterd_volume_brickinfo_get_by_brick (brick, volinfo, brickinfo); @@ -4284,9 +4122,10 @@ __glusterd_peer_rpc_notify (struct rpc_clnt *rpc, void *mydata, } else { list_for_each_entry (volinfo, &conf->volumes, vol_list) { - ret = glusterd_volume_unlock + ret = glusterd_mgmt_v3_unlock (volinfo->volname, - peerinfo->uuid); + peerinfo->uuid, + "vol"); if (ret) gf_log (this->name, GF_LOG_TRACE, @@ -4414,8 +4253,9 @@ rpcsvc_actor_t gd_svc_cli_actors[] = { [GLUSTER_CLI_STATEDUMP_VOLUME] = {"STATEDUMP_VOLUME", GLUSTER_CLI_STATEDUMP_VOLUME, glusterd_handle_cli_statedump_volume, NULL, 0, DRC_NA}, [GLUSTER_CLI_LIST_VOLUME] = {"LIST_VOLUME", GLUSTER_CLI_LIST_VOLUME, glusterd_handle_cli_list_volume, NULL, 0, DRC_NA}, [GLUSTER_CLI_CLRLOCKS_VOLUME] = {"CLEARLOCKS_VOLUME", GLUSTER_CLI_CLRLOCKS_VOLUME, glusterd_handle_cli_clearlocks_volume, NULL, 0, DRC_NA}, - [GLUSTER_CLI_COPY_FILE] = {"COPY_FILE", GLUSTER_CLI_COPY_FILE, glusterd_handle_copy_file, NULL, 0, DRC_NA}, - [GLUSTER_CLI_SYS_EXEC] = {"SYS_EXEC", GLUSTER_CLI_SYS_EXEC, glusterd_handle_sys_exec, NULL, 0, DRC_NA}, + [GLUSTER_CLI_COPY_FILE] = {"COPY_FILE", GLUSTER_CLI_COPY_FILE, glusterd_handle_copy_file, NULL, 0, DRC_NA}, + [GLUSTER_CLI_SYS_EXEC] = {"SYS_EXEC", GLUSTER_CLI_SYS_EXEC, glusterd_handle_sys_exec, NULL, 0, DRC_NA}, + [GLUSTER_CLI_SNAP] = {"SNAP", GLUSTER_CLI_SNAP, glusterd_handle_snapshot, NULL, 0, DRC_NA}, }; struct rpcsvc_program gd_svc_cli_prog = { @@ -4447,18 +4287,3 @@ struct rpcsvc_program gd_svc_cli_prog_ro = { .actors = gd_svc_cli_actors_ro, .synctask = _gf_true, }; - -rpcsvc_actor_t gd_svc_mgmt_v3_actors[] = { - [GLUSTERD_MGMT_V3_NULL] = { "NULL", GLUSTERD_MGMT_V3_NULL, glusterd_null, NULL, 0, DRC_NA}, - [GLUSTERD_MGMT_V3_VOLUME_LOCK] = { "VOL_LOCK", GLUSTERD_MGMT_V3_VOLUME_LOCK, glusterd_handle_volume_lock, NULL, 0, DRC_NA}, - [GLUSTERD_MGMT_V3_VOLUME_UNLOCK] = { "VOL_UNLOCK", GLUSTERD_MGMT_V3_VOLUME_UNLOCK, glusterd_handle_volume_unlock, NULL, 0, DRC_NA}, -}; - -struct rpcsvc_program gd_svc_mgmt_v3_prog = { - .progname = "GlusterD svc mgmt v3", - .prognum = GD_MGMT_PROGRAM, - .progver = GD_MGMT_V3_VERSION, - .numactors = GLUSTERD_MGMT_V3_MAXVALUE, - .actors = gd_svc_mgmt_v3_actors, - .synctask = _gf_true, -}; diff --git a/xlators/mgmt/glusterd/src/glusterd-handshake.c b/xlators/mgmt/glusterd/src/glusterd-handshake.c index e0508faf6..5078526e9 100644 --- a/xlators/mgmt/glusterd/src/glusterd-handshake.c +++ b/xlators/mgmt/glusterd/src/glusterd-handshake.c @@ -36,21 +36,139 @@ extern struct rpc_clnt_program gd_mgmt_v3_prog; typedef ssize_t (*gfs_serialize_t) (struct iovec outmsg, void *data); +static int +get_snap_volname_and_volinfo (const char *volpath, char **volname, + glusterd_volinfo_t **volinfo) +{ + int ret = -1; + char *save_ptr = NULL; + char *str_token = NULL; + char *snapname = NULL; + char *volname_token = NULL; + char *vol = NULL; + glusterd_snap_t *snap = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (volpath); + GF_ASSERT (volinfo); + + str_token = gf_strdup (volpath); + if (NULL == str_token) { + goto out; + } + + /* Input volname will have below formats: + * /snaps//. + * or + * /snaps// + * We need to extract snapname and parent_volname */ + + /*split string by "/" */ + strtok_r (str_token, "/", &save_ptr); + snapname = strtok_r(NULL, "/", &save_ptr); + if (!snapname) { + gf_log(this->name, GF_LOG_ERROR, "Invalid path: %s", volpath); + goto out; + } + + volname_token = strtok_r(NULL, "/", &save_ptr); + if (!volname_token) { + gf_log(this->name, GF_LOG_ERROR, "Invalid path: %s", volpath); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + gf_log(this->name, GF_LOG_ERROR, "Failed to " + "fetch snap %s", snapname); + goto out; + } + + /* Find if its a parent volume name or snap volume + * name. This function will succeed if volname_token + * is a parent volname + */ + ret = glusterd_volinfo_find (volname_token, volinfo); + if (ret) { + *volname = gf_strdup (volname_token); + if (NULL == *volname) { + ret = -1; + goto out; + } + + ret = glusterd_snap_volinfo_find (volname_token, snap, + volinfo); + if (ret) { + /* Split the volume name */ + vol = strtok_r (volname_token, ".", &save_ptr); + if (!vol) { + gf_log(this->name, GF_LOG_ERROR, "Invalid " + "volname (%s)", volname_token); + goto out; + } + + ret = glusterd_snap_volinfo_find (vol, snap, volinfo); + if (ret) { + gf_log(this->name, GF_LOG_ERROR, "Failed to " + "fetch snap volume from volname (%s)", + vol); + goto out; + } + } + } else { + /*volname_token is parent volname*/ + ret = glusterd_snap_volinfo_find_from_parent_volname ( + volname_token, snap, volinfo); + if (ret) { + gf_log(this->name, GF_LOG_ERROR, "Failed to " + "fetch snap volume from parent " + "volname (%s)", volname_token); + goto out; + } + + /* Since volname_token is a parent volname we should + * get the snap volname here*/ + *volname = gf_strdup ((*volinfo)->volname); + if (NULL == *volname) { + ret = -1; + goto out; + } + } + +out: + if (ret && NULL != *volname) { + GF_FREE (*volname); + *volname = NULL; + } + return ret; +} + static size_t build_volfile_path (const char *volname, char *path, size_t path_len, char *trusted_str) { - struct stat stbuf = {0,}; - int32_t ret = -1; - glusterd_conf_t *priv = NULL; - char *vol = NULL; - char *dup_volname = NULL; - char *free_ptr = NULL; - char *tmp = NULL; - glusterd_volinfo_t *volinfo = NULL; - char *server = NULL; - - priv = THIS->private; + struct stat stbuf = {0,}; + int32_t ret = -1; + glusterd_conf_t *priv = NULL; + char *vol = NULL; + char *dup_volname = NULL; + char *free_ptr = NULL; + char *save_ptr = NULL; + char *str_token = NULL; + glusterd_volinfo_t *volinfo = NULL; + char *server = NULL; + const char *volname_ptr = NULL; + char path_prefix [PATH_MAX] = {0,}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (volname); + GF_ASSERT (path); if (strstr (volname, "gluster/")) { server = strchr (volname, '/') + 1; @@ -58,6 +176,22 @@ build_volfile_path (const char *volname, char *path, path, path_len); ret = 1; goto out; + } else if ((str_token = strstr (volname, "/snaps/"))) { + ret = get_snap_volname_and_volinfo (str_token, &dup_volname, + &volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get snap" + " volinfo from path (%s)", volname); + ret = -1; + goto out; + } + + snprintf (path_prefix, sizeof (path_prefix), "%s/snaps/%s", + priv->workdir, volinfo->snapshot->snapname); + + free_ptr = dup_volname; + volname_ptr = dup_volname; + goto gotvolinfo; } else if (volname[0] != '/') { /* Normal behavior */ dup_volname = gf_strdup (volname); @@ -68,39 +202,53 @@ build_volfile_path (const char *volname, char *path, dup_volname = gf_strdup (&volname[1]); } + if (!dup_volname) { + gf_log(THIS->name, GF_LOG_ERROR, "strdup failed"); + ret = -1; + goto out; + } free_ptr = dup_volname; + volname_ptr = volname; + + snprintf (path_prefix, sizeof (path_prefix), "%s/vols", + priv->workdir); ret = glusterd_volinfo_find (dup_volname, &volinfo); + if (ret) { /* Split the volume name */ - vol = strtok_r (dup_volname, ".", &tmp); + vol = strtok_r (dup_volname, ".", &save_ptr); if (!vol) goto out; + ret = glusterd_volinfo_find (vol, &volinfo); if (ret) goto out; } +gotvolinfo: if (!glusterd_auth_get_username (volinfo)) trusted_str = NULL; - ret = snprintf (path, path_len, "%s/vols/%s/%s.vol", - priv->workdir, volinfo->volname, volname); + ret = snprintf (path, path_len, "%s/%s/%s.vol", path_prefix, + volinfo->volname, volname_ptr); if (ret == -1) goto out; ret = stat (path, &stbuf); if ((ret == -1) && (errno == ENOENT)) { - snprintf (path, path_len, "%s/vols/%s/%s%s-fuse.vol", - priv->workdir, volinfo->volname, - (trusted_str ? trusted_str : ""), dup_volname); + snprintf (path, path_len, "%s/%s/%s%s-fuse.vol", + path_prefix, volinfo->volname, + (trusted_str ? trusted_str : ""), + dup_volname); + ret = stat (path, &stbuf); } if ((ret == -1) && (errno == ENOENT)) { - snprintf (path, path_len, "%s/vols/%s/%s-tcp.vol", - priv->workdir, volinfo->volname, volname); + snprintf (path, path_len, "%s/%s/%s-tcp.vol", + path_prefix, volinfo->volname, volname_ptr); } ret = 1; diff --git a/xlators/mgmt/glusterd/src/glusterd-locks.c b/xlators/mgmt/glusterd/src/glusterd-locks.c index 9e8bbc21b..28358aa55 100644 --- a/xlators/mgmt/glusterd/src/glusterd-locks.c +++ b/xlators/mgmt/glusterd/src/glusterd-locks.c @@ -26,10 +26,40 @@ #include -/* Initialize the global vol-lock list(dict) when +#define GF_MAX_LOCKING_ENTITIES 2 + +/* Valid entities that the mgmt_v3 lock can hold locks upon * + * To add newer entities to be locked, we can just add more * + * entries to this table along with the type and default value */ +glusterd_valid_entities valid_types[] = { + { "vol", _gf_true }, + { "snap", _gf_false }, + { NULL }, +}; + +/* Checks if the lock request is for a valid entity */ +gf_boolean_t +glusterd_mgmt_v3_is_type_valid (char *type) +{ + int32_t i = 0; + gf_boolean_t ret = _gf_false; + + GF_ASSERT (type); + + for (i = 0; valid_types[i].type; i++) { + if (!strcmp (type, valid_types[i].type)) { + ret = _gf_true; + break; + } + } + + return ret; +} + +/* Initialize the global mgmt_v3 lock list(dict) when * glusterd is spawned */ int32_t -glusterd_vol_lock_init () +glusterd_mgmt_v3_lock_init () { int32_t ret = -1; xlator_t *this = NULL; @@ -40,8 +70,8 @@ glusterd_vol_lock_init () priv = this->private; GF_ASSERT (priv); - priv->vol_lock = dict_new (); - if (!priv->vol_lock) + priv->mgmt_v3_lock = dict_new (); + if (!priv->mgmt_v3_lock) goto out; ret = 0; @@ -49,10 +79,10 @@ out: return ret; } -/* Destroy the global vol-lock list(dict) when +/* Destroy the global mgmt_v3 lock list(dict) when * glusterd cleanup is performed */ void -glusterd_vol_lock_fini () +glusterd_mgmt_v3_lock_fini () { xlator_t *this = NULL; glusterd_conf_t *priv = NULL; @@ -62,31 +92,31 @@ glusterd_vol_lock_fini () priv = this->private; GF_ASSERT (priv); - if (priv->vol_lock) - dict_unref (priv->vol_lock); + if (priv->mgmt_v3_lock) + dict_unref (priv->mgmt_v3_lock); } int32_t -glusterd_get_vol_lock_owner (char *volname, uuid_t *uuid) +glusterd_get_mgmt_v3_lock_owner (char *key, uuid_t *uuid) { - int32_t ret = -1; - glusterd_vol_lock_obj *lock_obj = NULL; - glusterd_conf_t *priv = NULL; - uuid_t no_owner = {0,}; - xlator_t *this = NULL; + int32_t ret = -1; + glusterd_mgmt_v3_lock_obj *lock_obj = NULL; + glusterd_conf_t *priv = NULL; + uuid_t no_owner = {0,}; + xlator_t *this = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); - if (!volname || !uuid) { - gf_log ("", GF_LOG_ERROR, "volname or uuid is null."); + if (!key || !uuid) { + gf_log (this->name, GF_LOG_ERROR, "key or uuid is null."); ret = -1; goto out; } - ret = dict_get_bin (priv->vol_lock, volname, (void **) &lock_obj); + ret = dict_get_bin (priv->mgmt_v3_lock, key, (void **) &lock_obj); if (!ret) uuid_copy (*uuid, lock_obj->lock_owner); else @@ -94,49 +124,433 @@ glusterd_get_vol_lock_owner (char *volname, uuid_t *uuid) ret = 0; out: - gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* This function is called with the locked_count and type, to * + * release all the acquired locks. */ +static int32_t +glusterd_release_multiple_locks_per_entity (dict_t *dict, uuid_t uuid, + int32_t locked_count, + char *type) +{ + char name_buf[PATH_MAX] = ""; + char *name = NULL; + int32_t i = -1; + int32_t op_ret = 0; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT (dict); + GF_ASSERT (type); + + if (locked_count == 0) { + gf_log (this->name, GF_LOG_DEBUG, + "No %s locked as part of this transaction", + type); + goto out; + } + + /* Release all the locks held */ + for (i = 0; i < locked_count; i++) { + snprintf (name_buf, sizeof(name_buf), + "%sname%d", type, i+1); + + /* Looking for volname1, volname2 or snapname1, * + * as key in the dict snapname2 */ + ret = dict_get_str (dict, name_buf, &name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to get %s locked_count = %d", + name_buf, locked_count); + op_ret = ret; + continue; + } + + ret = glusterd_mgmt_v3_unlock (name, uuid, type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release lock for %s.", + name); + op_ret = ret; + } + } + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", op_ret); + return op_ret; +} + +/* Given the count and type of the entity this function acquires * + * locks on multiple elements of the same entity. For example: * + * If type is "vol" this function tries to acquire locks on multiple * + * volumes */ +static int32_t +glusterd_acquire_multiple_locks_per_entity (dict_t *dict, uuid_t uuid, + int32_t count, char *type) +{ + char name_buf[PATH_MAX] = ""; + char *name = NULL; + int32_t i = -1; + int32_t ret = -1; + int32_t locked_count = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT (dict); + GF_ASSERT (type); + + /* Locking one element after other */ + for (i = 0; i < count; i++) { + snprintf (name_buf, sizeof(name_buf), + "%sname%d", type, i+1); + + /* Looking for volname1, volname2 or snapname1, * + * as key in the dict snapname2 */ + ret = dict_get_str (dict, name_buf, &name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to get %s count = %d", + name_buf, count); + break; + } + + ret = glusterd_mgmt_v3_lock (name, uuid, type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to acquire lock for %s %s " + "on behalf of %s. Reversing " + "this transaction", type, name, + uuid_utoa(uuid)); + break; + } + locked_count++; + } + + if (count == locked_count) { + /* If all locking ops went successfuly, return as success */ + ret = 0; + goto out; + } + + /* If we failed to lock one element, unlock others and return failure */ + ret = glusterd_release_multiple_locks_per_entity (dict, uuid, + locked_count, + type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release multiple %s locks", + type); + } + ret = -1; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* Given the type of entity, this function figures out if it should unlock a * + * single element of multiple elements of the said entity. For example: * + * if the type is "vol", this function will accordingly unlock a single volume * + * or multiple volumes */ +static int32_t +glusterd_mgmt_v3_unlock_entity (dict_t *dict, uuid_t uuid, char *type, + gf_boolean_t default_value) +{ + char name_buf[PATH_MAX] = ""; + char *name = NULL; + int32_t count = -1; + int32_t ret = -1; + gf_boolean_t hold_locks = _gf_false; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT (dict); + GF_ASSERT (type); + + snprintf (name_buf, sizeof(name_buf), "hold_%s_locks", type); + hold_locks = dict_get_str_boolean (dict, name_buf, default_value); + + if (hold_locks == _gf_false) { + /* Locks were not held for this particular entity * + * Hence nothing to release */ + ret = 0; + goto out; + } + + /* Looking for volcount or snapcount in the dict */ + snprintf (name_buf, sizeof(name_buf), "%scount", type); + ret = dict_get_int32 (dict, name_buf, &count); + if (ret) { + /* count is not present. Only one * + * element name needs to be unlocked */ + snprintf (name_buf, sizeof(name_buf), "%sname", + type); + ret = dict_get_str (dict, name_buf, &name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch %sname", type); + goto out; + } + + ret = glusterd_mgmt_v3_unlock (name, uuid, type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release lock for %s %s " + "on behalf of %s.", type, name, + uuid_utoa(uuid)); + goto out; + } + } else { + /* Unlocking one element name after another */ + ret = glusterd_release_multiple_locks_per_entity (dict, + uuid, + count, + type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release all %s locks", type); + goto out; + } + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); return ret; } +/* Given the type of entity, this function figures out if it should lock a * + * single element or multiple elements of the said entity. For example: * + * if the type is "vol", this function will accordingly lock a single volume * + * or multiple volumes */ +static int32_t +glusterd_mgmt_v3_lock_entity (dict_t *dict, uuid_t uuid, char *type, + gf_boolean_t default_value) +{ + char name_buf[PATH_MAX] = ""; + char *name = NULL; + int32_t count = -1; + int32_t ret = -1; + gf_boolean_t hold_locks = _gf_false; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT (dict); + GF_ASSERT (type); + + snprintf (name_buf, sizeof(name_buf), "hold_%s_locks", type); + hold_locks = dict_get_str_boolean (dict, name_buf, default_value); + + if (hold_locks == _gf_false) { + /* Not holding locks for this particular entity */ + ret = 0; + goto out; + } + + /* Looking for volcount or snapcount in the dict */ + snprintf (name_buf, sizeof(name_buf), "%scount", type); + ret = dict_get_int32 (dict, name_buf, &count); + if (ret) { + /* count is not present. Only one * + * element name needs to be locked */ + snprintf (name_buf, sizeof(name_buf), "%sname", + type); + ret = dict_get_str (dict, name_buf, &name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch %sname", type); + goto out; + } + + ret = glusterd_mgmt_v3_lock (name, uuid, type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to acquire lock for %s %s " + "on behalf of %s.", type, name, + uuid_utoa(uuid)); + goto out; + } + } else { + /* Locking one element name after another */ + ret = glusterd_acquire_multiple_locks_per_entity (dict, + uuid, + count, + type); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to acquire all %s locks", type); + goto out; + } + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* Try to release locks of multiple entities like * + * volume, snaps etc. */ +int32_t +glusterd_multiple_mgmt_v3_unlock (dict_t *dict, uuid_t uuid) +{ + int32_t i = -1; + int32_t ret = -1; + int32_t op_ret = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + + if (!dict) { + gf_log (this->name, GF_LOG_ERROR, "dict is null."); + ret = -1; + goto out; + } + + for (i = 0; valid_types[i].type; i++) { + ret = glusterd_mgmt_v3_unlock_entity + (dict, uuid, + valid_types[i].type, + valid_types[i].default_value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to unlock all %s", + valid_types[i].type); + op_ret = ret; + } + } + + ret = op_ret; +out: + gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +/* Try to acquire locks on multiple entities like * + * volume, snaps etc. */ int32_t -glusterd_volume_lock (char *volname, uuid_t uuid) +glusterd_multiple_mgmt_v3_lock (dict_t *dict, uuid_t uuid) { - int32_t ret = -1; - glusterd_vol_lock_obj *lock_obj = NULL; - glusterd_conf_t *priv = NULL; - uuid_t owner = {0}; - xlator_t *this = NULL; + int32_t i = -1; + int32_t ret = -1; + int32_t locked_count = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + + if (!dict) { + gf_log (this->name, GF_LOG_ERROR, "dict is null."); + ret = -1; + goto out; + } + + /* Locking one entity after other */ + for (i = 0; valid_types[i].type; i++) { + ret = glusterd_mgmt_v3_lock_entity + (dict, uuid, + valid_types[i].type, + valid_types[i].default_value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to lock all %s", + valid_types[i].type); + break; + } + locked_count++; + } + + if (locked_count == GF_MAX_LOCKING_ENTITIES) { + /* If all locking ops went successfuly, return as success */ + ret = 0; + goto out; + } + + /* If we failed to lock one entity, unlock others and return failure */ + for (i = 0; i < locked_count; i++) { + ret = glusterd_mgmt_v3_unlock_entity + (dict, uuid, + valid_types[i].type, + valid_types[i].default_value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to unlock all %s", + valid_types[i].type); + } + } + ret = -1; +out: + gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int32_t +glusterd_mgmt_v3_lock (const char *name, uuid_t uuid, char *type) +{ + char key[PATH_MAX] = ""; + int32_t ret = -1; + glusterd_mgmt_v3_lock_obj *lock_obj = NULL; + glusterd_conf_t *priv = NULL; + gf_boolean_t is_valid = _gf_true; + uuid_t owner = {0}; + xlator_t *this = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); - if (!volname) { - gf_log ("", GF_LOG_ERROR, "volname is null."); + if (!name || !type) { + gf_log (this->name, GF_LOG_ERROR, "name or type is null."); ret = -1; goto out; } - ret = glusterd_get_vol_lock_owner (volname, &owner); + is_valid = glusterd_mgmt_v3_is_type_valid (type); + if (is_valid != _gf_true) { + gf_log (this->name, GF_LOG_ERROR, + "Invalid entity. Cannot perform locking " + "operation on %s types", type); + ret = -1; + goto out; + } + + ret = snprintf (key, sizeof(key), "%s_%s", name, type); + if (ret != strlen(name) + 1 + strlen(type)) { + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "Unable to create key"); + goto out; + } + + gf_log (this->name, GF_LOG_DEBUG, + "Trying to acquire lock of %s %s for %s as %s", + type, name, uuid_utoa (uuid), key); + + ret = glusterd_get_mgmt_v3_lock_owner (key, &owner); if (ret) { - gf_log ("", GF_LOG_WARNING, - "Unable to get volume lock owner"); + gf_log (this->name, GF_LOG_DEBUG, + "Unable to get mgmt_v3 lock owner"); goto out; } /* If the lock has already been held for the given volume * we fail */ if (!uuid_is_null (owner)) { - gf_log ("", GF_LOG_WARNING, "Unable to acquire lock. " - "Lock for %s held by %s", volname, - uuid_utoa (owner)); + gf_log (this->name, GF_LOG_WARNING, "Lock for %s held by %s", + name, uuid_utoa (owner)); ret = -1; goto out; } - lock_obj = GF_CALLOC (1, sizeof(glusterd_vol_lock_obj), - gf_common_mt_vol_lock_obj_t); + lock_obj = GF_CALLOC (1, sizeof(glusterd_mgmt_v3_lock_obj), + gf_common_mt_mgmt_v3_lock_obj_t); if (!lock_obj) { ret = -1; goto out; @@ -144,70 +558,99 @@ glusterd_volume_lock (char *volname, uuid_t uuid) uuid_copy (lock_obj->lock_owner, uuid); - ret = dict_set_bin (priv->vol_lock, volname, lock_obj, - sizeof(glusterd_vol_lock_obj)); + ret = dict_set_bin (priv->mgmt_v3_lock, key, lock_obj, + sizeof(glusterd_mgmt_v3_lock_obj)); if (ret) { - gf_log ("", GF_LOG_ERROR, "Unable to set lock owner " - "in volume lock"); + gf_log (this->name, GF_LOG_ERROR, + "Unable to set lock owner in mgmt_v3 lock"); if (lock_obj) GF_FREE (lock_obj); goto out; } - gf_log ("", GF_LOG_DEBUG, "Lock for %s successfully held by %s", - volname, uuid_utoa (uuid)); + gf_log (this->name, GF_LOG_DEBUG, + "Lock for %s %s successfully held by %s", + type, name, uuid_utoa (uuid)); ret = 0; out: - gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); return ret; } int32_t -glusterd_volume_unlock (char *volname, uuid_t uuid) +glusterd_mgmt_v3_unlock (const char *name, uuid_t uuid, char *type) { - int32_t ret = -1; - glusterd_conf_t *priv = NULL; - uuid_t owner = {0}; - xlator_t *this = NULL; + char key[PATH_MAX] = ""; + int32_t ret = -1; + gf_boolean_t is_valid = _gf_true; + glusterd_conf_t *priv = NULL; + uuid_t owner = {0}; + xlator_t *this = NULL; this = THIS; GF_ASSERT (this); priv = this->private; GF_ASSERT (priv); - if (!volname) { - gf_log ("", GF_LOG_ERROR, "volname is null."); + if (!name || !type) { + gf_log (this->name, GF_LOG_ERROR, "name is null."); + ret = -1; + goto out; + } + + is_valid = glusterd_mgmt_v3_is_type_valid (type); + if (is_valid != _gf_true) { + gf_log (this->name, GF_LOG_ERROR, + "Invalid entity. Cannot perform unlocking " + "operation on %s types", type); ret = -1; goto out; } - ret = glusterd_get_vol_lock_owner (volname, &owner); - if (ret) + ret = snprintf (key, sizeof(key), "%s_%s", + name, type); + if (ret != strlen(name) + 1 + strlen(type)) { + gf_log (this->name, GF_LOG_ERROR, "Unable to create key"); + ret = -1; goto out; + } + + gf_log (this->name, GF_LOG_DEBUG, + "Trying to release lock of %s %s for %s as %s", + type, name, uuid_utoa (uuid), key); + + ret = glusterd_get_mgmt_v3_lock_owner (key, &owner); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, + "Unable to get mgmt_v3 lock owner"); + goto out; + } if (uuid_is_null (owner)) { - gf_log ("", GF_LOG_WARNING, "Lock for %s not held", volname); + gf_log (this->name, GF_LOG_WARNING, + "Lock for %s %s not held", type, name); ret = -1; goto out; } ret = uuid_compare (uuid, owner); if (ret) { - gf_log (THIS->name, GF_LOG_WARNING, "Lock owner mismatch. " - "Lock for %s held by %s", - volname, uuid_utoa (owner)); + gf_log (this->name, GF_LOG_WARNING, "Lock owner mismatch. " + "Lock for %s %s held by %s", + type, name, uuid_utoa (owner)); goto out; } - /* Removing the volume lock from the global list */ - dict_del (priv->vol_lock, volname); + /* Removing the mgmt_v3 lock from the global list */ + dict_del (priv->mgmt_v3_lock, key); - gf_log ("", GF_LOG_DEBUG, "Lock for %s successfully released", - volname); + gf_log (this->name, GF_LOG_DEBUG, + "Lock for %s %s successfully released", + type, name); ret = 0; out: - gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); return ret; } diff --git a/xlators/mgmt/glusterd/src/glusterd-locks.h b/xlators/mgmt/glusterd/src/glusterd-locks.h index 6e3f56f9c..b9cc8c0d1 100644 --- a/xlators/mgmt/glusterd/src/glusterd-locks.h +++ b/xlators/mgmt/glusterd/src/glusterd-locks.h @@ -15,23 +15,37 @@ #include "config.h" #endif -typedef struct glusterd_volume_lock_object_ { +typedef struct glusterd_mgmt_v3_lock_object_ { uuid_t lock_owner; -} glusterd_vol_lock_obj; +} glusterd_mgmt_v3_lock_obj; + +typedef struct glusterd_mgmt_v3_lock_valid_entities { + char *type; /* Entity type like vol, snap */ + gf_boolean_t default_value; /* The default value that * + * determines if the locks * + * should be held for that * + * entity */ +} glusterd_valid_entities; int32_t -glusterd_vol_lock_init (); +glusterd_mgmt_v3_lock_init (); void -glusterd_vol_lock_fini (); +glusterd_mgmt_v3_lock_fini (); + +int32_t +glusterd_get_mgmt_v3_lock_owner (char *volname, uuid_t *uuid); + +int32_t +glusterd_mgmt_v3_lock (const char *key, uuid_t uuid, char *type); int32_t -glusterd_get_vol_lock_owner (char *volname, uuid_t *uuid); +glusterd_mgmt_v3_unlock (const char *key, uuid_t uuid, char *type); int32_t -glusterd_volume_lock (char *volname, uuid_t uuid); +glusterd_multiple_mgmt_v3_lock (dict_t *dict, uuid_t uuid); int32_t -glusterd_volume_unlock (char *volname, uuid_t uuid); +glusterd_multiple_mgmt_v3_unlock (dict_t *dict, uuid_t uuid); #endif diff --git a/xlators/mgmt/glusterd/src/glusterd-mem-types.h b/xlators/mgmt/glusterd/src/glusterd-mem-types.h index 51057c274..e6f6a0333 100644 --- a/xlators/mgmt/glusterd/src/glusterd-mem-types.h +++ b/xlators/mgmt/glusterd/src/glusterd-mem-types.h @@ -67,7 +67,9 @@ typedef enum gf_gld_mem_types_ { gf_gld_mt_hooks_priv_t = gf_common_mt_end + 51, gf_gld_mt_mop_commit_req_t = gf_common_mt_end + 52, gf_gld_mt_int = gf_common_mt_end + 53, - gf_gld_mt_end = gf_common_mt_end + 54, + gf_gld_mt_snap_t = gf_common_mt_end + 54, + gf_gld_mt_missed_snapinfo_t = gf_common_mt_end + 55, + gf_gld_mt_end = gf_common_mt_end + 56, } gf_gld_mem_types_t; #endif diff --git a/xlators/mgmt/glusterd/src/glusterd-mgmt-handler.c b/xlators/mgmt/glusterd/src/glusterd-mgmt-handler.c new file mode 100644 index 000000000..81c5aa579 --- /dev/null +++ b/xlators/mgmt/glusterd/src/glusterd-mgmt-handler.c @@ -0,0 +1,936 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +/* rpc related syncops */ +#include "rpc-clnt.h" +#include "protocol-common.h" +#include "xdr-generic.h" +#include "glusterd1-xdr.h" +#include "glusterd-syncop.h" + +#include "glusterd.h" +#include "glusterd-utils.h" +#include "glusterd-locks.h" +#include "glusterd-mgmt.h" +#include "glusterd-op-sm.h" + +static int +glusterd_mgmt_v3_null (rpcsvc_request_t *req) +{ + return 0; +} + +static int +glusterd_mgmt_v3_lock_send_resp (rpcsvc_request_t *req, int32_t status) +{ + + gd1_mgmt_v3_lock_rsp rsp = {{0},}; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + rsp.op_ret = status; + if (rsp.op_ret) + rsp.op_errno = errno; + + glusterd_get_uuid (&rsp.uuid); + + ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gd1_mgmt_v3_lock_rsp); + + gf_log (this->name, GF_LOG_DEBUG, + "Responded to mgmt_v3 lock, ret: %d", ret); + + return ret; +} + +static int +glusterd_synctasked_mgmt_v3_lock (rpcsvc_request_t *req, + gd1_mgmt_v3_lock_req *lock_req, + glusterd_op_lock_ctx_t *ctx) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (ctx); + GF_ASSERT (ctx->dict); + + /* Trying to acquire multiple mgmt_v3 locks */ + ret = glusterd_multiple_mgmt_v3_lock (ctx->dict, ctx->uuid); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "Failed to acquire mgmt_v3 locks for %s", + uuid_utoa (ctx->uuid)); + + ret = glusterd_mgmt_v3_lock_send_resp (req, ret); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_op_state_machine_mgmt_v3_lock (rpcsvc_request_t *req, + gd1_mgmt_v3_lock_req *lock_req, + glusterd_op_lock_ctx_t *ctx) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_op_info_t txn_op_info = {{0},}; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + glusterd_txn_opinfo_init (&txn_op_info, NULL, &lock_req->op, + ctx->dict, req); + + ret = glusterd_set_txn_opinfo (&lock_req->txn_id, &txn_op_info); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to set transaction's opinfo"); + goto out; + } + + ret = glusterd_op_sm_inject_event (GD_OP_EVENT_LOCK, + &lock_req->txn_id, ctx); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "Failed to inject event GD_OP_EVENT_LOCK"); + +out: + glusterd_friend_sm (); + glusterd_op_sm (); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_handle_mgmt_v3_lock_fn (rpcsvc_request_t *req) +{ + gd1_mgmt_v3_lock_req lock_req = {{0},}; + int32_t ret = -1; + glusterd_peerinfo_t *peerinfo = NULL; + glusterd_op_lock_ctx_t *ctx = NULL; + xlator_t *this = NULL; + gf_boolean_t is_synctasked = _gf_false; + gf_boolean_t free_ctx = _gf_false; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = xdr_to_generic (req->msg[0], &lock_req, + (xdrproc_t)xdr_gd1_mgmt_v3_lock_req); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "Failed to decode lock " + "request received from peer"); + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + gf_log (this->name, GF_LOG_DEBUG, "Received mgmt_v3 lock req " + "from uuid: %s", uuid_utoa (lock_req.uuid)); + + if (glusterd_friend_find_by_uuid (lock_req.uuid, &peerinfo)) { + gf_log (this->name, GF_LOG_WARNING, "%s doesn't " + "belong to the cluster. Ignoring request.", + uuid_utoa (lock_req.uuid)); + ret = -1; + goto out; + } + + ctx = GF_CALLOC (1, sizeof (*ctx), gf_gld_mt_op_lock_ctx_t); + if (!ctx) { + ret = -1; + goto out; + } + + uuid_copy (ctx->uuid, lock_req.uuid); + ctx->req = req; + + ctx->dict = dict_new (); + if (!ctx->dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (lock_req.dict.dict_val, + lock_req.dict.dict_len, &ctx->dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "failed to unserialize the dictionary"); + goto out; + } + + is_synctasked = dict_get_str_boolean (ctx->dict, + "is_synctasked", _gf_false); + if (is_synctasked) { + ret = glusterd_synctasked_mgmt_v3_lock (req, &lock_req, ctx); + /* The above function does not take ownership of ctx. + * Therefore we need to free the ctx explicitly. */ + free_ctx = _gf_true; + } + else { + ret = glusterd_op_state_machine_mgmt_v3_lock (req, &lock_req, + ctx); + } + +out: + + if (ret || free_ctx) { + if (ctx->dict) + dict_unref (ctx->dict); + if (ctx) + GF_FREE (ctx); + } + + free (lock_req.dict.dict_val); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_mgmt_v3_pre_validate_send_resp (rpcsvc_request_t *req, + int32_t op, int32_t status, + char *op_errstr, dict_t *rsp_dict) +{ + gd1_mgmt_v3_pre_val_rsp rsp = {{0},}; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + rsp.op_ret = status; + glusterd_get_uuid (&rsp.uuid); + rsp.op = op; + if (op_errstr) + rsp.op_errstr = op_errstr; + else + rsp.op_errstr = ""; + + ret = dict_allocate_and_serialize (rsp_dict, &rsp.dict.dict_val, + &rsp.dict.dict_len); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get serialized length of dict"); + goto out; + } + + ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gd1_mgmt_v3_pre_val_rsp); + + GF_FREE (rsp.dict.dict_val); +out: + gf_log (this->name, GF_LOG_DEBUG, + "Responded to pre validation, ret: %d", ret); + return ret; +} + +static int +glusterd_handle_pre_validate_fn (rpcsvc_request_t *req) +{ + int32_t ret = -1; + gd1_mgmt_v3_pre_val_req op_req = {{0},}; + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = NULL; + char *op_errstr = NULL; + dict_t *dict = NULL; + dict_t *rsp_dict = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = xdr_to_generic (req->msg[0], &op_req, + (xdrproc_t)xdr_gd1_mgmt_v3_pre_val_req); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to decode pre validation " + "request received from peer"); + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + if (glusterd_friend_find_by_uuid (op_req.uuid, &peerinfo)) { + gf_log (this->name, GF_LOG_WARNING, "%s doesn't " + "belong to the cluster. Ignoring request.", + uuid_utoa (op_req.uuid)); + ret = -1; + goto out; + } + + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (op_req.dict.dict_val, + op_req.dict.dict_len, &dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "failed to unserialize the dictionary"); + goto out; + } + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get new dictionary"); + return -1; + } + + ret = gd_mgmt_v3_pre_validate_fn (op_req.op, dict, &op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Pre Validation failed on operation %s", + gd_op_list[op_req.op]); + } + + ret = glusterd_mgmt_v3_pre_validate_send_resp (req, op_req.op, + ret, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to send Pre Validation " + "response for operation %s", + gd_op_list[op_req.op]); + goto out; + } + +out: + if (op_errstr && (strcmp (op_errstr, ""))) + GF_FREE (op_errstr); + + free (op_req.dict.dict_val); + + if (dict) + dict_unref (dict); + + if (rsp_dict) + dict_unref (rsp_dict); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_mgmt_v3_brick_op_send_resp (rpcsvc_request_t *req, + int32_t op, int32_t status, + char *op_errstr, dict_t *rsp_dict) +{ + gd1_mgmt_v3_brick_op_rsp rsp = {{0},}; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + rsp.op_ret = status; + glusterd_get_uuid (&rsp.uuid); + rsp.op = op; + if (op_errstr) + rsp.op_errstr = op_errstr; + else + rsp.op_errstr = ""; + + ret = dict_allocate_and_serialize (rsp_dict, &rsp.dict.dict_val, + &rsp.dict.dict_len); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get serialized length of dict"); + goto out; + } + + ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gd1_mgmt_v3_brick_op_rsp); + + GF_FREE (rsp.dict.dict_val); +out: + gf_log (this->name, GF_LOG_DEBUG, + "Responded to brick op, ret: %d", ret); + return ret; +} + +static int +glusterd_handle_brick_op_fn (rpcsvc_request_t *req) +{ + int32_t ret = -1; + gd1_mgmt_v3_brick_op_req op_req = {{0},}; + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = NULL; + char *op_errstr = NULL; + dict_t *dict = NULL; + dict_t *rsp_dict = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = xdr_to_generic (req->msg[0], &op_req, + (xdrproc_t)xdr_gd1_mgmt_v3_brick_op_req); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "Failed to decode brick op " + "request received from peer"); + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + if (glusterd_friend_find_by_uuid (op_req.uuid, &peerinfo)) { + gf_log (this->name, GF_LOG_WARNING, "%s doesn't " + "belong to the cluster. Ignoring request.", + uuid_utoa (op_req.uuid)); + ret = -1; + goto out; + } + + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (op_req.dict.dict_val, + op_req.dict.dict_len, &dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "failed to unserialize the dictionary"); + goto out; + } + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get new dictionary"); + return -1; + } + + ret = gd_mgmt_v3_brick_op_fn (op_req.op, dict, &op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Brick Op failed on operation %s", + gd_op_list[op_req.op]); + } + + ret = glusterd_mgmt_v3_brick_op_send_resp (req, op_req.op, + ret, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to send brick op " + "response for operation %s", + gd_op_list[op_req.op]); + goto out; + } + +out: + if (op_errstr && (strcmp (op_errstr, ""))) + GF_FREE (op_errstr); + + free (op_req.dict.dict_val); + + if (dict) + dict_unref (dict); + + if (rsp_dict) + dict_unref (rsp_dict); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_mgmt_v3_commit_send_resp (rpcsvc_request_t *req, + int32_t op, int32_t status, + char *op_errstr, dict_t *rsp_dict) +{ + gd1_mgmt_v3_commit_rsp rsp = {{0},}; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + rsp.op_ret = status; + glusterd_get_uuid (&rsp.uuid); + rsp.op = op; + if (op_errstr) + rsp.op_errstr = op_errstr; + else + rsp.op_errstr = ""; + + ret = dict_allocate_and_serialize (rsp_dict, &rsp.dict.dict_val, + &rsp.dict.dict_len); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get serialized length of dict"); + goto out; + } + + ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gd1_mgmt_v3_commit_rsp); + + GF_FREE (rsp.dict.dict_val); +out: + gf_log (this->name, GF_LOG_DEBUG, "Responded to commit, ret: %d", ret); + return ret; +} + +static int +glusterd_handle_commit_fn (rpcsvc_request_t *req) +{ + int32_t ret = -1; + gd1_mgmt_v3_commit_req op_req = {{0},}; + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = NULL; + char *op_errstr = NULL; + dict_t *dict = NULL; + dict_t *rsp_dict = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = xdr_to_generic (req->msg[0], &op_req, + (xdrproc_t)xdr_gd1_mgmt_v3_commit_req); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "Failed to decode commit " + "request received from peer"); + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + if (glusterd_friend_find_by_uuid (op_req.uuid, &peerinfo)) { + gf_log (this->name, GF_LOG_WARNING, "%s doesn't " + "belong to the cluster. Ignoring request.", + uuid_utoa (op_req.uuid)); + ret = -1; + goto out; + } + + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (op_req.dict.dict_val, + op_req.dict.dict_len, &dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "failed to unserialize the dictionary"); + goto out; + } + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get new dictionary"); + return -1; + } + + ret = gd_mgmt_v3_commit_fn (op_req.op, dict, &op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "commit failed on operation %s", + gd_op_list[op_req.op]); + } + + ret = glusterd_mgmt_v3_commit_send_resp (req, op_req.op, + ret, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to send commit " + "response for operation %s", + gd_op_list[op_req.op]); + goto out; + } + +out: + if (op_errstr && (strcmp (op_errstr, ""))) + GF_FREE (op_errstr); + + free (op_req.dict.dict_val); + + if (dict) + dict_unref (dict); + + if (rsp_dict) + dict_unref (rsp_dict); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_mgmt_v3_post_validate_send_resp (rpcsvc_request_t *req, + int32_t op, int32_t status, + char *op_errstr, dict_t *rsp_dict) +{ + gd1_mgmt_v3_post_val_rsp rsp = {{0},}; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + rsp.op_ret = status; + glusterd_get_uuid (&rsp.uuid); + rsp.op = op; + if (op_errstr) + rsp.op_errstr = op_errstr; + else + rsp.op_errstr = ""; + + ret = dict_allocate_and_serialize (rsp_dict, &rsp.dict.dict_val, + &rsp.dict.dict_len); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get serialized length of dict"); + goto out; + } + + ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gd1_mgmt_v3_post_val_rsp); + + GF_FREE (rsp.dict.dict_val); +out: + gf_log (this->name, GF_LOG_DEBUG, + "Responded to post validation, ret: %d", ret); + return ret; +} + +static int +glusterd_handle_post_validate_fn (rpcsvc_request_t *req) +{ + int32_t ret = -1; + gd1_mgmt_v3_post_val_req op_req = {{0},}; + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = NULL; + char *op_errstr = NULL; + dict_t *dict = NULL; + dict_t *rsp_dict = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = xdr_to_generic (req->msg[0], &op_req, + (xdrproc_t)xdr_gd1_mgmt_v3_post_val_req); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to decode post validation " + "request received from peer"); + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + if (glusterd_friend_find_by_uuid (op_req.uuid, &peerinfo)) { + gf_log (this->name, GF_LOG_WARNING, "%s doesn't " + "belong to the cluster. Ignoring request.", + uuid_utoa (op_req.uuid)); + ret = -1; + goto out; + } + + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (op_req.dict.dict_val, + op_req.dict.dict_len, &dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "failed to unserialize the dictionary"); + goto out; + } + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get new dictionary"); + return -1; + } + + ret = gd_mgmt_v3_post_validate_fn (op_req.op, op_req.op_ret, dict, + &op_errstr, rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Post Validation failed on operation %s", + gd_op_list[op_req.op]); + } + + ret = glusterd_mgmt_v3_post_validate_send_resp (req, op_req.op, + ret, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to send Post Validation " + "response for operation %s", + gd_op_list[op_req.op]); + goto out; + } + +out: + if (op_errstr && (strcmp (op_errstr, ""))) + GF_FREE (op_errstr); + + free (op_req.dict.dict_val); + + if (dict) + dict_unref (dict); + + if (rsp_dict) + dict_unref (rsp_dict); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_mgmt_v3_unlock_send_resp (rpcsvc_request_t *req, int32_t status) +{ + + gd1_mgmt_v3_unlock_rsp rsp = {{0},}; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + rsp.op_ret = status; + if (rsp.op_ret) + rsp.op_errno = errno; + + glusterd_get_uuid (&rsp.uuid); + + ret = glusterd_submit_reply (req, &rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gd1_mgmt_v3_unlock_rsp); + + gf_log (this->name, GF_LOG_DEBUG, + "Responded to mgmt_v3 unlock, ret: %d", ret); + + return ret; +} + +static int +glusterd_syctasked_mgmt_v3_unlock (rpcsvc_request_t *req, + gd1_mgmt_v3_unlock_req *unlock_req, + glusterd_op_lock_ctx_t *ctx) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (ctx); + + /* Trying to release multiple mgmt_v3 locks */ + ret = glusterd_multiple_mgmt_v3_unlock (ctx->dict, ctx->uuid); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release mgmt_v3 locks for %s", + uuid_utoa(ctx->uuid)); + } + + ret = glusterd_mgmt_v3_unlock_send_resp (req, ret); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + + +static int +glusterd_op_state_machine_mgmt_v3_unlock (rpcsvc_request_t *req, + gd1_mgmt_v3_unlock_req *lock_req, + glusterd_op_lock_ctx_t *ctx) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = glusterd_op_sm_inject_event (GD_OP_EVENT_UNLOCK, + &lock_req->txn_id, ctx); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "Failed to inject event GD_OP_EVENT_UNLOCK"); + + glusterd_friend_sm (); + glusterd_op_sm (); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int +glusterd_handle_mgmt_v3_unlock_fn (rpcsvc_request_t *req) +{ + gd1_mgmt_v3_unlock_req lock_req = {{0},}; + int32_t ret = -1; + glusterd_op_lock_ctx_t *ctx = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + xlator_t *this = NULL; + gf_boolean_t is_synctasked = _gf_false; + gf_boolean_t free_ctx = _gf_false; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + + ret = xdr_to_generic (req->msg[0], &lock_req, + (xdrproc_t)xdr_gd1_mgmt_v3_unlock_req); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "Failed to decode unlock " + "request received from peer"); + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + gf_log (this->name, GF_LOG_DEBUG, "Received volume unlock req " + "from uuid: %s", uuid_utoa (lock_req.uuid)); + + if (glusterd_friend_find_by_uuid (lock_req.uuid, &peerinfo)) { + gf_log (this->name, GF_LOG_WARNING, "%s doesn't " + "belong to the cluster. Ignoring request.", + uuid_utoa (lock_req.uuid)); + ret = -1; + goto out; + } + + ctx = GF_CALLOC (1, sizeof (*ctx), gf_gld_mt_op_lock_ctx_t); + if (!ctx) { + ret = -1; + goto out; + } + + uuid_copy (ctx->uuid, lock_req.uuid); + ctx->req = req; + + ctx->dict = dict_new (); + if (!ctx->dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (lock_req.dict.dict_val, + lock_req.dict.dict_len, &ctx->dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "failed to unserialize the dictionary"); + goto out; + } + + is_synctasked = dict_get_str_boolean (ctx->dict, + "is_synctasked", _gf_false); + if (is_synctasked) { + ret = glusterd_syctasked_mgmt_v3_unlock (req, &lock_req, ctx); + /* The above function does not take ownership of ctx. + * Therefore we need to free the ctx explicitly. */ + free_ctx = _gf_true; + } + else { + ret = glusterd_op_state_machine_mgmt_v3_unlock (req, &lock_req, + ctx); + } + +out: + + if (ret || free_ctx) { + if (ctx->dict) + dict_unref (ctx->dict); + if (ctx) + GF_FREE (ctx); + } + + free (lock_req.dict.dict_val); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_handle_mgmt_v3_lock (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, + glusterd_handle_mgmt_v3_lock_fn); +} + +static int +glusterd_handle_pre_validate (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, + glusterd_handle_pre_validate_fn); +} + +static int +glusterd_handle_brick_op (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, + glusterd_handle_brick_op_fn); +} + +static int +glusterd_handle_commit (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, + glusterd_handle_commit_fn); +} + +static int +glusterd_handle_post_validate (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, + glusterd_handle_post_validate_fn); +} + +int +glusterd_handle_mgmt_v3_unlock (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, + glusterd_handle_mgmt_v3_unlock_fn); +} + +rpcsvc_actor_t gd_svc_mgmt_v3_actors[] = { + [GLUSTERD_MGMT_V3_NULL] = { "NULL", GLUSTERD_MGMT_V3_NULL, glusterd_mgmt_v3_null, NULL, 0, DRC_NA}, + [GLUSTERD_MGMT_V3_LOCK] = { "MGMT_V3_LOCK", GLUSTERD_MGMT_V3_LOCK, glusterd_handle_mgmt_v3_lock, NULL, 0, DRC_NA}, + [GLUSTERD_MGMT_V3_PRE_VALIDATE] = { "PRE_VAL", GLUSTERD_MGMT_V3_PRE_VALIDATE, glusterd_handle_pre_validate, NULL, 0, DRC_NA}, + [GLUSTERD_MGMT_V3_BRICK_OP] = { "BRCK_OP", GLUSTERD_MGMT_V3_BRICK_OP, glusterd_handle_brick_op, NULL, 0, DRC_NA}, + [GLUSTERD_MGMT_V3_COMMIT] = { "COMMIT", GLUSTERD_MGMT_V3_COMMIT, glusterd_handle_commit, NULL, 0, DRC_NA}, + [GLUSTERD_MGMT_V3_POST_VALIDATE] = { "POST_VAL", GLUSTERD_MGMT_V3_POST_VALIDATE, glusterd_handle_post_validate, NULL, 0, DRC_NA}, + [GLUSTERD_MGMT_V3_UNLOCK] = { "MGMT_V3_UNLOCK", GLUSTERD_MGMT_V3_UNLOCK, glusterd_handle_mgmt_v3_unlock, NULL, 0, DRC_NA}, +}; + +struct rpcsvc_program gd_svc_mgmt_v3_prog = { + .progname = "GlusterD svc mgmt v3", + .prognum = GD_MGMT_PROGRAM, + .progver = GD_MGMT_V3_VERSION, + .numactors = GLUSTERD_MGMT_V3_MAXVALUE, + .actors = gd_svc_mgmt_v3_actors, + .synctask = _gf_true, +}; diff --git a/xlators/mgmt/glusterd/src/glusterd-mgmt.c b/xlators/mgmt/glusterd/src/glusterd-mgmt.c new file mode 100644 index 000000000..5295f889e --- /dev/null +++ b/xlators/mgmt/glusterd/src/glusterd-mgmt.c @@ -0,0 +1,1899 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +/* rpc related syncops */ +#include "rpc-clnt.h" +#include "protocol-common.h" +#include "xdr-generic.h" +#include "glusterd1-xdr.h" +#include "glusterd-syncop.h" + +#include "glusterd.h" +#include "glusterd-utils.h" +#include "glusterd-locks.h" +#include "glusterd-mgmt.h" +#include "glusterd-op-sm.h" + +extern struct rpc_clnt_program gd_mgmt_v3_prog; + + +static void +gd_mgmt_v3_collate_errors (struct syncargs *args, int op_ret, int op_errno, + char *op_errstr, int op_code, + glusterd_peerinfo_t *peerinfo, u_char *uuid) +{ + char *peer_str = NULL; + char err_str[PATH_MAX] = "Please check log file for details."; + char op_err[PATH_MAX] = ""; + int32_t len = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (args); + GF_ASSERT (uuid); + + if (op_ret) { + args->op_ret = op_ret; + args->op_errno = op_errno; + + if (peerinfo) + peer_str = peerinfo->hostname; + else + peer_str = uuid_utoa (uuid); + + if (op_errstr && strcmp (op_errstr, "")) { + len = snprintf (err_str, sizeof(err_str) - 1, + "Error: %s", op_errstr); + err_str[len] = '\0'; + } + + switch (op_code) { + case GLUSTERD_MGMT_V3_LOCK: + { + len = snprintf (op_err, sizeof(op_err) - 1, + "Locking failed " + "on %s. %s", peer_str, err_str); + break; + } + case GLUSTERD_MGMT_V3_PRE_VALIDATE: + { + len = snprintf (op_err, sizeof(op_err) - 1, + "Pre Validation failed " + "on %s. %s", peer_str, err_str); + break; + } + case GLUSTERD_MGMT_V3_BRICK_OP: + { + len = snprintf (op_err, sizeof(op_err) - 1, + "Brick ops failed " + "on %s. %s", peer_str, err_str); + break; + } + case GLUSTERD_MGMT_V3_COMMIT: + { + len = snprintf (op_err, sizeof(op_err) - 1, + "Commit failed on %s. %s", + peer_str, err_str); + break; + } + case GLUSTERD_MGMT_V3_POST_VALIDATE: + { + len = snprintf (op_err, sizeof(op_err) - 1, + "Post Validation failed " + "on %s. %s", peer_str, err_str); + break; + } + case GLUSTERD_MGMT_V3_UNLOCK: + { + len = snprintf (op_err, sizeof(op_err) - 1, + "Unlocking failed " + "on %s. %s", peer_str, err_str); + break; + } + } + op_err[len] = '\0'; + + if (args->errstr) { + len = snprintf (err_str, sizeof(err_str) - 1, + "%s\n%s", args->errstr, + op_err); + GF_FREE (args->errstr); + args->errstr = NULL; + } else + len = snprintf (err_str, sizeof(err_str) - 1, + "%s", op_err); + err_str[len] = '\0'; + + gf_log (this->name, GF_LOG_ERROR, "%s", op_err); + args->errstr = gf_strdup (err_str); + } + + return; +} + +int32_t +gd_mgmt_v3_pre_validate_fn (glusterd_op_t op, dict_t *dict, + char **op_errstr, dict_t *rsp_dict) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + + switch (op) { + case GD_OP_SNAP: + ret = glusterd_snapshot_prevalidate (dict, op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "Snapshot Prevalidate Failed"); + goto out; + } + + break; + + default: + break; + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_DEBUG, "OP = %d. Returning %d", op, ret); + return ret; +} + +int32_t +gd_mgmt_v3_brick_op_fn (glusterd_op_t op, dict_t *dict, + char **op_errstr, dict_t *rsp_dict) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + + switch (op) { + case GD_OP_SNAP: + { + ret = glusterd_snapshot_brickop (dict, op_errstr, rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "snapshot brickop " + "failed"); + goto out; + } + break; + } + default: + break; + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "OP = %d. Returning %d", op, ret); + return ret; +} + +int32_t +gd_mgmt_v3_commit_fn (glusterd_op_t op, dict_t *dict, + char **op_errstr, dict_t *rsp_dict) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + + switch (op) { + case GD_OP_SNAP: + { + ret = glusterd_snapshot (dict, op_errstr, rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "Snapshot Commit Failed"); + goto out; + } + break; + } + default: + break; + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_DEBUG, "OP = %d. Returning %d", op, ret); + return ret; +} + +int32_t +gd_mgmt_v3_post_validate_fn (glusterd_op_t op, int32_t op_ret, dict_t *dict, + char **op_errstr, dict_t *rsp_dict) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + + switch (op) { + case GD_OP_SNAP: + { + ret = glusterd_snapshot_postvalidate (dict, op_ret, + op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "postvalidate operation failed"); + goto out; + } + break; + } + default: + break; + } + + ret = 0; + +out: + gf_log (this->name, GF_LOG_TRACE, "OP = %d. Returning %d", op, ret); + return ret; +} + +int32_t +gd_mgmt_v3_lock_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int32_t ret = -1; + struct syncargs *args = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + gd1_mgmt_v3_lock_rsp rsp = {{0},}; + call_frame_t *frame = NULL; + int32_t op_ret = -1; + int32_t op_errno = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (myframe); + + /* Even though the lock command has failed, while collating the errors + (gd_mgmt_v3_collate_errors), args->op_ret and args->op_errno will be + used. @args is obtained from frame->local. So before checking the + status of the request and going out if its a failure, args should be + set to frame->local. Otherwise, while collating args will be NULL. + This applies to other phases such as prevalidate, brickop, commit and + postvalidate also. + */ + frame = myframe; + args = frame->local; + peerinfo = frame->cookie; + frame->local = NULL; + frame->cookie = NULL; + + if (-1 == req->rpc_status) { + op_errno = ENOTCONN; + goto out; + } + + if (!iov) { + gf_log (this->name, GF_LOG_ERROR, "iov is NULL"); + op_errno = EINVAL; + goto out; + } + + ret = xdr_to_generic (*iov, &rsp, + (xdrproc_t)xdr_gd1_mgmt_v3_lock_rsp); + if (ret < 0) + goto out; + + uuid_copy (args->uuid, rsp.uuid); + + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + +out: + gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, + GLUSTERD_MGMT_V3_LOCK, + peerinfo, rsp.uuid); + if (rsp.dict.dict_val) + free (rsp.dict.dict_val); + STACK_DESTROY (frame->root); + synctask_barrier_wake(args); + return 0; +} + +int32_t +gd_mgmt_v3_lock_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + return glusterd_big_locked_cbk (req, iov, count, myframe, + gd_mgmt_v3_lock_cbk_fn); +} + +int +gd_mgmt_v3_lock (glusterd_op_t op, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid) +{ + gd1_mgmt_v3_lock_req req = {{0},}; + glusterd_conf_t *conf = THIS->private; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_ctx); + GF_ASSERT (peerinfo); + GF_ASSERT (args); + + ret = dict_allocate_and_serialize (op_ctx, + &req.dict.dict_val, + &req.dict.dict_len); + if (ret) + goto out; + + uuid_copy (req.uuid, my_uuid); + req.op = op; + synclock_unlock (&conf->big_lock); + ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, + &gd_mgmt_v3_prog, + GLUSTERD_MGMT_V3_LOCK, + gd_mgmt_v3_lock_cbk, + (xdrproc_t) xdr_gd1_mgmt_v3_lock_req); + synclock_lock (&conf->big_lock); +out: + GF_FREE (req.dict.dict_val); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_mgmt_v3_initiate_lockdown (glusterd_conf_t *conf, glusterd_op_t op, + dict_t *dict, char **op_errstr, int npeers, + gf_boolean_t *is_acquired) +{ + char *volname = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + int32_t ret = -1; + int32_t peer_cnt = 0; + struct syncargs args = {0}; + struct list_head *peers = NULL; + uuid_t peer_uuid = {0}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (conf); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (is_acquired); + + peers = &conf->xaction_peers; + + /* Trying to acquire multiple mgmt_v3 locks on local node */ + ret = glusterd_multiple_mgmt_v3_lock (dict, MY_UUID); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to acquire mgmt_v3 locks on localhost"); + goto out; + } + + *is_acquired = _gf_true; + + if (!npeers) { + ret = 0; + goto out; + } + + /* Sending mgmt_v3 lock req to other nodes in the cluster */ + gd_syncargs_init (&args, NULL); + synctask_barrier_init((&args)); + peer_cnt = 0; + list_for_each_entry (peerinfo, peers, op_peers_list) { + gd_mgmt_v3_lock (op, dict, peerinfo, &args, + MY_UUID, peer_uuid); + peer_cnt++; + } + gd_synctask_barrier_wait((&args), peer_cnt); + + if (args.errstr) + *op_errstr = gf_strdup (args.errstr); + + ret = args.op_ret; + + gf_log (this->name, GF_LOG_DEBUG, "Sent lock op req for %s " + "to %d peers. Returning %d", gd_op_list[op], peer_cnt, ret); +out: + if (ret) { + if (*op_errstr) + gf_log (this->name, GF_LOG_ERROR, "%s", + *op_errstr); + + if (volname) + ret = gf_asprintf (op_errstr, + "Another transaction is in progress " + "for %s. Please try again after " + "sometime.", volname); + else + ret = gf_asprintf (op_errstr, + "Another transaction is in progress " + "Please try again after sometime."); + + if (ret == -1) + *op_errstr = NULL; + + ret = -1; + } + + return ret; +} + +int +glusterd_pre_validate_aggr_rsp_dict (glusterd_op_t op, + dict_t *aggr, dict_t *rsp) +{ + int32_t ret = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (aggr); + GF_ASSERT (rsp); + + switch (op) { + case GD_OP_SNAP: + ret = glusterd_snap_pre_validate_use_rsp_dict (aggr, rsp); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to aggregate prevalidate " + "response dictionaries."); + goto out; + } + break; + default: + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "Invalid op (%s)", + gd_op_list[op]); + + break; + } +out: + return ret; +} + +int32_t +gd_mgmt_v3_pre_validate_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int32_t ret = -1; + struct syncargs *args = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + gd1_mgmt_v3_pre_val_rsp rsp = {{0},}; + call_frame_t *frame = NULL; + int32_t op_ret = -1; + int32_t op_errno = -1; + dict_t *rsp_dict = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (myframe); + + frame = myframe; + args = frame->local; + peerinfo = frame->cookie; + frame->local = NULL; + frame->cookie = NULL; + + if (-1 == req->rpc_status) { + op_errno = ENOTCONN; + goto out; + } + + if (!iov) { + gf_log (this->name, GF_LOG_ERROR, "iov is NULL"); + op_errno = EINVAL; + } + + ret = xdr_to_generic (*iov, &rsp, + (xdrproc_t)xdr_gd1_mgmt_v3_pre_val_rsp); + if (ret < 0) + goto out; + + if (rsp.dict.dict_len) { + /* Unserialize the dictionary */ + rsp_dict = dict_new (); + + ret = dict_unserialize (rsp.dict.dict_val, + rsp.dict.dict_len, + &rsp_dict); + if (ret < 0) { + free (rsp.dict.dict_val); + goto out; + } else { + rsp_dict->extra_stdfree = rsp.dict.dict_val; + } + } + + uuid_copy (args->uuid, rsp.uuid); + pthread_mutex_lock (&args->lock_dict); + { + ret = glusterd_pre_validate_aggr_rsp_dict (rsp.op, args->dict, + rsp_dict); + } + pthread_mutex_unlock (&args->lock_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "%s", + "Failed to aggregate response from " + " node/brick"); + if (!rsp.op_ret) + op_ret = ret; + else { + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + } + } else { + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + } + +out: + if (rsp_dict) + dict_unref (rsp_dict); + + gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, + GLUSTERD_MGMT_V3_PRE_VALIDATE, + peerinfo, rsp.uuid); + + if (rsp.op_errstr) + free (rsp.op_errstr); + + STACK_DESTROY (frame->root); + synctask_barrier_wake(args); + return 0; +} + +int32_t +gd_mgmt_v3_pre_validate_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + return glusterd_big_locked_cbk (req, iov, count, myframe, + gd_mgmt_v3_pre_validate_cbk_fn); +} + +int +gd_mgmt_v3_pre_validate_req (glusterd_op_t op, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid) +{ + int32_t ret = -1; + gd1_mgmt_v3_pre_val_req req = {{0},}; + glusterd_conf_t *conf = THIS->private; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_ctx); + GF_ASSERT (peerinfo); + GF_ASSERT (args); + + ret = dict_allocate_and_serialize (op_ctx, + &req.dict.dict_val, + &req.dict.dict_len); + if (ret) + goto out; + + uuid_copy (req.uuid, my_uuid); + req.op = op; + synclock_unlock (&conf->big_lock); + ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, + &gd_mgmt_v3_prog, + GLUSTERD_MGMT_V3_PRE_VALIDATE, + gd_mgmt_v3_pre_validate_cbk, + (xdrproc_t) xdr_gd1_mgmt_v3_pre_val_req); + synclock_lock (&conf->big_lock); +out: + GF_FREE (req.dict.dict_val); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_mgmt_v3_pre_validate (glusterd_conf_t *conf, glusterd_op_t op, + dict_t *req_dict, char **op_errstr, int npeers) +{ + int32_t ret = -1; + int32_t peer_cnt = 0; + dict_t *rsp_dict = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + struct syncargs args = {0}; + struct list_head *peers = NULL; + uuid_t peer_uuid = {0}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (conf); + GF_ASSERT (req_dict); + GF_ASSERT (op_errstr); + + peers = &conf->xaction_peers; + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create response dictionary"); + goto out; + } + + /* Pre Validation on local node */ + ret = gd_mgmt_v3_pre_validate_fn (op, req_dict, op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Pre Validation failed for " + "operation %s on local node", + gd_op_list[op]); + + if (*op_errstr == NULL) { + ret = gf_asprintf (op_errstr, + "Pre-validation failed " + "on localhost. Please " + "check log file for details"); + if (ret == -1) + *op_errstr = NULL; + + ret = -1; + } + goto out; + } + + ret = glusterd_pre_validate_aggr_rsp_dict (op, req_dict, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "%s", + "Failed to aggregate response from " + " node/brick"); + goto out; + } + + dict_unref (rsp_dict); + rsp_dict = NULL; + + if (!npeers) { + ret = 0; + goto out; + } + + /* Sending Pre Validation req to other nodes in the cluster */ + gd_syncargs_init (&args, req_dict); + synctask_barrier_init((&args)); + peer_cnt = 0; + list_for_each_entry (peerinfo, peers, op_peers_list) { + gd_mgmt_v3_pre_validate_req (op, req_dict, peerinfo, &args, + MY_UUID, peer_uuid); + peer_cnt++; + } + gd_synctask_barrier_wait((&args), peer_cnt); + + if (args.op_ret) { + gf_log (this->name, GF_LOG_ERROR, + "Pre Validation failed on peers"); + + if (args.errstr) + *op_errstr = gf_strdup (args.errstr); + } + + ret = args.op_ret; + + gf_log (this->name, GF_LOG_DEBUG, "Sent pre valaidation req for %s " + "to %d peers. Returning %d", gd_op_list[op], peer_cnt, ret); +out: + return ret; +} + +int +glusterd_mgmt_v3_build_payload (dict_t **req, char **op_errstr, dict_t *dict, + glusterd_op_t op) +{ + int32_t ret = -1; + dict_t *req_dict = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (op_errstr); + GF_ASSERT (dict); + + req_dict = dict_new (); + if (!req_dict) + goto out; + + switch (op) { + case GD_OP_SNAP: + dict_copy (dict, req_dict); + break; + default: + break; + } + + *req = req_dict; + ret = 0; +out: + return ret; +} + +int32_t +gd_mgmt_v3_brick_op_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int32_t ret = -1; + struct syncargs *args = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + gd1_mgmt_v3_brick_op_rsp rsp = {{0},}; + call_frame_t *frame = NULL; + int32_t op_ret = -1; + int32_t op_errno = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (myframe); + + frame = myframe; + args = frame->local; + peerinfo = frame->cookie; + frame->local = NULL; + frame->cookie = NULL; + + /* If the operation failed, then iov can be NULL. So better check the + status of the operation and then worry about iov (if the status of + the command is success) + */ + if (-1 == req->rpc_status) { + op_errno = ENOTCONN; + goto out; + } + + if (!iov) { + gf_log (this->name, GF_LOG_ERROR, "iov is NULL"); + op_errno = EINVAL; + goto out; + } + + ret = xdr_to_generic (*iov, &rsp, + (xdrproc_t)xdr_gd1_mgmt_v3_brick_op_rsp); + if (ret < 0) + goto out; + + uuid_copy (args->uuid, rsp.uuid); + + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + +out: + gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, + GLUSTERD_MGMT_V3_BRICK_OP, + peerinfo, rsp.uuid); + + if (rsp.op_errstr) + free (rsp.op_errstr); + + if (rsp.dict.dict_val) + free (rsp.dict.dict_val); + + STACK_DESTROY (frame->root); + synctask_barrier_wake(args); + return 0; +} + +int32_t +gd_mgmt_v3_brick_op_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + return glusterd_big_locked_cbk (req, iov, count, myframe, + gd_mgmt_v3_brick_op_cbk_fn); +} + +int +gd_mgmt_v3_brick_op_req (glusterd_op_t op, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid) +{ + int32_t ret = -1; + gd1_mgmt_v3_brick_op_req req = {{0},}; + glusterd_conf_t *conf = THIS->private; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_ctx); + GF_ASSERT (peerinfo); + GF_ASSERT (args); + + ret = dict_allocate_and_serialize (op_ctx, + &req.dict.dict_val, + &req.dict.dict_len); + if (ret) + goto out; + + uuid_copy (req.uuid, my_uuid); + req.op = op; + synclock_unlock (&conf->big_lock); + ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, + &gd_mgmt_v3_prog, + GLUSTERD_MGMT_V3_BRICK_OP, + gd_mgmt_v3_brick_op_cbk, + (xdrproc_t) xdr_gd1_mgmt_v3_brick_op_req); + synclock_lock (&conf->big_lock); +out: + GF_FREE (req.dict.dict_val); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_mgmt_v3_brick_op (glusterd_conf_t *conf, glusterd_op_t op, + dict_t *req_dict, char **op_errstr, int npeers) +{ + int32_t ret = -1; + int32_t peer_cnt = 0; + dict_t *rsp_dict = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + struct syncargs args = {0}; + struct list_head *peers = NULL; + uuid_t peer_uuid = {0}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (conf); + GF_ASSERT (req_dict); + GF_ASSERT (op_errstr); + + peers = &conf->xaction_peers; + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create response dictionary"); + goto out; + } + + /* Perform brick op on local node */ + ret = gd_mgmt_v3_brick_op_fn (op, req_dict, op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Brick ops failed for " + "operation %s on local node", + gd_op_list[op]); + + if (*op_errstr == NULL) { + ret = gf_asprintf (op_errstr, + "Brick ops failed " + "on localhost. Please " + "check log file for details"); + if (ret == -1) + *op_errstr = NULL; + + ret = -1; + } + goto out; + } + + dict_unref (rsp_dict); + rsp_dict = NULL; + + if (!npeers) { + ret = 0; + goto out; + } + + /* Sending brick op req to other nodes in the cluster */ + gd_syncargs_init (&args, NULL); + synctask_barrier_init((&args)); + peer_cnt = 0; + list_for_each_entry (peerinfo, peers, op_peers_list) { + gd_mgmt_v3_brick_op_req (op, req_dict, peerinfo, &args, + MY_UUID, peer_uuid); + peer_cnt++; + } + gd_synctask_barrier_wait((&args), peer_cnt); + + if (args.op_ret) { + gf_log (this->name, GF_LOG_ERROR, + "Brick ops failed on peers"); + + if (args.errstr) + *op_errstr = gf_strdup (args.errstr); + } + + ret = args.op_ret; + + gf_log (this->name, GF_LOG_DEBUG, "Sent brick op req for %s " + "to %d peers. Returning %d", gd_op_list[op], peer_cnt, ret); +out: + return ret; +} + +int32_t +gd_mgmt_v3_commit_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int32_t ret = -1; + struct syncargs *args = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + gd1_mgmt_v3_commit_rsp rsp = {{0},}; + call_frame_t *frame = NULL; + int32_t op_ret = -1; + int32_t op_errno = -1; + dict_t *rsp_dict = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (myframe); + + frame = myframe; + args = frame->local; + peerinfo = frame->cookie; + frame->local = NULL; + frame->cookie = NULL; + + if (-1 == req->rpc_status) { + op_errno = ENOTCONN; + goto out; + } + + if (!iov) { + gf_log (this->name, GF_LOG_ERROR, "iov is NULL"); + op_errno = EINVAL; + goto out; + } + + ret = xdr_to_generic (*iov, &rsp, + (xdrproc_t)xdr_gd1_mgmt_v3_commit_rsp); + if (ret < 0) + goto out; + + if (rsp.dict.dict_len) { + /* Unserialize the dictionary */ + rsp_dict = dict_new (); + + ret = dict_unserialize (rsp.dict.dict_val, + rsp.dict.dict_len, + &rsp_dict); + if (ret < 0) { + free (rsp.dict.dict_val); + goto out; + } else { + rsp_dict->extra_stdfree = rsp.dict.dict_val; + } + } + + uuid_copy (args->uuid, rsp.uuid); + pthread_mutex_lock (&args->lock_dict); + { + ret = glusterd_syncop_aggr_rsp_dict (rsp.op, args->dict, + rsp_dict); + } + pthread_mutex_unlock (&args->lock_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "%s", + "Failed to aggregate response from " + " node/brick"); + if (!rsp.op_ret) + op_ret = ret; + else { + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + } + } else { + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + } + +out: + if (rsp_dict) + dict_unref (rsp_dict); + + gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, + GLUSTERD_MGMT_V3_COMMIT, + peerinfo, rsp.uuid); + + STACK_DESTROY (frame->root); + synctask_barrier_wake(args); + return 0; +} + +int32_t +gd_mgmt_v3_commit_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + return glusterd_big_locked_cbk (req, iov, count, myframe, + gd_mgmt_v3_commit_cbk_fn); +} + +int +gd_mgmt_v3_commit_req (glusterd_op_t op, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid) +{ + int32_t ret = -1; + gd1_mgmt_v3_commit_req req = {{0},}; + glusterd_conf_t *conf = THIS->private; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_ctx); + GF_ASSERT (peerinfo); + GF_ASSERT (args); + + ret = dict_allocate_and_serialize (op_ctx, + &req.dict.dict_val, + &req.dict.dict_len); + if (ret) + goto out; + + uuid_copy (req.uuid, my_uuid); + req.op = op; + synclock_unlock (&conf->big_lock); + ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, + &gd_mgmt_v3_prog, + GLUSTERD_MGMT_V3_COMMIT, + gd_mgmt_v3_commit_cbk, + (xdrproc_t) xdr_gd1_mgmt_v3_commit_req); + synclock_lock (&conf->big_lock); +out: + GF_FREE (req.dict.dict_val); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_mgmt_v3_commit (glusterd_conf_t *conf, glusterd_op_t op, + dict_t *op_ctx, dict_t *req_dict, + char **op_errstr, int npeers) +{ + int32_t ret = -1; + int32_t peer_cnt = 0; + dict_t *rsp_dict = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + struct syncargs args = {0}; + struct list_head *peers = NULL; + uuid_t peer_uuid = {0}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (conf); + GF_ASSERT (op_ctx); + GF_ASSERT (req_dict); + GF_ASSERT (op_errstr); + + peers = &conf->xaction_peers; + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create response dictionary"); + goto out; + } + + /* Commit on local node */ + ret = gd_mgmt_v3_commit_fn (op, req_dict, op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Commit failed for " + "operation %s on local node", + gd_op_list[op]); + + if (*op_errstr == NULL) { + ret = gf_asprintf (op_errstr, + "Commit failed " + "on localhost. Please " + "check log file for details."); + if (ret == -1) + *op_errstr = NULL; + + ret = -1; + } + goto out; + } + + ret = glusterd_syncop_aggr_rsp_dict (op, op_ctx, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "%s", + "Failed to aggregate response from " + " node/brick"); + goto out; + } + + dict_unref (rsp_dict); + rsp_dict = NULL; + + if (!npeers) { + ret = 0; + goto out; + } + + /* Sending commit req to other nodes in the cluster */ + gd_syncargs_init (&args, op_ctx); + synctask_barrier_init((&args)); + peer_cnt = 0; + list_for_each_entry (peerinfo, peers, op_peers_list) { + gd_mgmt_v3_commit_req (op, req_dict, peerinfo, &args, + MY_UUID, peer_uuid); + peer_cnt++; + } + gd_synctask_barrier_wait((&args), peer_cnt); + + if (args.op_ret) { + gf_log (this->name, GF_LOG_ERROR, + "Commit failed on peers"); + + if (args.errstr) + *op_errstr = gf_strdup (args.errstr); + } + + ret = args.op_ret; + + gf_log (this->name, GF_LOG_DEBUG, "Sent commit req for %s to %d " + "peers. Returning %d", gd_op_list[op], peer_cnt, ret); +out: + return ret; +} + +int32_t +gd_mgmt_v3_post_validate_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int32_t ret = -1; + struct syncargs *args = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + gd1_mgmt_v3_post_val_rsp rsp = {{0},}; + call_frame_t *frame = NULL; + int32_t op_ret = -1; + int32_t op_errno = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (myframe); + + frame = myframe; + args = frame->local; + peerinfo = frame->cookie; + frame->local = NULL; + frame->cookie = NULL; + + if (-1 == req->rpc_status) { + op_errno = ENOTCONN; + goto out; + } + + if (!iov) { + gf_log (this->name, GF_LOG_ERROR, "iov is NULL"); + op_errno = EINVAL; + goto out; + } + + ret = xdr_to_generic (*iov, &rsp, + (xdrproc_t)xdr_gd1_mgmt_v3_post_val_rsp); + if (ret < 0) + goto out; + + uuid_copy (args->uuid, rsp.uuid); + + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + +out: + gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, + GLUSTERD_MGMT_V3_POST_VALIDATE, + peerinfo, rsp.uuid); + if (rsp.op_errstr) + free (rsp.op_errstr); + + if (rsp.dict.dict_val) + free (rsp.dict.dict_val); + STACK_DESTROY (frame->root); + synctask_barrier_wake(args); + return 0; +} + +int32_t +gd_mgmt_v3_post_validate_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + return glusterd_big_locked_cbk (req, iov, count, myframe, + gd_mgmt_v3_post_validate_cbk_fn); +} + +int +gd_mgmt_v3_post_validate_req (glusterd_op_t op, int32_t op_ret, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid) +{ + int32_t ret = -1; + gd1_mgmt_v3_post_val_req req = {{0},}; + glusterd_conf_t *conf = THIS->private; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_ctx); + GF_ASSERT (peerinfo); + GF_ASSERT (args); + + ret = dict_allocate_and_serialize (op_ctx, + &req.dict.dict_val, + &req.dict.dict_len); + if (ret) + goto out; + + uuid_copy (req.uuid, my_uuid); + req.op = op; + req.op_ret = op_ret; + synclock_unlock (&conf->big_lock); + ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, + &gd_mgmt_v3_prog, + GLUSTERD_MGMT_V3_POST_VALIDATE, + gd_mgmt_v3_post_validate_cbk, + (xdrproc_t) xdr_gd1_mgmt_v3_post_val_req); + synclock_lock (&conf->big_lock); +out: + GF_FREE (req.dict.dict_val); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_mgmt_v3_post_validate (glusterd_conf_t *conf, glusterd_op_t op, + int32_t op_ret, dict_t *dict, dict_t *req_dict, + char **op_errstr, int npeers) +{ + int32_t ret = -1; + int32_t peer_cnt = 0; + dict_t *rsp_dict = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + struct syncargs args = {0}; + struct list_head *peers = NULL; + uuid_t peer_uuid = {0}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (conf); + GF_ASSERT (dict); + GF_ASSERT (req_dict); + GF_ASSERT (op_errstr); + + peers = &conf->xaction_peers; + GF_ASSERT (peers); + + rsp_dict = dict_new (); + if (!rsp_dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create response dictionary"); + goto out; + } + + /* Copy the contents of dict like missed snaps info to req_dict */ + dict_copy (dict, req_dict); + + /* Post Validation on local node */ + ret = gd_mgmt_v3_post_validate_fn (op, op_ret, req_dict, op_errstr, + rsp_dict); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Post Validation failed for " + "operation %s on local node", + gd_op_list[op]); + + if (*op_errstr == NULL) { + ret = gf_asprintf (op_errstr, + "Post-validation failed " + "on localhost. Please check " + "log file for details"); + if (ret == -1) + *op_errstr = NULL; + + ret = -1; + } + goto out; + } + + dict_unref (rsp_dict); + rsp_dict = NULL; + + if (!npeers) { + ret = 0; + goto out; + } + + /* Sending Post Validation req to other nodes in the cluster */ + gd_syncargs_init (&args, req_dict); + synctask_barrier_init((&args)); + peer_cnt = 0; + list_for_each_entry (peerinfo, peers, op_peers_list) { + gd_mgmt_v3_post_validate_req (op, op_ret, req_dict, peerinfo, + &args, MY_UUID, peer_uuid); + peer_cnt++; + } + gd_synctask_barrier_wait((&args), peer_cnt); + + if (args.op_ret) { + gf_log (this->name, GF_LOG_ERROR, + "Post Validation failed on peers"); + + if (args.errstr) + *op_errstr = gf_strdup (args.errstr); + } + + ret = args.op_ret; + + gf_log (this->name, GF_LOG_DEBUG, "Sent post valaidation req for %s " + "to %d peers. Returning %d", gd_op_list[op], peer_cnt, ret); +out: + return ret; +} + +int32_t +gd_mgmt_v3_unlock_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + int32_t ret = -1; + struct syncargs *args = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + gd1_mgmt_v3_unlock_rsp rsp = {{0},}; + call_frame_t *frame = NULL; + int32_t op_ret = -1; + int32_t op_errno = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (myframe); + + frame = myframe; + args = frame->local; + peerinfo = frame->cookie; + frame->local = NULL; + frame->cookie = NULL; + + if (-1 == req->rpc_status) { + op_errno = ENOTCONN; + goto out; + } + + if (!iov) { + gf_log (this->name, GF_LOG_ERROR, "iov is NULL"); + op_errno = EINVAL; + goto out; + } + + ret = xdr_to_generic (*iov, &rsp, + (xdrproc_t)xdr_gd1_mgmt_v3_unlock_rsp); + if (ret < 0) + goto out; + + uuid_copy (args->uuid, rsp.uuid); + + op_ret = rsp.op_ret; + op_errno = rsp.op_errno; + +out: + gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, + GLUSTERD_MGMT_V3_UNLOCK, + peerinfo, rsp.uuid); + if (rsp.dict.dict_val) + free (rsp.dict.dict_val); + STACK_DESTROY (frame->root); + synctask_barrier_wake(args); + return 0; +} + +int32_t +gd_mgmt_v3_unlock_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + return glusterd_big_locked_cbk (req, iov, count, myframe, + gd_mgmt_v3_unlock_cbk_fn); +} + +int +gd_mgmt_v3_unlock (glusterd_op_t op, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid) +{ + int32_t ret = -1; + gd1_mgmt_v3_unlock_req req = {{0},}; + glusterd_conf_t *conf = THIS->private; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_ctx); + GF_ASSERT (peerinfo); + GF_ASSERT (args); + + ret = dict_allocate_and_serialize (op_ctx, + &req.dict.dict_val, + &req.dict.dict_len); + if (ret) + goto out; + + uuid_copy (req.uuid, my_uuid); + req.op = op; + synclock_unlock (&conf->big_lock); + ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, + &gd_mgmt_v3_prog, + GLUSTERD_MGMT_V3_UNLOCK, + gd_mgmt_v3_unlock_cbk, + (xdrproc_t) xdr_gd1_mgmt_v3_unlock_req); + synclock_lock (&conf->big_lock); +out: + GF_FREE (req.dict.dict_val); + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_mgmt_v3_release_peer_locks (glusterd_conf_t *conf, glusterd_op_t op, + dict_t *dict, int32_t op_ret, + char **op_errstr, int npeers, + gf_boolean_t is_acquired) +{ + int32_t ret = -1; + int32_t peer_cnt = 0; + uuid_t peer_uuid = {0}; + xlator_t *this = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + struct syncargs args = {0}; + struct list_head *peers = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (conf); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + + peers = &conf->xaction_peers; + + /* If the lock has not been held during this + * transaction, do not send unlock requests */ + if (!is_acquired) + goto out; + + if (!npeers) { + ret = 0; + goto out; + } + + /* Sending mgmt_v3 unlock req to other nodes in the cluster */ + gd_syncargs_init (&args, NULL); + synctask_barrier_init((&args)); + peer_cnt = 0; + list_for_each_entry (peerinfo, peers, op_peers_list) { + gd_mgmt_v3_unlock (op, dict, peerinfo, &args, + MY_UUID, peer_uuid); + peer_cnt++; + } + gd_synctask_barrier_wait((&args), peer_cnt); + + if (args.op_ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unlock failed on peers"); + + if (!op_ret && args.errstr) + *op_errstr = gf_strdup (args.errstr); + } + + ret = args.op_ret; + + gf_log (this->name, GF_LOG_DEBUG, "Sent unlock op req for %s " + "to %d peers. Returning %d", gd_op_list[op], peer_cnt, ret); + +out: + return ret; +} + +int32_t +glusterd_mgmt_v3_initiate_all_phases (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict) +{ + int32_t ret = -1; + int32_t op_ret = -1; + int32_t npeers = 0; + dict_t *req_dict = NULL; + dict_t *tmp_dict = NULL; + glusterd_conf_t *conf = NULL; + char *op_errstr = NULL; + xlator_t *this = NULL; + gf_boolean_t is_acquired = _gf_false; + uuid_t *originator_uuid = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (dict); + conf = this->private; + GF_ASSERT (conf); + + /* Save the MY_UUID as the originator_uuid. This originator_uuid + * will be used by is_origin_glusterd() to determine if a node + * is the originator node for a command. */ + originator_uuid = GF_CALLOC (1, sizeof(uuid_t), + gf_common_mt_uuid_t); + if (!originator_uuid) { + ret = -1; + goto out; + } + + uuid_copy (*originator_uuid, MY_UUID); + ret = dict_set_bin (dict, "originator_uuid", + originator_uuid, sizeof (uuid_t)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set originator_uuid."); + goto out; + } + + /* Marking the operation as complete synctasked */ + ret = dict_set_int32 (dict, "is_synctasked", _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set synctasked flag."); + goto out; + } + + /* Use a copy at local unlock as cli response will be sent before + * the unlock and the volname in the dict might be removed */ + tmp_dict = dict_new(); + if (!tmp_dict) { + gf_log (this->name, GF_LOG_ERROR, "Unable to create dict"); + goto out; + } + dict_copy (dict, tmp_dict); + + /* BUILD PEERS LIST */ + INIT_LIST_HEAD (&conf->xaction_peers); + npeers = gd_build_peers_list (&conf->peers, &conf->xaction_peers, op); + + /* LOCKDOWN PHASE - Acquire mgmt_v3 locks */ + ret = glusterd_mgmt_v3_initiate_lockdown (conf, op, dict, &op_errstr, + npeers, &is_acquired); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "mgmt_v3 lockdown failed."); + goto out; + } + + /* BUILD PAYLOAD */ + ret = glusterd_mgmt_v3_build_payload (&req_dict, &op_errstr, dict, op); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, LOGSTR_BUILD_PAYLOAD, + gd_op_list[op]); + if (op_errstr == NULL) + gf_asprintf (&op_errstr, OPERRSTR_BUILD_PAYLOAD); + goto out; + } + + /* PRE-COMMIT VALIDATE PHASE */ + ret = glusterd_mgmt_v3_pre_validate (conf, op, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Pre Validation Failed"); + goto out; + } + + /* COMMIT OP PHASE */ + ret = glusterd_mgmt_v3_commit (conf, op, dict, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Commit Op Failed"); + goto out; + } + + /* POST-COMMIT VALIDATE PHASE */ + /* As of now, post_validate is not handling any other + commands other than snapshot. So as of now, I am + sending 0 (op_ret as 0). + */ + ret = glusterd_mgmt_v3_post_validate (conf, op, 0, dict, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Post Validation Failed"); + goto out; + } + + ret = 0; +out: + op_ret = ret; + /* UNLOCK PHASE FOR PEERS*/ + (void) glusterd_mgmt_v3_release_peer_locks (conf, op, dict, + op_ret, &op_errstr, + npeers, is_acquired); + + /* LOCAL VOLUME(S) UNLOCK */ + if (is_acquired) { + /* Trying to release multiple mgmt_v3 locks */ + ret = glusterd_multiple_mgmt_v3_unlock (tmp_dict, MY_UUID); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release mgmt_v3 locks on localhost"); + op_ret = ret; + } + } + + /* SEND CLI RESPONSE */ + glusterd_op_send_cli_response (op, op_ret, 0, req, dict, op_errstr); + + if (req_dict) + dict_unref (req_dict); + + if (tmp_dict) + dict_unref (tmp_dict); + + if (op_errstr) { + GF_FREE (op_errstr); + op_errstr = NULL; + } + + return 0; +} + +int32_t +glusterd_mgmt_v3_initiate_snap_phases (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict) +{ + int32_t ret = -1; + int32_t op_ret = -1; + int32_t npeers = 0; + dict_t *req_dict = NULL; + dict_t *tmp_dict = NULL; + glusterd_conf_t *conf = NULL; + char *op_errstr = NULL; + xlator_t *this = NULL; + gf_boolean_t is_acquired = _gf_false; + uuid_t *originator_uuid = NULL; + gf_boolean_t success = _gf_false; + char *tmp_errstr = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (dict); + conf = this->private; + GF_ASSERT (conf); + + /* Save the MY_UUID as the originator_uuid. This originator_uuid + * will be used by is_origin_glusterd() to determine if a node + * is the originator node for a command. */ + originator_uuid = GF_CALLOC (1, sizeof(uuid_t), + gf_common_mt_uuid_t); + if (!originator_uuid) { + ret = -1; + goto out; + } + + uuid_copy (*originator_uuid, MY_UUID); + ret = dict_set_bin (dict, "originator_uuid", + originator_uuid, sizeof (uuid_t)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set originator_uuid."); + goto out; + } + + /* Marking the operation as complete synctasked */ + ret = dict_set_int32 (dict, "is_synctasked", _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set synctasked flag."); + goto out; + } + + /* Use a copy at local unlock as cli response will be sent before + * the unlock and the volname in the dict might be removed */ + tmp_dict = dict_new(); + if (!tmp_dict) { + gf_log (this->name, GF_LOG_ERROR, "Unable to create dict"); + goto out; + } + dict_copy (dict, tmp_dict); + + /* BUILD PEERS LIST */ + INIT_LIST_HEAD (&conf->xaction_peers); + npeers = gd_build_peers_list (&conf->peers, &conf->xaction_peers, op); + + /* LOCKDOWN PHASE - Acquire mgmt_v3 locks */ + ret = glusterd_mgmt_v3_initiate_lockdown (conf, op, dict, &op_errstr, + npeers, &is_acquired); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "mgmt_v3 lockdown failed."); + goto out; + } + + /* BUILD PAYLOAD */ + ret = glusterd_mgmt_v3_build_payload (&req_dict, &op_errstr, dict, op); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, LOGSTR_BUILD_PAYLOAD, + gd_op_list[op]); + if (op_errstr == NULL) + gf_asprintf (&op_errstr, OPERRSTR_BUILD_PAYLOAD); + goto out; + } + + /* PRE-COMMIT VALIDATE PHASE */ + ret = glusterd_mgmt_v3_pre_validate (conf, op, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Pre Validation Failed"); + goto out; + } + + /* BRICK OP PHASE for initiating barrier*/ + ret = dict_set_int32 (req_dict, "barrier", 1); + if (ret) + goto out; + ret = glusterd_mgmt_v3_brick_op (conf, op, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Brick Ops Failed"); + goto unbarrier; + } + + /* COMMIT OP PHASE */ + /* TODO: As of now, the plan is to do quorum check before sending the + commit fop and if the quorum succeeds, then commit is sent to all + the other glusterds. + snap create functionality now creates the in memory and on disk + objects for the snapshot (marking them as incomplete), takes the lvm + snapshot and then updates the status of the in memory and on disk + snap objects as complete. Suppose one of the glusterds goes down + after taking the lvm snapshot, but before updating the snap object, + then treat it as a snapshot create failure and trigger cleanup. + i.e the number of commit responses received by the originator + glusterd shold be the same as the number of peers it has sent the + request to (i.e npeers variable). If not, then originator glusterd + will initiate cleanup in post-validate fop. + Question: What if one of the other glusterds goes down as explained + above and along with it the originator glusterd also goes down? + Who will initiate the cleanup? + */ + ret = glusterd_mgmt_v3_commit (conf, op, dict, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Commit Op Failed"); + /* If the main op fails, we should save the error string. + Because, op_errstr will be used for unbarrier and + unlock ops also. We might lose the actual error that + caused the failure. + */ + tmp_errstr = op_errstr; + op_errstr = NULL; + goto unbarrier; + } + + success = _gf_true; +unbarrier: + /* BRICK OP PHASE for removing the barrier*/ + ret = dict_set_int32 (req_dict, "barrier", 0); + if (ret) + goto out; + ret = glusterd_mgmt_v3_brick_op (conf, op, req_dict, + &op_errstr, npeers); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Brick Ops Failed"); + goto out; + } + + ret = 0; + +out: + op_ret = ret; + + if (success == _gf_false) + op_ret = -1; + + /* POST-COMMIT VALIDATE PHASE */ + ret = glusterd_mgmt_v3_post_validate (conf, op, op_ret, dict, req_dict, + &op_errstr, npeers); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Post Validation Failed"); + op_ret = -1; + } + + /* UNLOCK PHASE FOR PEERS*/ + (void) glusterd_mgmt_v3_release_peer_locks (conf, op, dict, + op_ret, &op_errstr, + npeers, is_acquired); + + /* If the commit op (snapshot taking) failed, then the error is stored + in tmp_errstr and unbarrier is called. Suppose, if unbarrier also + fails, then the error happened in unbarrier is logged and freed. + The error happened in commit op, which is stored in tmp_errstr + is sent to cli. + */ + if (tmp_errstr) { + if (op_errstr) { + gf_log (this->name, GF_LOG_ERROR, "unbarrier brick op" + "failed with the error %s", op_errstr); + GF_FREE (op_errstr); + op_errstr = NULL; + } + op_errstr = tmp_errstr; + } + + /* LOCAL VOLUME(S) UNLOCK */ + if (is_acquired) { + /* Trying to release multiple mgmt_v3 locks */ + ret = glusterd_multiple_mgmt_v3_unlock (tmp_dict, MY_UUID); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to release mgmt_v3 locks on localhost"); + op_ret = ret; + } + } + + /* SEND CLI RESPONSE */ + glusterd_op_send_cli_response (op, op_ret, 0, req, dict, op_errstr); + + if (req_dict) + dict_unref (req_dict); + + if (tmp_dict) + dict_unref (tmp_dict); + + if (op_errstr) { + GF_FREE (op_errstr); + op_errstr = NULL; + } + + return 0; +} diff --git a/xlators/mgmt/glusterd/src/glusterd-mgmt.h b/xlators/mgmt/glusterd/src/glusterd-mgmt.h new file mode 100644 index 000000000..b185a9bec --- /dev/null +++ b/xlators/mgmt/glusterd/src/glusterd-mgmt.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#ifndef _GLUSTERD_MGMT_H_ +#define _GLUSTERD_MGMT_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +int32_t +gd_mgmt_v3_pre_validate_fn (glusterd_op_t op, dict_t *dict, + char **op_errstr, dict_t *rsp_dict); + +int32_t +gd_mgmt_v3_brick_op_fn (glusterd_op_t op, dict_t *dict, + char **op_errstr, dict_t *rsp_dict); + +int32_t +gd_mgmt_v3_commit_fn (glusterd_op_t op, dict_t *dict, + char **op_errstr, dict_t *rsp_dict); + +int32_t +gd_mgmt_v3_post_validate_fn (glusterd_op_t op, int32_t op_ret, dict_t *dict, + char **op_errstr, dict_t *rsp_dict); + +int32_t +glusterd_mgmt_v3_initiate_all_phases (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict); + +int32_t +glusterd_mgmt_v3_initiate_snap_phases (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict); + +int +glusterd_snap_pre_validate_use_rsp_dict (dict_t *dst, dict_t *src); + +#endif /* _GLUSTERD_MGMT_H_ */ diff --git a/xlators/mgmt/glusterd/src/glusterd-op-sm.c b/xlators/mgmt/glusterd/src/glusterd-op-sm.c index e3ae369e4..fb5e097d9 100644 --- a/xlators/mgmt/glusterd/src/glusterd-op-sm.c +++ b/xlators/mgmt/glusterd/src/glusterd-op-sm.c @@ -158,7 +158,7 @@ glusterd_generate_txn_id (dict_t *dict, uuid_t **txn_id) uuid_generate (**txn_id); ret = dict_set_bin (dict, "transaction_id", - *txn_id, sizeof (uuid_t)); + *txn_id, sizeof (**txn_id)); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set transaction id."); @@ -517,7 +517,20 @@ glusterd_brick_op_build_payload (glusterd_op_t op, glusterd_brickinfo_t *brickin brick_req->name = gf_strdup (name); break; + case GD_OP_SNAP: + brick_req = GF_CALLOC (1, sizeof (*brick_req), + gf_gld_mt_mop_brick_req_t); + if (!brick_req) + goto out; + + brick_req->op = GLUSTERD_VOLUME_BARRIER_OP; + ret = dict_get_str (dict, "volname", &volname); + if (ret) + goto out; + snprintf (name, 1024, "%s-server",volname); + brick_req->name = gf_strdup (name); + break; default: goto out; break; @@ -1659,14 +1672,25 @@ glusterd_stop_bricks (glusterd_volinfo_t *volinfo) int glusterd_start_bricks (glusterd_volinfo_t *volinfo) { - glusterd_brickinfo_t *brickinfo = NULL; + int ret = -1; + glusterd_brickinfo_t *brickinfo = NULL; + + GF_ASSERT (volinfo); list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { - if (glusterd_brick_start (volinfo, brickinfo, _gf_false)) - return -1; + ret = glusterd_brick_start (volinfo, brickinfo, _gf_false); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, + "Failed to start %s:%s for %s", + brickinfo->hostname, brickinfo->path, + volinfo->volname); + goto out; + } } - return 0; + ret = 0; +out: + return ret; } static int @@ -2687,7 +2711,7 @@ glusterd_op_ac_send_lock (glusterd_op_sm_event_t *event, void *ctx) (glusterd_op_get_op() != GD_OP_SYNC_VOLUME)) continue; - /* Based on the op_version, acquire a cluster or volume lock */ + /* Based on the op_version, acquire a cluster or mgmt_v3 lock */ if (priv->op_version < GD_OP_VERSION_4) { proc = &peerinfo->mgmt->proctable [GLUSTERD_MGMT_CLUSTER_LOCK]; @@ -2709,7 +2733,7 @@ glusterd_op_ac_send_lock (glusterd_op_sm_event_t *event, void *ctx) dict_ref (dict); proc = &peerinfo->mgmt_v3->proctable - [GLUSTERD_MGMT_V3_VOLUME_LOCK]; + [GLUSTERD_MGMT_V3_LOCK]; if (proc->fn) { ret = dict_set_static_ptr (dict, "peerinfo", peerinfo); @@ -2723,7 +2747,7 @@ glusterd_op_ac_send_lock (glusterd_op_sm_event_t *event, void *ctx) ret = proc->fn (NULL, this, dict); if (ret) { gf_log (this->name, GF_LOG_WARNING, - "Failed to send volume lock " + "Failed to send mgmt_v3 lock " "request for operation " "'Volume %s' to peer %s", gd_op_list[opinfo.op], @@ -2770,7 +2794,7 @@ glusterd_op_ac_send_unlock (glusterd_op_sm_event_t *event, void *ctx) continue; /* Based on the op_version, - * release the cluster or volume lock */ + * release the cluster or mgmt_v3 lock */ if (priv->op_version < GD_OP_VERSION_4) { proc = &peerinfo->mgmt->proctable [GLUSTERD_MGMT_CLUSTER_UNLOCK]; @@ -2792,7 +2816,7 @@ glusterd_op_ac_send_unlock (glusterd_op_sm_event_t *event, void *ctx) dict_ref (dict); proc = &peerinfo->mgmt_v3->proctable - [GLUSTERD_MGMT_V3_VOLUME_UNLOCK]; + [GLUSTERD_MGMT_V3_UNLOCK]; if (proc->fn) { ret = dict_set_static_ptr (dict, "peerinfo", peerinfo); @@ -2870,7 +2894,7 @@ glusterd_op_ac_lock (glusterd_op_sm_event_t *event, void *ctx) /* If the req came from a node running on older op_version * the dict won't be present. Based on it acquiring a cluster - * or volume lock */ + * or mgmt_v3 lock */ if (lock_ctx->dict == NULL) { ret = glusterd_lock (lock_ctx->uuid); glusterd_op_lock_send_resp (lock_ctx->req, ret); @@ -2880,14 +2904,15 @@ glusterd_op_ac_lock (glusterd_op_sm_event_t *event, void *ctx) gf_log (this->name, GF_LOG_ERROR, "Unable to acquire volname"); else { - ret = glusterd_volume_lock (volname, lock_ctx->uuid); + ret = glusterd_mgmt_v3_lock (volname, lock_ctx->uuid, + "vol"); if (ret) gf_log (this->name, GF_LOG_ERROR, "Unable to acquire lock for %s", volname); } - glusterd_op_volume_lock_send_resp (lock_ctx->req, + glusterd_op_mgmt_v3_lock_send_resp (lock_ctx->req, &event->txn_id, ret); dict_unref (lock_ctx->dict); @@ -2917,7 +2942,7 @@ glusterd_op_ac_unlock (glusterd_op_sm_event_t *event, void *ctx) /* If the req came from a node running on older op_version * the dict won't be present. Based on it releasing the cluster - * or volume lock */ + * or mgmt_v3 lock */ if (lock_ctx->dict == NULL) { ret = glusterd_unlock (lock_ctx->uuid); glusterd_op_unlock_send_resp (lock_ctx->req, ret); @@ -2927,14 +2952,15 @@ glusterd_op_ac_unlock (glusterd_op_sm_event_t *event, void *ctx) gf_log (this->name, GF_LOG_ERROR, "Unable to acquire volname"); else { - ret = glusterd_volume_unlock (volname, lock_ctx->uuid); + ret = glusterd_mgmt_v3_unlock (volname, lock_ctx->uuid, + "vol"); if (ret) gf_log (this->name, GF_LOG_ERROR, "Unable to release lock for %s", volname); } - glusterd_op_volume_unlock_send_resp (lock_ctx->req, + glusterd_op_mgmt_v3_unlock_send_resp (lock_ctx->req, &event->txn_id, ret); dict_unref (lock_ctx->dict); @@ -3466,7 +3492,7 @@ glusterd_op_start_rb_timer (dict_t *dict, uuid_t *txn_id) } ret = dict_set_bin (rb_ctx, "transaction_id", - txn_id, sizeof (uuid_t)); + txn_id, sizeof (*txn_id)); if (ret) { gf_log ("", GF_LOG_ERROR, "Failed to set transaction id."); @@ -4243,7 +4269,7 @@ glusterd_op_txn_complete (uuid_t *txn_id) glusterd_op_reset_ctx (); glusterd_op_clear_errstr (); - /* Based on the op-version, we release the cluster or volume lock */ + /* Based on the op-version, we release the cluster or mgmt_v3 lock */ if (priv->op_version < GD_OP_VERSION_4) { ret = glusterd_unlock (MY_UUID); /* unlock cant/shouldnt fail here!! */ @@ -4260,7 +4286,8 @@ glusterd_op_txn_complete (uuid_t *txn_id) "Locks have not been held."); if (volname) { - ret = glusterd_volume_unlock (volname, MY_UUID); + ret = glusterd_mgmt_v3_unlock (volname, MY_UUID, + "vol"); if (ret) gf_log (this->name, GF_LOG_ERROR, "Unable to release lock for %s", @@ -4355,7 +4382,7 @@ glusterd_op_ac_stage_op (glusterd_op_sm_event_t *event, void *ctx) } ret = dict_set_bin (rsp_dict, "transaction_id", - txn_id, sizeof(uuid_t)); + txn_id, sizeof(*txn_id)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set transaction id."); @@ -4467,7 +4494,7 @@ glusterd_op_ac_commit_op (glusterd_op_sm_event_t *event, void *ctx) } ret = dict_set_bin (rsp_dict, "transaction_id", - txn_id, sizeof(uuid_t)); + txn_id, sizeof(*txn_id)); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to set transaction id."); @@ -5224,6 +5251,61 @@ _select_rxlators_for_full_self_heal (xlator_t *this, } +static int +glusterd_bricks_select_snap (dict_t *dict, char **op_errstr, + struct list_head *selected) +{ + int ret = -1; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + glusterd_pending_node_t *pending_node = NULL; + glusterd_volinfo_t *volinfo = NULL; + char *volname = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + int brick_index = -1; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + ret = dict_get_str (dict, "volname", &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to get" + " volname"); + goto out; + } + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) + goto out; + + list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { + brick_index++; + if (uuid_compare (brickinfo->uuid, MY_UUID) || + !glusterd_is_brick_started (brickinfo)) { + continue; + } + pending_node = GF_CALLOC (1, sizeof (*pending_node), + gf_gld_mt_pending_node_t); + if (!pending_node) { + ret = -1; + goto out; + } + pending_node->node = brickinfo; + pending_node->type = GD_NODE_BRICK; + pending_node->index = brick_index; + list_add_tail (&pending_node->list, + selected); + pending_node = NULL; + } + + ret = 0; + +out: + gf_log (THIS->name, GF_LOG_DEBUG, "Returning ret %d", ret); + return ret; +} + static int fill_shd_status_for_local_bricks (dict_t *dict, glusterd_volinfo_t *volinfo, cli_cmd_type type, dict_t *req_dict) @@ -5795,7 +5877,9 @@ glusterd_op_bricks_select (glusterd_op_t op, dict_t *dict, char **op_errstr, ret = glusterd_bricks_select_rebalance_volume (dict, op_errstr, selected); break; - + case GD_OP_SNAP: + ret = glusterd_bricks_select_snap (dict, op_errstr, selected); + break; default: break; } diff --git a/xlators/mgmt/glusterd/src/glusterd-replace-brick.c b/xlators/mgmt/glusterd/src/glusterd-replace-brick.c index e78eff44d..ed6d7fd57 100644 --- a/xlators/mgmt/glusterd/src/glusterd-replace-brick.c +++ b/xlators/mgmt/glusterd/src/glusterd-replace-brick.c @@ -688,7 +688,7 @@ rb_src_brick_restart (glusterd_volinfo_t *volinfo, sleep (2); ret = glusterd_volume_start_glusterfs (volinfo, src_brickinfo, - _gf_false); + _gf_false); if (ret) { gf_log ("", GF_LOG_ERROR, "Unable to start " "glusterfs, ret: %d", ret); diff --git a/xlators/mgmt/glusterd/src/glusterd-rpc-ops.c b/xlators/mgmt/glusterd/src/glusterd-rpc-ops.c index 18f37c190..27910d132 100644 --- a/xlators/mgmt/glusterd/src/glusterd-rpc-ops.c +++ b/xlators/mgmt/glusterd/src/glusterd-rpc-ops.c @@ -137,6 +137,7 @@ glusterd_op_send_cli_response (glusterd_op_t op, int32_t op_ret, case GD_OP_CLEARLOCKS_VOLUME: case GD_OP_HEAL_VOLUME: case GD_OP_QUOTA: + case GD_OP_SNAP: { /*nothing specific to be done*/ break; @@ -645,10 +646,10 @@ glusterd_cluster_lock_cbk (struct rpc_req *req, struct iovec *iov, } static int32_t -glusterd_vol_lock_cbk_fn (struct rpc_req *req, struct iovec *iov, +glusterd_mgmt_v3_lock_peers_cbk_fn (struct rpc_req *req, struct iovec *iov, int count, void *myframe) { - gd1_mgmt_volume_lock_rsp rsp = {{0},}; + gd1_mgmt_v3_lock_rsp rsp = {{0},}; int ret = -1; int32_t op_ret = -1; glusterd_op_sm_event_type_t event_type = GD_OP_EVENT_NONE; @@ -667,10 +668,10 @@ glusterd_vol_lock_cbk_fn (struct rpc_req *req, struct iovec *iov, } ret = xdr_to_generic (*iov, &rsp, - (xdrproc_t)xdr_gd1_mgmt_volume_lock_rsp); + (xdrproc_t)xdr_gd1_mgmt_v3_lock_rsp); if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, - "Failed to decode volume lock " + "Failed to decode mgmt_v3 lock " "response received from peer"); rsp.op_ret = -1; rsp.op_errno = EINVAL; @@ -682,13 +683,13 @@ glusterd_vol_lock_cbk_fn (struct rpc_req *req, struct iovec *iov, txn_id = &rsp.txn_id; gf_log (this->name, (op_ret) ? GF_LOG_ERROR : GF_LOG_DEBUG, - "Received volume lock %s from uuid: %s", + "Received mgmt_v3 lock %s from uuid: %s", (op_ret) ? "RJT" : "ACC", uuid_utoa (rsp.uuid)); ret = glusterd_friend_find (rsp.uuid, NULL, &peerinfo); if (ret) { gf_log (this->name, GF_LOG_CRITICAL, - "Volume lock response received " + "mgmt_v3 lock response received " "from unknown peer: %s. Ignoring response", uuid_utoa (rsp.uuid)); goto out; @@ -717,18 +718,18 @@ out: } int32_t -glusterd_vol_lock_cbk (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +glusterd_mgmt_v3_lock_peers_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) { return glusterd_big_locked_cbk (req, iov, count, myframe, - glusterd_vol_lock_cbk_fn); + glusterd_mgmt_v3_lock_peers_cbk_fn); } static int32_t -glusterd_vol_unlock_cbk_fn (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +glusterd_mgmt_v3_unlock_peers_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) { - gd1_mgmt_volume_unlock_rsp rsp = {{0},}; + gd1_mgmt_v3_unlock_rsp rsp = {{0},}; int ret = -1; int32_t op_ret = -1; glusterd_op_sm_event_type_t event_type = GD_OP_EVENT_NONE; @@ -747,10 +748,10 @@ glusterd_vol_unlock_cbk_fn (struct rpc_req *req, struct iovec *iov, } ret = xdr_to_generic (*iov, &rsp, - (xdrproc_t)xdr_gd1_mgmt_volume_unlock_rsp); + (xdrproc_t)xdr_gd1_mgmt_v3_unlock_rsp); if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, - "Failed to decode volume unlock " + "Failed to decode mgmt_v3 unlock " "response received from peer"); rsp.op_ret = -1; rsp.op_errno = EINVAL; @@ -762,7 +763,7 @@ glusterd_vol_unlock_cbk_fn (struct rpc_req *req, struct iovec *iov, txn_id = &rsp.txn_id; gf_log (this->name, (op_ret) ? GF_LOG_ERROR : GF_LOG_DEBUG, - "Received volume unlock %s from uuid: %s", + "Received mgmt_v3 unlock %s from uuid: %s", (op_ret) ? "RJT" : "ACC", uuid_utoa (rsp.uuid)); @@ -770,7 +771,7 @@ glusterd_vol_unlock_cbk_fn (struct rpc_req *req, struct iovec *iov, if (ret) { gf_log (this->name, GF_LOG_CRITICAL, - "Volume unlock response received " + "mgmt_v3 unlock response received " "from unknown peer: %s. Ignoring response", uuid_utoa (rsp.uuid)); goto out; @@ -799,11 +800,11 @@ out: } int32_t -glusterd_vol_unlock_cbk (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +glusterd_mgmt_v3_unlock_peers_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) { return glusterd_big_locked_cbk (req, iov, count, myframe, - glusterd_vol_unlock_cbk_fn); + glusterd_mgmt_v3_unlock_peers_cbk_fn); } int32_t @@ -1404,10 +1405,10 @@ out: } int32_t -glusterd_vol_lock (call_frame_t *frame, xlator_t *this, - void *data) +glusterd_mgmt_v3_lock_peers (call_frame_t *frame, xlator_t *this, + void *data) { - gd1_mgmt_volume_lock_req req = {{0},}; + gd1_mgmt_v3_lock_req req = {{0},}; int ret = -1; glusterd_peerinfo_t *peerinfo = NULL; glusterd_conf_t *priv = NULL; @@ -1440,6 +1441,7 @@ glusterd_vol_lock (call_frame_t *frame, xlator_t *this, goto out; } + /* Sending valid transaction ID to peers */ ret = dict_get_bin (dict, "transaction_id", (void **)&txn_id); if (ret) { @@ -1460,19 +1462,19 @@ glusterd_vol_lock (call_frame_t *frame, xlator_t *this, ret = glusterd_submit_request (peerinfo->rpc, &req, dummy_frame, peerinfo->mgmt_v3, - GLUSTERD_MGMT_V3_VOLUME_LOCK, NULL, - this, glusterd_vol_lock_cbk, - (xdrproc_t)xdr_gd1_mgmt_volume_lock_req); + GLUSTERD_MGMT_V3_LOCK, NULL, + this, glusterd_mgmt_v3_lock_peers_cbk, + (xdrproc_t)xdr_gd1_mgmt_v3_lock_req); out: gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); return ret; } int32_t -glusterd_vol_unlock (call_frame_t *frame, xlator_t *this, +glusterd_mgmt_v3_unlock_peers (call_frame_t *frame, xlator_t *this, void *data) { - gd1_mgmt_volume_unlock_req req = {{0},}; + gd1_mgmt_v3_unlock_req req = {{0},}; int ret = -1; glusterd_peerinfo_t *peerinfo = NULL; glusterd_conf_t *priv = NULL; @@ -1505,6 +1507,7 @@ glusterd_vol_unlock (call_frame_t *frame, xlator_t *this, goto out; } + /* Sending valid transaction ID to peers */ ret = dict_get_bin (dict, "transaction_id", (void **)&txn_id); if (ret) { @@ -1525,10 +1528,10 @@ glusterd_vol_unlock (call_frame_t *frame, xlator_t *this, ret = glusterd_submit_request (peerinfo->rpc, &req, dummy_frame, peerinfo->mgmt_v3, - GLUSTERD_MGMT_V3_VOLUME_UNLOCK, NULL, - this, glusterd_vol_unlock_cbk, + GLUSTERD_MGMT_V3_UNLOCK, NULL, + this, glusterd_mgmt_v3_unlock_peers_cbk, (xdrproc_t) - xdr_gd1_mgmt_volume_unlock_req); + xdr_gd1_mgmt_v3_unlock_req); out: gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); return ret; @@ -1955,9 +1958,9 @@ struct rpc_clnt_procedure gd_mgmt_actors[GLUSTERD_MGMT_MAXVALUE] = { }; struct rpc_clnt_procedure gd_mgmt_v3_actors[GLUSTERD_MGMT_V3_MAXVALUE] = { - [GLUSTERD_MGMT_V3_NULL] = {"NULL", NULL }, - [GLUSTERD_MGMT_V3_VOLUME_LOCK] = {"VOLUME_LOCK", glusterd_vol_lock}, - [GLUSTERD_MGMT_V3_VOLUME_UNLOCK] = {"VOLUME_UNLOCK", glusterd_vol_unlock}, + [GLUSTERD_MGMT_V3_NULL] = {"NULL", NULL }, + [GLUSTERD_MGMT_V3_LOCK] = {"MGMT_V3_LOCK", glusterd_mgmt_v3_lock_peers}, + [GLUSTERD_MGMT_V3_UNLOCK] = {"MGMT_V3_UNLOCK", glusterd_mgmt_v3_unlock_peers}, }; struct rpc_clnt_program gd_mgmt_prog = { diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot.c b/xlators/mgmt/glusterd/src/glusterd-snapshot.c new file mode 100644 index 000000000..e19ee78ec --- /dev/null +++ b/xlators/mgmt/glusterd/src/glusterd-snapshot.c @@ -0,0 +1,5595 @@ +/* + Copyright (c) 2013-2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "globals.h" +#include "compat.h" +#include "protocol-common.h" +#include "xlator.h" +#include "logging.h" +#include "timer.h" +#include "glusterd-mem-types.h" +#include "glusterd.h" +#include "glusterd-sm.h" +#include "glusterd-op-sm.h" +#include "glusterd-utils.h" +#include "glusterd-store.h" +#include "run.h" +#include "glusterd-volgen.h" +#include "glusterd-mgmt.h" +#include "glusterd-syncop.h" + +#include "syscall.h" +#include "cli1-xdr.h" +#include "xdr-generic.h" + +#ifdef GF_LINUX_HOST_OS +#include +#endif + +char snap_mount_folder[PATH_MAX]; + +static int32_t +glusterd_find_missed_snap (dict_t *rsp_dict, glusterd_volinfo_t *vol, + char *snap_uuid, struct list_head *peers, + int32_t op); + +/* This function will restore a snapshot volumes + * + * @param dict dictionary containing snapshot restore request + * @param op_errstr In case of any failure error message will be returned + * in this variable + * @return Negative value on Failure and 0 in success + */ +int +glusterd_snapshot_restore (dict_t *dict, char **op_errstr, dict_t *rsp_dict) +{ + int ret = -1; + char *volname = NULL; + char *snapname = NULL; + xlator_t *this = NULL; + glusterd_volinfo_t *snap_volinfo = NULL; + glusterd_volinfo_t *volinfo = NULL; + glusterd_snap_t *snap = NULL; + glusterd_conf_t *priv = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + + priv = this->private; + GF_ASSERT (priv); + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get " + "snap name"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (NULL == snap) { + ret = gf_asprintf (op_errstr, "Snap (%s) not found", + snapname); + if (ret < 0) { + goto out; + } + gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); + ret = -1; + goto out; + } + + /* TODO : As of now there is only volume in snapshot. + * Change this when multiple volume snapshot is introduced + */ + snap_volinfo = list_entry (snap->volumes.next, glusterd_volinfo_t, + vol_list); + + ret = glusterd_volinfo_find (snap_volinfo->parent_volname, &volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not get volinfo of " + "%s", snap_volinfo->parent_volname); + goto out; + } + + if (is_origin_glusterd (dict) == _gf_true) { + /* From origin glusterd check if * + * any peers with snap bricks is down */ + ret = glusterd_find_missed_snap (rsp_dict, snap_volinfo, + snap_volinfo->volname, + &priv->peers, + GF_SNAP_OPTION_TYPE_RESTORE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to find missed snap restores"); + goto out; + } + } + + ret = gd_restore_snap_volume (rsp_dict, volinfo, snap_volinfo); + if (ret) { + /* No need to update op_errstr because it is assumed + * that the called function will do that in case of + * failure. + */ + gf_log (this->name, GF_LOG_ERROR, "Failed to restore " + "snap for %s volume", volname); + goto out; + } + + ret = 0; + + /* TODO: Need to check if we need to delete the snap after the + * operation is successful or not. Also need to persist the state + * of restore operation in the store. + */ +out: + return ret; +} + +/* This function is called before actual restore is taken place. This function + * will validate whether the snapshot volumes are ready to be restored or not. + * + * @param dict dictionary containing snapshot restore request + * @param op_errstr In case of any failure error message will be returned + * in this variable + * @param rsp_dict response dictionary + * @return Negative value on Failure and 0 in success + */ +int +glusterd_snapshot_restore_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int ret = -1; + int32_t i = 0; + int32_t volcount = 0; + gf_boolean_t snap_restored = _gf_false; + char key[PATH_MAX] = {0, }; + char *volname = NULL; + char *snapname = NULL; + glusterd_volinfo_t *volinfo = NULL; + glusterd_snap_t *snap = NULL; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get " + "snap name"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (NULL == snap) { + ret = gf_asprintf (op_errstr, "Snap (%s) not found", + snapname); + if (ret < 0) { + goto out; + } + gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); + ret = -1; + goto out; + } + + snap_restored = snap->snap_restored; + + if (snap_restored) { + ret = gf_asprintf (op_errstr, "Snap (%s) is already " + "restored", snapname); + if (ret < 0) { + goto out; + } + gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); + ret = -1; + goto out; + } + + ret = dict_set_str (rsp_dict, "snapname", snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "snap name(%s)", snapname); + goto out; + } + + ret = dict_get_int32 (dict, "volcount", &volcount); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get volume count"); + goto out; + } + + /* Snapshot restore will only work if all the volumes, + that are part of the snapshot, are stopped. */ + for (i = 1; i <= volcount; ++i) { + snprintf (key, sizeof (key), "volname%d", i); + ret = dict_get_str (dict, key, &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "get volume name"); + goto out; + } + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + ret = gf_asprintf (op_errstr, "Volume (%s) not found", + volname); + if (ret < 0) { + goto out; + } + gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); + ret = -1; + goto out; + } + + if (glusterd_is_volume_started (volinfo)) { + ret = gf_asprintf (op_errstr, "Volume (%s) has been " + "started. Volume needs to be stopped before restoring " + "a snapshot.", volname); + if (ret < 0) { + goto out; + } + gf_log (this->name, GF_LOG_ERROR, "%s", *op_errstr); + ret = -1; + goto out; + } + } + + ret = 0; +out: + return ret; +} + +int +snap_max_hard_limits_validate (dict_t *dict, char *volname, + uint64_t value, char **op_errstr) +{ + char err_str[PATH_MAX] = ""; + glusterd_conf_t *conf = NULL; + glusterd_volinfo_t *volinfo = NULL; + int ret = -1; + uint64_t max_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + + conf = this->private; + + GF_ASSERT (conf); + + if (volname) { + ret = glusterd_volinfo_find (volname, &volinfo); + if (!ret) { + if (volinfo->is_snap_volume) { + ret = -1; + snprintf (err_str, PATH_MAX, + "%s is a snap volume. Configuring " + "snap-max-hard-limit for a snap " + "volume is prohibited.", volname); + goto out; + } + } + } + + if (value) { + /* Max limit for the system is GLUSTERD_SNAPS_MAX_HARD_LIMIT + * but max limit for a volume is conf->snap_max_hard_limit. + */ + if (volname) { + max_limit = conf->snap_max_hard_limit; + } else { + max_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; + } + } + + if ((value < 0) || (value > max_limit)) { + ret = -1; + snprintf (err_str, PATH_MAX, "Invalid snap-max-hard-limit" + "%"PRIu64 ". Expected range 0 - %"PRIu64, + value, max_limit); + goto out; + } + + ret = 0; +out: + if (ret) { + *op_errstr = gf_strdup (err_str); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + } + return ret; +} + +int +glusterd_snapshot_config_prevalidate (dict_t *dict, char **op_errstr) +{ + char *volname = NULL; + glusterd_volinfo_t *volinfo = NULL; + xlator_t *this = NULL; + int ret = -1; + int config_command = 0; + char err_str[PATH_MAX] = {0,}; + glusterd_conf_t *conf = NULL; + uint64_t value = 0; + uint64_t hard_limit = 0; + uint64_t soft_limit = 0; + gf_loglevel_t loglevel = GF_LOG_ERROR; + uint64_t max_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + + conf = this->private; + + GF_ASSERT (conf); + + ret = dict_get_int32 (dict, "config-command", &config_command); + if (ret) { + snprintf (err_str, sizeof (err_str), + "failed to get config-command type"); + goto out; + } + + ret = dict_get_uint64 (dict, "snap-max-hard-limit", &hard_limit); + + ret = dict_get_uint64 (dict, "snap-max-soft-limit", &soft_limit); + + ret = dict_get_str (dict, "volname", &volname); + + if (volname) { + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, sizeof (err_str), + "Volume %s does not exist.", volname); + goto out; + } + } + + switch (config_command) { + case GF_SNAP_CONFIG_TYPE_SET: + if (hard_limit) { + /* Validations for snap-max-hard-limits */ + ret = snap_max_hard_limits_validate (dict, volname, + hard_limit, op_errstr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "snap-max-hard-limit validation " + "failed."); + goto out; + } + } + + if (soft_limit) { + max_limit = GLUSTERD_SNAPS_MAX_SOFT_LIMIT_PERCENT; + if ((soft_limit < 0) || (soft_limit > max_limit)) { + ret = -1; + snprintf (err_str, PATH_MAX, "Invalid " + "snap-max-soft-limit ""%" + PRIu64 ". Expected range 0 - %"PRIu64, + value, max_limit); + goto out; + } + break; + } + default: + break; + } + + ret = 0; +out: + + if (ret && err_str[0] != '\0') { + gf_log (this->name, loglevel, "%s", err_str); + *op_errstr = gf_strdup (err_str); + } + + return ret; +} + +int +glusterd_snap_create_pre_val_use_rsp_dict (dict_t *dst, dict_t *src) +{ + char *snap_brick_dir = NULL; + char *snap_device = NULL; + char *tmpstr = NULL; + char key[PATH_MAX] = ""; + char snapbrckcnt[PATH_MAX] = ""; + char snapbrckord[PATH_MAX] = ""; + int ret = -1; + int64_t i = -1; + int64_t j = -1; + int64_t volume_count = 0; + int64_t brick_count = 0; + int64_t brick_order = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dst); + GF_ASSERT (src); + + ret = dict_get_int64 (src, "volcount", &volume_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "get the volume count"); + goto out; + } + + for (i = 0; i < volume_count; i++) { + memset (snapbrckcnt, '\0', sizeof(snapbrckcnt)); + ret = snprintf (snapbrckcnt, sizeof(snapbrckcnt) - 1, + "vol%ld_brickcount", i+1); + ret = dict_get_int64 (src, snapbrckcnt, &brick_count); + if (ret) { + gf_log (this->name, GF_LOG_TRACE, + "No bricks for this volume in this dict"); + continue; + } + + for (j = 0; j < brick_count; j++) { + /* Fetching data from source dict */ + snprintf (key, sizeof(key) - 1, + "vol%ld.brickdir%ld", i+1, j); + + ret = dict_get_ptr (src, key, + (void **)&snap_brick_dir); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "Unable to fetch %s", key); + continue; + } + + snprintf (key, sizeof(key) - 1, + "vol%ld.brick_snapdevice%ld", i+1, j); + + ret = dict_get_ptr (src, key, + (void **)&snap_device); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch snap_device"); + goto out; + } + + snprintf (snapbrckord, sizeof(snapbrckord) - 1, + "vol%ld.brick%ld.order", i+1, j); + + ret = dict_get_int64 (src, snapbrckord, &brick_order); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get brick order"); + goto out; + } + + /* Adding the data in the dst dict */ + snprintf (key, sizeof(key) - 1, + "vol%ld.brickdir%ld", i+1, brick_order); + + tmpstr = gf_strdup (snap_brick_dir); + if (!tmpstr) { + gf_log (this->name, GF_LOG_ERROR, + "Out Of Memory"); + ret = -1; + goto out; + } + ret = dict_set_dynstr (dst, key, tmpstr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set %s", key); + GF_FREE (tmpstr); + goto out; + } + + snprintf (key, sizeof(key) - 1, + "vol%ld.brick_snapdevice%ld", + i+1, brick_order); + + tmpstr = gf_strdup (snap_device); + if (!tmpstr) { + ret = -1; + goto out; + } + ret = dict_set_dynstr (dst, key, tmpstr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set %s", key); + GF_FREE (tmpstr); + goto out; + } + } + } + + ret = 0; +out: + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_snap_pre_validate_use_rsp_dict (dict_t *dst, dict_t *src) +{ + int ret = -1; + int32_t snap_command = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + if (!dst || !src) { + gf_log (this->name, GF_LOG_ERROR, "Source or Destination " + "dict is empty."); + goto out; + } + + ret = dict_get_int32 (dst, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case GF_SNAP_OPTION_TYPE_CREATE: + ret = glusterd_snap_create_pre_val_use_rsp_dict (dst, src); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to use " + "rsp dict"); + goto out; + } + break; + default: + break; + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +int +glusterd_snapshot_create_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + char *volname = NULL; + char *snapname = NULL; + char *device = NULL; + char *tmpstr = NULL; + char *brick_dir = NULL; + char snap_brick_dir[PATH_MAX] = ""; + char *mnt_pt = NULL; + char key[PATH_MAX] = ""; + char snap_mount[PATH_MAX] = ""; + char snap_volname[64] = ""; + char err_str[PATH_MAX] = ""; + int ret = -1; + int64_t i = 0; + int64_t volcount = 0; + int64_t brick_count = 0; + int64_t brick_order = 0; + glusterd_brickinfo_t *brickinfo = NULL; + glusterd_volinfo_t *volinfo = NULL; + xlator_t *this = NULL; + uuid_t *snap_volid = NULL; + gf_loglevel_t loglevel = GF_LOG_ERROR; + glusterd_conf_t *conf = NULL; + int64_t effective_max_limit = 0; + + this = THIS; + GF_ASSERT (op_errstr); + conf = this->private; + GF_ASSERT (conf); + + ret = dict_get_int64 (dict, "volcount", &volcount); + if (ret) { + snprintf (err_str, sizeof (err_str), "Failed to " + "get the volume count"); + goto out; + } + if (volcount <= 0) { + snprintf (err_str, sizeof (err_str), "Invalid volume count %ld " + "supplied", volcount); + ret = -1; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + snprintf (err_str, sizeof (err_str), "Failed to get snapname"); + goto out; + } + + if (glusterd_find_snap_by_name (snapname)) { + ret = -1; + snprintf (err_str, sizeof (err_str), "Snap %s already exists", + snapname); + goto out; + } + + for (i = 1; i <= volcount; i++) { + snprintf (key, sizeof (key), "volname%ld", i); + ret = dict_get_str (dict, key, &volname); + if (ret) { + snprintf (err_str, sizeof (err_str), + "failed to get volume name"); + goto out; + } + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, sizeof (err_str), + "Volume (%s) does not exist ", volname); + goto out; + } + + ret = -1; + if (!glusterd_is_volume_started (volinfo)) { + snprintf (err_str, sizeof (err_str), "volume %s is " + "not started", volinfo->volname); + loglevel = GF_LOG_WARNING; + goto out; + } + if (glusterd_is_defrag_on (volinfo)) { + snprintf (err_str, sizeof (err_str), + "rebalance process is running for the " + "volume %s", volname); + loglevel = GF_LOG_WARNING; + goto out; + } + /* TODO: Also check whether geo replication is running */ + + if (volinfo->is_snap_volume == _gf_true) { + snprintf (err_str, sizeof (err_str), + "Volume %s is a snap volume", volname); + loglevel = GF_LOG_WARNING; + goto out; + } + + if (volinfo->snap_max_hard_limit < conf->snap_max_hard_limit) + effective_max_limit = volinfo->snap_max_hard_limit; + else + effective_max_limit = conf->snap_max_hard_limit; + + if (volinfo->snap_count >= effective_max_limit) { + snprintf (err_str, sizeof (err_str), + "The number of existing snaps has reached " + "the effective maximum limit of %"PRIu64" ," + "for the volume %s", effective_max_limit, + volname); + loglevel = GF_LOG_WARNING; + goto out; + } + + snprintf (key, sizeof(key) - 1, "vol%ld_volid", i); + ret = dict_get_bin (dict, key, (void **)&snap_volid); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch snap_volid"); + goto out; + } + + /* snap volume uuid is used as lvm snapshot name. + This will avoid restrictions on snapshot names + provided by user */ + GLUSTERD_GET_UUID_NOHYPHEN (snap_volname, *snap_volid); + + brick_count = 0; + brick_order = 0; + /* Adding snap bricks mount paths to the dict */ + list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { + if (uuid_compare (brickinfo->uuid, MY_UUID)) { + brick_order++; + continue; + } + + if (!glusterd_is_brick_started (brickinfo)) { + gf_log (this->name, GF_LOG_WARNING, + "brick %s:%s is not started", + brickinfo->hostname, + brickinfo->path); + brick_order++; + brick_count++; + continue; + } + + device = glusterd_get_brick_mount_details (brickinfo); + if (!device) { + snprintf (err_str, sizeof (err_str), + "getting device name for the brick " + "%s:%s failed", brickinfo->hostname, + brickinfo->path); + ret = -1; + goto out; + } + + device = glusterd_build_snap_device_path (device, + snap_volname); + if (!device) { + snprintf (err_str, sizeof (err_str), + "cannot copy the snapshot device " + "name (volname: %s, snapname: %s)", + volinfo->volname, snapname); + loglevel = GF_LOG_WARNING; + ret = -1; + goto out; + } + + snprintf (key, sizeof(key), + "vol%ld.brick_snapdevice%ld", i, + brick_count); + ret = dict_set_dynstr (rsp_dict, key, device); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set %s", key); + GF_FREE (device); + goto out; + } + + ret = glusterd_get_brick_root (brickinfo->path, + &mnt_pt); + if (ret) { + snprintf (err_str, sizeof (err_str), + "could not get the root of the brick path %s", + brickinfo->path); + loglevel = GF_LOG_WARNING; + goto out; + } + if (strncmp (brickinfo->path, mnt_pt, strlen(mnt_pt))) { + snprintf (err_str, sizeof (err_str), + "brick: %s brick mount: %s", + brickinfo->path, mnt_pt); + loglevel = GF_LOG_WARNING; + goto out; + } + + brick_dir = &brickinfo->path[strlen (mnt_pt)]; + brick_dir++; + + snprintf (snap_brick_dir, sizeof (snap_brick_dir), + "/%s", brick_dir); + + tmpstr = gf_strdup (snap_brick_dir); + if (!tmpstr) { + ret = -1; + goto out; + } + snprintf (key, sizeof(key), "vol%ld.brickdir%ld", i, + brick_count); + ret = dict_set_dynstr (rsp_dict, key, tmpstr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set %s", snap_mount); + goto out; + } + tmpstr = NULL; + + snprintf (key, sizeof(key) - 1, "vol%ld.brick%ld.order", + i, brick_count); + ret = dict_set_int64 (rsp_dict, key, brick_order); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set %s", key); + goto out; + } + + brick_count++; + brick_order++; + } + snprintf (key, sizeof(key) - 1, "vol%ld_brickcount", i); + ret = dict_set_int64 (rsp_dict, key, brick_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", + key); + goto out; + } + } + + ret = dict_set_int64 (rsp_dict, "volcount", volcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set volcount"); + goto out; + } + + ret = 0; +out: + if (ret) + GF_FREE (tmpstr); + + if (ret && err_str[0] != '\0') { + gf_log (this->name, loglevel, "%s", err_str); + *op_errstr = gf_strdup (err_str); + } + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +glusterd_snap_t* +glusterd_new_snap_object() +{ + glusterd_snap_t *snap = NULL; + + snap = GF_CALLOC (1, sizeof (*snap), gf_gld_mt_snap_t); + + if (snap) { + if (LOCK_INIT (&snap->lock)) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed initiating" + " snap lock"); + GF_FREE (snap); + return NULL; + } + + INIT_LIST_HEAD (&snap->snap_list); + INIT_LIST_HEAD (&snap->volumes); + snap->snapname[0] = 0; + snap->snap_status = GD_SNAP_STATUS_INIT; + } + + return snap; + +}; + +/* Function glusterd_list_add_snapvol adds the volinfo object (snapshot volume) + to the snapshot object list and to the parent volume list */ +int32_t +glusterd_list_add_snapvol (glusterd_volinfo_t *origin_vol, + glusterd_volinfo_t *snap_vol) +{ + int ret = -1; + glusterd_snap_t *snap = NULL; + + GF_VALIDATE_OR_GOTO ("glusterd", origin_vol, out); + GF_VALIDATE_OR_GOTO ("glusterd", snap_vol, out); + + snap = snap_vol->snapshot; + GF_ASSERT (snap); + + list_add_tail (&snap_vol->vol_list, &snap->volumes); + LOCK (&origin_vol->lock); + { + list_add_order (&snap_vol->snapvol_list, + &origin_vol->snap_volumes, + glusterd_compare_snap_vol_time); + origin_vol->snap_count++; + } + UNLOCK (&origin_vol->lock); + + gf_log (THIS->name, GF_LOG_DEBUG, "Snap %s added to the list", + snap->snapname); + ret = 0; + out: + return ret; +} + +glusterd_snap_t* +glusterd_find_snap_by_name (char *snapname) +{ + glusterd_snap_t *snap = NULL; + glusterd_conf_t *priv = NULL; + + priv = THIS->private; + GF_ASSERT (priv); + GF_ASSERT (snapname); + + list_for_each_entry (snap, &priv->snapshots, snap_list) { + if (!strcmp (snap->snapname, snapname)) { + gf_log (THIS->name, GF_LOG_DEBUG, "Found " + "snap %s (%s)", snap->snapname, + uuid_utoa (snap->snap_id)); + goto out; + } + } + snap = NULL; +out: + return snap; +} + +glusterd_snap_t* +glusterd_find_snap_by_id (uuid_t snap_id) +{ + glusterd_snap_t *snap = NULL; + glusterd_conf_t *priv = NULL; + + priv = THIS->private; + GF_ASSERT (priv); + + if (uuid_is_null(snap_id)) + goto out; + + list_for_each_entry (snap, &priv->snapshots, snap_list) { + if (!uuid_compare (snap->snap_id, snap_id)) { + gf_log (THIS->name, GF_LOG_DEBUG, "Found " + "snap %s (%s)", snap->snapname, + uuid_utoa (snap->snap_id)); + goto out; + } + } + snap = NULL; +out: + return snap; +} + +int +glusterd_do_lvm_snapshot_remove (glusterd_volinfo_t *snap_vol, + glusterd_brickinfo_t *brickinfo, + const char *mount_pt, const char *snap_device) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + runner_t runner = {0,}; + char msg[1024] = {0, }; + char pidfile[PATH_MAX] = {0, }; + pid_t pid = -1; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + if (!brickinfo) { + gf_log (this->name, GF_LOG_ERROR, "brickinfo NULL"); + goto out; + } + GF_ASSERT (snap_vol); + GF_ASSERT (mount_pt); + GF_ASSERT (snap_device); + + GLUSTERD_GET_BRICK_PIDFILE (pidfile, snap_vol, brickinfo, priv); + if (gf_is_service_running (pidfile, &pid)) { + ret = kill (pid, SIGKILL); + if (ret && errno != ESRCH) { + gf_log (this->name, GF_LOG_ERROR, "Unable to kill pid " + "%d reason : %s", pid, strerror(errno)); + goto out; + } + } + + runinit (&runner); + snprintf (msg, sizeof (msg), "umount the snapshot mounted path %s", + mount_pt); + runner_add_args (&runner, "umount", mount_pt, NULL); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + + /* We need not do synclock_unlock => runner_run => synclock_lock here. + Because it is needed if we are running a glusterfs process in + runner_run, so that when the glusterfs process started wants to + communicate to glusterd, glusterd wont be able to respond if it + has held the big lock. So we do unlock, run glusterfs process + (thus communicate to glusterd), lock. But since this is not a + glusterfs command that is being run, unlocking and then relocking + is not needed. + */ + ret = runner_run (&runner); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "unmounting the " + "path %s (brick: %s) failed (%s)", mount_pt, + brickinfo->path, strerror (errno)); + goto out; + } + + runinit (&runner); + snprintf (msg, sizeof(msg), "remove snapshot of the brick %s:%s, " + "device: %s", brickinfo->hostname, brickinfo->path, + snap_device); + runner_add_args (&runner, "/sbin/lvremove", "-f", snap_device, NULL); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + + ret = runner_run (&runner); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "removing snapshot of the " + "brick (%s:%s) of device %s failed", + brickinfo->hostname, brickinfo->path, snap_device); + goto out; + } + +out: + return ret; +} + +int32_t +glusterd_lvm_snapshot_remove (dict_t *rsp_dict, glusterd_volinfo_t *snap_vol) +{ + char *mnt_pt = NULL; + struct mntent *entry = NULL; + int32_t brick_count = -1; + int32_t ret = -1; + glusterd_brickinfo_t *brickinfo = NULL; + xlator_t *this = NULL; + FILE *mtab = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + GF_ASSERT (snap_vol); + + if (!snap_vol) { + gf_log (this->name, GF_LOG_ERROR, "snap volinfo is NULL"); + goto out; + } + + brick_count = -1; + list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { + brick_count++; + if (uuid_compare (brickinfo->uuid, MY_UUID)) + continue; + + if (brickinfo->snap_status == -1) { + gf_log (this->name, GF_LOG_INFO, + "snapshot was pending. lvm not present " + "for brick %s:%s of the snap %s.", + brickinfo->hostname, brickinfo->path, + snap_vol->snapshot->snapname); + + /* Adding missed delete to the dict */ + ret = glusterd_add_missed_snaps_to_dict + (rsp_dict, + snap_vol->volname, + brickinfo, + brick_count + 1, + GF_SNAP_OPTION_TYPE_DELETE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to add missed snapshot info " + "for %s:%s in the rsp_dict", + brickinfo->hostname, + brickinfo->path); + goto out; + } + + continue; + } + + ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "getting the root " + "of the brick for volume %s (snap %s) failed ", + snap_vol->volname, snap_vol->snapshot->snapname); + goto out; + } + + entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); + if (!entry) { + gf_log (this->name, GF_LOG_WARNING, "getting the mount" + " entry for the brick %s:%s of the snap %s " + "(volume: %s) failed", brickinfo->hostname, + brickinfo->path, snap_vol->snapshot->snapname, + snap_vol->volname); + ret = -1; + goto out; + } + ret = glusterd_do_lvm_snapshot_remove (snap_vol, brickinfo, + mnt_pt, + entry->mnt_fsname); + if (mtab) + endmntent (mtab); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "remove the snapshot %s (%s)", + brickinfo->path, entry->mnt_fsname); + goto out; + } + + } + + ret = 0; +out: + return ret; +} + + +int32_t +glusterd_snap_volume_remove (dict_t *rsp_dict, + glusterd_volinfo_t *snap_vol, + gf_boolean_t remove_lvm, + gf_boolean_t force) +{ + int ret = -1; + int save_ret = 0; + glusterd_brickinfo_t *brickinfo = NULL; + glusterd_volinfo_t *origin_vol = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + GF_ASSERT (snap_vol); + + if (!snap_vol) { + gf_log(this->name, GF_LOG_WARNING, "snap_vol in NULL"); + ret = -1; + goto out; + } + + list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { + if (uuid_compare (brickinfo->uuid, MY_UUID)) + continue; + + ret = glusterd_brick_stop (snap_vol, brickinfo, _gf_false); + if (ret) { + gf_log(this->name, GF_LOG_WARNING, "Failed to stop " + "brick for volume %s", snap_vol->volname); + save_ret = ret; + + /* Continue to cleaning up the snap in case of error + if force flag is enabled */ + if (!force) + goto out; + } + } + + /* Only remove the backend lvm when required */ + if (remove_lvm) { + ret = glusterd_lvm_snapshot_remove (rsp_dict, snap_vol); + if (ret) { + gf_log(this->name, GF_LOG_WARNING, "Failed to remove " + "lvm snapshot volume %s", snap_vol->volname); + save_ret = ret; + if (!force) + goto out; + } + } + + ret = glusterd_store_delete_volume (snap_vol); + if (ret) { + gf_log(this->name, GF_LOG_WARNING, "Failed to remove volume %s " + "from store", snap_vol->volname); + save_ret = ret; + if (!force) + goto out; + } + + if (!list_empty(&snap_vol->snapvol_list)) { + ret = glusterd_volinfo_find (snap_vol->parent_volname, + &origin_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get " + "parent volinfo %s for volume %s", + snap_vol->parent_volname, snap_vol->volname); + save_ret = ret; + if (!force) + goto out; + } + origin_vol->snap_count--; + } + + ret = glusterd_volinfo_delete (snap_vol); + if (ret) { + gf_log(this->name, GF_LOG_WARNING, "Failed to remove volinfo " + "%s ", snap_vol->volname); + save_ret = ret; + if (!force) + goto out; + } + + if (save_ret) + ret = save_ret; +out: + gf_log (this->name, GF_LOG_TRACE, "returning %d", ret); + return ret; +} + +int32_t +glusterd_snapobject_delete (glusterd_snap_t *snap) +{ + if (snap == NULL) { + gf_log(THIS->name, GF_LOG_WARNING, "snap is NULL"); + return -1; + } + + list_del_init (&snap->snap_list); + list_del_init (&snap->volumes); + if (LOCK_DESTROY(&snap->lock)) + gf_log (THIS->name, GF_LOG_WARNING, "Failed destroying lock" + "of snap %s", snap->snapname); + + GF_FREE (snap->description); + GF_FREE (snap); + + return 0; +} + +int32_t +glusterd_snap_remove (dict_t *rsp_dict, + glusterd_snap_t *snap, + gf_boolean_t remove_lvm, + gf_boolean_t force) +{ + int ret = -1; + int save_ret = 0; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_volinfo_t *tmp = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + GF_ASSERT (snap); + + if (!snap) { + gf_log(this->name, GF_LOG_WARNING, "snap is NULL"); + ret = -1; + goto out; + } + + list_for_each_entry_safe (snap_vol, tmp, &snap->volumes, vol_list) { + ret = glusterd_snap_volume_remove (rsp_dict, snap_vol, + remove_lvm, force); + if (ret) { + gf_log(this->name, GF_LOG_WARNING, "Failed to remove " + "volinfo %s for snap %s", snap_vol->volname, + snap->snapname); + save_ret = ret; + + /* Continue to cleaning up the snap in case of error + if force flag is enabled */ + if (!force) + goto out; + } + } + + ret = glusterd_store_delete_snap (snap); + if (ret) { + gf_log(this->name, GF_LOG_WARNING, "Failed to remove snap %s " + "from store", snap->snapname); + save_ret = ret; + if (!force) + goto out; + } + + ret = glusterd_snapobject_delete (snap); + if (ret) + gf_log (this->name, GF_LOG_WARNING, "Failed to delete " + "snap object %s", snap->snapname); + + if (save_ret) + ret = save_ret; +out: + gf_log (THIS->name, GF_LOG_TRACE, "returning %d", ret); + return ret; +} + +static int +glusterd_snapshot_get_snapvol_detail (dict_t *dict, + glusterd_volinfo_t *snap_vol, + char *keyprefix, int detail) +{ + int ret = -1; + int snap_limit = 0; + char key[PATH_MAX] = {0,}; + char *value = NULL; + glusterd_volinfo_t *origin_vol = NULL; + glusterd_conf_t *conf = NULL; + xlator_t *this = NULL; + + this = THIS; + conf = this->private; + GF_ASSERT (conf); + + GF_ASSERT (dict); + GF_ASSERT (snap_vol); + GF_ASSERT (keyprefix); + + /* Volume Name */ + value = gf_strdup (snap_vol->volname); + if (!value) + goto out; + + snprintf (key, sizeof (key), "%s.volname", keyprefix); + ret = dict_set_dynstr (dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "volume name in dictionary: %s", key); + goto out; + } + + /* Volume ID */ + value = gf_strdup (uuid_utoa (snap_vol->volume_id)); + if (NULL == value) { + ret = -1; + goto out; + } + + snprintf (key, sizeof (key), "%s.vol-id", keyprefix); + ret = dict_set_dynstr (dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "volume id in dictionary: %s", key); + goto out; + } + value = NULL; + + /* volume status */ + snprintf (key, sizeof (key), "%s.vol-status", keyprefix); + switch (snap_vol->status) { + case GLUSTERD_STATUS_STARTED: + ret = dict_set_str (dict, key, "Started"); + break; + case GLUSTERD_STATUS_STOPPED: + ret = dict_set_str (dict, key, "Stopped"); + break; + case GD_SNAP_STATUS_NONE: + ret = dict_set_str (dict, key, "None"); + break; + default: + gf_log (this->name, GF_LOG_ERROR, "Invalid volume status"); + ret = -1; + goto out; + } + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set volume status" + " in dictionary: %s", key); + goto out; + } + + + ret = glusterd_volinfo_find (snap_vol->parent_volname, &origin_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the parent " + "volinfo for the volume %s", snap_vol->volname); + goto out; + } + + /* Snaps available */ + if (conf->snap_max_hard_limit < origin_vol->snap_max_hard_limit) { + snap_limit = conf->snap_max_hard_limit; + gf_log(this->name, GF_LOG_DEBUG, "system snap-max-hard-limit is" + " lesser than volume snap-max-hard-limit, " + "snap-max-hard-limit value is set to %d", snap_limit); + } else { + snap_limit = origin_vol->snap_max_hard_limit; + gf_log(this->name, GF_LOG_DEBUG, "volume snap-max-hard-limit is" + " lesser than system snap-max-hard-limit, " + "snap-max-hard-limit value is set to %d", snap_limit); + } + + snprintf (key, sizeof (key), "%s.snaps-available", keyprefix); + if (snap_limit > origin_vol->snap_count) + ret = dict_set_int32 (dict, key, + snap_limit - origin_vol->snap_count); + else + ret = dict_set_int32 (dict, key, 0); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set available snaps"); + goto out; + } + + snprintf (key, sizeof (key), "%s.snapcount", keyprefix); + ret = dict_set_int32 (dict, key, origin_vol->snap_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not save snapcount"); + goto out; + } + + if (!detail) + goto out; + + /* Parent volume name */ + value = gf_strdup (snap_vol->parent_volname); + if (!value) + goto out; + + snprintf (key, sizeof (key), "%s.origin-volname", keyprefix); + ret = dict_set_dynstr (dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set parent " + "volume name in dictionary: %s", key); + goto out; + } + value = NULL; + + ret = 0; +out: + if (value) + GF_FREE (value); + + return ret; +} + +static int +glusterd_snapshot_get_snap_detail (dict_t *dict, glusterd_snap_t *snap, + char *keyprefix, glusterd_volinfo_t *volinfo) +{ + int ret = -1; + int volcount = 0; + char key[PATH_MAX] = {0,}; + char *value = NULL; + char *timestr = NULL; + struct tm *tmptr = NULL; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_volinfo_t *tmp_vol = NULL; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (dict); + GF_ASSERT (snap); + GF_ASSERT (keyprefix); + + /* Snap Name */ + value = gf_strdup (snap->snapname); + if (!value) + goto out; + + snprintf (key, sizeof (key), "%s.snapname", keyprefix); + ret = dict_set_dynstr (dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "snap name in dictionary"); + goto out; + } + + /* Snap ID */ + value = gf_strdup (uuid_utoa (snap->snap_id)); + if (NULL == value) { + ret = -1; + goto out; + } + + snprintf (key, sizeof (key), "%s.snap-id", keyprefix); + ret = dict_set_dynstr (dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "snap id in dictionary"); + goto out; + } + value = NULL; + + tmptr = localtime (&(snap->time_stamp)); + if (NULL == tmptr) { + gf_log (this->name, GF_LOG_ERROR, "Failed to convert " + "time_t to *tm"); + ret = -1; + goto out; + } + + timestr = GF_CALLOC (1, PATH_MAX, gf_gld_mt_char); + if (NULL == timestr) { + ret = -1; + goto out; + } + + ret = strftime (timestr, PATH_MAX, "%Y-%m-%d %H:%M:%S", tmptr); + if (0 == ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to convert time_t " + "to string"); + ret = -1; + goto out; + } + + snprintf (key, sizeof (key), "%s.snap-time", keyprefix); + ret = dict_set_dynstr (dict, key, timestr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "snap time stamp in dictionary"); + goto out; + } + timestr = NULL; + + /* If snap description is provided then add that into dictionary */ + if (NULL != snap->description) { + value = gf_strdup (snap->description); + if (NULL == value) { + ret = -1; + goto out; + } + + snprintf (key, sizeof (key), "%s.snap-desc", keyprefix); + ret = dict_set_dynstr (dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "snap description in dictionary"); + goto out; + } + value = NULL; + } + + snprintf (key, sizeof (key), "%s.snap-status", keyprefix); + switch (snap->snap_status) { + case GD_SNAP_STATUS_INIT: + ret = dict_set_str (dict, key, "Init"); + break; + case GD_SNAP_STATUS_IN_USE: + ret = dict_set_str (dict, key, "In-use"); + break; + case GD_SNAP_STATUS_DECOMMISSION: + ret = dict_set_str (dict, key, "Decommisioned"); + break; + case GD_SNAP_STATUS_RESTORED: + ret = dict_set_str (dict, key, "Restored"); + break; + case GD_SNAP_STATUS_NONE: + ret = dict_set_str (dict, key, "None"); + break; + default: + gf_log (this->name, GF_LOG_ERROR, "Invalid snap status"); + ret = -1; + goto out; + } + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snap status " + "in dictionary"); + goto out; + } + + if (volinfo) { + volcount = 1; + snprintf (key, sizeof (key), "%s.vol%d", keyprefix, volcount); + ret = glusterd_snapshot_get_snapvol_detail (dict, + volinfo, key, 0); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "get volume detail %s for snap %s", + snap_vol->volname, snap->snapname); + goto out; + } + goto done; + } + + list_for_each_entry_safe (snap_vol, tmp_vol, &snap->volumes, vol_list) { + volcount++; + snprintf (key, sizeof (key), "%s.vol%d", keyprefix, volcount); + ret = glusterd_snapshot_get_snapvol_detail (dict, + snap_vol, key, 1); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "get volume detail %s for snap %s", + snap_vol->volname, snap->snapname); + goto out; + } + } + +done: + snprintf (key, sizeof (key), "%s.vol-count", keyprefix); + ret = dict_set_int32 (dict, key, volcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", + key); + goto out; + } + + ret = 0; +out: + if (value) + GF_FREE (value); + + if (timestr) + GF_FREE(timestr); + + return ret; +} + +static int +glusterd_snapshot_get_all_snap_info (dict_t *dict) +{ + int ret = -1; + int snapcount = 0; + char key[PATH_MAX] = {0,}; + glusterd_snap_t *snap = NULL; + glusterd_snap_t *tmp_snap = NULL; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + + /* General parameter validation */ + GF_ASSERT (dict); + + list_for_each_entry_safe (snap, tmp_snap, &priv->snapshots, snap_list) { + snapcount++; + snprintf (key, sizeof (key), "snap%d", snapcount); + ret = glusterd_snapshot_get_snap_detail (dict, snap, key, NULL); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get " + "snapdetail for snap %s", snap->snapname); + goto out; + } + } + + ret = dict_set_int32 (dict, "snap-count", snapcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); + goto out; + } + + ret = 0; +out: + return ret; +} + +int +glusterd_snapshot_get_info_by_volume (dict_t *dict, char *volname, + char *err_str, size_t len) +{ + int ret = -1; + int snapcount = 0; + int snap_limit = 0; + char *value = NULL; + char key[PATH_MAX] = ""; + glusterd_volinfo_t *volinfo = NULL; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_volinfo_t *tmp_vol = NULL; + glusterd_conf_t *conf = NULL; + xlator_t *this = NULL; + + this = THIS; + conf = this->private; + GF_ASSERT (conf); + + GF_ASSERT (dict); + GF_ASSERT (volname); + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, len, "Volume (%s) does not exist", volname); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + goto out; + } + + /* Snaps available */ + if (conf->snap_max_hard_limit < volinfo->snap_max_hard_limit) { + snap_limit = conf->snap_max_hard_limit; + gf_log(this->name, GF_LOG_DEBUG, "system snap-max-hard-limit is" + " lesser than volume snap-max-hard-limit, " + "snap-max-hard-limit value is set to %d", snap_limit); + } else { + snap_limit = volinfo->snap_max_hard_limit; + gf_log(this->name, GF_LOG_DEBUG, "volume snap-max-hard-limit is" + " lesser than system snap-max-hard-limit, " + "snap-max-hard-limit value is set to %d", snap_limit); + } + + if (snap_limit > volinfo->snap_count) + ret = dict_set_int32 (dict, "snaps-available", + snap_limit - volinfo->snap_count); + else + ret = dict_set_int32 (dict, "snaps-available", 0); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set available snaps"); + goto out; + } + + /* Origin volume name */ + value = gf_strdup (volinfo->volname); + if (!value) + goto out; + + ret = dict_set_dynstr (dict, "origin-volname", value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set parent " + "volume name in dictionary: %s", key); + goto out; + } + value = NULL; + + list_for_each_entry_safe (snap_vol, tmp_vol, &volinfo->snap_volumes, + snapvol_list) { + snapcount++; + snprintf (key, sizeof (key), "snap%d", snapcount); + ret = glusterd_snapshot_get_snap_detail (dict, + snap_vol->snapshot, + key, snap_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get " + "snapdetail for snap %s", + snap_vol->snapshot->snapname); + goto out; + } + } + ret = dict_set_int32 (dict, "snap-count", snapcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); + goto out; + } + + ret = 0; +out: + if (value) + GF_FREE (value); + + return ret; +} + +/* This function will be called from RPC handler routine. + * This function is responsible for getting the requested + * snapshot info into the dictionary. + * + * @param req RPC request object. Required for sending a response back. + * @param op glusterd operation. Required for sending a response back. + * @param dict pointer to dictionary which will contain both + * request and response key-pair values. + * @return -1 on error and 0 on success + */ +int +glusterd_handle_snapshot_info (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict, char *err_str, size_t len) +{ + int ret = -1; + int8_t snap_driven = 1; + char *volname = NULL; + char *snapname = NULL; + glusterd_snap_t *snap = NULL; + xlator_t *this = NULL; + int32_t cmd = GF_SNAP_INFO_TYPE_ALL; + + this = THIS; + GF_ASSERT (this); + + GF_VALIDATE_OR_GOTO (this->name, req, out); + GF_VALIDATE_OR_GOTO (this->name, dict, out); + + + ret = dict_get_int32 (dict, "cmd", &cmd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get type " + "of snapshot info"); + goto out; + } + + switch (cmd) { + case GF_SNAP_INFO_TYPE_ALL: + { + ret = glusterd_snapshot_get_all_snap_info (dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get info of all snaps"); + goto out; + } + break; + } + + case GF_SNAP_INFO_TYPE_SNAP: + { + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get snap name"); + goto out; + } + + ret = dict_set_int32 (dict, "snap-count", 1); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set snapcount"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + snprintf (err_str, len, + "Snap (%s) does not exist", snapname); + gf_log (this->name, GF_LOG_ERROR, + "%s", err_str); + ret = -1; + goto out; + } + ret = glusterd_snapshot_get_snap_detail (dict, snap, + "snap1", NULL); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get snap detail of snap " + "%s", snap->snapname); + goto out; + } + break; + } + + case GF_SNAP_INFO_TYPE_VOL: + { + ret = dict_get_str (dict, "volname", &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get volname"); + goto out; + } + ret = glusterd_snapshot_get_info_by_volume (dict, + volname, err_str, len); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get volume info of volume " + "%s", volname); + goto out; + } + snap_driven = 0; + break; + } + } + + ret = dict_set_int8 (dict, "snap-driven", snap_driven); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snap-driven"); + goto out; + } + + /* If everything is successful then send the response back to cli. + * In case of failure the caller of this function will take care + of the response */ + ret = glusterd_op_send_cli_response (op, 0, 0, req, dict, err_str); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to send cli " + "response"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +/* This function sets all the snapshot names in the dictionary */ +int +glusterd_snapshot_get_all_snapnames (dict_t *dict) +{ + int ret = -1; + int snapcount = 0; + char *snapname = NULL; + char key[PATH_MAX] = {0,}; + glusterd_snap_t *snap = NULL; + glusterd_snap_t *tmp_snap = NULL; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (dict); + + list_for_each_entry_safe (snap, tmp_snap, &priv->snapshots, snap_list) { + snapcount++; + snapname = gf_strdup (snap->snapname); + if (!snapname) { + gf_log (this->name, GF_LOG_ERROR, "strdup failed"); + ret = -1; + goto out; + } + snprintf (key, sizeof (key), "snapname%d", snapcount); + ret = dict_set_dynstr (dict, key, snapname); + if (ret) { + GF_FREE (snapname); + gf_log (this->name, GF_LOG_ERROR, "Failed to set %s", + key); + goto out; + } + } + + ret = dict_set_int32 (dict, "snap-count", snapcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); + goto out; + } + + ret = 0; +out: + + return ret; +} + +/* This function sets all the snapshot names + under a given volume in the dictionary */ +int +glusterd_snapshot_get_vol_snapnames (dict_t *dict, glusterd_volinfo_t *volinfo) +{ + int ret = -1; + int snapcount = 0; + char *snapname = NULL; + char key[PATH_MAX] = {0,}; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_volinfo_t *tmp_vol = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (dict); + GF_ASSERT (volinfo); + + list_for_each_entry_safe (snap_vol, tmp_vol, + &volinfo->snap_volumes, snapvol_list) { + snapcount++; + snapname = gf_strdup (snap_vol->snapshot->snapname); + if (!snapname) { + gf_log (this->name, GF_LOG_ERROR, + "strdup failed"); + ret = -1; + goto out; + } + snprintf (key, sizeof (key), "snapname%d", snapcount); + ret = dict_set_dynstr (dict, key, snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "set %s", key); + GF_FREE (snapname); + goto out; + } + } + + ret = dict_set_int32 (dict, "snap-count", snapcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snapcount"); + goto out; + } + + ret = 0; +out: + + return ret; +} + +int +glusterd_handle_snapshot_list (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict, char *err_str, size_t len) +{ + int ret = -1; + char *volname = NULL; + glusterd_volinfo_t *volinfo = NULL; + xlator_t *this = NULL; + + this = THIS; + + GF_VALIDATE_OR_GOTO (this->name, req, out); + GF_VALIDATE_OR_GOTO (this->name, dict, out); + + /* Ignore error for getting volname as it is optional */ + ret = dict_get_str (dict, "volname", &volname); + + if (NULL == volname) { + ret = glusterd_snapshot_get_all_snapnames (dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get snapshot list"); + goto out; + } + } else { + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, len, + "Volume (%s) does not exist", volname); + gf_log (this->name, GF_LOG_ERROR, + "%s", err_str); + goto out; + } + + ret = glusterd_snapshot_get_vol_snapnames (dict, volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get snapshot list for volume %s", + volname); + goto out; + } + } + + /* If everything is successful then send the response back to cli. + In case of failure the caller of this function will take of response.*/ + ret = glusterd_op_send_cli_response (op, 0, 0, req, dict, err_str); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to send cli " + "response"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +/* This is a snapshot create handler function. This function will be + * executed in the originator node. This function is responsible for + * calling mgmt_v3 framework to do the actual snap creation on all the bricks + * + * @param req RPC request object + * @param op gluster operation + * @param dict dictionary containing snapshot restore request + * @param err_str In case of an err this string should be populated + * @param len length of err_str buffer + * + * @return Negative value on Failure and 0 in success + */ +int +glusterd_handle_snapshot_create (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict, char *err_str, size_t len) +{ + int ret = -1; + char *volname = NULL; + char *snapname = NULL; + int64_t volcount = 0; + xlator_t *this = NULL; + char key[PATH_MAX] = ""; + char *username = NULL; + char *password = NULL; + uuid_t *uuid_ptr = NULL; + uuid_t tmp_uuid = {0}; + int i = 0; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (req); + GF_ASSERT (dict); + GF_ASSERT (err_str); + + ret = dict_get_int64 (dict, "volcount", &volcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "get the volume count"); + goto out; + } + if (volcount <= 0) { + gf_log (this->name, GF_LOG_ERROR, "Invalid volume count %ld " + "supplied", volcount); + ret = -1; + goto out; + } + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the snapname"); + goto out; + } + + if (strlen(snapname) >= GLUSTERD_MAX_SNAP_NAME) { + snprintf (err_str, len, "snapname cannot exceed 255 " + "characters"); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + ret = -1; + goto out; + } + + uuid_ptr = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); + if (!uuid_ptr) { + gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); + ret = -1; + goto out; + } + + uuid_generate (*uuid_ptr); + ret = dict_set_bin (dict, "snap-id", uuid_ptr, sizeof(uuid_t)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to set snap-id"); + GF_FREE (uuid_ptr); + goto out; + } + uuid_ptr = NULL; + + ret = dict_set_int64 (dict, "snap-time", (int64_t)time(NULL)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to set snap-time"); + goto out; + } + + for (i = 1; i <= volcount; i++) { + snprintf (key, sizeof (key), "volname%d", i); + ret = dict_get_str (dict, key, &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get volume name"); + goto out; + } + + /* generate internal username and password for the snap*/ + uuid_generate (tmp_uuid); + username = gf_strdup (uuid_utoa (tmp_uuid)); + snprintf (key, sizeof(key), "volume%d_username", i); + ret = dict_set_dynstr (dict, key, username); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snap " + "username for volume %s", volname); + GF_FREE (username); + goto out; + } + + uuid_generate (tmp_uuid); + password = gf_strdup (uuid_utoa (tmp_uuid)); + snprintf (key, sizeof(key), "volume%d_password", i); + ret = dict_set_dynstr (dict, key, password); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set snap " + "password for volume %s", volname); + GF_FREE (password); + goto out; + } + + uuid_ptr = GF_CALLOC (1, sizeof(uuid_t), gf_common_mt_uuid_t); + if (!uuid_ptr) { + gf_log (this->name, GF_LOG_ERROR, "Out Of Memory"); + ret = -1; + goto out; + } + + snprintf (key, sizeof(key) - 1, "vol%d_volid", i); + uuid_generate (*uuid_ptr); + ret = dict_set_bin (dict, key, uuid_ptr, sizeof(uuid_t)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to set snap_volid"); + GF_FREE (uuid_ptr); + goto out; + } + } + + ret = glusterd_mgmt_v3_initiate_snap_phases (req, op, dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to initiate snap " + "phases"); + } + +out: + return ret; +} + +/* This is a snapshot status handler function. This function will be + * executed in a originator node. This function is responsible for + * calling mgmt v3 framework to get the actual snapshot status from + * all the bricks + * + * @param req RPC request object + * @param op gluster operation + * @param dict dictionary containing snapshot status request + * @param err_str In case of an err this string should be populated + * @param len length of err_str buffer + * + * return : 0 in case of success. + * -1 in case of failure. + * + */ +int +glusterd_handle_snapshot_status (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict, char *err_str, size_t len) +{ + int ret = -1; + char *volname = NULL; + char *snapname = NULL; + char *buf = NULL; + glusterd_conf_t *conf = NULL; + xlator_t *this = NULL; + int32_t cmd = -1; + int i = 0; + dict_t *voldict = NULL; + char key[PATH_MAX] = ""; + glusterd_volinfo_t *volinfo = NULL; + glusterd_snap_t *snap = NULL; + glusterd_volinfo_t *snap_volinfo = NULL; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + + GF_ASSERT (conf); + GF_ASSERT (req); + GF_ASSERT (dict); + GF_ASSERT (err_str); + + ret = dict_get_int32 (dict, "cmd", &cmd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not get status type"); + goto out; + } + switch (cmd) { + case GF_SNAP_STATUS_TYPE_ALL: + { + /* IF we give "gluster snapshot status" + * then lock is held on all snaps. + * This is the place where necessary information + * (snapname and snapcount)is populated in dictionary + * for locking. + */ + ++i; + list_for_each_entry (snap, &conf->snapshots, snap_list) + { + snprintf (key, sizeof (key), "snapname%d", i); + buf = gf_strdup (snap->snapname); + if (!buf) { + ret = -1; + goto out; + } + ret = dict_set_dynstr (dict, key, buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save snapname (%s) " + "in the dictionary", + snap->snapname); + GF_FREE (buf); + goto out; + } + + buf = NULL; + i++; + } + + ret = dict_set_int32 (dict, "snapcount", i - 1); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not " + "save snapcount in the dictionary"); + goto out; + } + break; + } + + case GF_SNAP_STATUS_TYPE_SNAP: + { + /* IF we give "gluster snapshot status " + * then lock is held on single snap. + * This is the place where necessary information + * (snapname)is populated in dictionary + * for locking. + */ + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to fetch snap name"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + snprintf (err_str, len, "Snap (%s)" + "does not exist", snapname); + gf_log(this->name, GF_LOG_ERROR, + "%s", err_str); + ret = -1; + goto out; + } + break; + } + case GF_SNAP_STATUS_TYPE_VOL: + ret = dict_get_str (dict, "volname", &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to fetch volname"); + goto out; + } + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, len, "Volume (%s) " + "does not exist", volname); + gf_log (this->name, GF_LOG_ERROR, + "%s", err_str); + goto out; + } + + i = 1; + list_for_each_entry (snap_volinfo, + &volinfo->snap_volumes, snapvol_list) { + snprintf (key, sizeof (key), "snapname%d", i); + + buf = gf_strdup + (snap_volinfo->snapshot->snapname); + if (!buf) { + ret = -1; + goto out; + } + + ret = dict_set_dynstr (dict, key, buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save snapname"); + GF_FREE (buf); + goto out; + } + + buf = NULL; + i++; + } + + ret = dict_set_int32 (dict, "snapcount", i-1); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save snapcount"); + goto out; + } + break; + default: + { + gf_log (this->name, GF_LOG_ERROR, "Unknown type"); + ret = -1; + goto out; + } + } + + /* Volume lock is not necessary for snapshot status, hence + * turning it off + */ + ret = dict_set_int8 (dict, "hold_vol_locks", 0); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Setting volume lock " + "flag failed"); + goto out; + } + + ret = glusterd_mgmt_v3_initiate_snap_phases (req, op, dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to initiate " + "snap phases"); + goto out; + } + + ret = 0; + +out: + if (voldict) { + dict_unref (voldict); + } + return ret; +} + + +/* This is a snapshot restore handler function. This function will be + * executed in the originator node. This function is responsible for + * calling mgmt_v3 framework to do the actual restore on all the bricks + * + * @param req RPC request object + * @param op gluster operation + * @param dict dictionary containing snapshot restore request + * @param err_str In case of an err this string should be populated + * @param len length of err_str buffer + * + * @return Negative value on Failure and 0 in success + */ +int +glusterd_handle_snapshot_restore (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict, char *err_str, size_t len) +{ + int ret = -1; + char *snapname = NULL; + char *buf = NULL; + glusterd_conf_t *conf = NULL; + xlator_t *this = NULL; + glusterd_snap_t *snap = NULL; + glusterd_volinfo_t *snap_volinfo = NULL; + int32_t i = 0; + char key[PATH_MAX] = ""; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + + GF_ASSERT (conf); + GF_ASSERT (req); + GF_ASSERT (dict); + GF_ASSERT (err_str); + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "get snapname"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + snprintf (err_str, len, "Snap (%s) does not exist", snapname); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + ret = -1; + goto out; + } + + list_for_each_entry (snap_volinfo, &snap->volumes, vol_list) { + i++; + snprintf (key, sizeof (key), "volname%d", i); + buf = gf_strdup (snap_volinfo->parent_volname); + if (!buf) { + ret = -1; + goto out; + } + ret = dict_set_dynstr (dict, key, buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not set " + "parent volume name %s in the dict", + snap_volinfo->parent_volname); + GF_FREE (buf); + goto out; + } + buf = NULL; + } + + ret = dict_set_int32 (dict, "volcount", i); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save volume count"); + goto out; + } + + ret = glusterd_mgmt_v3_initiate_snap_phases (req, op, dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to initiate snap " + "phases"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +glusterd_snap_t* +glusterd_create_snap_object (dict_t *dict, dict_t *rsp_dict) +{ + char *snapname = NULL; + uuid_t *snap_id = NULL; + char *description = NULL; + glusterd_snap_t *snap = NULL; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + int ret = -1; + int64_t time_stamp = 0; + + this = THIS; + priv = this->private; + + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + + /* Fetch snapname, description, id and time from dict */ + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snapname"); + goto out; + } + + /* Ignore ret value for description*/ + ret = dict_get_str (dict, "description", &description); + + ret = dict_get_bin (dict, "snap-id", (void **)&snap_id); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snap_id"); + goto out; + } + + ret = dict_get_int64 (dict, "snap-time", &time_stamp); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snap-time"); + goto out; + } + if (time_stamp <= 0) { + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "Invalid time-stamp: %ld", + time_stamp); + goto out; + } + + list_for_each_entry (snap, &priv->snapshots, snap_list) { + if (!strcmp (snap->snapname, snapname) || + !uuid_compare (snap->snap_id, *snap_id)) { + gf_log (THIS->name, GF_LOG_ERROR, + "Found duplicate snap %s (%s)", + snap->snapname, uuid_utoa (snap->snap_id)); + ret = -1; + break; + } + } + if (ret) { + snap = NULL; + goto out; + } + + snap = glusterd_new_snap_object (); + if (!snap) { + gf_log (this->name, GF_LOG_ERROR, "Could not create " + "the snap object for snap %s", snapname); + goto out; + } + + strcpy (snap->snapname, snapname); + uuid_copy (snap->snap_id, *snap_id); + snap->time_stamp = (time_t)time_stamp; + /* Set the status as GD_SNAP_STATUS_INIT and once the backend snapshot + is taken and snap is really ready to use, set the status to + GD_SNAP_STATUS_IN_USE. This helps in identifying the incomplete + snapshots and cleaning them up. + */ + snap->snap_status = GD_SNAP_STATUS_INIT; + if (description) { + snap->description = gf_strdup (description); + if (snap->description == NULL) { + gf_log (this->name, GF_LOG_ERROR, + "Saving the Snap Description Failed"); + ret = -1; + goto out; + } + } + + ret = glusterd_store_snap (snap); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Could not store snap" + "object %s", snap->snapname); + goto out; + } + + list_add_order (&snap->snap_list, &priv->snapshots, + glusterd_compare_snap_time); + + gf_log (this->name, GF_LOG_TRACE, "Snap %s added to the list", + snap->snapname); + + ret = 0; + +out: + if (ret) { + if (snap) + glusterd_snap_remove (rsp_dict, snap, + _gf_true, _gf_true); + snap = NULL; + } + + return snap; +} + +/* Added missed_snap_entry to rsp_dict */ +int32_t +glusterd_add_missed_snaps_to_dict (dict_t *rsp_dict, char *snap_uuid, + glusterd_brickinfo_t *brickinfo, + int32_t brick_number, int32_t op) +{ + char *buf = NULL; + char missed_snap_entry[PATH_MAX] = ""; + char name_buf[PATH_MAX] = ""; + int32_t missed_snap_count = -1; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + GF_ASSERT (snap_uuid); + GF_ASSERT (brickinfo); + + snprintf (missed_snap_entry, sizeof(missed_snap_entry), + "%s:%s=%d:%s:%d:%d", uuid_utoa(brickinfo->uuid), + snap_uuid, brick_number, brickinfo->path, op, + GD_MISSED_SNAP_PENDING); + + buf = gf_strdup (missed_snap_entry); + if (!buf) { + ret = -1; + goto out; + } + + /* Fetch the missed_snap_count from the dict */ + ret = dict_get_int32 (rsp_dict, "missed_snap_count", + &missed_snap_count); + if (ret) { + /* Initialize the missed_snap_count for the first time */ + missed_snap_count = 0; + } + + /* Setting the missed_snap_entry in the rsp_dict */ + snprintf (name_buf, sizeof(name_buf), "missed_snaps_%d", + missed_snap_count); + ret = dict_set_dynstr (rsp_dict, name_buf, buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set missed_snap_entry (%s) " + "in the rsp_dict.", buf); + GF_FREE (buf); + goto out; + } + missed_snap_count++; + + /* Setting the new missed_snap_count in the dict */ + ret = dict_set_int32 (rsp_dict, "missed_snap_count", + missed_snap_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set missed_snap_count for %s " + "in the rsp_dict.", missed_snap_entry); + goto out; + } + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* This function is called to get the device path of the snap lvm. Usually + if /dev/mapper/- is the device for the lvm, + then the snap device will be /dev//. + This function takes care of building the path for the snap device. +*/ +char * +glusterd_build_snap_device_path (char *device, char *snapname) +{ + char snap[PATH_MAX] = ""; + char msg[1024] = ""; + char volgroup[PATH_MAX] = ""; + char *snap_device = NULL; + xlator_t *this = NULL; + runner_t runner = {0,}; + char *ptr = NULL; + int ret = -1; + + this = THIS; + GF_ASSERT (this); + if (!device) { + gf_log (this->name, GF_LOG_ERROR, "device is NULL"); + goto out; + } + if (!snapname) { + gf_log (this->name, GF_LOG_ERROR, "snapname is NULL"); + goto out; + } + + runinit (&runner); + runner_add_args (&runner, "/sbin/lvs", "--noheadings", "-o", "vg_name", + device, NULL); + runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); + snprintf (msg, sizeof (msg), "Get volume group for device %s", device); + runner_log (&runner, this->name, GF_LOG_DEBUG, msg); + ret = runner_start (&runner); + if (ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get volume group " + "for device %s", device); + runner_end (&runner); + goto out; + } + ptr = fgets(volgroup, sizeof(volgroup), + runner_chio (&runner, STDOUT_FILENO)); + if (!ptr || !strlen(volgroup)) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get volume group " + "for snap %s", snapname); + runner_end (&runner); + ret = -1; + goto out; + } + runner_end (&runner); + + snprintf (snap, sizeof(snap), "/dev/%s/%s", gf_trim(volgroup), + snapname); + snap_device = gf_strdup (snap); + if (!snap_device) { + gf_log (this->name, GF_LOG_WARNING, "Cannot copy the " + "snapshot device name for snapname: %s)", snapname); + } + +out: + return snap_device; +} + +/* This function actually calls the command (or the API) for taking the + snapshot of the backend brick filesystem. If this is successful, + then call the glusterd_snap_create function to create the snap object + for glusterd +*/ +char * +glusterd_take_lvm_snapshot (glusterd_volinfo_t *snap_vol, + glusterd_brickinfo_t *brickinfo) +{ + char msg[NAME_MAX] = ""; + char buf[PATH_MAX] = ""; + char *snap_device = NULL; + char *ptr = NULL; + char *device = NULL; + int ret = -1; + gf_boolean_t match = _gf_false; + runner_t runner = {0,}; + xlator_t *this = NULL; + + this = THIS; + + if (!brickinfo) { + gf_log (this->name, GF_LOG_ERROR, "brickinfo NULL"); + goto out; + } + + device = glusterd_get_brick_mount_details (brickinfo); + if (!device) { + gf_log (this->name, GF_LOG_ERROR, "getting device name for " + "the brick %s:%s failed", brickinfo->hostname, + brickinfo->path); + goto out; + } + + /* Figuring out if setactivationskip flag is supported or not */ + runinit (&runner); + snprintf (msg, sizeof (msg), "running lvcreate help"); + runner_add_args (&runner, "/sbin/lvcreate", "--help", NULL); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); + ret = runner_start (&runner); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to run lvcreate help"); + runner_end (&runner); + goto out; + } + + /* Looking for setactivationskip in lvcreate --help */ + do { + ptr = fgets(buf, sizeof(buf), + runner_chio (&runner, STDOUT_FILENO)); + if (ptr) { + if (strstr(buf, "setactivationskip")) { + match = _gf_true; + break; + } + } + } while (ptr != NULL); + runner_end (&runner); + + /* Takng the actual snapshot */ + runinit (&runner); + snprintf (msg, sizeof (msg), "taking snapshot of the brick %s:%s", + brickinfo->hostname, brickinfo->path); + if (match == _gf_true) + runner_add_args (&runner, "/sbin/lvcreate", "-s", device, + "--setactivationskip", "n", "--name", + snap_vol->volname, NULL); + else + runner_add_args (&runner, "/sbin/lvcreate", "-s", device, + "--name", snap_vol->volname, NULL); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + ret = runner_start (&runner); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "taking snapshot of the " + "brick (%s:%s) of device %s failed", + brickinfo->hostname, brickinfo->path, device); + runner_end (&runner); + goto out; + } + runner_end (&runner); + + snap_device = glusterd_build_snap_device_path (device, + snap_vol->volname); + if (!snap_device) { + gf_log (this->name, GF_LOG_WARNING, "Cannot copy the snapshot " + "device name for snap %s (volume id: %s)", + snap_vol->snapshot->snapname, snap_vol->volname); + ret = -1; + goto out; + } + +out: + return snap_device; +} + +int32_t +glusterd_snap_brick_create (char *device, glusterd_volinfo_t *snap_volinfo, + glusterd_brickinfo_t *original_brickinfo, + int32_t brick_count, char *snap_brick_dir) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + char snap_brick_mount_path[PATH_MAX] = ""; + char snap_brick_path[PATH_MAX] = ""; + char msg[1024] = ""; + struct stat statbuf = {0, }; + runner_t runner = {0, }; + + this = THIS; + priv = this->private; + + GF_ASSERT (device); + GF_ASSERT (snap_volinfo); + GF_ASSERT (original_brickinfo); + GF_ASSERT (snap_brick_dir); + + snprintf (snap_brick_mount_path, sizeof (snap_brick_mount_path), + "%s/%s/brick%d", snap_mount_folder, snap_volinfo->volname, + brick_count+1); + + snprintf (snap_brick_path, sizeof (snap_brick_path), "%s%s", + snap_brick_mount_path, snap_brick_dir); + + ret = mkdir_p (snap_brick_mount_path, 0777, _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "creating the brick directory" + " %s for the snapshot %s(device: %s) failed", + snap_brick_mount_path, snap_volinfo->volname, device); + goto out; + } + /* mount the snap logical device on the directory inside + /run/gluster/snaps//@snap_brick_mount_path + Way to mount the snap brick via mount api is this. + ret = mount (device, snap_brick_mount_path, entry->mnt_type, + MS_MGC_VAL, "nouuid"); + But for now, mounting using runner apis. + */ + runinit (&runner); + snprintf (msg, sizeof (msg), "mounting snapshot of the brick %s:%s", + original_brickinfo->hostname, original_brickinfo->path); + runner_add_args (&runner, "mount", "-o", "nouuid", device, + snap_brick_mount_path, NULL); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + + /* let glusterd get blocked till snapshot is over */ + synclock_unlock (&priv->big_lock); + ret = runner_run (&runner); + synclock_lock (&priv->big_lock); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "mounting the snapshot " + "logical device %s failed (error: %s)", device, + strerror (errno)); + goto out; + } else + gf_log (this->name, GF_LOG_DEBUG, "mounting the snapshot " + "logical device %s successful", device); + + ret = stat (snap_brick_path, &statbuf); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "stat of the brick %s" + "(brick mount: %s) failed (%s)", snap_brick_path, + snap_brick_mount_path, strerror (errno)); + goto out; + } + ret = sys_lsetxattr (snap_brick_path, + GF_XATTR_VOL_ID_KEY, + snap_volinfo->volume_id, 16, + XATTR_REPLACE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "extended attribute %s on %s. Reason: " + "%s, snap: %s", GF_XATTR_VOL_ID_KEY, + snap_brick_path, strerror (errno), + snap_volinfo->volname); + goto out; + } + +out: + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "unmounting the snap brick" + " mount %s", snap_brick_mount_path); + umount (snap_brick_mount_path); + } + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int32_t +glusterd_add_bricks_to_snap_volume (dict_t *dict, dict_t *rsp_dict, + glusterd_volinfo_t *snap_vol, + glusterd_brickinfo_t *original_brickinfo, + glusterd_brickinfo_t *snap_brickinfo, + char **snap_brick_dir, int64_t volcount, + int32_t brick_count) +{ + char key[PATH_MAX] = ""; + char snap_brick_path[PATH_MAX] = ""; + char *snap_device = NULL; + gf_boolean_t add_missed_snap = _gf_false; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + GF_ASSERT (snap_vol); + GF_ASSERT (original_brickinfo); + GF_ASSERT (snap_brickinfo); + GF_ASSERT (snap_brick_dir); + + snprintf (key, sizeof(key) - 1, "vol%ld.brickdir%d", volcount, + brick_count); + ret = dict_get_ptr (dict, key, (void **)snap_brick_dir); + if (ret) { + /* Using original brickinfo here because it will be a + * pending snapshot and storing the original brickinfo + * will help in mapping while recreating the missed snapshot + */ + gf_log (this->name, GF_LOG_WARNING, "Unable to fetch " + "snap mount path (%s). Using original brickinfo", key); + snap_brickinfo->snap_status = -1; + strcpy (snap_brick_path, original_brickinfo->path); + + /* In origiator node add snaps missed + * from different nodes to the dict + */ + if (is_origin_glusterd (dict) == _gf_true) + add_missed_snap = _gf_true; + } else { + /* Create brick-path in the format /var/run/gluster/snaps/ * + * //snap-brick-dir * + */ + snprintf (snap_brick_path, sizeof(snap_brick_path), + "%s/%s/brick%d%s", snap_mount_folder, + snap_vol->volname, brick_count+1, + *snap_brick_dir); + } + + if ((snap_brickinfo->snap_status != -1) && + (!uuid_compare (original_brickinfo->uuid, MY_UUID)) && + (!glusterd_is_brick_started (original_brickinfo))) { + /* In case if the brick goes down after prevalidate. */ + gf_log (this->name, GF_LOG_WARNING, "brick %s:%s is not" + " started (snap: %s)", + original_brickinfo->hostname, + original_brickinfo->path, + snap_vol->snapshot->snapname); + + snap_brickinfo->snap_status = -1; + strcpy (snap_brick_path, original_brickinfo->path); + add_missed_snap = _gf_true; + } + + if (add_missed_snap) { + ret = glusterd_add_missed_snaps_to_dict (rsp_dict, + snap_vol->volname, + original_brickinfo, + brick_count + 1, + GF_SNAP_OPTION_TYPE_CREATE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to add missed" + " snapshot info for %s:%s in the rsp_dict", + original_brickinfo->hostname, + original_brickinfo->path); + goto out; + } + } + + snprintf (key, sizeof(key), "vol%ld.brick_snapdevice%d", + volcount, brick_count); + ret = dict_get_ptr (dict, key, (void **)&snap_device); + if (ret) { + /* If the device name is empty, so will be the brick path + * Hence the missed snap has already been added above + */ + gf_log (this->name, GF_LOG_ERROR, "Unable to fetch " + "snap device (%s). Leaving empty", key); + } else + strcpy (snap_brickinfo->device_path, snap_device); + + ret = gf_canonicalize_path (snap_brick_path); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to canonicalize path"); + goto out; + } + + strcpy (snap_brickinfo->hostname, original_brickinfo->hostname); + strcpy (snap_brickinfo->path, snap_brick_path); + uuid_copy (snap_brickinfo->uuid, original_brickinfo->uuid); + list_add_tail (&snap_brickinfo->brick_list, &snap_vol->bricks); + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +static int32_t +glusterd_take_brick_snapshot (glusterd_volinfo_t *origin_vol, + glusterd_volinfo_t *snap_vol, dict_t *rsp_dict, + glusterd_brickinfo_t *original_brickinfo, + glusterd_brickinfo_t *snap_brickinfo, + char *snap_brick_dir, int32_t brick_count) +{ + char *device = NULL; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (origin_vol); + GF_ASSERT (snap_vol); + GF_ASSERT (rsp_dict); + GF_ASSERT (original_brickinfo); + GF_ASSERT (snap_brickinfo); + GF_ASSERT (snap_brick_dir); + + device = glusterd_take_lvm_snapshot (snap_vol, original_brickinfo); + /* Fail the snapshot even though snapshot on one of + the bricks fails. At the end when we check whether + the snapshot volume meets quorum or not, then the + the snapshot can either be treated as success, or + in case of failure we can undo the changes and return + failure to cli. */ + if (!device) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to take snapshot of %s:%s", + original_brickinfo->hostname, + original_brickinfo->path); + goto out; + } + + /* create the complete brick here */ + ret = glusterd_snap_brick_create (device, snap_vol, + original_brickinfo, + brick_count, snap_brick_dir); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "not able to" + " create the brickinfo for the snap %s" + ", volume %s", snap_vol->snapshot->snapname, + origin_vol->volname); + goto out; + } + +out: + if (device) + GF_FREE (device); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* Look for disconnected peers, for missed snap creates or deletes */ +static int32_t +glusterd_find_missed_snap (dict_t *rsp_dict, glusterd_volinfo_t *vol, + char *snap_uuid, struct list_head *peers, + int32_t op) +{ + int32_t brick_count = -1; + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_peerinfo_t *peerinfo = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + GF_ASSERT (peers); + GF_ASSERT (vol); + GF_ASSERT (snap_uuid); + + brick_count = 0; + list_for_each_entry (brickinfo, &vol->bricks, brick_list) { + if (!uuid_compare (brickinfo->uuid, MY_UUID)) { + /* If the brick belongs to the same node */ + brick_count++; + continue; + } + + list_for_each_entry (peerinfo, peers, uuid_list) { + if (uuid_compare (peerinfo->uuid, brickinfo->uuid)) { + /* If the brick doesnt belong to this peer */ + continue; + } + + /* Found peer who owns the brick, * + * if peer is not connected or not * + * friend add it to missed snap list */ + if (!(peerinfo->connected) || + (peerinfo->state.state != + GD_FRIEND_STATE_BEFRIENDED)) { + ret = glusterd_add_missed_snaps_to_dict + (rsp_dict, + snap_uuid, + brickinfo, + brick_count + 1, + op); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to add missed snapshot " + "info for %s:%s in the " + "rsp_dict", brickinfo->hostname, + brickinfo->path); + goto out; + } + } + } + brick_count++; + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +glusterd_volinfo_t * +glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap, + dict_t *dict, dict_t *rsp_dict, int64_t volcount) +{ + char key[PATH_MAX] = ""; + char *snap_brick_dir = NULL; + char *username = NULL; + char *password = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + glusterd_conf_t *priv = NULL; + glusterd_volinfo_t *snap_vol = NULL; + uuid_t *snap_volid = NULL; + int32_t ret = -1; + int32_t brick_count = 0; + glusterd_brickinfo_t *snap_brickinfo = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (dict); + GF_ASSERT (origin_vol); + GF_ASSERT (rsp_dict); + + /* fetch username, password and vol_id from dict*/ + snprintf (key, sizeof(key), "volume%ld_username", volcount); + ret = dict_get_str (dict, key, &username); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get %s for " + "snap %s", key, snap->snapname); + goto out; + } + + snprintf (key, sizeof(key), "volume%ld_password", volcount); + ret = dict_get_str (dict, key, &password); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get %s for " + "snap %s", key, snap->snapname); + goto out; + } + + snprintf (key, sizeof(key) - 1, "vol%ld_volid", volcount); + ret = dict_get_bin (dict, key, (void **)&snap_volid); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch snap_volid"); + goto out; + } + + /* We are not setting the username and password here as + * we need to set the user name and password passed in + * the dictionary + */ + ret = glusterd_volinfo_dup (origin_vol, &snap_vol, _gf_false); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to duplicate volinfo " + "for the snapshot %s", snap->snapname); + goto out; + } + + /* uuid is used as lvm snapshot name. + This will avoid restrictions on snapshot names provided by user */ + GLUSTERD_GET_UUID_NOHYPHEN (snap_vol->volname, *snap_volid); + uuid_copy (snap_vol->volume_id, *snap_volid); + snap_vol->is_snap_volume = _gf_true; + strcpy (snap_vol->parent_volname, origin_vol->volname); + snap_vol->snapshot = snap; + + glusterd_auth_set_username (snap_vol, username); + glusterd_auth_set_password (snap_vol, password); + + /* Adding snap brickinfos to the snap volinfo */ + brick_count = 0; + list_for_each_entry (brickinfo, &origin_vol->bricks, brick_list) { + snap_brickinfo = NULL; + + ret = glusterd_brickinfo_new (&snap_brickinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "initializing the brick for the snap " + "volume failed (snapname: %s)", snap->snapname); + goto out; + } + + ret = glusterd_add_bricks_to_snap_volume (dict, rsp_dict, + snap_vol, + brickinfo, + snap_brickinfo, + &snap_brick_dir, + volcount, + brick_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to add the snap brick for " + "%s:%s to the snap volume", + brickinfo->hostname, brickinfo->path); + GF_FREE (snap_brickinfo); + goto out; + } + + /*Update the brickid for the new brick in new volume*/ + GLUSTERD_ASSIGN_BRICKID_TO_BRICKINFO (snap_brickinfo, snap_vol, + brick_count); + + /* Take snapshot of the brick */ + if ((uuid_compare (brickinfo->uuid, MY_UUID)) || + (snap_brickinfo->snap_status == -1)) { + brick_count++; + continue; + } + + ret = glusterd_take_brick_snapshot (origin_vol, snap_vol, + rsp_dict, brickinfo, + snap_brickinfo, + snap_brick_dir, + brick_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to take snapshot for %s:%s", + brickinfo->hostname, brickinfo->path); + goto out; + } + brick_count++; + } + + /*TODO: the quorum check of the snap volume here */ + + ret = glusterd_store_volinfo (snap_vol, + GLUSTERD_VOLINFO_VER_AC_INCREMENT); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to store snapshot " + "volinfo (%s) for snap %s", snap_vol->volname, + snap->snapname); + goto out; + } + + ret = generate_brick_volfiles (snap_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "generating the brick " + "volfiles for the snap %s (volume: %s) failed", + snap->snapname, origin_vol->volname); + goto out; + } + + ret = generate_client_volfiles (snap_vol, GF_CLIENT_TRUSTED); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "generating the trusted " + "client volfiles for the snap %s (volume: %s) failed", + snap->snapname, origin_vol->volname); + goto out; + } + + ret = generate_client_volfiles (snap_vol, GF_CLIENT_OTHER); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "generating the client " + "volfiles for the snap %s (volume: %s) failed", + snap->snapname, origin_vol->volname); + goto out; + } + + ret = glusterd_list_add_snapvol (origin_vol, snap_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "could not add the snap " + "volume %s to the list", snap_vol->volname); + goto out; + } + + list_for_each_entry (brickinfo, &snap_vol->bricks, brick_list) { + if (uuid_compare (brickinfo->uuid, MY_UUID)) + continue; + + if (brickinfo->snap_status == -1) { + gf_log (this->name, GF_LOG_INFO, + "not starting snap brick %s:%s for " + "for the snap %s (volume: %s)", + brickinfo->hostname, brickinfo->path, + snap->snapname, origin_vol->volname); + continue; + } + + ret = glusterd_brick_start (snap_vol, brickinfo, _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "starting the " + "brick %s:%s for the snap %s (volume: %s) " + "failed", brickinfo->hostname, brickinfo->path, + snap->snapname, origin_vol->volname); + goto out; + } + } + + snap_vol->status = GLUSTERD_STATUS_STARTED; + ret = glusterd_store_volinfo (snap_vol, + GLUSTERD_VOLINFO_VER_AC_INCREMENT); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to store snap volinfo"); + goto out; + } + +out: + if (ret) { + if (snap_vol) + glusterd_snap_volume_remove (rsp_dict, snap_vol, + _gf_true, _gf_true); + snap_vol = NULL; + } + + return snap_vol; +} + +/* This is a snapshot remove handler function. This function will be + * executed in the originator node. This function is responsible for + * calling mgmt v3 framework to do the actual remove on all the bricks + * + * @param req RPC request object + * @param op gluster operation + * @param dict dictionary containing snapshot remove request + * @param err_str In case of an err this string should be populated + * @param len length of err_str buffer + * + * @return Negative value on Failure and 0 in success + */ +int +glusterd_handle_snapshot_remove (rpcsvc_request_t *req, glusterd_op_t op, + dict_t *dict, char *err_str, size_t len) +{ + int ret = -1; + int64_t volcount = 0; + char *snapname = NULL; + char *volname = NULL; + char key[PATH_MAX] = ""; + glusterd_snap_t *snap = NULL; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_volinfo_t *tmp = NULL; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (req); + GF_ASSERT (dict); + GF_ASSERT (err_str); + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get snapname"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + snprintf (err_str, len, "Snap (%s) does not exist", snapname); + gf_log (this->name, GF_LOG_ERROR, + "%s", err_str); + ret = -1; + goto out; + } + + /* Set volnames in the dict to get mgmt_v3 lock */ + list_for_each_entry_safe (snap_vol, tmp, &snap->volumes, vol_list) { + volcount++; + volname = gf_strdup (snap_vol->parent_volname); + if (!volname) { + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "strdup failed"); + goto out; + } + + snprintf (key, sizeof (key), "volname%ld", volcount); + ret = dict_set_dynstr (dict, key, volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "volume name in dictionary"); + GF_FREE (volname); + goto out; + } + volname = NULL; + } + ret = dict_set_int64 (dict, "volcount", volcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set volcount"); + goto out; + } + + ret = glusterd_mgmt_v3_initiate_snap_phases (req, op, dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to initiate snap " + "phases"); + goto out; + } + + ret = 0; +out: + return ret; +} + +int +glusterd_snapshot_remove_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int32_t ret = -1; + char *snapname = NULL; + xlator_t *this = NULL; + glusterd_snap_t *snap = NULL; + + this = THIS; + + if (!dict || !op_errstr) { + gf_log (this->name, GF_LOG_ERROR, "input parameters NULL"); + goto out; + } + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Getting the snap name " + "failed"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + gf_log (this->name, GF_LOG_ERROR, "Snap %s does not exist", + snapname); + ret = -1; + goto out; + } + + ret = 0; +out: + return ret; +} + +int +glusterd_snapshot_status_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int ret = -1; + char *snapname = NULL; + glusterd_conf_t *conf = NULL; + xlator_t *this = NULL; + int32_t cmd = -1; + glusterd_volinfo_t *volinfo = NULL; + char *volname = NULL; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + + GF_ASSERT (conf); + GF_ASSERT (op_errstr); + if (!dict) { + gf_log (this->name, GF_LOG_ERROR, "Input dict is NULL"); + goto out; + } + + ret = dict_get_int32 (dict, "cmd", &cmd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not fetch status cmd"); + goto out; + } + + switch (cmd) { + case GF_SNAP_STATUS_TYPE_ALL: + { + break; + } + case GF_SNAP_STATUS_TYPE_SNAP: + { + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not fetch snapname"); + goto out; + } + + if (!glusterd_find_snap_by_name (snapname)) { + ret = gf_asprintf (op_errstr, "Snap (%s) " + "not found", snapname); + if (ret < 0) { + goto out; + } + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "Snap (%s) " + "not found", snapname); + goto out; + } + break; + } + case GF_SNAP_STATUS_TYPE_VOL: + { + ret = dict_get_str (dict, "volname", &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not fetch volname"); + goto out; + } + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + ret = gf_asprintf (op_errstr, "Volume (%s)" + "not found", volname); + if (ret < 0) { + goto out; + } + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "Volume " + "%s not present", volname); + goto out; + } + break; + + } + default: + { + gf_log (this->name, GF_LOG_ERROR, "Invalid command"); + break; + } + } + ret = 0; + +out: + return ret; +} + +int32_t +glusterd_snapshot_remove_commit (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int32_t ret = -1; + char *snapname = NULL; + char *dup_snapname = NULL; + glusterd_snap_t *snap = NULL; + glusterd_conf_t *priv = NULL; + glusterd_volinfo_t *snap_volinfo = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + GF_ASSERT (op_errstr); + + priv = this->private; + GF_ASSERT (priv); + + if (!dict || !op_errstr) { + gf_log (this->name, GF_LOG_ERROR, "input parameters NULL"); + goto out; + } + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Getting the snap name " + "failed"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + gf_log (this->name, GF_LOG_ERROR, "Snap %s does not exist", + snapname); + ret = -1; + goto out; + } + + if (is_origin_glusterd (dict) == _gf_true) { + /* TODO : As of now there is only volume in snapshot. + * Change this when multiple volume snapshot is introduced + */ + snap_volinfo = list_entry (snap->volumes.next, + glusterd_volinfo_t, + vol_list); + if (!snap_volinfo) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch snap_volinfo"); + ret = -1; + goto out; + } + + /* From origin glusterd check if * + * any peers with snap bricks is down */ + ret = glusterd_find_missed_snap (rsp_dict, snap_volinfo, + snap_volinfo->volname, + &priv->peers, + GF_SNAP_OPTION_TYPE_DELETE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to find missed snap deletes"); + goto out; + } + } + + ret = glusterd_snap_remove (rsp_dict, snap, _gf_true, _gf_false); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to remove snap %s", + snapname); + goto out; + } + + dup_snapname = gf_strdup (snapname); + if (!dup_snapname) { + gf_log (this->name, GF_LOG_ERROR, "Strdup failed"); + ret = -1; + goto out; + } + + ret = dict_set_dynstr (rsp_dict, "snapname", dup_snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set the snapname"); + GF_FREE (dup_snapname); + goto out; + } + + ret = 0; +out: + return ret; +} + +int32_t +glusterd_do_snap_cleanup (dict_t *dict, char **op_errstr, dict_t *rsp_dict) +{ + int32_t ret = -1; + char *name = NULL; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + glusterd_volinfo_t *volinfo = NULL; + glusterd_snap_t *snap = NULL; + + this = THIS; + GF_ASSERT (this); + conf = this->private; + GF_ASSERT (conf); + + if (!dict || !op_errstr) { + gf_log (this->name, GF_LOG_ERROR, "input parameters NULL"); + goto out; + } + + ret = dict_get_str (dict, "snapname", &name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "getting the snap " + "name failed (volume: %s)", volinfo->volname); + goto out; + } + + /* + If the snapname is not found that means the failure happened at + staging, or in commit, before the snap object is created, in which + case there is nothing to cleanup. So set ret to 0. + */ + snap = glusterd_find_snap_by_name (name); + if (!snap) { + gf_log (this->name, GF_LOG_INFO, "snap %s is not found", name); + ret = 0; + goto out; + } + + ret = glusterd_snap_remove (rsp_dict, snap, _gf_true, _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "removing the snap %s failed", + name); + goto out; + } + + name = NULL; + + ret = 0; + +out: + + return ret; +} + +/* In case of a successful, delete or create operation, during post_validate * + * look for missed snap operations and update the missed snap lists */ +int32_t +glusterd_snapshot_update_snaps_post_validate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int32_t ret = -1; + int32_t missed_snap_count = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + GF_ASSERT (op_errstr); + + ret = dict_get_int32 (dict, "missed_snap_count", + &missed_snap_count); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, "No missed snaps"); + ret = 0; + goto out; + } + + ret = glusterd_store_update_missed_snaps (dict, missed_snap_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to update missed_snaps_list"); + goto out; + } + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int32_t +glusterd_snapshot_create_commit (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int ret = -1; + int64_t i = 0; + int64_t volcount = 0; + char *snapname = NULL; + char *volname = NULL; + char *tmp_name = NULL; + char key[PATH_MAX] = ""; + xlator_t *this = NULL; + glusterd_snap_t *snap = NULL; + glusterd_volinfo_t *origin_vol = NULL; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_conf_t *priv = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT(dict); + GF_ASSERT(op_errstr); + GF_ASSERT(rsp_dict); + priv = this->private; + GF_ASSERT(priv); + + ret = dict_get_int64 (dict, "volcount", &volcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "get the volume count"); + goto out; + } + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to fetch snapname"); + goto out; + } + tmp_name = gf_strdup (snapname); + if (!tmp_name) { + gf_log (this->name, GF_LOG_ERROR, "Out of memory"); + ret = -1; + goto out; + } + + ret = dict_set_dynstr (rsp_dict, "snapname", tmp_name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to set snapname in rsp_dict"); + GF_FREE (tmp_name); + goto out; + } + tmp_name = NULL; + + snap = glusterd_create_snap_object (dict, rsp_dict); + if (!snap) { + gf_log (this->name, GF_LOG_ERROR, "creating the" + "snap object %s failed", snapname); + ret = -1; + goto out; + } + + for (i = 1; i <= volcount; i++) { + snprintf (key, sizeof (key), "volname%ld", i); + ret = dict_get_str (dict, key, &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get volume name"); + goto out; + } + + ret = glusterd_volinfo_find (volname, &origin_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get the volinfo for " + "the volume %s", volname); + goto out; + } + + /* TODO: Create a stub where the bricks are + added parallely by worker threads so that + the snap creating happens parallely. */ + snap_vol = glusterd_do_snap_vol (origin_vol, snap, dict, + rsp_dict, i); + if (!snap_vol) { + ret = -1; + gf_log (this->name, GF_LOG_WARNING, "taking the " + "snapshot of the volume %s failed", volname); + goto out; + } + } + + snap->snap_status = GD_SNAP_STATUS_IN_USE; + ret = glusterd_store_snap (snap); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Could not store snap" + "object %s", snap->snapname); + goto out; + } + + ret = 0; + +out: + if (ret) { + if (snap) + glusterd_snap_remove (rsp_dict, snap, + _gf_true, _gf_true); + snap = NULL; + } + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +snap_max_hard_limit_set_commit (dict_t *dict, uint64_t value, + char *volname, char **op_errstr) +{ + char err_str[PATH_MAX] = ""; + glusterd_conf_t *conf = NULL; + glusterd_volinfo_t *volinfo = NULL; + int ret = -1; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (volname); + GF_ASSERT (op_errstr); + + conf = this->private; + + GF_ASSERT (conf); + + /* TODO: Initiate auto deletion when there is a limit change */ + if (!volname) { + /* For system limit */ + conf->snap_max_hard_limit = value; + + ret = glusterd_store_global_info (this); + if (ret) { + snprintf (err_str, PATH_MAX, "Failed to store " + "snap-max-hard-limit for system"); + goto out; + } + } else { + /* For one volume */ + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, PATH_MAX, "Failed to get the" + " volinfo for volume %s", volname); + goto out; + } + + volinfo->snap_max_hard_limit = value; + + ret = glusterd_store_volinfo (volinfo, + GLUSTERD_VOLINFO_VER_AC_INCREMENT); + if (ret) { + snprintf (err_str, PATH_MAX, "Failed to store " + "snap-max-hard-limit for volume %s", volname); + goto out; + } + } + + ret = 0; +out: + if (ret) { + *op_errstr = gf_strdup (err_str); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + } + return ret; +} + +int +snap_max_limits_display_commit (dict_t *rsp_dict, char *volname, + char **op_errstr) +{ + char err_str[PATH_MAX] = ""; + char buf[PATH_MAX] = ""; + glusterd_conf_t *conf = NULL; + glusterd_volinfo_t *volinfo = NULL; + int ret = -1; + uint64_t active_hard_limit = 0; + uint64_t snap_max_limit = 0; + uint64_t soft_limit_value = -1; + uint64_t count = 0; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + GF_ASSERT (volname); + GF_ASSERT (op_errstr); + + conf = this->private; + + GF_ASSERT (conf); + + if (!volname) { + /* For system limit */ + list_for_each_entry (volinfo, &conf->volumes, vol_list) { + if (volinfo->is_snap_volume == _gf_true) + continue; + snap_max_limit = volinfo->snap_max_hard_limit; + if (snap_max_limit > conf->snap_max_hard_limit) + active_hard_limit = conf->snap_max_hard_limit; + else + active_hard_limit = snap_max_limit; + soft_limit_value = (active_hard_limit * + conf->snap_max_soft_limit) / 100; + + snprintf (buf, sizeof(buf), "volume%ld-volname", count); + ret = dict_set_str (rsp_dict, buf, volinfo->volname); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-hard-limit", count); + ret = dict_set_uint64 (rsp_dict, buf, snap_max_limit); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-active-hard-limit", count); + ret = dict_set_uint64 (rsp_dict, buf, + active_hard_limit); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-soft-limit", count); + ret = dict_set_uint64 (rsp_dict, buf, soft_limit_value); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + count++; + } + + ret = dict_set_uint64 (rsp_dict, "voldisplaycount", count); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set voldisplaycount"); + goto out; + } + } else { + /* For one volume */ + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + snprintf (err_str, PATH_MAX, "Failed to get the" + " volinfo for volume %s", volname); + goto out; + } + + snap_max_limit = volinfo->snap_max_hard_limit; + if (snap_max_limit > conf->snap_max_hard_limit) + active_hard_limit = conf->snap_max_hard_limit; + else + active_hard_limit = snap_max_limit; + + soft_limit_value = (active_hard_limit * + conf->snap_max_soft_limit) / 100; + + snprintf (buf, sizeof(buf), "volume%ld-volname", count); + ret = dict_set_str (rsp_dict, buf, volinfo->volname); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-hard-limit", count); + ret = dict_set_uint64 (rsp_dict, buf, snap_max_limit); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-active-hard-limit", count); + ret = dict_set_uint64 (rsp_dict, buf, active_hard_limit); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-soft-limit", count); + ret = dict_set_uint64 (rsp_dict, buf, soft_limit_value); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set %s", buf); + goto out; + } + + count++; + + ret = dict_set_uint64 (rsp_dict, "voldisplaycount", count); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set voldisplaycount"); + goto out; + } + + } + + ret = dict_set_uint64 (rsp_dict, "snap-max-hard-limit", + conf->snap_max_hard_limit); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set sys-snap-max-hard-limit "); + goto out; + } + + ret = dict_set_uint64 (rsp_dict, "snap-max-soft-limit", + conf->snap_max_soft_limit); + if (ret) { + snprintf (err_str, PATH_MAX, + "Failed to set sys-snap-max-hard-limit "); + goto out; + } + + ret = 0; +out: + if (ret) { + *op_errstr = gf_strdup (err_str); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + } + return ret; +} + +int +glusterd_snapshot_config_commit (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + char *volname = NULL; + xlator_t *this = NULL; + int ret = -1; + char err_str[PATH_MAX] = {0,}; + glusterd_conf_t *conf = NULL; + int config_command = 0; + uint64_t hard_limit = 0; + uint64_t soft_limit = 0; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + + conf = this->private; + + GF_ASSERT (conf); + + ret = dict_get_int32 (dict, "config-command", &config_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "failed to get config-command type"); + goto out; + } + + /* Ignore the return value of the following dict_get, + * as they are optional + */ + ret = dict_get_str (dict, "volname", &volname); + + ret = dict_get_uint64 (dict, "snap-max-hard-limit", &hard_limit); + + ret = dict_get_uint64 (dict, "snap-max-soft-limit", &soft_limit); + + switch (config_command) { + case GF_SNAP_CONFIG_TYPE_SET: + if (hard_limit) { + /* Commit ops for snap-max-hard-limit */ + ret = snap_max_hard_limit_set_commit (dict, hard_limit, + volname, + op_errstr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "snap-max-hard-limit set " + "commit failed."); + goto out; + } + } + + if (soft_limit) { + /* For system limit */ + conf->snap_max_soft_limit = soft_limit; + + ret = glusterd_store_global_info (this); + if (ret) { + snprintf (err_str, PATH_MAX, "Failed to store " + "snap-max-soft-limit for system"); + *op_errstr = gf_strdup (err_str); + gf_log (this->name, GF_LOG_ERROR, "%s", + err_str); + goto out; + } + } + break; + + case GF_SNAP_CONFIG_DISPLAY: + /* Reading data from local node only */ + if (!is_origin_glusterd (dict)) { + ret = 0; + break; + } + + ret = snap_max_limits_display_commit (rsp_dict, volname, + op_errstr); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "snap-max-limit " + "display commit failed."); + goto out; + } + break; + default: + break; + } + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_get_brick_lvm_details (dict_t *rsp_dict, + glusterd_brickinfo_t *brickinfo, char *volname, + char *device, char *key_prefix) +{ + + int ret = -1; + glusterd_conf_t *priv = NULL; + runner_t runner = {0,}; + xlator_t *this = NULL; + char msg[PATH_MAX] = ""; + char buf[PATH_MAX] = ""; + char *ptr = NULL; + char *token = NULL; + char key[PATH_MAX] = ""; + char *value = NULL; + + GF_ASSERT (rsp_dict); + GF_ASSERT (brickinfo); + GF_ASSERT (volname); + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + device = glusterd_get_brick_mount_details (brickinfo); + if (!device) { + gf_log (this->name, GF_LOG_ERROR, "Getting device name for " + "the brick %s:%s failed", brickinfo->hostname, + brickinfo->path); + goto out; + } + runinit (&runner); + snprintf (msg, sizeof (msg), "running lvs command, " + "for getting snap status"); + /* Using lvs command fetch the Volume Group name, + * Percentage of data filled and Logical Volume size + * + * "-o" argument is used to get the desired information, + * example : "lvs /dev/VolGroup/thin_vol -o vgname,lv_size", + * will get us Volume Group name and Logical Volume size. + * + * Here separator used is ":", + * for the above given command with separator ":", + * The output will be "vgname:lvsize" + */ + runner_add_args (&runner, "lvs", device, "--noheading", "-o", + "vg_name,data_percent,lv_size", + "--separator", ":", NULL); + runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); + runner_log (&runner, "", GF_LOG_DEBUG, msg); + ret = runner_start (&runner); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not perform lvs action"); + goto end; + } + do { + ptr = fgets (buf, sizeof (buf), + runner_chio (&runner, STDOUT_FILENO)); + + if (ptr == NULL) + break; + token = strtok (buf, ":"); + if (token != NULL) { + while (token && token[0] == ' ') + token++; + if (!token) { + ret = -1; + gf_log (this->name, GF_LOG_ERROR, + "Invalid vg entry"); + goto end; + } + value = gf_strdup (token); + if (!value) { + ret = -1; + goto end; + } + ret = snprintf (key, sizeof (key), "%s.vgname", + key_prefix); + if (ret < 0) { + goto end; + } + + ret = dict_set_dynstr (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save vgname "); + goto end; + } + } + + token = strtok (NULL, ":"); + if (token != NULL) { + value = gf_strdup (token); + if (!value) { + ret = -1; + goto end; + } + ret = snprintf (key, sizeof (key), "%s.data", + key_prefix); + if (ret < 0) { + goto end; + } + + ret = dict_set_dynstr (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save data percent "); + goto end; + } + } + token = strtok (NULL, ":"); + if (token != NULL) { + value = gf_strdup (token); + if (!value) { + ret = -1; + goto end; + } + ret = snprintf (key, sizeof (key), "%s.lvsize", + key_prefix); + if (ret < 0) { + goto end; + } + + ret = dict_set_dynstr (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save meta data percent "); + goto end; + } + } + + } while (ptr != NULL); + + ret = 0; + +end: + runner_end (&runner); + +out: + if (ret && value) { + GF_FREE (value); + } + + return ret; +} + +int +glusterd_get_single_brick_status (char **op_errstr, dict_t *rsp_dict, + char *keyprefix, int index, + glusterd_volinfo_t *snap_volinfo, + glusterd_brickinfo_t *brickinfo) +{ + int ret = -1; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + char key[PATH_MAX] = ""; + char *device = NULL; + char *value = NULL; + char brick_path[PATH_MAX] = ""; + char pidfile[PATH_MAX] = ""; + pid_t pid = -1; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + GF_ASSERT (keyprefix); + GF_ASSERT (snap_volinfo); + GF_ASSERT (brickinfo); + + ret = snprintf (key, sizeof (key), "%s.brick%d.path", keyprefix, + index); + if (ret < 0) { + goto out; + } + + ret = snprintf (brick_path, sizeof (brick_path), + "%s:%s", brickinfo->hostname, brickinfo->path); + if (ret < 0) { + goto out; + } + + value = gf_strdup (brick_path); + if (!value) { + ret = -1; + goto out; + } + + ret = dict_set_dynstr (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to store " + "brick_path %s", brickinfo->path); + goto out; + } + + if (brickinfo->snap_status == -1) { + /* Setting vgname as "Pending Snapshot" */ + value = gf_strdup ("Pending Snapshot"); + if (!value) { + ret = -1; + goto out; + } + + snprintf (key, sizeof (key), "%s.brick%d.vgname", + keyprefix, index); + ret = dict_set_dynstr (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save vgname "); + goto out; + } + + ret = 0; + goto out; + } + value = NULL; + + ret = snprintf (key, sizeof (key), "%s.brick%d.status", + keyprefix, index); + if (ret < 0) { + goto out; + } + + if (brickinfo->status == GF_BRICK_STOPPED) { + value = gf_strdup ("No"); + if (!value) { + ret = -1; + goto out; + } + ret = dict_set_str (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save brick status"); + goto out; + } + value = NULL; + } else { + value = gf_strdup ("Yes"); + if (!value) { + ret = -1; + goto out; + } + ret = dict_set_str (rsp_dict, key, value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save brick status"); + goto out; + } + value = NULL; + + GLUSTERD_GET_BRICK_PIDFILE (pidfile, snap_volinfo, + brickinfo, priv); + ret = gf_is_service_running (pidfile, &pid); + + ret = snprintf (key, sizeof (key), "%s.brick%d.pid", + keyprefix, index); + if (ret < 0) { + goto out; + } + + ret = dict_set_int32 (rsp_dict, key, pid); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save pid %d", pid); + goto out; + } + } + + ret = snprintf (key, sizeof (key), "%s.brick%d", + keyprefix, index); + if (ret < 0) { + goto out; + } + + ret = glusterd_get_brick_lvm_details (rsp_dict, brickinfo, + snap_volinfo->volname, + device, key); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get " + "brick LVM details"); + goto out; + } +out: + if (ret && value) { + GF_FREE (value); + } + + return ret; +} + +int +glusterd_get_single_snap_status (char **op_errstr, dict_t *rsp_dict, + char *keyprefix, glusterd_snap_t *snap) +{ + int ret = -1; + xlator_t *this = NULL; + char key[PATH_MAX] = ""; + char brickkey[PATH_MAX] = ""; + glusterd_volinfo_t *snap_volinfo = NULL; + glusterd_volinfo_t *tmp_volinfo = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + int volcount = 0; + int brickcount = 0; + + this = THIS; + GF_ASSERT (this); + + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + GF_ASSERT (keyprefix); + GF_ASSERT (snap); + + list_for_each_entry_safe (snap_volinfo, tmp_volinfo, &snap->volumes, + vol_list) { + ret = snprintf (key, sizeof (key), "%s.vol%d", keyprefix, + volcount); + if (ret < 0) { + goto out; + } + list_for_each_entry (brickinfo, &snap_volinfo->bricks, + brick_list) { + if (!glusterd_is_local_brick (this, snap_volinfo, + brickinfo)) { + brickcount++; + continue; + } + + ret = glusterd_get_single_brick_status (op_errstr, + rsp_dict, key, brickcount, + snap_volinfo, brickinfo); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Getting " + "single snap status failed"); + goto out; + } + brickcount++; + } + ret = snprintf (brickkey, sizeof (brickkey), "%s.brickcount", + key); + if (ret < 0) { + goto out; + } + + ret = dict_set_int32 (rsp_dict, brickkey, brickcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save brick count"); + goto out; + } + volcount++; + } + + ret = snprintf (key, sizeof (key), "%s.volcount", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_set_int32 (rsp_dict, key, volcount); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save volcount"); + goto out; + } + +out: + + return ret; +} + +int +glusterd_get_each_snap_object_status (char **op_errstr, dict_t *rsp_dict, + glusterd_snap_t *snap, char *keyprefix) +{ + int ret = -1; + char key[PATH_MAX] = ""; + char *temp = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + GF_ASSERT (snap); + GF_ASSERT (keyprefix); + + /* TODO : Get all the snap volume info present in snap object, + * as of now, There will be only one snapvolinfo per snap object + */ + ret = snprintf (key, sizeof (key), "%s.snapname", keyprefix); + if (ret < 0) { + goto out; + } + + temp = gf_strdup (snap->snapname); + if (temp == NULL) { + ret = -1; + goto out; + } + ret = dict_set_dynstr (rsp_dict, key, temp); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not save " + "snap name"); + goto out; + } + + temp = NULL; + + ret = snprintf (key, sizeof (key), "%s.uuid", keyprefix); + if (ret < 0) { + goto out; + } + + temp = gf_strdup (uuid_utoa (snap->snap_id)); + if (temp == NULL) { + ret = -1; + goto out; + } + + ret = dict_set_dynstr (rsp_dict, key, temp); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not save " + "snap UUID"); + goto out; + } + + temp = NULL; + + ret = glusterd_get_single_snap_status (op_errstr, rsp_dict, keyprefix, + snap); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not get single snap status"); + goto out; + } + + ret = snprintf (key, sizeof (key), "%s.volcount", keyprefix); + if (ret < 0) { + goto out; + } + + ret = dict_set_int32 (rsp_dict, key, 1); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not save volcount"); + goto out; + } +out: + if (ret && temp) + GF_FREE (temp); + + return ret; +} + +int +glusterd_get_snap_status_of_volume (char **op_errstr, dict_t *rsp_dict, + char *volname, char *keyprefix) { + int ret = -1; + glusterd_volinfo_t *snap_volinfo = NULL; + glusterd_volinfo_t *temp_volinfo = NULL; + glusterd_volinfo_t *volinfo = NULL; + char key[PATH_MAX] = ""; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + int i = 0; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + GF_ASSERT (op_errstr); + GF_ASSERT (rsp_dict); + GF_ASSERT (volname); + GF_ASSERT (keyprefix); + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get volinfo of " + "volume %s", volname); + goto out; + } + + list_for_each_entry_safe (snap_volinfo, temp_volinfo, + &volinfo->snap_volumes, snapvol_list) { + ret = snprintf (key, sizeof (key), "status.snap%d", i); + if (ret < 0) { + goto out; + } + + ret = glusterd_get_each_snap_object_status (op_errstr, + rsp_dict, snap_volinfo->snapshot, key); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Function : " + "glusterd_get_single_snap_status failed"); + goto out; + } + i++; + } + + ret = dict_set_int32 (rsp_dict, "status.snapcount", i); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to save snapcount"); + ret = -1; + goto out; + } +out: + return ret; +} + +int +glusterd_get_all_snapshot_status (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int32_t i = 0; + int ret = -1; + char key[PATH_MAX] = ""; + glusterd_conf_t *priv = NULL; + glusterd_snap_t *snap = NULL; + glusterd_snap_t *tmp_snap = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + + list_for_each_entry_safe (snap, tmp_snap, + &priv->snapshots, snap_list) { + ret = snprintf (key, sizeof (key), "status.snap%d", i); + if (ret < 0) { + goto out; + } + + ret = glusterd_get_each_snap_object_status (op_errstr, + rsp_dict, snap, key); + + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not get " + "the details of a snap object: %s", + snap->snapname); + goto out; + } + i++; + } + + ret = dict_set_int32 (rsp_dict, "status.snapcount", i); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Could not save snapcount"); + goto out; + } + + ret = 0; +out: + return ret; +} + + +int +glusterd_snapshot_status_commit (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + xlator_t *this = NULL; + int ret = -1; + glusterd_conf_t *conf = NULL; + char *get_buffer = NULL; + int32_t cmd = -1; + char *snapname = NULL; + glusterd_snap_t *snap = NULL; + char *volname = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (op_errstr); + + conf = this->private; + + GF_ASSERT (conf); + ret = dict_get_int32 (dict, "cmd", &cmd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to get status cmd type"); + goto out; + } + + ret = dict_set_int32 (rsp_dict, "cmd", cmd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Could not save status cmd in rsp dictionary"); + goto out; + } + switch (cmd) { + case GF_SNAP_STATUS_TYPE_ALL: + { + ret = glusterd_get_all_snapshot_status (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to " + "get snapshot status"); + goto out; + } + break; + } + case GF_SNAP_STATUS_TYPE_SNAP: + { + + ret = dict_get_str (dict, "snapname", &snapname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to " + "get snap name"); + goto out; + } + + snap = glusterd_find_snap_by_name (snapname); + if (!snap) { + ret = gf_asprintf (op_errstr, "Snap (%s) " + "not found", snapname); + if (ret < 0) { + goto out; + } + ret = -1; + gf_log (this->name, GF_LOG_ERROR, "Unable to " + "get snap volinfo"); + goto out; + } + ret = glusterd_get_each_snap_object_status (op_errstr, + rsp_dict, snap, "status.snap0"); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to " + "get status of snap %s", get_buffer); + goto out; + } + break; + } + case GF_SNAP_STATUS_TYPE_VOL: + { + ret = dict_get_str (dict, "volname", &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to" + " get volume name"); + goto out; + } + + ret = glusterd_get_snap_status_of_volume (op_errstr, + rsp_dict, volname, "status.vol0"); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Function :" + " glusterd_get_snap_status_of_volume " + "failed"); + goto out; + } + } + } + ret = 0; +out: + return ret; +} + +int32_t +glusterd_snapshot_create_postvalidate (dict_t *dict, int32_t op_ret, + char **op_errstr, dict_t *rsp_dict) +{ + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + int ret = -1; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + + priv = this->private; + GF_ASSERT (priv); + + if (op_ret) { + ret = glusterd_do_snap_cleanup (dict, op_errstr, rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "cleanup operation " + "failed"); + goto out; + } + } else { + ret = glusterd_snapshot_update_snaps_post_validate (dict, + op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "create snapshot"); + goto out; + } + } + + ret = 0; +out: + return ret; +} + +int32_t +glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict) +{ + + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + int32_t snap_command = 0; + int ret = -1; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + + priv = this->private; + GF_ASSERT (priv); + + ret = dict_get_int32 (dict, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case (GF_SNAP_OPTION_TYPE_CREATE): + ret = glusterd_snapshot_create_commit (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "create snapshot"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_CONFIG: + ret = glusterd_snapshot_config_commit (dict, op_errstr, + rsp_dict); + break; + + case GF_SNAP_OPTION_TYPE_DELETE: + ret = glusterd_snapshot_remove_commit (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "delete snapshot"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_RESTORE: + ret = glusterd_snapshot_restore (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Failed to " + "restore snapshot"); + goto out; + } + + break; + + case GF_SNAP_OPTION_TYPE_STATUS: + ret = glusterd_snapshot_status_commit (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "show snapshot status"); + goto out; + } + break; + + + default: + gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); + goto out; + break; + } + + ret = 0; + +out: + return ret; +} + +int +glusterd_snapshot_brickop (dict_t *dict, char **op_errstr, dict_t *rsp_dict) +{ + int ret = -1; + int64_t vol_count = 0; + int64_t count = 1; + char key[1024] = {0,}; + char *volname = NULL; + int32_t snap_command = 0; + xlator_t *this = NULL; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + + ret = dict_get_int32 (dict, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case GF_SNAP_OPTION_TYPE_CREATE: + ret = dict_get_int64 (dict, "volcount", &vol_count); + if (ret) + goto out; + while (count <= vol_count) { + snprintf (key, 1024, "volname%"PRId64, count); + ret = dict_get_str (dict, key, &volname); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to get volname"); + goto out; + } + ret = dict_set_str (dict, "volname", volname); + if (ret) + goto out; + + ret = gd_brick_op_phase (GD_OP_SNAP, NULL, dict, + op_errstr); + if (ret) + goto out; + volname = NULL; + count++; + } + + dict_del (dict, "volname"); + ret = 0; + break; + case GF_SNAP_OPTION_TYPE_DELETE: + break; + default: + break; + } + +out: + return ret; +} + +int +glusterd_snapshot_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict) +{ + int snap_command = 0; + xlator_t *this = NULL; + int ret = -1; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + + ret = dict_get_int32 (dict, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case (GF_SNAP_OPTION_TYPE_CREATE): + ret = glusterd_snapshot_create_prevalidate (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot create " + "pre-validation failed"); + goto out; + } + break; + + case (GF_SNAP_OPTION_TYPE_CONFIG): + ret = glusterd_snapshot_config_prevalidate (dict, op_errstr); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot config " + "pre-validation failed"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_RESTORE: + ret = glusterd_snapshot_restore_prevalidate (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot restore " + "validation failed"); + goto out; + } + break; + case GF_SNAP_OPTION_TYPE_DELETE: + ret = glusterd_snapshot_remove_prevalidate (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot remove " + "validation failed"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_STATUS: + ret = glusterd_snapshot_status_prevalidate (dict, op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot status " + "validation failed"); + goto out; + } + break; + + default: + gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); + goto out; + } + + ret = 0; +out: + return ret; +} + +int +glusterd_snapshot_postvalidate (dict_t *dict, int32_t op_ret, char **op_errstr, + dict_t *rsp_dict) +{ + int snap_command = 0; + xlator_t *this = NULL; + int ret = -1; + + this = THIS; + + GF_ASSERT (this); + GF_ASSERT (dict); + GF_ASSERT (rsp_dict); + + ret = dict_get_int32 (dict, "type", &snap_command); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case GF_SNAP_OPTION_TYPE_CREATE: + ret = glusterd_snapshot_create_postvalidate (dict, op_ret, + op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot create " + "post-validation failed"); + goto out; + } + break; + + case GF_SNAP_OPTION_TYPE_DELETE: + case GF_SNAP_OPTION_TYPE_RESTORE: + ret = glusterd_snapshot_update_snaps_post_validate (dict, + op_errstr, + rsp_dict); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "update missed snaps list"); + goto out; + } + break; + + default: + gf_log (this->name, GF_LOG_WARNING, "invalid snap command"); + goto out; + } + + ret = 0; +out: + return ret; +} + +int +glusterd_handle_snapshot_fn (rpcsvc_request_t *req) +{ + int32_t ret = 0; + dict_t *dict = NULL; + gf_cli_req cli_req = {{0},}; + glusterd_op_t cli_op = GD_OP_SNAP; + int type = 0; + glusterd_conf_t *conf = NULL; + char *host_uuid = NULL; + char err_str[2048] = {0,}; + xlator_t *this = NULL; + char *volname = NULL; + + GF_ASSERT (req); + + this = THIS; + GF_ASSERT (this); + conf = this->private; + GF_ASSERT (conf); + + ret = xdr_to_generic (req->msg[0], &cli_req, + (xdrproc_t)xdr_gf_cli_req); + if (ret < 0) { + req->rpc_err = GARBAGE_ARGS; + goto out; + } + + if (cli_req.dict.dict_len > 0) { + dict = dict_new (); + if (!dict) + goto out; + + ret = dict_unserialize (cli_req.dict.dict_val, + cli_req.dict.dict_len, + &dict); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "unserialize req-buffer to dictionary"); + snprintf (err_str, sizeof (err_str), "Unable to decode " + "the command"); + goto out; + } + + dict->extra_stdfree = cli_req.dict.dict_val; + + host_uuid = gf_strdup (uuid_utoa(MY_UUID)); + if (host_uuid == NULL) { + snprintf (err_str, sizeof (err_str), "Failed to get " + "the uuid of local glusterd"); + ret = -1; + goto out; + } + ret = dict_set_dynstr (dict, "host-uuid", host_uuid); + if (ret) { + GF_FREE (host_uuid); + goto out; + } + + + } else { + gf_log (this->name, GF_LOG_ERROR, "request dict length is %d", + cli_req.dict.dict_len); + goto out; + } + + ret = dict_get_int32 (dict, "type", &type); + if (ret < 0) { + snprintf (err_str, sizeof (err_str), "Command type not found"); + gf_log (this->name, GF_LOG_ERROR, "%s", err_str); + goto out; + } + + switch (type) { + case GF_SNAP_OPTION_TYPE_CREATE: + ret = glusterd_handle_snapshot_create (req, cli_op, dict, + err_str, sizeof (err_str)); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot create " + "failed: %s", err_str); + } + break; + case GF_SNAP_OPTION_TYPE_RESTORE: + ret = glusterd_handle_snapshot_restore (req, cli_op, dict, + err_str, sizeof (err_str)); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot restore " + "failed: %s", err_str); + } + + break; + case GF_SNAP_OPTION_TYPE_INFO: + ret = glusterd_handle_snapshot_info (req, cli_op, dict, + err_str, sizeof (err_str)); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot info " + "failed"); + } + break; + case GF_SNAP_OPTION_TYPE_LIST: + ret = glusterd_handle_snapshot_list (req, cli_op, dict, + err_str, sizeof (err_str)); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot list " + "failed"); + } + break; + case GF_SNAP_OPTION_TYPE_CONFIG: + /* TODO : Type of lock to be taken when we are setting + * limits system wide + */ + ret = dict_get_str (dict, "volname", &volname); + if (!volname) { + ret = dict_set_int32 (dict, "hold_vol_locks", + _gf_false); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Unable to set hold_vol_locks value " + "as _gf_false"); + goto out; + } + + } + ret = glusterd_mgmt_v3_initiate_all_phases (req, cli_op, dict); + break; + case GF_SNAP_OPTION_TYPE_DELETE: + ret = glusterd_handle_snapshot_remove (req, cli_op, dict, + err_str, + sizeof (err_str)); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot delete " + "failed: %s", err_str); + } + break; + case GF_SNAP_OPTION_TYPE_START: + case GF_SNAP_OPTION_TYPE_STOP: + case GF_SNAP_OPTION_TYPE_STATUS: + ret = glusterd_handle_snapshot_status (req, cli_op, dict, + err_str, + sizeof (err_str)); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Snapshot status " + "failed: %s", err_str); + } + break; + default: + gf_log (this->name, GF_LOG_ERROR, "Unkown snapshot request " + "type (%d)", type); + ret = -1; /* Failure */ + } + +out: + if (ret) { + if (err_str[0] == '\0') + snprintf (err_str, sizeof (err_str), + "Operation failed"); + ret = glusterd_op_send_cli_response (cli_op, ret, 0, req, + dict, err_str); + } + + return ret; +} + +int +glusterd_handle_snapshot (rpcsvc_request_t *req) +{ + return glusterd_big_locked_handler (req, glusterd_handle_snapshot_fn); +} + +static inline void +glusterd_free_snap_op (glusterd_snap_op_t *snap_op) +{ + if (snap_op) { + if (snap_op->brick_path) + GF_FREE (snap_op->brick_path); + + GF_FREE (snap_op); + } +} + +/* Look for duplicates and accordingly update the list */ +int32_t +glusterd_update_missed_snap_entry (glusterd_missed_snap_info *missed_snapinfo, + glusterd_snap_op_t *missed_snap_op) +{ + int32_t ret = -1; + glusterd_snap_op_t *snap_opinfo = NULL; + gf_boolean_t match = _gf_false; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT(missed_snapinfo); + GF_ASSERT(missed_snap_op); + + list_for_each_entry (snap_opinfo, &missed_snapinfo->snap_ops, + snap_ops_list) { + if ((!strcmp (snap_opinfo->brick_path, + missed_snap_op->brick_path)) && + (snap_opinfo->op == missed_snap_op->op)) { + /* If two entries have conflicting status + * GD_MISSED_SNAP_DONE takes precedence + */ + if ((snap_opinfo->status == GD_MISSED_SNAP_PENDING) && + (missed_snap_op->status == GD_MISSED_SNAP_DONE)) { + snap_opinfo->status = GD_MISSED_SNAP_DONE; + gf_log (this->name, GF_LOG_INFO, + "Updating missed snap status " + "for %s:%d:%s:%d as DONE", + missed_snapinfo->node_snap_info, + snap_opinfo->brick_num, + snap_opinfo->brick_path, + snap_opinfo->op); + ret = 0; + glusterd_free_snap_op (missed_snap_op); + goto out; + } + match = _gf_true; + break; + } else if ((snap_opinfo->brick_num == + missed_snap_op->brick_num) && + (snap_opinfo->op == GF_SNAP_OPTION_TYPE_CREATE) && + (missed_snap_op->op == + GF_SNAP_OPTION_TYPE_DELETE)) { + /* Optimizing create and delete entries for the same + * brick and same node + */ + gf_log (this->name, GF_LOG_INFO, + "Updating missed snap status " + "for %s:%d:%s:%d as DONE", + missed_snapinfo->node_snap_info, + snap_opinfo->brick_num, + snap_opinfo->brick_path, + snap_opinfo->op); + snap_opinfo->status = GD_MISSED_SNAP_DONE; + ret = 0; + glusterd_free_snap_op (missed_snap_op); + goto out; + } + } + + if (match == _gf_true) { + gf_log (this->name, GF_LOG_INFO, + "Duplicate entry. Not updating"); + glusterd_free_snap_op (missed_snap_op); + } else { + list_add_tail (&missed_snap_op->snap_ops_list, + &missed_snapinfo->snap_ops); + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* Add new missed snap entry to the missed_snaps list. */ +int32_t +glusterd_store_missed_snaps_list (char *missed_info, int32_t brick_num, + char *brick_path, int32_t snap_op, + int32_t snap_status) +{ + int32_t ret = -1; + glusterd_missed_snap_info *missed_snapinfo = NULL; + glusterd_snap_op_t *missed_snap_op = NULL; + glusterd_conf_t *priv = NULL; + gf_boolean_t match = _gf_false; + gf_boolean_t free_missed_snap_info = _gf_false; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT(missed_info); + GF_ASSERT(brick_path); + + priv = this->private; + GF_ASSERT (priv); + + /* Create the snap_op object consisting of the * + * snap id and the op */ + ret = glusterd_missed_snap_op_new (&missed_snap_op); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create new missed snap object."); + ret = -1; + goto out; + } + + missed_snap_op->brick_path = gf_strdup(brick_path); + if (!missed_snap_op->brick_path) { + ret = -1; + goto out; + } + missed_snap_op->brick_num = brick_num; + missed_snap_op->op = snap_op; + missed_snap_op->status = snap_status; + + /* Look for other entries for the same node and same snap */ + list_for_each_entry (missed_snapinfo, &priv->missed_snaps_list, + missed_snaps) { + if (!strcmp (missed_snapinfo->node_snap_info, + missed_info)) { + /* Found missed snapshot info for * + * the same node and same snap */ + match = _gf_true; + break; + } + } + + if (match == _gf_false) { + /* First snap op missed for the brick */ + ret = glusterd_missed_snapinfo_new (&missed_snapinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create missed snapinfo"); + goto out; + } + free_missed_snap_info = _gf_true; + missed_snapinfo->node_snap_info = gf_strdup(missed_info); + if (!missed_snapinfo->node_snap_info) { + ret = -1; + goto out; + } + + list_add_tail (&missed_snap_op->snap_ops_list, + &missed_snapinfo->snap_ops); + list_add_tail (&missed_snapinfo->missed_snaps, + &priv->missed_snaps_list); + + ret = 0; + goto out; + } else { + ret = glusterd_update_missed_snap_entry (missed_snapinfo, + missed_snap_op); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to update existing missed snap entry."); + goto out; + } + } + +out: + if (ret) { + glusterd_free_snap_op (missed_snap_op); + + if (missed_snapinfo && + (free_missed_snap_info == _gf_true)) { + if (missed_snapinfo->node_snap_info) + GF_FREE (missed_snapinfo->node_snap_info); + + GF_FREE (missed_snapinfo); + } + } + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* Add missing snap entries to the in-memory conf->missed_snap_list */ +int32_t +glusterd_add_missed_snaps_to_list (dict_t *dict, int32_t missed_snap_count) +{ + char *buf = NULL; + char *tmp = NULL; + char *save_ptr = NULL; + char *nodeid = NULL; + char *snap_uuid = NULL; + char *brick_path = NULL; + char missed_info[PATH_MAX] = ""; + char name_buf[PATH_MAX] = ""; + int32_t i = -1; + int32_t ret = -1; + int32_t brick_num = -1; + int32_t snap_op = -1; + int32_t snap_status = -1; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT(dict); + + priv = this->private; + GF_ASSERT (priv); + + /* We can update the missed_snaps_list without acquiring * + * any additional locks as big lock will be held. */ + for (i = 0; i < missed_snap_count; i++) { + snprintf (name_buf, sizeof(name_buf), "missed_snaps_%d", + i); + ret = dict_get_str (dict, name_buf, &buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch %s", name_buf); + goto out; + } + + gf_log (this->name, GF_LOG_DEBUG, "missed_snap_entry = %s", + buf); + + /* Need to make a duplicate string coz the same dictionary * + * is resent to the non-originator nodes */ + tmp = gf_strdup (buf); + if (!tmp) { + ret = -1; + goto out; + } + + /* Fetch the node-id, snap-id, brick_num, + * brick_path, snap_op and snap status + */ + nodeid = strtok_r (tmp, ":", &save_ptr); + snap_uuid = strtok_r (NULL, "=", &save_ptr); + brick_num = atoi(strtok_r (NULL, ":", &save_ptr)); + brick_path = strtok_r (NULL, ":", &save_ptr); + snap_op = atoi(strtok_r (NULL, ":", &save_ptr)); + snap_status = atoi(strtok_r (NULL, ":", &save_ptr)); + + if (!nodeid || !snap_uuid || !brick_path || + brick_num < 1 || snap_op < 1 || + snap_status < 1) { + gf_log (this->name, GF_LOG_ERROR, + "Invalid missed_snap_entry"); + ret = -1; + goto out; + } + + snprintf (missed_info, sizeof(missed_info), "%s:%s", + nodeid, snap_uuid); + + ret = glusterd_store_missed_snaps_list (missed_info, + brick_num, + brick_path, + snap_op, + snap_status); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to store missed snaps_list"); + goto out; + } + + GF_FREE (tmp); + tmp = NULL; + } + +out: + if (tmp) + GF_FREE (tmp); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} diff --git a/xlators/mgmt/glusterd/src/glusterd-store.c b/xlators/mgmt/glusterd/src/glusterd-store.c index d5bfeda3f..e28a30c5a 100644 --- a/xlators/mgmt/glusterd/src/glusterd-store.c +++ b/xlators/mgmt/glusterd/src/glusterd-store.c @@ -46,7 +46,7 @@ #include void -glusterd_replace_slash_with_hipen (char *str) +glusterd_replace_slash_with_hyphen (char *str) { char *ptr = NULL; @@ -85,7 +85,7 @@ glusterd_store_key_vol_brick_set (glusterd_brickinfo_t *brickinfo, GF_ASSERT (len >= PATH_MAX); snprintf (key_vol_brick, len, "%s", brickinfo->path); - glusterd_replace_slash_with_hipen (key_vol_brick); + glusterd_replace_slash_with_hyphen (key_vol_brick); } static void @@ -246,6 +246,20 @@ glusterd_store_brickinfo_write (int fd, glusterd_brickinfo_t *brickinfo) if (ret) goto out; + if (strlen(brickinfo->device_path) > 0) { + snprintf (value, sizeof(value), "%s", brickinfo->device_path); + ret = gf_store_save_value (fd, + GLUSTERD_STORE_KEY_BRICK_DEVICE_PATH, value); + if (ret) + goto out; + } + + snprintf (value, sizeof(value), "%d", brickinfo->snap_status); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_BRICK_SNAP_STATUS, + value); + if (ret) + goto out; + if (!brickinfo->vg[0]) goto out; @@ -511,14 +525,13 @@ int _storeopts (dict_t *this, char *key, data_t *value, void *data) int32_t glusterd_volume_exclude_options_write (int fd, glusterd_volinfo_t *volinfo) { - char *str = NULL; + char *str = NULL; + char buf[PATH_MAX] = {0,}; + int32_t ret = -1; GF_ASSERT (fd > 0); GF_ASSERT (volinfo); - char buf[PATH_MAX] = {0,}; - int32_t ret = -1; - snprintf (buf, sizeof (buf), "%d", volinfo->type); ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_VOL_TYPE, buf); if (ret) @@ -560,6 +573,14 @@ glusterd_volume_exclude_options_write (int fd, glusterd_volinfo_t *volinfo) if (ret) goto out; + snprintf (buf, sizeof (buf), "%s", volinfo->parent_volname); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_PARENT_VOLNAME, buf); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to store " + GLUSTERD_STORE_KEY_PARENT_VOLNAME); + goto out; + } + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_VOL_ID, uuid_utoa (volinfo->volume_id)); if (ret) @@ -599,6 +620,23 @@ glusterd_volume_exclude_options_write (int fd, glusterd_volinfo_t *volinfo) goto out; } + snprintf (buf, sizeof (buf), "%d", volinfo->is_volume_restored); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_VOL_IS_RESTORED, buf); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, + "Unable to write is_volume_restored"); + goto out; + } + + snprintf (buf, sizeof (buf), "%"PRIu64, volinfo->snap_max_hard_limit); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT, + buf); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, + "Unable to write snap-max-hard-limit"); + goto out; + } + out: if (ret) gf_log (THIS->name, GF_LOG_ERROR, "Unable to write volume " @@ -616,8 +654,7 @@ glusterd_store_voldirpath_set (glusterd_volinfo_t *volinfo, char *voldirpath, priv = THIS->private; GF_ASSERT (priv); - snprintf (voldirpath, len, "%s/%s/%s", priv->workdir, - GLUSTERD_VOLUME_DIR_PREFIX, volinfo->volname); + GLUSTERD_GET_VOLUME_DIR (voldirpath, volinfo, priv); } static int32_t @@ -631,10 +668,32 @@ glusterd_store_create_volume_dir (glusterd_volinfo_t *volinfo) glusterd_store_voldirpath_set (volinfo, voldirpath, sizeof (voldirpath)); ret = gf_store_mkdir (voldirpath); + gf_log (THIS->name, GF_LOG_DEBUG, "Returning with %d", ret); return ret; } +static int32_t +glusterd_store_create_snap_dir (glusterd_snap_t *snap) +{ + int32_t ret = -1; + char snapdirpath[PATH_MAX] = {0,}; + glusterd_conf_t *priv = NULL; + + priv = THIS->private; + GF_ASSERT (priv); + GF_ASSERT (snap); + + GLUSTERD_GET_SNAP_DIR (snapdirpath, snap, priv); + + ret = mkdir_p (snapdirpath, 0755, _gf_true); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to create snaps dir " + "%s", snapdirpath); + } + return ret; +} + int32_t glusterd_store_volinfo_write (int fd, glusterd_volinfo_t *volinfo) { @@ -659,6 +718,49 @@ out: return ret; } +int32_t +glusterd_store_snapinfo_write (glusterd_snap_t *snap) +{ + int32_t ret = -1; + int fd = 0; + char buf[PATH_MAX] = ""; + + GF_ASSERT (snap); + + fd = gf_store_mkstemp (snap->shandle); + if (fd <= 0) + goto out; + + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_SNAP_ID, + uuid_utoa (snap->snap_id)); + if (ret) + goto out; + + snprintf (buf, sizeof (buf), "%d", snap->snap_status); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_SNAP_STATUS, buf); + if (ret) + goto out; + + snprintf (buf, sizeof (buf), "%d", snap->snap_restored); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_SNAP_RESTORED, buf); + if (ret) + goto out; + + if (snap->description) { + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_SNAP_DESC, + snap->description); + if (ret) + goto out; + } + + snprintf (buf, sizeof (buf), "%ld", snap->time_stamp); + ret = gf_store_save_value (fd, GLUSTERD_STORE_KEY_SNAP_TIMESTAMP, buf); + +out: + gf_log (THIS->name, GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + static void glusterd_store_rbstatepath_set (glusterd_volinfo_t *volinfo, char *rbstatepath, size_t len) @@ -718,6 +820,36 @@ glusterd_store_quota_conf_path_set (glusterd_volinfo_t *volinfo, GLUSTERD_VOLUME_QUOTA_CONFIG); } +static void +glusterd_store_missed_snaps_list_path_set (char *missed_snaps_list, + size_t len) +{ + glusterd_conf_t *priv = NULL; + + priv = THIS->private; + GF_ASSERT (priv); + GF_ASSERT (missed_snaps_list); + GF_ASSERT (len <= PATH_MAX); + + snprintf (missed_snaps_list, len, "%s/snaps/" + GLUSTERD_MISSED_SNAPS_LIST_FILE, priv->workdir); +} + +static void +glusterd_store_snapfpath_set (glusterd_snap_t *snap, char *snap_fpath, + size_t len) +{ + glusterd_conf_t *priv = NULL; + priv = THIS->private; + GF_ASSERT (priv); + GF_ASSERT (snap); + GF_ASSERT (snap_fpath); + GF_ASSERT (len <= PATH_MAX); + + snprintf (snap_fpath, len, "%s/snaps/%s/%s", priv->workdir, + snap->snapname, GLUSTERD_SNAP_INFO_FILE); +} + int32_t glusterd_store_create_rbstate_shandle_on_absence (glusterd_volinfo_t *volinfo) { @@ -778,6 +910,43 @@ glusterd_store_create_quota_conf_sh_on_absence (glusterd_volinfo_t *volinfo) return ret; } + +static int32_t +glusterd_store_create_missed_snaps_list_shandle_on_absence () +{ + char missed_snaps_list[PATH_MAX] = ""; + int32_t ret = -1; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + priv = this->private; + GF_ASSERT (priv); + + glusterd_store_missed_snaps_list_path_set (missed_snaps_list, + sizeof(missed_snaps_list)); + + ret = gf_store_handle_create_on_absence + (&priv->missed_snaps_list_shandle, + missed_snaps_list); + return ret; +} + +int32_t +glusterd_store_create_snap_shandle_on_absence (glusterd_snap_t *snap) +{ + char snapfpath[PATH_MAX] = {0}; + int32_t ret = 0; + + GF_ASSERT (snap); + + glusterd_store_snapfpath_set (snap, snapfpath, sizeof (snapfpath)); + ret = gf_store_handle_create_on_absence (&snap->shandle, snapfpath); + return ret; +} + int32_t glusterd_store_brickinfos (glusterd_volinfo_t *volinfo, int vol_fd) { @@ -789,7 +958,7 @@ glusterd_store_brickinfos (glusterd_volinfo_t *volinfo, int vol_fd) list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { ret = glusterd_store_brickinfo (volinfo, brickinfo, - brick_count, vol_fd); + brick_count, vol_fd); if (ret) goto out; brick_count++; @@ -1093,6 +1262,60 @@ out: return ret; } +int32_t +glusterd_store_snap_atomic_update (glusterd_snap_t *snap) +{ + int ret = -1; + GF_ASSERT (snap); + + ret = gf_store_rename_tmppath (snap->shandle); + if (ret) + gf_log (THIS->name, GF_LOG_ERROR, "Couldn't rename " + "temporary file(s): Reason %s", strerror (errno)); + + return ret; +} + +int32_t +glusterd_store_snap (glusterd_snap_t *snap) +{ + int32_t ret = -1; + + GF_ASSERT (snap); + + ret = glusterd_store_create_snap_dir (snap); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to create snap dir"); + goto out; + } + + ret = glusterd_store_create_snap_shandle_on_absence (snap); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to create snap info " + "file"); + goto out; + } + + ret = glusterd_store_snapinfo_write (snap); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to write snap info"); + goto out; + } + + ret = glusterd_store_snap_atomic_update (snap); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR,"Failed to do automic update"); + goto out; + } + +out: + if (ret && snap->shandle) + gf_store_unlink_tmppath (snap->shandle); + + gf_log (THIS->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + int32_t glusterd_store_volinfo (glusterd_volinfo_t *volinfo, glusterd_volinfo_ver_ac_t ac) { @@ -1150,7 +1373,6 @@ out: return ret; } - int32_t glusterd_store_delete_volume (glusterd_volinfo_t *volinfo) { @@ -1177,8 +1399,8 @@ glusterd_store_delete_volume (glusterd_volinfo_t *volinfo) GLUSTERD_GET_VOLUME_DIR (pathname, volinfo, priv); snprintf (delete_path, sizeof (delete_path), - "%s/"GLUSTERD_TRASH"/%s.deleted", priv->workdir, - uuid_utoa (volinfo->volume_id)); + "%s/"GLUSTERD_TRASH"/%s.deleted", priv->workdir, + uuid_utoa (volinfo->volume_id)); snprintf (trashdir, sizeof (trashdir), "%s/"GLUSTERD_TRASH, priv->workdir); @@ -1270,6 +1492,116 @@ out: return ret; } +/*TODO: cleanup the duplicate code and implement a generic function for + * deleting snap/volume depending on the parameter flag */ +int32_t +glusterd_store_delete_snap (glusterd_snap_t *snap) +{ + char pathname[PATH_MAX] = {0,}; + int32_t ret = 0; + glusterd_conf_t *priv = NULL; + DIR *dir = NULL; + struct dirent *entry = NULL; + char path[PATH_MAX] = {0,}; + char delete_path[PATH_MAX] = {0,}; + char trashdir[PATH_MAX] = {0,}; + struct stat st = {0, }; + xlator_t *this = NULL; + gf_boolean_t rename_fail = _gf_false; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + + GF_ASSERT (snap); + GLUSTERD_GET_SNAP_DIR (pathname, snap, priv); + + snprintf (delete_path, sizeof (delete_path), + "%s/"GLUSTERD_TRASH"/snap-%s.deleted", priv->workdir, + uuid_utoa (snap->snap_id)); + + snprintf (trashdir, sizeof (trashdir), "%s/"GLUSTERD_TRASH, + priv->workdir); + + ret = mkdir (trashdir, 0777); + if (ret && errno != EEXIST) { + gf_log (this->name, GF_LOG_ERROR, "Failed to create trash " + "directory, reason : %s", strerror (errno)); + ret = -1; + goto out; + } + + ret = rename (pathname, delete_path); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to rename snap " + "directory %s to %s", pathname, delete_path); + rename_fail = _gf_true; + goto out; + } + + dir = opendir (delete_path); + if (!dir) { + gf_log (this->name, GF_LOG_DEBUG, "Failed to open directory %s." + " Reason : %s", delete_path, strerror (errno)); + ret = 0; + goto out; + } + + glusterd_for_each_entry (entry, dir); + while (entry) { + snprintf (path, PATH_MAX, "%s/%s", delete_path, entry->d_name); + ret = stat (path, &st); + if (ret == -1) { + gf_log (this->name, GF_LOG_DEBUG, "Failed to stat " + "entry %s : %s", path, strerror (errno)); + goto stat_failed; + } + + if (S_ISDIR (st.st_mode)) + ret = rmdir (path); + else + ret = unlink (path); + + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, " Failed to remove " + "%s. Reason : %s", path, strerror (errno)); + } + + gf_log (this->name, GF_LOG_DEBUG, "%s %s", + ret ? "Failed to remove":"Removed", + entry->d_name); +stat_failed: + memset (path, 0, sizeof(path)); + glusterd_for_each_entry (entry, dir); + } + + ret = closedir (dir); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, "Failed to close dir %s. " + "Reason : %s",delete_path, strerror (errno)); + } + + ret = rmdir (delete_path); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, "Failed to rmdir: %s,err: %s", + delete_path, strerror (errno)); + } + ret = rmdir (trashdir); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, "Failed to rmdir: %s, Reason:" + " %s", trashdir, strerror (errno)); + } + +out: + if (snap->shandle) { + gf_store_handle_destroy (snap->shandle); + snap->shandle = NULL; + } + ret = (rename_fail == _gf_true) ? -1: 0; + + gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} int glusterd_store_global_info (xlator_t *this) @@ -1280,6 +1612,7 @@ glusterd_store_global_info (xlator_t *this) char path[PATH_MAX] = {0,}; gf_store_handle_t *handle = NULL; char *uuid_str = NULL; + char buf[256] = {0, }; conf = this->private; @@ -1332,6 +1665,24 @@ glusterd_store_global_info (xlator_t *this) goto out; } + snprintf (buf, sizeof (buf), "%"PRIu64, conf->snap_max_hard_limit); + ret = gf_store_save_value (handle->fd, + GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT, buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Storing snap-max-hard-limit failed ret = %d", ret); + goto out; + } + + snprintf (buf, sizeof (buf), "%"PRIu64, conf->snap_max_soft_limit); + ret = gf_store_save_value (handle->fd, + GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT, buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Storing snap-max-soft-limit failed ret = %d", ret); + goto out; + } + ret = gf_store_rename_tmppath (handle); out: if (handle) { @@ -1405,14 +1756,93 @@ out: return ret; } -static int -glusterd_restore_op_version (xlator_t *this) +int +glusterd_retrieve_sys_snap_max_limit (xlator_t *this, uint64_t *limit, + char *key) { - glusterd_conf_t *conf = NULL; - int ret = 0; - int op_version = 0; - - conf = this->private; + char *limit_str = NULL; + glusterd_conf_t *priv = NULL; + int ret = -1; + uint64_t tmp_limit = 0; + char *tmp = NULL; + char path[PATH_MAX] = {0,}; + gf_store_handle_t *handle = NULL; + + GF_ASSERT (this); + priv = this->private; + + GF_ASSERT (priv); + GF_ASSERT (limit); + GF_ASSERT (key); + + if (!priv->handle) { + snprintf (path, PATH_MAX, "%s/%s", priv->workdir, + GLUSTERD_INFO_FILE); + ret = gf_store_handle_retrieve (path, &handle); + + if (ret) { + gf_log ("", GF_LOG_DEBUG, "Unable to get store " + "handle!"); + goto out; + } + + priv->handle = handle; + } + + ret = gf_store_retrieve_value (priv->handle, + key, + &limit_str); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, + "No previous %s present", key); + goto out; + } + + tmp_limit = strtoul (limit_str, &tmp, 10); + if ((tmp_limit <= 0) || (tmp && strlen (tmp) > 1)) { + gf_log (this->name, GF_LOG_WARNING, "invalid version number"); + goto out; + } + + *limit = tmp_limit; + + ret = 0; +out: + if (limit_str) + GF_FREE (limit_str); + + return ret; +} +static int +glusterd_restore_op_version (xlator_t *this) +{ + glusterd_conf_t *conf = NULL; + int ret = 0; + int op_version = 0; + + conf = this->private; + + ret = glusterd_retrieve_sys_snap_max_limit (this, + &conf->snap_max_hard_limit, + GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "Unable to retrieve system snap-max-hard-limit, " + "setting it to default value(%d)", + GLUSTERD_SNAPS_MAX_HARD_LIMIT); + conf->snap_max_hard_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; + } + + ret = glusterd_retrieve_sys_snap_max_limit (this, + &conf->snap_max_soft_limit, + GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "Unable to retrieve system snap-max-soft-limit, " + "setting it to default value(%d)", + GLUSTERD_SNAPS_DEF_SOFT_LIMIT_PERCENT); + conf->snap_max_soft_limit = GLUSTERD_SNAPS_DEF_SOFT_LIMIT_PERCENT; + } ret = glusterd_retrieve_op_version (this, &op_version); if (!ret) { @@ -1501,7 +1931,6 @@ out: int32_t glusterd_store_retrieve_bricks (glusterd_volinfo_t *volinfo) { - int32_t ret = 0; glusterd_brickinfo_t *brickinfo = NULL; gf_store_iter_t *iter = NULL; @@ -1523,7 +1952,7 @@ glusterd_store_retrieve_bricks (glusterd_volinfo_t *volinfo) priv = THIS->private; - GLUSTERD_GET_BRICK_DIR (brickdir, volinfo, priv) + GLUSTERD_GET_BRICK_DIR (brickdir, volinfo, priv); ret = gf_store_iter_new (volinfo->shandle, &tmpiter); @@ -1606,6 +2035,13 @@ glusterd_store_retrieve_bricks (glusterd_volinfo_t *volinfo) } else if (!strncmp (key, GLUSTERD_STORE_KEY_BRICK_DECOMMISSIONED, strlen (GLUSTERD_STORE_KEY_BRICK_DECOMMISSIONED))) { gf_string2int (value, &brickinfo->decommissioned); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_BRICK_DEVICE_PATH, + strlen (GLUSTERD_STORE_KEY_BRICK_DEVICE_PATH))) { + strncpy (brickinfo->device_path, value, + sizeof (brickinfo->device_path)); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_BRICK_SNAP_STATUS, + strlen (GLUSTERD_STORE_KEY_BRICK_SNAP_STATUS))) { + gf_string2int (value, &brickinfo->snap_status); } else if (!strncmp (key, GLUSTERD_STORE_KEY_BRICK_VGNAME, strlen (GLUSTERD_STORE_KEY_BRICK_VGNAME))) { @@ -1659,10 +2095,9 @@ out: int32_t -glusterd_store_retrieve_rbstate (char *volname) +glusterd_store_retrieve_rbstate (glusterd_volinfo_t *volinfo) { int32_t ret = -1; - glusterd_volinfo_t *volinfo = NULL; gf_store_iter_t *iter = NULL; char *key = NULL; char *value = NULL; @@ -1670,15 +2105,13 @@ glusterd_store_retrieve_rbstate (char *volname) glusterd_conf_t *priv = NULL; char path[PATH_MAX] = {0,}; gf_store_op_errno_t op_errno = GD_STORE_SUCCESS; + xlator_t *this = NULL; - priv = THIS->private; - - ret = glusterd_volinfo_find (volname, &volinfo); - if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "Couldn't get" - "volinfo for %s.", volname); - goto out; - } + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (volinfo); GLUSTERD_GET_VOLUME_DIR(volpath, volinfo, priv); snprintf (path, sizeof (path), "%s/%s", volpath, @@ -1755,16 +2188,15 @@ glusterd_store_retrieve_rbstate (char *volname) goto out; out: - gf_log ("", GF_LOG_DEBUG, "Returning with %d", ret); + gf_log (this->name, GF_LOG_TRACE, "Returning with %d", ret); return ret; } int32_t -glusterd_store_retrieve_node_state (char *volname) +glusterd_store_retrieve_node_state (glusterd_volinfo_t *volinfo) { int32_t ret = -1; - glusterd_volinfo_t *volinfo = NULL; gf_store_iter_t *iter = NULL; char *key = NULL; char *value = NULL; @@ -1779,13 +2211,8 @@ glusterd_store_retrieve_node_state (char *volname) this = THIS; GF_ASSERT (this); priv = this->private; - - ret = glusterd_volinfo_find (volname, &volinfo); - if (ret) { - gf_log (THIS->name, GF_LOG_ERROR, "Couldn't get" - "volinfo for %s.", volname); - goto out; - } + GF_ASSERT (priv); + GF_ASSERT (volinfo); GLUSTERD_GET_VOLUME_DIR(volpath, volinfo, priv); snprintf (path, sizeof (path), "%s/%s", volpath, @@ -1866,50 +2293,59 @@ out: dict_unref (volinfo->rebal.dict); if (tmp_dict) dict_unref (tmp_dict); - gf_log ("", GF_LOG_DEBUG, "Returning with %d", ret); + + gf_log (this->name, GF_LOG_TRACE, "Returning with %d", ret); return ret; } -int32_t -glusterd_store_retrieve_volume (char *volname) -{ - int32_t ret = -1; - glusterd_volinfo_t *volinfo = NULL; - gf_store_iter_t *iter = NULL; - char *key = NULL; - char *value = NULL; - char volpath[PATH_MAX] = {0,}; - glusterd_conf_t *priv = NULL; - char path[PATH_MAX] = {0,}; - int exists = 0; - gf_store_op_errno_t op_errno = GD_STORE_SUCCESS; - ret = glusterd_volinfo_new (&volinfo); - if (ret) - goto out; +int +glusterd_store_update_volinfo (glusterd_volinfo_t *volinfo) +{ + int ret = -1; + int exists = 0; + char *key = NULL; + char *value = NULL; + char volpath[PATH_MAX] = {0,}; + char path[PATH_MAX] = {0,}; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + gf_store_iter_t *iter = NULL; + gf_store_op_errno_t op_errno = GD_STORE_SUCCESS; - strncpy (volinfo->volname, volname, GLUSTERD_MAX_VOLUME_NAME); + this = THIS; + GF_ASSERT (this); + conf = THIS->private; + GF_ASSERT (volinfo); - priv = THIS->private; + GLUSTERD_GET_VOLUME_DIR(volpath, volinfo, conf); - GLUSTERD_GET_VOLUME_DIR(volpath, volinfo, priv); snprintf (path, sizeof (path), "%s/%s", volpath, GLUSTERD_VOLUME_INFO_FILE); ret = gf_store_handle_retrieve (path, &volinfo->shandle); - if (ret) + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "volinfo handle is NULL"); goto out; + } ret = gf_store_iter_new (volinfo->shandle, &iter); - if (ret) + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get new store " + "iter"); goto out; + } ret = gf_store_iter_get_next (iter, &key, &value, &op_errno); - if (ret) + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get next store " + "iter"); goto out; + } while (!ret) { + gf_log ("", GF_LOG_DEBUG, "key = %s value = %s", key, value); if (!strncmp (key, GLUSTERD_STORE_KEY_VOL_TYPE, strlen (GLUSTERD_STORE_KEY_VOL_TYPE))) { volinfo->type = atoi (value); @@ -1978,6 +2414,15 @@ glusterd_store_retrieve_volume (char *volname) } else if (!strncmp (key, GLUSTERD_STORE_KEY_VOL_CAPS, strlen (GLUSTERD_STORE_KEY_VOL_CAPS))) { volinfo->caps = atoi (value); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT, + strlen (GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT))) { + volinfo->snap_max_hard_limit = (uint64_t) atoll (value); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_VOL_IS_RESTORED, + strlen (GLUSTERD_STORE_KEY_VOL_IS_RESTORED))) { + volinfo->is_volume_restored = atoi (value); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_PARENT_VOLNAME, + strlen (GLUSTERD_STORE_KEY_PARENT_VOLNAME))) { + strncpy (volinfo->parent_volname, value, sizeof(volinfo->parent_volname) - 1); } else { if (is_key_glusterd_hooks_friendly (key)) { @@ -2076,10 +2521,49 @@ glusterd_store_retrieve_volume (char *volname) goto out; ret = gf_store_iter_destroy (iter); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to destroy store " + "iter"); + goto out; + } + + ret = 0; +out: + return ret; +} + +glusterd_volinfo_t* +glusterd_store_retrieve_volume (char *volname, glusterd_snap_t *snap) +{ + int32_t ret = -1; + glusterd_volinfo_t *volinfo = NULL; + glusterd_volinfo_t *origin_volinfo = NULL; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (volname); + + ret = glusterd_volinfo_new (&volinfo); if (ret) goto out; + strncpy (volinfo->volname, volname, GLUSTERD_MAX_VOLUME_NAME); + volinfo->snapshot = snap; + if (snap) + volinfo->is_snap_volume = _gf_true; + + ret = glusterd_store_update_volinfo (volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to update volinfo " + "for %s volume", volname); + goto out; + } + ret = glusterd_store_retrieve_bricks (volinfo); if (ret) goto out; @@ -2105,13 +2589,30 @@ glusterd_store_retrieve_volume (char *volname) goto out; - list_add_order (&volinfo->vol_list, &priv->volumes, - glusterd_compare_volume_name); + if (!snap) { + list_add_order (&volinfo->vol_list, &priv->volumes, + glusterd_compare_volume_name); + } else { + ret = glusterd_volinfo_find (volinfo->parent_volname, + &origin_volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Parent volinfo " + "not found for %s volume", volname); + goto out; + } + glusterd_list_add_snapvol (origin_volinfo, volinfo); + } out: - gf_log ("", GF_LOG_DEBUG, "Returning with %d", ret); + if (ret) { + if (volinfo) + glusterd_volinfo_delete (volinfo); + volinfo = NULL; + } - return ret; + gf_log (this->name, GF_LOG_TRACE, "Returning with %d", ret); + + return volinfo; } inline void @@ -2211,9 +2712,9 @@ out: } int32_t -glusterd_store_retrieve_volumes (xlator_t *this) +glusterd_store_retrieve_volumes (xlator_t *this, glusterd_snap_t *snap) { - int32_t ret = 0; + int32_t ret = -1; char path[PATH_MAX] = {0,}; glusterd_conf_t *priv = NULL; DIR *dir = NULL; @@ -2225,51 +2726,58 @@ glusterd_store_retrieve_volumes (xlator_t *this) GF_ASSERT (priv); - snprintf (path, PATH_MAX, "%s/%s", priv->workdir, - GLUSTERD_VOLUME_DIR_PREFIX); + if (snap) + snprintf (path, PATH_MAX, "%s/snaps/%s", priv->workdir, + snap->snapname); + else + snprintf (path, PATH_MAX, "%s/%s", priv->workdir, + GLUSTERD_VOLUME_DIR_PREFIX); dir = opendir (path); if (!dir) { gf_log ("", GF_LOG_ERROR, "Unable to open dir %s", path); - ret = -1; goto out; } glusterd_for_each_entry (entry, dir); while (entry) { - ret = glusterd_store_retrieve_volume (entry->d_name); - if (ret) { + if ( entry->d_type != DT_DIR ) + goto next; + + volinfo = glusterd_store_retrieve_volume (entry->d_name, snap); + if (!volinfo) { gf_log ("", GF_LOG_ERROR, "Unable to restore " "volume: %s", entry->d_name); + ret = -1; goto out; } - ret = glusterd_store_retrieve_rbstate (entry->d_name); + ret = glusterd_store_retrieve_rbstate (volinfo); if (ret) { /* Backward compatibility */ gf_log ("", GF_LOG_INFO, "Creating a new rbstate " "for volume: %s.", entry->d_name); - ret = glusterd_volinfo_find (entry->d_name, &volinfo); ret = glusterd_store_create_rbstate_shandle_on_absence (volinfo); ret = glusterd_store_perform_rbstate_store (volinfo); } - ret = glusterd_store_retrieve_node_state (entry->d_name); + ret = glusterd_store_retrieve_node_state (volinfo); if (ret) { /* Backward compatibility */ gf_log ("", GF_LOG_INFO, "Creating a new node_state " "for volume: %s.", entry->d_name); - ret = glusterd_volinfo_find (entry->d_name, &volinfo); - ret = glusterd_store_create_nodestate_sh_on_absence (volinfo); ret = glusterd_store_perform_node_state_store (volinfo); } +next: glusterd_for_each_entry (entry, dir); } + ret = 0; + out: if (dir) closedir (dir); @@ -2278,6 +2786,481 @@ out: return ret; } +int32_t +glusterd_resolve_snap_bricks (xlator_t *this, glusterd_snap_t *snap) +{ + int32_t ret = -1; + glusterd_volinfo_t *volinfo = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + + GF_ASSERT (this); + GF_VALIDATE_OR_GOTO (this->name, snap, out); + + list_for_each_entry (volinfo, &snap->volumes, vol_list) { + list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { + ret = glusterd_resolve_brick (brickinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "resolve brick failed in restore"); + goto out; + } + } + } + + ret = 0; + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning with %d", ret); + + return ret; +} + +int +glusterd_store_update_snap (glusterd_snap_t *snap) +{ + int ret = -1; + char *key = NULL; + char *value = NULL; + char snappath[PATH_MAX] = {0,}; + char path[PATH_MAX] = {0,}; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + gf_store_iter_t *iter = NULL; + gf_store_op_errno_t op_errno = GD_STORE_SUCCESS; + + this = THIS; + conf = this->private; + GF_ASSERT (snap); + + GLUSTERD_GET_SNAP_DIR (snappath, snap, conf); + + snprintf (path, sizeof (path), "%s/%s", snappath, + GLUSTERD_SNAP_INFO_FILE); + + ret = gf_store_handle_retrieve (path, &snap->shandle); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "snap handle is NULL"); + goto out; + } + + ret = gf_store_iter_new (snap->shandle, &iter); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get new store " + "iter"); + goto out; + } + + ret = gf_store_iter_get_next (iter, &key, &value, &op_errno); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get next store " + "iter"); + goto out; + } + + while (!ret) { + gf_log (this->name, GF_LOG_DEBUG, "key = %s value = %s", + key, value); + + if (!strncmp (key, GLUSTERD_STORE_KEY_SNAP_ID, + strlen (GLUSTERD_STORE_KEY_SNAP_ID))) { + ret = uuid_parse (value, snap->snap_id); + if (ret) + gf_log (this->name, GF_LOG_WARNING, + "Failed to parse uuid"); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_SNAP_RESTORED, + strlen (GLUSTERD_STORE_KEY_SNAP_RESTORED))) { + snap->snap_restored = atoi (value); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_SNAP_STATUS, + strlen (GLUSTERD_STORE_KEY_SNAP_STATUS))) { + snap->snap_status = atoi (value); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_SNAP_DESC, + strlen (GLUSTERD_STORE_KEY_SNAP_DESC))) { + snap->description = gf_strdup (value); + } else if (!strncmp (key, GLUSTERD_STORE_KEY_SNAP_TIMESTAMP, + strlen (GLUSTERD_STORE_KEY_SNAP_TIMESTAMP))) { + snap->time_stamp = atoi (value); + } + + GF_FREE (key); + GF_FREE (value); + key = NULL; + value = NULL; + + ret = gf_store_iter_get_next (iter, &key, &value, &op_errno); + } + + if (op_errno != GD_STORE_EOF) + goto out; + + ret = gf_store_iter_destroy (iter); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to destroy store " + "iter"); + } + +out: + return ret; +} + +int32_t +glusterd_store_retrieve_snap (char *snapname) +{ + int32_t ret = -1; + dict_t *dict = NULL; + glusterd_snap_t *snap = NULL; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (snapname); + + dict = dict_new(); + if (!dict) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create dict"); + ret = -1; + goto out; + } + + snap = glusterd_new_snap_object (); + if (!snap) { + gf_log (this->name, GF_LOG_ERROR, "Failed to create " + " snap object"); + goto out; + } + + strncpy (snap->snapname, snapname, strlen(snapname)); + ret = glusterd_store_update_snap (snap); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to update snapshot " + "for %s snap", snapname); + goto out; + } + + ret = glusterd_store_retrieve_volumes (this, snap); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to retrieve " + "snap volumes for snap %s", snapname); + goto out; + } + + /* Unlike bricks of normal volumes which are resolved at the end of + the glusterd restore, the bricks belonging to the snap volumes of + each snap should be resolved as part of snapshot restore itself. + Because if the snapshot has to be removed, then resolving bricks + helps glusterd in understanding what all bricks have its own uuid + and killing those bricks. + */ + ret = glusterd_resolve_snap_bricks (this, snap); + if (ret) + gf_log (this->name, GF_LOG_WARNING, "resolving the snap bricks" + " failed (snap: %s)", snap?snap->snapname:""); + + /* When the snapshot command from cli is received, the on disk and + in memory structures for the snapshot are created (with the status) + being marked as GD_SNAP_STATUS_INIT. Once the backend snapshot is + taken, the status is changed to GD_SNAP_STATUS_IN_USE. If glusterd + dies after taking the backend snapshot, but before updating the + status, then when glusterd comes up, it should treat that snapshot + as a failed snapshot and clean it up. + */ + if (snap->snap_status != GD_SNAP_STATUS_IN_USE) { + ret = glusterd_snap_remove (dict, snap, _gf_true, _gf_true); + if (ret) + gf_log (this->name, GF_LOG_WARNING, "failed to remove" + " the snapshot %s", snap->snapname); + goto out; + } + + /* TODO: list_add_order can do 'N-square' comparisions and + is not efficient. Find a better solution to store the snap + in order */ + list_add_order (&snap->snap_list, &priv->snapshots, + glusterd_compare_snap_time); + +out: + if (dict) + dict_unref (dict); + + gf_log (this->name, GF_LOG_TRACE, "Returning with %d", ret); + return ret; +} + +/* Read the missed_snap_list and update the in-memory structs */ +int32_t +glusterd_store_retrieve_missed_snaps_list (xlator_t *this) +{ + char buf[PATH_MAX] = ""; + char path[PATH_MAX] = ""; + char *missed_node_info = NULL; + char *brick_path = NULL; + char *value = NULL; + char *save_ptr = NULL; + FILE *fp = NULL; + int32_t brick_num = -1; + int32_t snap_op = -1; + int32_t snap_status = -1; + int32_t ret = -1; + glusterd_conf_t *priv = NULL; + gf_store_op_errno_t store_errno = GD_STORE_SUCCESS; + + GF_ASSERT (this); + priv = this->private; + GF_ASSERT (priv); + + /* Get the path of the missed_snap_list */ + glusterd_store_missed_snaps_list_path_set (path, sizeof(path)); + + fp = fopen (path, "r"); + if (!fp) { + /* If errno is ENOENT then there are no missed snaps yet */ + if (errno != ENOENT) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to open %s. " + "Error: %s", path, strerror(errno)); + } else { + gf_log (this->name, GF_LOG_INFO, + "No missed snaps list."); + ret = 0; + } + goto out; + } + + do { + ret = gf_store_read_and_tokenize (fp, buf, + &missed_node_info, &value, + &store_errno); + if (ret) { + if (store_errno == GD_STORE_EOF) { + gf_log (this->name, + GF_LOG_DEBUG, + "EOF for missed_snap_list"); + ret = 0; + break; + } + gf_log (this->name, GF_LOG_ERROR, + "Failed to fetch data from " + "missed_snaps_list. Error: %s", + gf_store_strerror (store_errno)); + goto out; + } + + /* Fetch the brick_num, brick_path, snap_op and snap status */ + brick_num = atoi(strtok_r (value, ":", &save_ptr)); + brick_path = strtok_r (NULL, ":", &save_ptr); + snap_op = atoi(strtok_r (NULL, ":", &save_ptr)); + snap_status = atoi(strtok_r (NULL, ":", &save_ptr)); + + if (!missed_node_info || !brick_path || + brick_num < 1 || snap_op < 1 || + snap_status < 1) { + gf_log (this->name, GF_LOG_ERROR, + "Invalid missed_snap_entry"); + ret = -1; + goto out; + } + + ret = glusterd_store_missed_snaps_list (missed_node_info, + brick_num, + brick_path, + snap_op, + snap_status); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to store missed snaps_list"); + goto out; + } + + } while (store_errno == GD_STORE_SUCCESS); + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning with %d", ret); + return ret; +} + +int32_t +glusterd_store_retrieve_snaps (xlator_t *this) +{ + int32_t ret = 0; + char path[PATH_MAX] = {0,}; + glusterd_conf_t *priv = NULL; + DIR *dir = NULL; + struct dirent *entry = NULL; + + GF_ASSERT (this); + priv = this->private; + + GF_ASSERT (priv); + + snprintf (path, PATH_MAX, "%s/snaps", priv->workdir); + + dir = opendir (path); + + if (!dir) { + /* If snaps dir doesn't exists ignore the error for + backward compatibility */ + if (errno != ENOENT) { + ret = -1; + gf_log ("", GF_LOG_ERROR, "Unable to open dir %s", path); + } + goto out; + } + + glusterd_for_each_entry (entry, dir); + + while (entry) { + if (entry->d_type == DT_DIR) { + ret = glusterd_store_retrieve_snap (entry->d_name); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to restore snapshot: %s", + entry->d_name); + goto out; + } + } + + glusterd_for_each_entry (entry, dir); + } + + /* Retrieve missed_snaps_list */ + ret = glusterd_store_retrieve_missed_snaps_list (this); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, + "Failed to retrieve missed_snaps_list"); + goto out; + } + +out: + if (dir) + closedir (dir); + gf_log ("", GF_LOG_DEBUG, "Returning with %d", ret); + + return ret; +} + +/* Writes all the contents of conf->missed_snap_list */ +int32_t +glusterd_store_write_missed_snapinfo (int32_t fd) +{ + char value[PATH_MAX] = ""; + int32_t ret = -1; + glusterd_conf_t *priv = NULL; + glusterd_missed_snap_info *missed_snapinfo = NULL; + glusterd_snap_op_t *snap_opinfo = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + + priv = this->private; + GF_ASSERT (priv); + + /* Write the missed_snap_entry */ + list_for_each_entry (missed_snapinfo, &priv->missed_snaps_list, + missed_snaps) { + list_for_each_entry (snap_opinfo, + &missed_snapinfo->snap_ops, + snap_ops_list) { + snprintf (value, sizeof(value), "%d:%s:%d:%d", + snap_opinfo->brick_num, + snap_opinfo->brick_path, + snap_opinfo->op, snap_opinfo->status); + ret = gf_store_save_value + (fd, + missed_snapinfo->node_snap_info, + value); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to write missed snapinfo"); + goto out; + } + } + } + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +/* Adds the missed snap entries to the in-memory conf->missed_snap_list * + * and writes them to disk */ +int32_t +glusterd_store_update_missed_snaps (dict_t *dict, int32_t missed_snap_count) +{ + int32_t fd = -1; + int32_t ret = -1; + glusterd_conf_t *priv = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT(dict); + + priv = this->private; + GF_ASSERT (priv); + + if (missed_snap_count < 1) { + gf_log (this->name, GF_LOG_DEBUG, "No missed snaps"); + ret = 0; + goto out; + } + + ret = glusterd_store_create_missed_snaps_list_shandle_on_absence (); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Unable to obtain " + "missed_snaps_list store handle."); + goto out; + } + + fd = gf_store_mkstemp (priv->missed_snaps_list_shandle); + if (fd <= 0) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to create tmp file"); + ret = -1; + goto out; + } + + ret = glusterd_add_missed_snaps_to_list (dict, missed_snap_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to add missed snaps to list"); + goto out; + } + + ret = glusterd_store_write_missed_snapinfo (fd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to write missed snaps to disk"); + goto out; + } + + ret = gf_store_rename_tmppath (priv->missed_snaps_list_shandle); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to rename the tmp file"); + goto out; + } +out: + if (ret && (fd > 0)) { + ret = gf_store_unlink_tmppath (priv->missed_snaps_list_shandle); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to unlink the tmp file"); + } + ret = -1; + } + + if (fd > 0) + close (fd); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + int32_t glusterd_store_delete_peerinfo (glusterd_peerinfo_t *peerinfo) { @@ -2681,7 +3664,11 @@ glusterd_restore () goto out; } - ret = glusterd_store_retrieve_volumes (this); + ret = glusterd_store_retrieve_volumes (this, NULL); + if (ret) + goto out; + + ret = glusterd_store_retrieve_snaps (this); if (ret) goto out; diff --git a/xlators/mgmt/glusterd/src/glusterd-store.h b/xlators/mgmt/glusterd/src/glusterd-store.h index 955abb09f..64c073a8a 100644 --- a/xlators/mgmt/glusterd/src/glusterd-store.h +++ b/xlators/mgmt/glusterd/src/glusterd-store.h @@ -35,43 +35,56 @@ typedef enum glusterd_store_ver_ac_{ } glusterd_volinfo_ver_ac_t; -#define GLUSTERD_STORE_UUID_KEY "UUID" - -#define GLUSTERD_STORE_KEY_VOL_TYPE "type" -#define GLUSTERD_STORE_KEY_VOL_COUNT "count" -#define GLUSTERD_STORE_KEY_VOL_STATUS "status" -#define GLUSTERD_STORE_KEY_VOL_PORT "port" -#define GLUSTERD_STORE_KEY_VOL_SUB_COUNT "sub_count" -#define GLUSTERD_STORE_KEY_VOL_STRIPE_CNT "stripe_count" -#define GLUSTERD_STORE_KEY_VOL_REPLICA_CNT "replica_count" -#define GLUSTERD_STORE_KEY_VOL_BRICK "brick" -#define GLUSTERD_STORE_KEY_VOL_VERSION "version" -#define GLUSTERD_STORE_KEY_VOL_TRANSPORT "transport-type" -#define GLUSTERD_STORE_KEY_VOL_ID "volume-id" -#define GLUSTERD_STORE_KEY_RB_STATUS "rb_status" -#define GLUSTERD_STORE_KEY_RB_SRC_BRICK "rb_src" -#define GLUSTERD_STORE_KEY_RB_DST_BRICK "rb_dst" -#define GLUSTERD_STORE_KEY_RB_DST_PORT "rb_port" -#define GLUSTERD_STORE_KEY_VOL_DEFRAG "rebalance_status" -#define GLUSTERD_STORE_KEY_DEFRAG_OP "rebalance_op" -#define GLUSTERD_STORE_KEY_USERNAME "username" -#define GLUSTERD_STORE_KEY_PASSWORD "password" -#define GLUSTERD_STORE_KEY_VOL_OP_VERSION "op-version" +#define GLUSTERD_STORE_UUID_KEY "UUID" + +#define GLUSTERD_STORE_KEY_VOL_TYPE "type" +#define GLUSTERD_STORE_KEY_VOL_COUNT "count" +#define GLUSTERD_STORE_KEY_VOL_STATUS "status" +#define GLUSTERD_STORE_KEY_VOL_PORT "port" +#define GLUSTERD_STORE_KEY_VOL_SUB_COUNT "sub_count" +#define GLUSTERD_STORE_KEY_VOL_STRIPE_CNT "stripe_count" +#define GLUSTERD_STORE_KEY_VOL_REPLICA_CNT "replica_count" +#define GLUSTERD_STORE_KEY_VOL_BRICK "brick" +#define GLUSTERD_STORE_KEY_VOL_VERSION "version" +#define GLUSTERD_STORE_KEY_VOL_TRANSPORT "transport-type" +#define GLUSTERD_STORE_KEY_VOL_ID "volume-id" +#define GLUSTERD_STORE_KEY_VOL_IS_RESTORED "is-volume-restored" +#define GLUSTERD_STORE_KEY_RB_STATUS "rb_status" +#define GLUSTERD_STORE_KEY_RB_SRC_BRICK "rb_src" +#define GLUSTERD_STORE_KEY_RB_DST_BRICK "rb_dst" +#define GLUSTERD_STORE_KEY_RB_DST_PORT "rb_port" +#define GLUSTERD_STORE_KEY_VOL_DEFRAG "rebalance_status" +#define GLUSTERD_STORE_KEY_DEFRAG_OP "rebalance_op" +#define GLUSTERD_STORE_KEY_USERNAME "username" +#define GLUSTERD_STORE_KEY_PASSWORD "password" +#define GLUSTERD_STORE_KEY_PARENT_VOLNAME "parent_volname" +#define GLUSTERD_STORE_KEY_VOL_OP_VERSION "op-version" #define GLUSTERD_STORE_KEY_VOL_CLIENT_OP_VERSION "client-op-version" -#define GLUSTERD_STORE_KEY_BRICK_HOSTNAME "hostname" -#define GLUSTERD_STORE_KEY_BRICK_PATH "path" -#define GLUSTERD_STORE_KEY_BRICK_PORT "listen-port" -#define GLUSTERD_STORE_KEY_BRICK_RDMA_PORT "rdma.listen-port" +#define GLUSTERD_STORE_KEY_SNAP_NAME "name" +#define GLUSTERD_STORE_KEY_SNAP_ID "snap-id" +#define GLUSTERD_STORE_KEY_SNAP_DESC "desc" +#define GLUSTERD_STORE_KEY_SNAP_TIMESTAMP "time-stamp" +#define GLUSTERD_STORE_KEY_SNAP_STATUS "status" +#define GLUSTERD_STORE_KEY_SNAP_RESTORED "snap-restored" +#define GLUSTERD_STORE_KEY_SNAP_MAX_HARD_LIMIT "snap-max-hard-limit" +#define GLUSTERD_STORE_KEY_SNAP_MAX_SOFT_LIMIT "snap-max-soft-limit" + +#define GLUSTERD_STORE_KEY_BRICK_HOSTNAME "hostname" +#define GLUSTERD_STORE_KEY_BRICK_PATH "path" +#define GLUSTERD_STORE_KEY_BRICK_PORT "listen-port" +#define GLUSTERD_STORE_KEY_BRICK_RDMA_PORT "rdma.listen-port" #define GLUSTERD_STORE_KEY_BRICK_DECOMMISSIONED "decommissioned" -#define GLUSTERD_STORE_KEY_BRICK_VGNAME "vg" -#define GLUSTERD_STORE_KEY_BRICK_ID "brick-id" +#define GLUSTERD_STORE_KEY_BRICK_VGNAME "vg" +#define GLUSTERD_STORE_KEY_BRICK_DEVICE_PATH "device_path" +#define GLUSTERD_STORE_KEY_BRICK_SNAP_STATUS "snap-status" +#define GLUSTERD_STORE_KEY_BRICK_ID "brick-id" -#define GLUSTERD_STORE_KEY_PEER_UUID "uuid" -#define GLUSTERD_STORE_KEY_PEER_HOSTNAME "hostname" -#define GLUSTERD_STORE_KEY_PEER_STATE "state" +#define GLUSTERD_STORE_KEY_PEER_UUID "uuid" +#define GLUSTERD_STORE_KEY_PEER_HOSTNAME "hostname" +#define GLUSTERD_STORE_KEY_PEER_STATE "state" -#define GLUSTERD_STORE_KEY_VOL_CAPS "caps" +#define GLUSTERD_STORE_KEY_VOL_CAPS "caps" #define glusterd_for_each_entry(entry, dir) \ do {\ @@ -93,6 +106,9 @@ glusterd_store_volinfo (glusterd_volinfo_t *volinfo, glusterd_volinfo_ver_ac_t a int32_t glusterd_store_delete_volume (glusterd_volinfo_t *volinfo); +int32_t +glusterd_store_delete_snap (glusterd_snap_t *snap); + int32_t glusterd_retrieve_uuid (); @@ -127,9 +143,18 @@ glusterd_store_global_info (xlator_t *this); int32_t glusterd_store_retrieve_options (xlator_t *this); +int32_t +glusterd_store_retrieve_bricks (glusterd_volinfo_t *volinfo); + int32_t glusterd_store_options (xlator_t *this, dict_t *opts); +void +glusterd_replace_slash_with_hyphen (char *str); + +int32_t +glusterd_store_perform_volume_store (glusterd_volinfo_t *volinfo); + int32_t glusterd_store_create_quota_conf_sh_on_absence (glusterd_volinfo_t *volinfo); @@ -139,4 +164,11 @@ glusterd_store_retrieve_quota_version (glusterd_volinfo_t *volinfo); int glusterd_store_save_quota_version_and_cksum (glusterd_volinfo_t *volinfo); +int32_t +glusterd_store_snap (glusterd_snap_t *snap); + +int32_t +glusterd_store_update_missed_snaps (dict_t *dict, + int32_t missed_snap_count); + #endif diff --git a/xlators/mgmt/glusterd/src/glusterd-syncop.c b/xlators/mgmt/glusterd/src/glusterd-syncop.c index 578bce897..b36d6f616 100644 --- a/xlators/mgmt/glusterd/src/glusterd-syncop.c +++ b/xlators/mgmt/glusterd/src/glusterd-syncop.c @@ -21,7 +21,7 @@ extern glusterd_op_info_t opinfo; -static inline void +void gd_synctask_barrier_wait (struct syncargs *args, int count) { glusterd_conf_t *conf = THIS->private; @@ -56,17 +56,17 @@ gd_mgmt_v3_collate_errors (struct syncargs *args, int op_ret, int op_errno, "Error: %s", op_errstr); switch (op_code) { - case GLUSTERD_MGMT_V3_VOLUME_LOCK: + case GLUSTERD_MGMT_V3_LOCK: { snprintf (op_err, sizeof(op_err) - 1, - "Locking volume failed " + "Locking failed " "on %s. %s", peer_str, err_str); break; } - case GLUSTERD_MGMT_V3_VOLUME_UNLOCK: + case GLUSTERD_MGMT_V3_UNLOCK: { snprintf (op_err, sizeof(op_err) - 1, - "Unlocking volume failed " + "Unlocking failed " "on %s. %s", peer_str, err_str); break; } @@ -164,7 +164,7 @@ gd_collate_errors (struct syncargs *args, int op_ret, int op_errno, return; } -static void +void gd_syncargs_init (struct syncargs *args, dict_t *op_ctx) { args->dict = op_ctx; @@ -268,7 +268,7 @@ extern struct rpc_clnt_program gd_mgmt_prog; extern struct rpc_clnt_program gd_brick_prog; extern struct rpc_clnt_program gd_mgmt_v3_prog; -static int +int glusterd_syncop_aggr_rsp_dict (glusterd_op_t op, dict_t *aggr, dict_t *rsp) { int ret = 0; @@ -339,6 +339,12 @@ glusterd_syncop_aggr_rsp_dict (glusterd_op_t op, dict_t *aggr, dict_t *rsp) goto out; break; + case GD_OP_SNAP: + ret = glusterd_snap_use_rsp_dict (aggr, rsp); + if (ret) + goto out; + break; + default: break; } @@ -347,13 +353,13 @@ out: } int32_t -_gd_syncop_mgmt_volume_lock_cbk (struct rpc_req *req, struct iovec *iov, +gd_syncop_mgmt_v3_lock_cbk_fn (struct rpc_req *req, struct iovec *iov, int count, void *myframe) { int ret = -1; struct syncargs *args = NULL; glusterd_peerinfo_t *peerinfo = NULL; - gd1_mgmt_volume_lock_rsp rsp = {{0},}; + gd1_mgmt_v3_lock_rsp rsp = {{0},}; call_frame_t *frame = NULL; int op_ret = -1; int op_errno = -1; @@ -374,7 +380,7 @@ _gd_syncop_mgmt_volume_lock_cbk (struct rpc_req *req, struct iovec *iov, } ret = xdr_to_generic (*iov, &rsp, - (xdrproc_t)xdr_gd1_mgmt_volume_lock_rsp); + (xdrproc_t)xdr_gd1_mgmt_v3_lock_rsp); if (ret < 0) goto out; @@ -384,7 +390,7 @@ _gd_syncop_mgmt_volume_lock_cbk (struct rpc_req *req, struct iovec *iov, op_errno = rsp.op_errno; out: gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, - GLUSTERD_MGMT_V3_VOLUME_LOCK, + GLUSTERD_MGMT_V3_LOCK, peerinfo, rsp.uuid); STACK_DESTROY (frame->root); synctask_barrier_wake(args); @@ -392,21 +398,21 @@ out: } int32_t -gd_syncop_mgmt_volume_lock_cbk (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +gd_syncop_mgmt_v3_lock_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) { return glusterd_big_locked_cbk (req, iov, count, myframe, - _gd_syncop_mgmt_volume_lock_cbk); + gd_syncop_mgmt_v3_lock_cbk_fn); } int -gd_syncop_mgmt_volume_lock (glusterd_op_t op, dict_t *op_ctx, - glusterd_peerinfo_t *peerinfo, - struct syncargs *args, uuid_t my_uuid, - uuid_t recv_uuid, uuid_t txn_id) +gd_syncop_mgmt_v3_lock (glusterd_op_t op, dict_t *op_ctx, + glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid, uuid_t txn_id) { int ret = -1; - gd1_mgmt_volume_lock_req req = {{0},}; + gd1_mgmt_v3_lock_req req = {{0},}; glusterd_conf_t *conf = THIS->private; GF_ASSERT(op_ctx); @@ -425,10 +431,10 @@ gd_syncop_mgmt_volume_lock (glusterd_op_t op, dict_t *op_ctx, synclock_unlock (&conf->big_lock); ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, &gd_mgmt_v3_prog, - GLUSTERD_MGMT_V3_VOLUME_LOCK, - gd_syncop_mgmt_volume_lock_cbk, + GLUSTERD_MGMT_V3_LOCK, + gd_syncop_mgmt_v3_lock_cbk, (xdrproc_t) - xdr_gd1_mgmt_volume_lock_req); + xdr_gd1_mgmt_v3_lock_req); synclock_lock (&conf->big_lock); out: gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); @@ -436,13 +442,13 @@ out: } int32_t -_gd_syncop_mgmt_volume_unlock_cbk (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +gd_syncop_mgmt_v3_unlock_cbk_fn (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) { int ret = -1; struct syncargs *args = NULL; glusterd_peerinfo_t *peerinfo = NULL; - gd1_mgmt_volume_unlock_rsp rsp = {{0},}; + gd1_mgmt_v3_unlock_rsp rsp = {{0},}; call_frame_t *frame = NULL; int op_ret = -1; int op_errno = -1; @@ -463,7 +469,7 @@ _gd_syncop_mgmt_volume_unlock_cbk (struct rpc_req *req, struct iovec *iov, } ret = xdr_to_generic (*iov, &rsp, - (xdrproc_t)xdr_gd1_mgmt_volume_unlock_rsp); + (xdrproc_t)xdr_gd1_mgmt_v3_unlock_rsp); if (ret < 0) goto out; @@ -476,7 +482,7 @@ _gd_syncop_mgmt_volume_unlock_cbk (struct rpc_req *req, struct iovec *iov, op_errno = rsp.op_errno; out: gd_mgmt_v3_collate_errors (args, op_ret, op_errno, NULL, - GLUSTERD_MGMT_V3_VOLUME_UNLOCK, + GLUSTERD_MGMT_V3_UNLOCK, peerinfo, rsp.uuid); STACK_DESTROY (frame->root); synctask_barrier_wake(args); @@ -484,20 +490,20 @@ out: } int32_t -gd_syncop_mgmt_volume_unlock_cbk (struct rpc_req *req, struct iovec *iov, - int count, void *myframe) +gd_syncop_mgmt_v3_unlock_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) { return glusterd_big_locked_cbk (req, iov, count, myframe, - _gd_syncop_mgmt_volume_unlock_cbk); + gd_syncop_mgmt_v3_unlock_cbk_fn); } int -gd_syncop_mgmt_volume_unlock (dict_t *op_ctx, glusterd_peerinfo_t *peerinfo, - struct syncargs *args, uuid_t my_uuid, - uuid_t recv_uuid, uuid_t txn_id) +gd_syncop_mgmt_v3_unlock (dict_t *op_ctx, glusterd_peerinfo_t *peerinfo, + struct syncargs *args, uuid_t my_uuid, + uuid_t recv_uuid, uuid_t txn_id) { int ret = -1; - gd1_mgmt_volume_unlock_req req = {{0},}; + gd1_mgmt_v3_unlock_req req = {{0},}; glusterd_conf_t *conf = THIS->private; GF_ASSERT(op_ctx); @@ -515,10 +521,10 @@ gd_syncop_mgmt_volume_unlock (dict_t *op_ctx, glusterd_peerinfo_t *peerinfo, synclock_unlock (&conf->big_lock); ret = gd_syncop_submit_request (peerinfo->rpc, &req, args, peerinfo, &gd_mgmt_v3_prog, - GLUSTERD_MGMT_V3_VOLUME_UNLOCK, - gd_syncop_mgmt_volume_unlock_cbk, + GLUSTERD_MGMT_V3_UNLOCK, + gd_syncop_mgmt_v3_unlock_cbk, (xdrproc_t) - xdr_gd1_mgmt_volume_unlock_req); + xdr_gd1_mgmt_v3_unlock_req); synclock_lock (&conf->big_lock); out: gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); @@ -884,10 +890,12 @@ gd_syncop_mgmt_brick_op (struct rpc_clnt *rpc, glusterd_pending_node_t *pnode, GD_SYNCOP (rpc, (&args), NULL, gd_syncop_brick_op_cbk, req, &gd_brick_prog, req->op, xdr_gd1_mgmt_brick_op_req); - if (args.errstr && errstr) - *errstr = args.errstr; - else - GF_FREE (args.errstr); + if (args.errstr) { + if ((strlen(args.errstr) > 0) && errstr) + *errstr = args.errstr; + else + GF_FREE (args.errstr); + } if (GD_OP_STATUS_VOLUME == op) { ret = dict_set_int32 (args.dict, "index", pnode->index); @@ -1097,8 +1105,8 @@ gd_lock_op_phase (glusterd_conf_t *conf, glusterd_op_t op, dict_t *op_ctx, gd_syncop_mgmt_lock (peerinfo, &args, MY_UUID, peer_uuid); } else - gd_syncop_mgmt_volume_lock (op, op_ctx, peerinfo, &args, - MY_UUID, peer_uuid, txn_id); + gd_syncop_mgmt_v3_lock (op, op_ctx, peerinfo, &args, + MY_UUID, peer_uuid, txn_id); peer_cnt++; } gd_synctask_barrier_wait((&args), peer_cnt); @@ -1321,7 +1329,7 @@ gd_unlock_op_phase (glusterd_conf_t *conf, glusterd_op_t op, int *op_ret, glusterd_peerinfo_t *peerinfo = NULL; glusterd_peerinfo_t *tmp = NULL; uuid_t tmp_uuid = {0}; - int peer_cnt = 0; + int peer_cnt = 0; int ret = -1; xlator_t *this = NULL; struct syncargs args = {0}; @@ -1358,9 +1366,9 @@ gd_unlock_op_phase (glusterd_conf_t *conf, glusterd_op_t op, int *op_ret, if (volname) { list_for_each_entry_safe (peerinfo, tmp, peers, op_peers_list) { - gd_syncop_mgmt_volume_unlock (op_ctx, peerinfo, - &args, MY_UUID, - tmp_uuid, txn_id); + gd_syncop_mgmt_v3_unlock (op_ctx, peerinfo, + &args, MY_UUID, + tmp_uuid, txn_id); peer_cnt++; list_del_init (&peerinfo->op_peers_list); } @@ -1386,7 +1394,7 @@ out: if (is_acquired) { /* Based on the op-version, - * we release the cluster or volume lock + * we release the cluster or mgmt_v3 lock * and clear the op */ glusterd_op_clear_op (op); @@ -1394,7 +1402,8 @@ out: glusterd_unlock (MY_UUID); else { if (volname) { - ret = glusterd_volume_unlock (volname, MY_UUID); + ret = glusterd_mgmt_v3_unlock (volname, MY_UUID, + "vol"); if (ret) gf_log (this->name, GF_LOG_ERROR, "Unable to release lock for %s", @@ -1551,7 +1560,7 @@ gd_sync_task_begin (dict_t *op_ctx, rpcsvc_request_t * req) goto out; } - /* Based on the op_version, acquire a cluster or volume lock */ + /* Based on the op_version, acquire a cluster or mgmt_v3 lock */ if (conf->op_version < GD_OP_VERSION_4) { ret = glusterd_lock (MY_UUID); if (ret) { @@ -1580,7 +1589,7 @@ gd_sync_task_begin (dict_t *op_ctx, rpcsvc_request_t * req) goto out; } - ret = glusterd_volume_lock (volname, MY_UUID); + ret = glusterd_mgmt_v3_lock (volname, MY_UUID, "vol"); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Unable to acquire lock for %s", volname); diff --git a/xlators/mgmt/glusterd/src/glusterd-syncop.h b/xlators/mgmt/glusterd/src/glusterd-syncop.h index 7e4d47f9a..e83ea2f4c 100644 --- a/xlators/mgmt/glusterd/src/glusterd-syncop.h +++ b/xlators/mgmt/glusterd/src/glusterd-syncop.h @@ -12,6 +12,7 @@ #include "syncop.h" #include "glusterd-sm.h" +#include "glusterd.h" #define GD_SYNC_OPCODE_KEY "sync-mgmt-operation" @@ -51,4 +52,20 @@ int gd_syncop_mgmt_stage_op (struct rpc_clnt *rpc, struct syncargs *arg, int gd_syncop_mgmt_commit_op (struct rpc_clnt *rpc, struct syncargs *arg, uuid_t my_uuid, uuid_t recv_uuid, int op, dict_t *dict_out, dict_t *op_ctx); + +void +gd_synctask_barrier_wait (struct syncargs *args, int count); + +int +gd_build_peers_list (struct list_head *peers, struct list_head *xact_peers, + glusterd_op_t op); +int +gd_brick_op_phase (glusterd_op_t op, dict_t *op_ctx, dict_t *req_dict, + char **op_errstr); + +int +glusterd_syncop_aggr_rsp_dict (glusterd_op_t op, dict_t *aggr, dict_t *rsp); + +void +gd_syncargs_init (struct syncargs *args, dict_t *op_ctx); #endif /* __RPC_SYNCOP_H */ diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.c b/xlators/mgmt/glusterd/src/glusterd-utils.c index 8c5cd7a43..721ffe27f 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-utils.c @@ -491,8 +491,11 @@ glusterd_volinfo_new (glusterd_volinfo_t **volinfo) if (!new_volinfo) goto out; + LOCK_INIT (&new_volinfo->lock); INIT_LIST_HEAD (&new_volinfo->vol_list); + INIT_LIST_HEAD (&new_volinfo->snapvol_list); INIT_LIST_HEAD (&new_volinfo->bricks); + INIT_LIST_HEAD (&new_volinfo->snap_volumes); new_volinfo->dict = dict_new (); if (!new_volinfo->dict) { @@ -508,6 +511,10 @@ glusterd_volinfo_new (glusterd_volinfo_t **volinfo) goto out; } + snprintf (new_volinfo->parent_volname, GLUSTERD_MAX_VOLUME_NAME, "N/A"); + + new_volinfo->snap_max_hard_limit = GLUSTERD_SNAPS_MAX_HARD_LIMIT; + new_volinfo->xl = THIS; pthread_mutex_init (&new_volinfo->reflock, NULL); @@ -520,6 +527,224 @@ out: return ret; } +/* This function will create a new volinfo and then + * dup the entries from volinfo to the new_volinfo. + * + * @param volinfo volinfo which will be duplicated + * @param dup_volinfo new volinfo which will be created + * @param set_userauth if this true then auth info is also set + * + * @return 0 on success else -1 + */ +int32_t +glusterd_volinfo_dup (glusterd_volinfo_t *volinfo, + glusterd_volinfo_t **dup_volinfo, + gf_boolean_t set_userauth) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_volinfo_t *new_volinfo = NULL; + + this = THIS; + GF_ASSERT (this); + GF_VALIDATE_OR_GOTO (this->name, volinfo, out); + GF_VALIDATE_OR_GOTO (this->name, dup_volinfo, out); + + ret = glusterd_volinfo_new (&new_volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "not able to create the " + "duplicate volinfo for the volume %s", + volinfo->volname); + goto out; + } + + new_volinfo->type = volinfo->type; + new_volinfo->replica_count = volinfo->replica_count; + new_volinfo->stripe_count = volinfo->stripe_count; + new_volinfo->dist_leaf_count = volinfo->dist_leaf_count; + new_volinfo->sub_count = volinfo->sub_count; + new_volinfo->transport_type = volinfo->transport_type; + new_volinfo->nfs_transport_type = volinfo->nfs_transport_type; + new_volinfo->brick_count = volinfo->brick_count; + + dict_copy (volinfo->dict, new_volinfo->dict); + gd_update_volume_op_versions (new_volinfo); + + if (set_userauth) { + glusterd_auth_set_username (new_volinfo, + volinfo->auth.username); + glusterd_auth_set_password (new_volinfo, + volinfo->auth.password); + } + + *dup_volinfo = new_volinfo; + ret = 0; +out: + if (ret && (NULL != new_volinfo)) { + (void) glusterd_volinfo_delete (new_volinfo); + } + return ret; +} + +/* This function will duplicate brickinfo + * + * @param brickinfo Source brickinfo + * @param dup_brickinfo Destination brickinfo + * + * @return 0 on success else -1 + */ +int32_t +glusterd_brickinfo_dup (glusterd_brickinfo_t *brickinfo, + glusterd_brickinfo_t *dup_brickinfo) +{ + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + GF_VALIDATE_OR_GOTO (this->name, brickinfo, out); + GF_VALIDATE_OR_GOTO (this->name, dup_brickinfo, out); + + strcpy (dup_brickinfo->hostname, brickinfo->hostname); + strcpy (dup_brickinfo->path, brickinfo->path); + strcpy (dup_brickinfo->device_path, brickinfo->device_path); + ret = gf_canonicalize_path (dup_brickinfo->path); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to canonicalize " + "brick path"); + goto out; + } + uuid_copy (dup_brickinfo->uuid, brickinfo->uuid); + + dup_brickinfo->port = brickinfo->port; + dup_brickinfo->rdma_port = brickinfo->rdma_port; + if (NULL != brickinfo->logfile) { + dup_brickinfo->logfile = gf_strdup (brickinfo->logfile); + if (NULL == dup_brickinfo->logfile) { + ret = -1; + goto out; + } + } + dup_brickinfo->status = brickinfo->status; + dup_brickinfo->snap_status = brickinfo->snap_status; +out: + return ret; +} + +/* This function will copy snap volinfo to the new + * passed volinfo and regenerate backend store files + * for the restored snap. + * + * @param new_volinfo new volinfo + * @param snap_volinfo volinfo of snap volume + * + * @return 0 on success and -1 on failure + * + * TODO: Duplicate all members of volinfo, e.g. geo-rep sync slaves + */ +int32_t +glusterd_snap_volinfo_restore (dict_t *rsp_dict, + glusterd_volinfo_t *new_volinfo, + glusterd_volinfo_t *snap_volinfo) +{ + int32_t brick_count = -1; + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_brickinfo_t *brickinfo = NULL; + glusterd_brickinfo_t *new_brickinfo = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + + GF_VALIDATE_OR_GOTO (this->name, new_volinfo, out); + GF_VALIDATE_OR_GOTO (this->name, snap_volinfo, out); + + brick_count = 0; + list_for_each_entry (brickinfo, &snap_volinfo->bricks, brick_list) { + ret = glusterd_brickinfo_new (&new_brickinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to create " + "new brickinfo"); + goto out; + } + + /* Duplicate brickinfo */ + ret = glusterd_brickinfo_dup (brickinfo, new_brickinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to dup " + "brickinfo"); + goto out; + } + + /*Update the brickid for the new brick in new volume*/ + GLUSTERD_ASSIGN_BRICKID_TO_BRICKINFO (new_brickinfo, + new_volinfo, + brick_count); + + /* If the brick is not of this peer, or snapshot is missed * + * for the brick do not replace the xattr for it */ + if ((!uuid_compare (brickinfo->uuid, MY_UUID)) && + (brickinfo->snap_status != -1)) { + /* We need to replace the volume id of all the bricks + * to the volume id of the origin volume. new_volinfo + * has the origin volume's volume id*/ + ret = sys_lsetxattr (new_brickinfo->path, + GF_XATTR_VOL_ID_KEY, + new_volinfo->volume_id, + sizeof (new_volinfo->volume_id), + XATTR_REPLACE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "set extended attribute %s on %s. " + "Reason: %s, snap: %s", + GF_XATTR_VOL_ID_KEY, + new_brickinfo->path, strerror (errno), + new_volinfo->volname); + goto out; + } + } + + /* If a snapshot is pending for this brick then + * restore should also be pending + */ + if (brickinfo->snap_status == -1) { + /* Adding missed delete to the dict */ + ret = glusterd_add_missed_snaps_to_dict + (rsp_dict, + snap_volinfo->volname, + brickinfo, + brick_count + 1, + GF_SNAP_OPTION_TYPE_RESTORE); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to add missed snapshot info " + "for %s:%s in the rsp_dict", + brickinfo->hostname, + brickinfo->path); + goto out; + } + } + + list_add_tail (&new_brickinfo->brick_list, + &new_volinfo->bricks); + /* ownership of new_brickinfo is passed to new_volinfo */ + new_brickinfo = NULL; + brick_count++; + } + + /* Regenerate all volfiles */ + ret = glusterd_create_volfiles_and_notify_services (new_volinfo); + +out: + if (ret && (NULL != new_brickinfo)) { + (void) glusterd_brickinfo_delete (new_brickinfo); + } + + return ret; +} + void glusterd_auth_cleanup (glusterd_volinfo_t *volinfo) { @@ -620,6 +845,7 @@ glusterd_volinfo_delete (glusterd_volinfo_t *volinfo) GF_ASSERT (volinfo); list_del_init (&volinfo->vol_list); + list_del_init (&volinfo->snapvol_list); ret = glusterd_volume_brickinfos_delete (volinfo); if (ret) @@ -645,7 +871,6 @@ out: return ret; } - int32_t glusterd_brickinfo_new (glusterd_brickinfo_t **brickinfo) { @@ -1160,6 +1385,42 @@ glusterd_volinfo_find_by_volume_id (uuid_t volume_id, glusterd_volinfo_t **volin return ret; } +int +glusterd_snap_volinfo_find_by_volume_id (uuid_t volume_id, + glusterd_volinfo_t **volinfo) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_volinfo_t *voliter = NULL; + glusterd_snap_t *snap = NULL; + glusterd_conf_t *priv = NULL; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (volinfo); + + if (uuid_is_null(volume_id)) { + gf_log (this->name, GF_LOG_WARNING, "Volume UUID is NULL"); + goto out; + } + + list_for_each_entry (snap, &priv->snapshots, snap_list) { + list_for_each_entry (voliter, &snap->volumes, vol_list) { + if (uuid_compare (volume_id, voliter->volume_id)) + continue; + *volinfo = voliter; + ret = 0; + goto out; + } + } + + gf_log (this->name, GF_LOG_WARNING, "Snap volume not found"); +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + int32_t glusterd_volinfo_find (char *volname, glusterd_volinfo_t **volinfo) { @@ -1169,7 +1430,6 @@ glusterd_volinfo_find (char *volname, glusterd_volinfo_t **volinfo) glusterd_conf_t *priv = NULL; GF_ASSERT (volname); - this = THIS; GF_ASSERT (this); @@ -1178,7 +1438,8 @@ glusterd_volinfo_find (char *volname, glusterd_volinfo_t **volinfo) list_for_each_entry (tmp_volinfo, &priv->volumes, vol_list) { if (!strcmp (tmp_volinfo->volname, volname)) { - gf_log (this->name, GF_LOG_DEBUG, "Volume %s found", volname); + gf_log (this->name, GF_LOG_DEBUG, "Volume %s found", + volname); ret = 0; *volinfo = tmp_volinfo; break; @@ -1189,6 +1450,68 @@ glusterd_volinfo_find (char *volname, glusterd_volinfo_t **volinfo) return ret; } +int32_t +glusterd_snap_volinfo_find (char *snap_volname, glusterd_snap_t *snap, + glusterd_volinfo_t **volinfo) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_conf_t *priv = NULL; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (snap); + GF_ASSERT (snap_volname); + + list_for_each_entry (snap_vol, &snap->volumes, vol_list) { + if (!strcmp (snap_vol->volname, snap_volname)) { + ret = 0; + *volinfo = snap_vol; + goto out; + } + } + + gf_log (this->name, GF_LOG_WARNING, "Snap volume %s not found", + snap_volname); +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int32_t +glusterd_snap_volinfo_find_from_parent_volname (char *origin_volname, + glusterd_snap_t *snap, + glusterd_volinfo_t **volinfo) +{ + int32_t ret = -1; + xlator_t *this = NULL; + glusterd_volinfo_t *snap_vol = NULL; + glusterd_conf_t *priv = NULL; + + this = THIS; + priv = this->private; + GF_ASSERT (priv); + GF_ASSERT (snap); + GF_ASSERT (origin_volname); + + list_for_each_entry (snap_vol, &snap->volumes, vol_list) { + if (!strcmp (snap_vol->parent_volname, origin_volname)) { + ret = 0; + *volinfo = snap_vol; + goto out; + } + } + + gf_log (this->name, GF_LOG_DEBUG, "Snap volume not found(snap: %s, " + "origin-volume: %s", snap->snapname, origin_volname); + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + int32_t glusterd_service_stop (const char *service, char *pidfile, int sig, gf_boolean_t force_kill) @@ -1281,10 +1604,9 @@ glusterd_set_brick_socket_filepath (glusterd_volinfo_t *volinfo, */ int32_t glusterd_brick_connect (glusterd_volinfo_t *volinfo, - glusterd_brickinfo_t *brickinfo) + glusterd_brickinfo_t *brickinfo, char *socketpath) { int ret = 0; - char socketpath[PATH_MAX] = {0}; char volume_id_str[64]; char *brickid = NULL; dict_t *options = NULL; @@ -1293,12 +1615,9 @@ glusterd_brick_connect (glusterd_volinfo_t *volinfo, GF_ASSERT (volinfo); GF_ASSERT (brickinfo); + GF_ASSERT (socketpath); if (brickinfo->rpc == NULL) { - glusterd_set_brick_socket_filepath (volinfo, brickinfo, - socketpath, - sizeof (socketpath)); - /* Setting frame-timeout to 10mins (600seconds). * Unix domain sockets ensures that the connection is reliable. * The default timeout of 30mins used for unreliable network @@ -1379,9 +1698,23 @@ glusterd_volume_start_glusterfs (glusterd_volinfo_t *volinfo, priv = this->private; GF_ASSERT (priv); + if (brickinfo->snap_status == -1) { + gf_log (this->name, GF_LOG_INFO, + "Snapshot is pending on %s:%s. " + "Hence not starting the brick", + brickinfo->hostname, + brickinfo->path); + ret = 0; + goto out; + } + ret = _mk_rundir_p (volinfo); if (ret) goto out; + + glusterd_set_brick_socket_filepath (volinfo, brickinfo, socketpath, + sizeof (socketpath)); + GLUSTERD_GET_BRICK_PIDFILE (pidfile, volinfo, brickinfo, priv); if (gf_is_service_running (pidfile, NULL)) goto connect; @@ -1417,8 +1750,15 @@ glusterd_volume_start_glusterfs (glusterd_volinfo_t *volinfo, runner_argprintf (&runner, "--log-file=%s", valgrind_logfile); } - snprintf (volfile, PATH_MAX, "%s.%s.%s", volinfo->volname, - brickinfo->hostname, exp_path); + if (volinfo->is_snap_volume) { + snprintf (volfile, PATH_MAX,"/%s/%s/%s.%s.%s", + GLUSTERD_VOL_SNAP_DIR_PREFIX, + volinfo->snapshot->snapname, volinfo->volname, + brickinfo->hostname, exp_path); + } else { + snprintf (volfile, PATH_MAX, "%s.%s.%s", volinfo->volname, + brickinfo->hostname, exp_path); + } if (volinfo->logdir) { snprintf (logfile, PATH_MAX, "%s/%s.log", @@ -1430,9 +1770,6 @@ glusterd_volume_start_glusterfs (glusterd_volinfo_t *volinfo, if (!brickinfo->logfile) brickinfo->logfile = gf_strdup (logfile); - glusterd_set_brick_socket_filepath (volinfo, brickinfo, socketpath, - sizeof (socketpath)); - (void) snprintf (glusterd_uuid, 1024, "*-posix.glusterd-uuid=%s", uuid_utoa (MY_UUID)); runner_add_args (&runner, SBIN_DIR"/glusterfsd", @@ -1480,9 +1817,13 @@ glusterd_volume_start_glusterfs (glusterd_volinfo_t *volinfo, brickinfo->rdma_port = rdma_port; connect: - ret = glusterd_brick_connect (volinfo, brickinfo); - if (ret) + ret = glusterd_brick_connect (volinfo, brickinfo, socketpath); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to connect to brick %s:%s on %s", + brickinfo->hostname, brickinfo->path, socketpath); goto out; + } out: return ret; } @@ -1546,10 +1887,10 @@ glusterd_volume_stop_glusterfs (glusterd_volinfo_t *volinfo, glusterd_brickinfo_t *brickinfo, gf_boolean_t del_brick) { - xlator_t *this = NULL; - glusterd_conf_t *priv = NULL; - char pidfile[PATH_MAX] = {0,}; - int ret = 0; + xlator_t *this = NULL; + glusterd_conf_t *priv = NULL; + char pidfile[PATH_MAX] = {0,}; + int ret = 0; GF_ASSERT (volinfo); GF_ASSERT (brickinfo); @@ -1803,7 +2144,7 @@ out: } int glusterd_compute_cksum (glusterd_volinfo_t *volinfo, - gf_boolean_t is_quota_conf) + gf_boolean_t is_quota_conf) { int ret = -1; uint32_t cs = 0; @@ -1918,7 +2259,10 @@ glusterd_add_volume_to_dict (glusterd_volinfo_t *volinfo, glusterd_dict_ctx_t ctx = {0}; char *rebalance_id_str = NULL; char *rb_id_str = NULL; + xlator_t *this = NULL; + this = THIS; + GF_ASSERT (this); GF_ASSERT (dict); GF_ASSERT (volinfo); @@ -1933,6 +2277,15 @@ glusterd_add_volume_to_dict (glusterd_volinfo_t *volinfo, if (ret) goto out; + snprintf (key, sizeof (key), "volume%d.is_volume_restored", count); + ret = dict_set_int32 (dict, key, volinfo->is_volume_restored); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to set " + "is_volume_restored option for %s volume", + volinfo->volname); + goto out; + } + memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "volume%d.brick_count", count); ret = dict_set_int32 (dict, key, volinfo->brick_count); @@ -1987,6 +2340,20 @@ glusterd_add_volume_to_dict (glusterd_volinfo_t *volinfo, if (ret) goto out; + snprintf (key, sizeof (key), "volume%d.is_snap_volume", count); + ret = dict_set_uint32 (dict, key, volinfo->is_snap_volume); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Unable to set %s", key); + goto out; + } + + snprintf (key, sizeof (key), "volume%d.snap-max-hard-limit", count); + ret = dict_set_uint64 (dict, key, volinfo->snap_max_hard_limit); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Unable to set %s", key); + goto out; + } + volume_id_str = gf_strdup (uuid_utoa (volinfo->volume_id)); if (!volume_id_str) { ret = -1; @@ -2162,6 +2529,28 @@ glusterd_add_volume_to_dict (glusterd_volinfo_t *volinfo, if (ret) goto out; + snprintf (key, sizeof (key), "volume%d.brick%d.snap_status", + count, i); + ret = dict_set_int32 (dict, key, brickinfo->snap_status); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set snap_status for %s:%s", + brickinfo->hostname, + brickinfo->path); + goto out; + } + + snprintf (key, sizeof (key), "volume%d.brick%d.device_path", + count, i); + ret = dict_set_str (dict, key, brickinfo->device_path); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Failed to set snap_device for %s:%s", + brickinfo->hostname, + brickinfo->path); + goto out; + } + i++; } @@ -2716,7 +3105,7 @@ glusterd_do_volume_quorum_action (xlator_t *this, glusterd_volinfo_t *volinfo, gf_boolean_t meets_quorum) { glusterd_brickinfo_t *brickinfo = NULL; - glusterd_conf_t *conf = NULL; + glusterd_conf_t *conf = NULL; conf = this->private; if (volinfo->status != GLUSTERD_STATUS_STARTED) @@ -2834,6 +3223,8 @@ glusterd_import_new_brick (dict_t *vols, int32_t vol_count, { char key[512] = {0,}; int ret = -1; + int32_t snap_status = 0; + char *snap_device = NULL; char *hostname = NULL; char *path = NULL; char *brick_id = NULL; @@ -2877,12 +3268,30 @@ glusterd_import_new_brick (dict_t *vols, int32_t vol_count, ret = 0; } + snprintf (key, sizeof (key), "volume%d.brick%d.snap_status", + vol_count, brick_count); + ret = dict_get_int32 (vols, key, &snap_status); + if (ret) { + snprintf (msg, sizeof (msg), "%s missing in payload", key); + goto out; + } + + snprintf (key, sizeof (key), "volume%d.brick%d.device_path", + vol_count, brick_count); + ret = dict_get_str (vols, key, &snap_device); + if (ret) { + snprintf (msg, sizeof (msg), "%s missing in payload", key); + goto out; + } + ret = glusterd_brickinfo_new (&new_brickinfo); if (ret) goto out; strcpy (new_brickinfo->path, path); strcpy (new_brickinfo->hostname, hostname); + strcpy (new_brickinfo->device_path, snap_device); + new_brickinfo->snap_status = snap_status; new_brickinfo->decommissioned = decommissioned; if (brick_id) strcpy (new_brickinfo->brick_id, brick_id); @@ -3092,6 +3501,7 @@ glusterd_import_volinfo (dict_t *vols, int count, char *rb_id_str = NULL; int op_version = 0; int client_op_version = 0; + uint32_t is_snap_volume = 0; GF_ASSERT (vols); GF_ASSERT (volinfo); @@ -3103,6 +3513,22 @@ glusterd_import_volinfo (dict_t *vols, int count, goto out; } + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "volume%d.is_snap_volume", count); + ret = dict_get_uint32 (vols, key, &is_snap_volume); + if (ret) { + snprintf (msg, sizeof (msg), "%s missing in payload for %s", + key, volname); + goto out; + } + + if (is_snap_volume == _gf_true) { + gf_log (THIS->name, GF_LOG_DEBUG, + "Not syncing snap volume %s", volname); + ret = 0; + goto out; + } + ret = glusterd_volinfo_new (&new_volinfo); if (ret) goto out; @@ -3229,6 +3655,25 @@ glusterd_import_volinfo (dict_t *vols, int count, goto out; } + new_volinfo->is_snap_volume = is_snap_volume; + + snprintf (key, sizeof (key), "volume%d.is_volume_restored", count); + ret = dict_get_uint32 (vols, key, &new_volinfo->is_volume_restored); + if (ret) { + gf_log (THIS->name, GF_LOG_ERROR, "Failed to get " + "is_volume_restored option for %s", + volname); + goto out; + } + + snprintf (key, sizeof (key), "volume%d.snap-max-hard-limit", count); + ret = dict_get_uint64 (vols, key, &new_volinfo->snap_max_hard_limit); + if (ret) { + snprintf (msg, sizeof (msg), "%s missing in payload for %s", + key, volname); + goto out; + } + memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "volume%d.rebalance", count); ret = dict_get_uint32 (vols, key, &new_volinfo->rebal.defrag_cmd); @@ -3568,6 +4013,12 @@ glusterd_import_friend_volume (dict_t *vols, size_t count) if (ret) goto out; + if (!new_volinfo) { + gf_log (this->name, GF_LOG_DEBUG, + "Not importing snap volume"); + goto out; + } + ret = glusterd_volinfo_find (new_volinfo->volname, &old_volinfo); if (0 == ret) { (void) gd_check_and_update_rebalance_info (old_volinfo, @@ -4756,20 +5207,42 @@ out: int glusterd_restart_bricks (glusterd_conf_t *conf) { + int ret = 0; glusterd_volinfo_t *volinfo = NULL; glusterd_brickinfo_t *brickinfo = NULL; + glusterd_snap_t *snap = NULL; gf_boolean_t start_nodesvcs = _gf_false; - int ret = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); list_for_each_entry (volinfo, &conf->volumes, vol_list) { if (volinfo->status != GLUSTERD_STATUS_STARTED) continue; start_nodesvcs = _gf_true; + gf_log (this->name, GF_LOG_DEBUG, "starting the volume %s", + volinfo->volname); list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { glusterd_brick_start (volinfo, brickinfo, _gf_false); } } + list_for_each_entry (snap, &conf->snapshots, snap_list) { + list_for_each_entry (volinfo, &snap->volumes, vol_list) { + if (volinfo->status != GLUSTERD_STATUS_STARTED) + continue; + start_nodesvcs = _gf_true; + gf_log (this->name, GF_LOG_DEBUG, "starting the snap " + "volume %s", volinfo->volname); + list_for_each_entry (brickinfo, &volinfo->bricks, + brick_list) { + glusterd_brick_start (volinfo, brickinfo, + _gf_false); + } + } + } + if (start_nodesvcs) glusterd_nodesvcs_handle_graph_change (NULL); @@ -4986,7 +5459,7 @@ out: } #ifdef GF_LINUX_HOST_OS -static int +int glusterd_get_brick_root (char *path, char **mount_point) { char *ptr = NULL; @@ -5165,19 +5638,44 @@ glusterd_add_inode_size_to_dict (dict_t *dict, int count) return ret; } -static int -glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, - dict_t *dict, int count) +struct mntent * +glusterd_get_mnt_entry_info (char *mnt_pt, FILE *mtab) { - int ret = -1; - char key[1024] = {0}; + struct mntent *entry = NULL; + + mtab = setmntent (_PATH_MOUNTED, "r"); + if (!mtab) + goto out; + + entry = getmntent (mtab); + + while (1) { + if (!entry) + goto out; + + if (!strcmp (entry->mnt_dir, mnt_pt) && + strcmp (entry->mnt_type, "rootfs")) + break; + entry = getmntent (mtab); + } + +out: + return entry; +} + +static int +glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, + dict_t *dict, int count) +{ + int ret = -1; + char key[1024] = {0}; char base_key[1024] = {0}; char *mnt_pt = NULL; char *fs_name = NULL; char *mnt_options = NULL; char *device = NULL; - FILE *mtab = NULL; struct mntent *entry = NULL; + FILE *mtab = NULL; snprintf (base_key, sizeof (base_key), "brick%d", count); @@ -5185,25 +5683,12 @@ glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, if (ret) goto out; - mtab = setmntent (_PATH_MOUNTED, "r"); - if (!mtab) { + entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); + if (!entry) { ret = -1; goto out; } - entry = getmntent (mtab); - - while (1) { - if (!entry) { - ret = -1; - goto out; - } - if (!strcmp (entry->mnt_dir, mnt_pt) && - strcmp (entry->mnt_type, "rootfs")) - break; - entry = getmntent (mtab); - } - /* get device file */ memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "%s.device", base_key); @@ -5236,6 +5721,45 @@ glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, return ret; } + +char* +glusterd_get_brick_mount_details (glusterd_brickinfo_t *brickinfo) +{ + int ret = -1; + char *mnt_pt = NULL; + char *device = NULL; + FILE *mtab = NULL; + struct mntent *entry = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (brickinfo); + + ret = glusterd_get_brick_root (brickinfo->path, &mnt_pt); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get mount point " + "for %s brick", brickinfo->path); + goto out; + } + + entry = glusterd_get_mnt_entry_info (mnt_pt, mtab); + if (NULL == entry) { + gf_log (this->name, GF_LOG_ERROR, "Failed to get mnt entry " + "for %s mount path", mnt_pt); + goto out; + } + + /* get the fs_name/device */ + device = gf_strdup (entry->mnt_fsname); + +out: + if (NULL != mtab) { + endmntent (mtab); + } + + return device; +} #endif int @@ -8237,6 +8761,279 @@ out: return ret; } +int +glusterd_snap_config_use_rsp_dict (dict_t *dst, dict_t *src) +{ + char buf[PATH_MAX] = ""; + char *volname = NULL; + int ret = -1; + int config_command = 0; + uint64_t i = 0; + uint64_t value = 0; + uint64_t voldisplaycount = 0; + + if (!dst || !src) { + gf_log ("", GF_LOG_ERROR, "Source or Destination " + "dict is empty."); + goto out; + } + + ret = dict_get_int32 (dst, "config-command", &config_command); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "failed to get config-command type"); + goto out; + } + + switch (config_command) { + case GF_SNAP_CONFIG_DISPLAY: + ret = dict_get_uint64 (src, "snap-max-hard-limit", &value); + if (!ret) { + ret = dict_set_uint64 (dst, "snap-max-hard-limit", value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set snap_max_hard_limit"); + goto out; + } + } else { + /* Received dummy response from other nodes */ + ret = 0; + goto out; + } + + ret = dict_get_uint64 (src, "snap-max-soft-limit", &value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get snap_max_soft_limit"); + goto out; + } + + ret = dict_set_uint64 (dst, "snap-max-soft-limit", value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set snap_max_soft_limit"); + goto out; + } + + ret = dict_get_uint64 (src, "voldisplaycount", + &voldisplaycount); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get voldisplaycount"); + goto out; + } + + ret = dict_set_uint64 (dst, "voldisplaycount", + voldisplaycount); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set voldisplaycount"); + goto out; + } + + for (i = 0; i < voldisplaycount; i++) { + snprintf (buf, sizeof(buf), "volume%ld-volname", i); + ret = dict_get_str (src, buf, &volname); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get %s", buf); + goto out; + } + ret = dict_set_str (dst, buf, volname); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-hard-limit", i); + ret = dict_get_uint64 (src, buf, &value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get %s", buf); + goto out; + } + ret = dict_set_uint64 (dst, buf, value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-active-hard-limit", i); + ret = dict_get_uint64 (src, buf, &value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get %s", buf); + goto out; + } + ret = dict_set_uint64 (dst, buf, value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set %s", buf); + goto out; + } + + snprintf (buf, sizeof(buf), + "volume%ld-snap-max-soft-limit", i); + ret = dict_get_uint64 (src, buf, &value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to get %s", buf); + goto out; + } + ret = dict_set_uint64 (dst, buf, value); + if (ret) { + gf_log ("", GF_LOG_ERROR, + "Unable to set %s", buf); + goto out; + } + } + + break; + default: + break; + } + + ret = 0; +out: + gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + +/* Aggregate missed_snap_counts from different nodes and save it * + * in the req_dict of the originator node */ +int +glusterd_snap_create_use_rsp_dict (dict_t *dst, dict_t *src) +{ + char *buf = NULL; + char *tmp_str = NULL; + char name_buf[PATH_MAX] = ""; + int32_t i = -1; + int32_t ret = -1; + int32_t src_missed_snap_count = -1; + int32_t dst_missed_snap_count = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + if (!dst || !src) { + gf_log (this->name, GF_LOG_ERROR, "Source or Destination " + "dict is empty."); + goto out; + } + + ret = dict_get_int32 (src, "missed_snap_count", + &src_missed_snap_count); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, "No missed snaps"); + ret = 0; + goto out; + } + + ret = dict_get_int32 (dst, "missed_snap_count", + &dst_missed_snap_count); + if (ret) { + /* Initialize dst_missed_count for the first time */ + dst_missed_snap_count = 0; + } + + for (i = 0; i < src_missed_snap_count; i++) { + snprintf (name_buf, sizeof(name_buf), "missed_snaps_%d", + i); + ret = dict_get_str (src, name_buf, &buf); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to fetch %s", name_buf); + goto out; + } + + snprintf (name_buf, sizeof(name_buf), "missed_snaps_%d", + dst_missed_snap_count); + + tmp_str = gf_strdup (buf); + if (!tmp_str) { + ret = -1; + goto out; + } + + ret = dict_set_dynstr (dst, name_buf, tmp_str); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to set %s", name_buf); + goto out; + } + + tmp_str = NULL; + dst_missed_snap_count++; + } + + ret = dict_set_int32 (dst, "missed_snap_count", dst_missed_snap_count); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Unable to set dst_missed_snap_count"); + goto out; + } + +out: + if (ret && tmp_str) + GF_FREE(tmp_str); + + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int +glusterd_snap_use_rsp_dict (dict_t *dst, dict_t *src) +{ + int ret = -1; + int32_t snap_command = 0; + + if (!dst || !src) { + gf_log ("", GF_LOG_ERROR, "Source or Destination " + "dict is empty."); + goto out; + } + + ret = dict_get_int32 (dst, "type", &snap_command); + if (ret) { + gf_log ("", GF_LOG_ERROR, "unable to get the type of " + "the snapshot command"); + goto out; + } + + switch (snap_command) { + case GF_SNAP_OPTION_TYPE_CREATE: + case GF_SNAP_OPTION_TYPE_DELETE: + ret = glusterd_snap_create_use_rsp_dict (dst, src); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to use rsp dict"); + goto out; + } + break; + case GF_SNAP_OPTION_TYPE_CONFIG: + ret = glusterd_snap_config_use_rsp_dict (dst, src); + if (ret) { + gf_log ("", GF_LOG_ERROR, "Unable to use rsp dict"); + goto out; + } + break; + default: + // copy the response dictinary's contents to the dict to be + // sent back to the cli + dict_copy (src, dst); + break; + } + + ret = 0; +out: + gf_log ("", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + int glusterd_sys_exec_output_rsp_dict (dict_t *dst, dict_t *src) { @@ -9146,6 +9943,102 @@ out: return is_status_tasks; } +int +glusterd_compare_snap_time(struct list_head *list1, struct list_head *list2) +{ + glusterd_snap_t *snap1 = NULL; + glusterd_snap_t *snap2 = NULL; + double diff_time = 0; + + GF_ASSERT (list1); + GF_ASSERT (list2); + + snap1 = list_entry(list1, glusterd_snap_t, snap_list); + snap2 = list_entry(list2, glusterd_snap_t, snap_list); + diff_time = difftime(snap1->time_stamp, snap2->time_stamp); + + return ((int)diff_time); +} + +int +glusterd_compare_snap_vol_time(struct list_head *list1, struct list_head *list2) +{ + glusterd_volinfo_t *snapvol1 = NULL; + glusterd_volinfo_t *snapvol2 = NULL; + double diff_time = 0; + + GF_ASSERT (list1); + GF_ASSERT (list2); + + snapvol1 = list_entry(list1, glusterd_volinfo_t, snapvol_list); + snapvol2 = list_entry(list2, glusterd_volinfo_t, snapvol_list); + diff_time = difftime(snapvol1->snapshot->time_stamp, + snapvol2->snapshot->time_stamp); + + return ((int)diff_time); +} + +int32_t +glusterd_missed_snapinfo_new (glusterd_missed_snap_info **missed_snapinfo) +{ + glusterd_missed_snap_info *new_missed_snapinfo = NULL; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (missed_snapinfo); + + new_missed_snapinfo = GF_CALLOC (1, sizeof(*new_missed_snapinfo), + gf_gld_mt_missed_snapinfo_t); + + if (!new_missed_snapinfo) + goto out; + + new_missed_snapinfo->node_snap_info = NULL; + INIT_LIST_HEAD (&new_missed_snapinfo->missed_snaps); + INIT_LIST_HEAD (&new_missed_snapinfo->snap_ops); + + *missed_snapinfo = new_missed_snapinfo; + + ret = 0; + +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + +int32_t +glusterd_missed_snap_op_new (glusterd_snap_op_t **snap_op) +{ + glusterd_snap_op_t *new_snap_op = NULL; + int32_t ret = -1; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (snap_op); + + new_snap_op = GF_CALLOC (1, sizeof(*new_snap_op), + gf_gld_mt_missed_snapinfo_t); + + if (!new_snap_op) + goto out; + + new_snap_op->brick_path = NULL; + new_snap_op->brick_num = -1; + new_snap_op->op = -1; + new_snap_op->status = -1; + INIT_LIST_HEAD (&new_snap_op->snap_ops_list); + + *snap_op = new_snap_op; + + ret = 0; +out: + gf_log (this->name, GF_LOG_TRACE, "Returning %d", ret); + return ret; +} + /* Tells if rebalance needs to be started for the given volume on the peer * * Rebalance should be started on a peer only if an involved brick is present on diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.h b/xlators/mgmt/glusterd/src/glusterd-utils.h index ec59d9143..1964c88c5 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.h +++ b/xlators/mgmt/glusterd/src/glusterd-utils.h @@ -85,6 +85,11 @@ glusterd_submit_request (struct rpc_clnt *rpc, void *req, int32_t glusterd_volinfo_new (glusterd_volinfo_t **volinfo); +int32_t +glusterd_volinfo_dup (glusterd_volinfo_t *volinfo, + glusterd_volinfo_t **dup_volinfo, + gf_boolean_t set_userauth); + char * glusterd_auth_get_username (glusterd_volinfo_t *volinfo); @@ -118,12 +123,24 @@ glusterd_peer_destroy (glusterd_peerinfo_t *peerinfo); int32_t glusterd_peer_hostname_new (char *hostname, glusterd_peer_hostname_t **name); +int32_t +glusterd_snap_volinfo_find (char *volname, glusterd_snap_t *snap, + glusterd_volinfo_t **volinfo); +int32_t +glusterd_snap_volinfo_find_from_parent_volname (char *origin_volname, + glusterd_snap_t *snap, + glusterd_volinfo_t **volinfo); + int32_t glusterd_volinfo_find (char *volname, glusterd_volinfo_t **volinfo); int glusterd_volinfo_find_by_volume_id (uuid_t volume_id, glusterd_volinfo_t **volinfo); +int +glusterd_snap_volinfo_find_by_volume_id (uuid_t volume_id, + glusterd_volinfo_t **volinfo); + int32_t glusterd_service_stop(const char *service, char *pidfile, int sig, gf_boolean_t force_kill); @@ -302,6 +319,7 @@ glusterd_is_defrag_on (glusterd_volinfo_t *volinfo); int32_t glusterd_volinfo_bricks_delete (glusterd_volinfo_t *volinfo); + int glusterd_friend_find_by_uuid (uuid_t uuid, glusterd_peerinfo_t **peerinfo); @@ -371,7 +389,7 @@ gf_boolean_t glusterd_peerinfo_is_uuid_unknown (glusterd_peerinfo_t *peerinfo); int32_t glusterd_brick_connect (glusterd_volinfo_t *volinfo, - glusterd_brickinfo_t *brickinfo); + glusterd_brickinfo_t *brickinfo, char *socketpath); int32_t glusterd_brick_disconnect (glusterd_brickinfo_t *brickinfo); int32_t @@ -379,12 +397,16 @@ glusterd_delete_volume (glusterd_volinfo_t *volinfo); int32_t glusterd_delete_brick (glusterd_volinfo_t* volinfo, glusterd_brickinfo_t *brickinfo); + int32_t glusterd_delete_all_bricks (glusterd_volinfo_t* volinfo); + int glusterd_spawn_daemons (void *opaque); + int glusterd_restart_gsyncds (glusterd_conf_t *conf); + int glusterd_start_gsync (glusterd_volinfo_t *master_vol, char *slave, char *path_list, char *conf_path, @@ -510,6 +532,8 @@ int glusterd_use_rsp_dict (dict_t *aggr, dict_t *rsp_dict); int glusterd_sys_exec_output_rsp_dict (dict_t *aggr, dict_t *rsp_dict); +int +glusterd_snap_use_rsp_dict (dict_t *aggr, dict_t *rsp_dict); int32_t glusterd_handle_node_rsp (dict_t *req_ctx, void *pending_entry, glusterd_op_t op, dict_t *rsp_dict, dict_t *op_ctx, @@ -660,4 +684,37 @@ glusterd_rpc_clnt_unref (glusterd_conf_t *conf, rpc_clnt_t *rpc); int32_t glusterd_compare_volume_name(struct list_head *, struct list_head *); +#ifdef GF_LINUX_HOST_OS +char* +glusterd_get_brick_mount_details (glusterd_brickinfo_t *brickinfo); +struct mntent * +glusterd_get_mnt_entry_info (char *mnt_pt, FILE *mtab); +int +glusterd_get_brick_root (char *path, char **mount_point); +#endif //LINUX_HOST + +int +glusterd_compare_snap_time(struct list_head *, struct list_head *); + +int +glusterd_compare_snap_vol_time(struct list_head *, struct list_head *); + +int32_t +glusterd_snap_volinfo_restore (dict_t *rsp_dict, + glusterd_volinfo_t *new_volinfo, + glusterd_volinfo_t *snap_volinfo); +int32_t +glusterd_lvm_snapshot_remove (dict_t *rsp_dict, glusterd_volinfo_t *snap_vol); + +int32_t +glusterd_missed_snapinfo_new (glusterd_missed_snap_info **missed_snapinfo); + +int32_t +glusterd_missed_snap_op_new (glusterd_snap_op_t **snap_op); + +int32_t +glusterd_add_missed_snaps_to_dict (dict_t *rsp_dict, char *snap_uuid, + glusterd_brickinfo_t *brickinfo, + int32_t brick_number, int32_t op); + #endif diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c index 9012003c9..783958279 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.c +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c @@ -28,6 +28,8 @@ #include "logging.h" #include "dict.h" #include "graph-utils.h" +#include "glusterd-store.h" +#include "glusterd-hooks.h" #include "trie.h" #include "glusterd-mem-types.h" #include "cli1-xdr.h" @@ -1648,7 +1650,8 @@ server_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, } /* Check for read-only volume option, and add it to the graph */ - if (dict_get_str_boolean (set_dict, "features.read-only", 0)) { + if (dict_get_str_boolean (set_dict, "features.read-only", 0) + || volinfo -> is_snap_volume) { xl = volgen_graph_add (graph, "features/read-only", volname); if (!xl) { ret = -1; @@ -3553,6 +3556,8 @@ generate_client_volfiles (glusterd_volinfo_t *volinfo, out: if (dict) dict_unref (dict); + + gf_log ("", GF_LOG_TRACE, "Returning %d", ret); return ret; } @@ -4182,3 +4187,128 @@ gd_is_boolean_option (char *key) return _gf_false; } + +/* This function will restore origin volume to it's snap. + * The restore operation will simply replace the Gluster origin + * volume with the snap volume. + * TODO: Multi-volume delete to be done. + * Cleanup in case of restore failure is pending. + * + * @param orig_vol volinfo of origin volume + * @param snap_vol volinfo of snapshot volume + * + * @return 0 on success and negative value on error + */ +int +gd_restore_snap_volume (dict_t *rsp_dict, + glusterd_volinfo_t *orig_vol, + glusterd_volinfo_t *snap_vol) +{ + int ret = -1; + glusterd_volinfo_t *new_volinfo = NULL; + glusterd_snap_t *snap = NULL; + xlator_t *this = NULL; + glusterd_conf_t *conf = NULL; + glusterd_volinfo_t *temp_volinfo = NULL; + glusterd_volinfo_t *voliter = NULL; + + this = THIS; + GF_ASSERT (this); + GF_ASSERT (rsp_dict); + conf = this->private; + GF_ASSERT (conf); + + GF_VALIDATE_OR_GOTO (this->name, orig_vol, out); + GF_VALIDATE_OR_GOTO (this->name, snap_vol, out); + snap = snap_vol->snapshot; + GF_VALIDATE_OR_GOTO (this->name, snap, out); + + /* Snap volume must be stoped before performing the + * restore operation. + */ + ret = glusterd_stop_volume (snap_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to stop " + "snap volume(%s)", snap_vol->volname); + goto out; + } + + /* Create a new volinfo for the restored volume */ + ret = glusterd_volinfo_dup (snap_vol, &new_volinfo, _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to create volinfo"); + goto out; + } + + /* Following entries need to be derived from origin volume. */ + strcpy (new_volinfo->volname, orig_vol->volname); + uuid_copy (new_volinfo->volume_id, orig_vol->volume_id); + new_volinfo->snap_count = orig_vol->snap_count; + new_volinfo->snap_max_hard_limit = orig_vol->snap_max_hard_limit; + new_volinfo->is_volume_restored = _gf_true; + + /* Bump the version of the restored volume, so that nodes * + * which are done can sync during handshake */ + new_volinfo->version = orig_vol->version; + + list_for_each_entry_safe (voliter, temp_volinfo, + &orig_vol->snap_volumes, snapvol_list) { + list_add_tail (&voliter->snapvol_list, + &new_volinfo->snap_volumes); + } + /* Copy the snap vol info to the new_volinfo.*/ + ret = glusterd_snap_volinfo_restore (rsp_dict, new_volinfo, snap_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to restore snap"); + (void)glusterd_volinfo_delete (new_volinfo); + goto out; + } + + /* If the orig_vol is already restored then we should delete + * the backend LVMs */ + if (orig_vol->is_volume_restored) { + ret = glusterd_lvm_snapshot_remove (rsp_dict, orig_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to remove " + "LVM backend"); + (void)glusterd_volinfo_delete (new_volinfo); + goto out; + } + } + + /* Once the new_volinfo is completely constructed then delete + * the orinal volinfo + */ + ret = glusterd_volinfo_delete (orig_vol); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to delete volinfo"); + (void)glusterd_volinfo_delete (new_volinfo); + goto out; + } + /* New volinfo always shows the status as created. Therefore + * set the status to stop. */ + glusterd_set_volume_status (new_volinfo, GLUSTERD_STATUS_STOPPED); + + list_add_tail (&new_volinfo->vol_list, &conf->volumes); + + /* Now delete the snap entry. As a first step delete the snap + * volume information stored in store. */ + ret = glusterd_snap_remove (rsp_dict, snap, _gf_false, _gf_true); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "Failed to delete " + "snap %s", snap->snapname); + goto out; + } + + ret = glusterd_store_volinfo (new_volinfo, + GLUSTERD_VOLINFO_VER_AC_INCREMENT); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to store volinfo"); + goto out; + } + + ret = 0; +out: + + return ret; +} diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.h b/xlators/mgmt/glusterd/src/glusterd-volgen.h index 1683f9050..1f9416106 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.h +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.h @@ -126,6 +126,10 @@ int glusterd_create_quotad_volfile (); int glusterd_delete_volfile (glusterd_volinfo_t *volinfo, glusterd_brickinfo_t *brickinfo); +int +glusterd_delete_snap_volfile (glusterd_volinfo_t *volinfo, + glusterd_volinfo_t *snap_volinfo, + glusterd_brickinfo_t *brickinfo); int glusterd_volinfo_get (glusterd_volinfo_t *volinfo, char *key, char **value); int glusterd_volinfo_get_boolean (glusterd_volinfo_t *volinfo, char *key); @@ -139,8 +143,15 @@ glusterd_check_voloption_flags (char *key, int32_t flags); gf_boolean_t glusterd_is_valid_volfpath (char *volname, char *brick); int generate_brick_volfiles (glusterd_volinfo_t *volinfo); +int generate_snap_brick_volfiles (glusterd_volinfo_t *volinfo, + glusterd_volinfo_t *snap_volinfo); int generate_client_volfiles (glusterd_volinfo_t *volinfo, glusterd_client_type_t client_type); +int +generate_snap_client_volfiles (glusterd_volinfo_t *actual_volinfo, + glusterd_volinfo_t *snap_volinfo, + glusterd_client_type_t client_type, + gf_boolean_t vol_restore); int glusterd_get_volopt_content (dict_t *dict, gf_boolean_t xml_out); char* glusterd_get_trans_type_rb (gf_transport_type ttype); @@ -161,4 +172,7 @@ gd_is_xlator_option (char *key); gf_boolean_t gd_is_boolean_option (char *key); +int gd_restore_snap_volume (dict_t *rsp_dict, + glusterd_volinfo_t *orig_vol, + glusterd_volinfo_t *snap_vol); #endif diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-ops.c b/xlators/mgmt/glusterd/src/glusterd-volume-ops.c index 135faa40a..eac926d95 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volume-ops.c +++ b/xlators/mgmt/glusterd/src/glusterd-volume-ops.c @@ -1211,6 +1211,16 @@ glusterd_op_stage_delete_volume (dict_t *dict, char **op_errstr) goto out; } + if (volinfo->snap_count > 0 || !list_empty(&volinfo->snap_volumes)) { + snprintf (msg, sizeof (msg), "Cannot delete Volume %s ," + "as it has %ld snapshots. " + "To delete the volume, " + "first delete all the snapshots under it.", + volname, volinfo->snap_count); + ret = -1; + goto out; + } + ret = 0; out: @@ -1772,54 +1782,48 @@ out: return ret; } - int -glusterd_op_stop_volume (dict_t *dict) +glusterd_stop_volume (glusterd_volinfo_t *volinfo) { - int ret = 0; - int flags = 0; - char *volname = NULL; - glusterd_volinfo_t *volinfo = NULL; - glusterd_brickinfo_t *brickinfo = NULL; - xlator_t *this = NULL; - char mountdir[PATH_MAX] = {0,}; - runner_t runner = {0,}; - char pidfile[PATH_MAX] = {0,}; + int ret = -1; + glusterd_brickinfo_t *brickinfo = NULL; + char mountdir[PATH_MAX] = {0,}; + runner_t runner = {0,}; + char pidfile[PATH_MAX] = {0,}; + xlator_t *this = NULL; this = THIS; GF_ASSERT (this); - ret = glusterd_op_stop_volume_args_get (dict, &volname, &flags); - if (ret) - goto out; - - ret = glusterd_volinfo_find (volname, &volinfo); - if (ret) { - gf_log (this->name, GF_LOG_ERROR, FMTSTR_CHECK_VOL_EXISTS, - volname); - goto out; - } + GF_VALIDATE_OR_GOTO (this->name, volinfo, out); list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { ret = glusterd_brick_stop (volinfo, brickinfo, _gf_false); - if (ret) + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to stop " + "brick (%s)", brickinfo->path); goto out; + } } glusterd_set_volume_status (volinfo, GLUSTERD_STATUS_STOPPED); ret = glusterd_store_volinfo (volinfo, GLUSTERD_VOLINFO_VER_AC_INCREMENT); - if (ret) + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to store volinfo of " + "%s volume", volinfo->volname); goto out; + } /* If quota auxiliary mount is present, unmount it */ - GLUSTERFS_GET_AUX_MOUNT_PIDFILE (pidfile, volname); + GLUSTERFS_GET_AUX_MOUNT_PIDFILE (pidfile, volinfo->volname); if (!gf_is_service_running (pidfile, NULL)) { gf_log (this->name, GF_LOG_DEBUG, "Aux mount of volume %s " - "absent", volname); + "absent", volinfo->volname); } else { - GLUSTERD_GET_QUOTA_AUX_MOUNT_PATH (mountdir, volname, "/"); + GLUSTERD_GET_QUOTA_AUX_MOUNT_PATH (mountdir, volinfo->volname, + "/"); runinit (&runner); runner_add_args (&runner, "umount", @@ -1837,6 +1841,45 @@ glusterd_op_stop_volume (dict_t *dict) } ret = glusterd_nodesvcs_handle_graph_change (volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to notify graph " + "change for %s volume", volinfo->volname); + goto out; + } + +out: + return ret; +} + +int +glusterd_op_stop_volume (dict_t *dict) +{ + int ret = 0; + int flags = 0; + char *volname = NULL; + glusterd_volinfo_t *volinfo = NULL; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + ret = glusterd_op_stop_volume_args_get (dict, &volname, &flags); + if (ret) + goto out; + + ret = glusterd_volinfo_find (volname, &volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, FMTSTR_CHECK_VOL_EXISTS, + volname); + goto out; + } + + ret = glusterd_stop_volume (volinfo); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to stop %s volume", + volname); + goto out; + } out: return ret; } diff --git a/xlators/mgmt/glusterd/src/glusterd.c b/xlators/mgmt/glusterd/src/glusterd.c index a5f694545..85a6b920a 100644 --- a/xlators/mgmt/glusterd/src/glusterd.c +++ b/xlators/mgmt/glusterd/src/glusterd.c @@ -56,6 +56,8 @@ extern struct rpcsvc_program gd_svc_cli_prog_ro; extern struct rpc_clnt_program gd_brick_prog; extern struct rpcsvc_program glusterd_mgmt_hndsk_prog; +extern char snap_mount_folder[PATH_MAX]; + rpcsvc_cbk_program_t glusterd_cbk_prog = { .progname = "Gluster Callback", .prognum = GLUSTER_CBK_PROGRAM, @@ -110,6 +112,7 @@ const char *gd_op_list[GD_OP_MAX + 1] = { [GD_OP_COPY_FILE] = "Copy File", [GD_OP_SYS_EXEC] = "Execute system commands", [GD_OP_GSYNC_CREATE] = "Geo-replication Create", + [GD_OP_SNAP] = "Snapshot", [GD_OP_MAX] = "Invalid op" }; @@ -1092,6 +1095,71 @@ glusterd_stop_uds_listener (xlator_t *this) return; } +static int +glusterd_init_snap_folder (xlator_t *this) +{ + int ret = -1; + struct stat buf = {0,}; + + GF_ASSERT (this); + + /* Snapshot volumes are mounted under /var/run/gluster/snaps folder. + * But /var/run is normally a symbolic link to /run folder, which + * creates problems as the entry point in the mtab for the mount point + * and glusterd maintained entry point will be different. Therefore + * identify the correct run folder and use it for snap volume mounting. + */ + ret = lstat (GLUSTERD_VAR_RUN_DIR, &buf); + if (ret != 0) { + gf_log (this->name, GF_LOG_ERROR, + "stat fails on %s, exiting. (errno = %d)", + GLUSTERD_VAR_RUN_DIR, errno); + goto out; + } + + /* If /var/run is symlink then use /run folder */ + if (S_ISLNK (buf.st_mode)) { + strcpy (snap_mount_folder, GLUSTERD_RUN_DIR); + } else { + strcpy (snap_mount_folder, GLUSTERD_VAR_RUN_DIR); + } + + strcat (snap_mount_folder, GLUSTERD_DEFAULT_SNAPS_BRICK_DIR); + + ret = stat (snap_mount_folder, &buf); + if ((ret != 0) && (ENOENT != errno)) { + gf_log (this->name, GF_LOG_ERROR, + "stat fails on %s, exiting. (errno = %d)", + snap_mount_folder, errno); + ret = -1; + goto out; + } + + if ((!ret) && (!S_ISDIR(buf.st_mode))) { + gf_log (this->name, GF_LOG_CRITICAL, + "Provided snap path %s is not a directory," + "exiting", snap_mount_folder); + ret = -1; + goto out; + } + + if ((-1 == ret) && (ENOENT == errno)) { + /* Create missing folders */ + ret = mkdir_p (snap_mount_folder, 0777, _gf_false); + + if (-1 == ret) { + gf_log (this->name, GF_LOG_CRITICAL, + "Unable to create directory %s" + " ,errno = %d", snap_mount_folder, errno); + goto out; + } + } + +out: + return ret; +} + + /* * init - called during glusterd initialization * @@ -1158,6 +1226,14 @@ init (xlator_t *this) gf_log (this->name, GF_LOG_INFO, "Using %s as working directory", workdir); + ret = glusterd_init_snap_folder (this); + + if (ret) { + gf_log (this->name, GF_LOG_CRITICAL, "Unable to create " + "snap backend folder"); + exit (1); + } + snprintf (cmd_log_filename, PATH_MAX,"%s/.cmd_log_history", DEFAULT_LOG_FILE_DIRECTORY); ret = gf_cmd_log_init (cmd_log_filename); @@ -1301,6 +1377,9 @@ init (xlator_t *this) INIT_LIST_HEAD (&conf->peers); INIT_LIST_HEAD (&conf->volumes); + INIT_LIST_HEAD (&conf->snapshots); + INIT_LIST_HEAD (&conf->missed_snaps_list); + pthread_mutex_init (&conf->mutex, NULL); conf->rpc = rpc; conf->uds_rpc = uds_rpc; @@ -1342,7 +1421,7 @@ init (xlator_t *this) } this->private = conf; - glusterd_vol_lock_init (); + glusterd_mgmt_v3_lock_init (); glusterd_txn_opinfo_dict_init (); (void) glusterd_nodesvc_set_online_status ("glustershd", _gf_false); @@ -1435,7 +1514,7 @@ fini (xlator_t *this) if (conf->handle) gf_store_handle_destroy (conf->handle); glusterd_sm_tr_log_delete (&conf->op_sm_log); - glusterd_vol_lock_fini (); + glusterd_mgmt_v3_lock_fini (); glusterd_txn_opinfo_dict_fini (); GF_FREE (conf); @@ -1553,5 +1632,9 @@ struct volume_options options[] = { .type = GF_OPTION_TYPE_INT, .description = "Sets the base port for portmap query" }, + { .key = {"snap-brick-path"}, + .type = GF_OPTION_TYPE_STR, + .description = "directory where the bricks for the snapshots will be created" + }, { .key = {NULL} }, }; diff --git a/xlators/mgmt/glusterd/src/glusterd.h b/xlators/mgmt/glusterd/src/glusterd.h index 0694f7386..7568a2e9a 100644 --- a/xlators/mgmt/glusterd/src/glusterd.h +++ b/xlators/mgmt/glusterd/src/glusterd.h @@ -52,6 +52,9 @@ "S56glusterd-geo-rep-create-post.sh" +#define GLUSTERD_SNAPS_MAX_HARD_LIMIT 256 +#define GLUSTERD_SNAPS_DEF_SOFT_LIMIT_PERCENT 90 +#define GLUSTERD_SNAPS_MAX_SOFT_LIMIT_PERCENT 100 #define GLUSTERD_SERVER_QUORUM "server" #define FMTSTR_CHECK_VOL_EXISTS "Volume %s does not exist" @@ -72,6 +75,9 @@ struct glusterd_volinfo_; typedef struct glusterd_volinfo_ glusterd_volinfo_t; +struct glusterd_snap_; +typedef struct glusterd_snap_ glusterd_snap_t; + typedef enum glusterd_op_ { GD_OP_NONE = 0, GD_OP_CREATE_VOLUME, @@ -101,6 +107,7 @@ typedef enum glusterd_op_ { GD_OP_COPY_FILE, GD_OP_SYS_EXEC, GD_OP_GSYNC_CREATE, + GD_OP_SNAP, GD_OP_MAX, } glusterd_op_t; @@ -136,13 +143,15 @@ typedef struct { nodesrv_t *quotad; struct pmap_registry *pmap; struct list_head volumes; + struct list_head snapshots; /*List of snap volumes */ pthread_mutex_t xprt_lock; struct list_head xprt_list; gf_store_handle_t *handle; gf_timer_t *timer; glusterd_sm_tr_log_t op_sm_log; struct rpc_clnt_program *gfs_mgmt; - dict_t *vol_lock; /* Dict for saving vol locks */ + dict_t *mgmt_v3_lock; /* Dict for saving + * mgmt_v3 locks */ dict_t *glusterd_txn_opinfo; /* Dict for saving * transaction opinfos */ uuid_t global_txn_id; /* To be used in @@ -150,20 +159,25 @@ typedef struct { * cluster with no * transaction ids */ - struct list_head mount_specs; - gf_boolean_t valgrind; - pthread_t brick_thread; - void *hooks_priv; + struct list_head mount_specs; + gf_boolean_t valgrind; + pthread_t brick_thread; + void *hooks_priv; /* need for proper handshake_t */ - int op_version; /* Starts with 1 for 3.3.0 */ - xlator_t *xl; /* Should be set to 'THIS' before creating thread */ - gf_boolean_t pending_quorum_action; - dict_t *opts; - synclock_t big_lock; - gf_boolean_t restart_done; - rpcsvc_t *uds_rpc; /* RPCSVC for the unix domain socket */ - uint32_t base_port; + int op_version; /* Starts with 1 for 3.3.0 */ + xlator_t *xl; /* Should be set to 'THIS' before creating thread */ + gf_boolean_t pending_quorum_action; + dict_t *opts; + synclock_t big_lock; + gf_boolean_t restart_done; + rpcsvc_t *uds_rpc; /* RPCSVC for the unix domain socket */ + uint32_t base_port; + uint64_t snap_max_hard_limit; + uint64_t snap_max_soft_limit; + char *snap_bricks_directory; + gf_store_handle_t *missed_snaps_list_shandle; + struct list_head missed_snaps_list; } glusterd_conf_t; @@ -175,6 +189,7 @@ typedef enum gf_brick_status { struct glusterd_brickinfo { char hostname[1024]; char path[PATH_MAX]; + char device_path[PATH_MAX]; char brick_id[1024];/*Client xlator name, AFR changelog name*/ struct list_head brick_list; uuid_t uuid; @@ -188,6 +203,7 @@ struct glusterd_brickinfo { int decommissioned; char vg[PATH_MAX]; /* FIXME: Use max size for length of vg */ int caps; /* Capability */ + int32_t snap_status; }; typedef struct glusterd_brickinfo glusterd_brickinfo_t; @@ -278,11 +294,36 @@ struct glusterd_replace_brick_ { typedef struct glusterd_replace_brick_ glusterd_replace_brick_t; struct glusterd_volinfo_ { + gf_lock_t lock; char volname[GLUSTERD_MAX_VOLUME_NAME]; + gf_boolean_t is_snap_volume; + glusterd_snap_t *snapshot; + gf_boolean_t is_volume_restored; + char parent_volname[GLUSTERD_MAX_VOLUME_NAME]; + /* In case of a snap volume + i.e (is_snap_volume == TRUE) this + field will contain the name of + the volume which is snapped. In + case of a non-snap volume, this + field will be initialized as N/A */ int type; int brick_count; + uint64_t snap_count; + uint64_t snap_max_hard_limit; struct list_head vol_list; + /* In case of a snap volume + i.e (is_snap_volume == TRUE) this + is linked to glusterd_snap_t->volumes. + In case of a non-snap volume, this is + linked to glusterd_conf_t->volumes */ + struct list_head snapvol_list; + /* This is a current pointer for + glusterd_volinfo_t->snap_volumes */ struct list_head bricks; + struct list_head snap_volumes; + /* TODO : Need to remove this, as this + * is already part of snapshot object. + */ glusterd_volume_status status; int sub_count; /* backward compatibility */ int stripe_count; @@ -291,11 +332,11 @@ struct glusterd_volinfo_ { distribute volume */ int dist_leaf_count; /* Number of bricks in one distribute subvolume */ - int port; - gf_store_handle_t *shandle; - gf_store_handle_t *rb_shandle; - gf_store_handle_t *node_state_shandle; - gf_store_handle_t *quota_conf_shandle; + int port; + gf_store_handle_t *shandle; + gf_store_handle_t *rb_shandle; + gf_store_handle_t *node_state_shandle; + gf_store_handle_t *quota_conf_shandle; /* Defrag/rebalance related */ glusterd_rebalance_t rebal; @@ -303,12 +344,12 @@ struct glusterd_volinfo_ { /* Replace brick status */ glusterd_replace_brick_t rep_brick; - int version; - uint32_t quota_conf_version; - uint32_t cksum; - uint32_t quota_conf_cksum; - gf_transport_type transport_type; - gf_transport_type nfs_transport_type; + int version; + uint32_t quota_conf_version; + uint32_t cksum; + uint32_t quota_conf_cksum; + gf_transport_type transport_type; + gf_transport_type nfs_transport_type; dict_t *dict; @@ -330,6 +371,41 @@ struct glusterd_volinfo_ { int refcnt; }; +typedef enum gd_snap_status_ { + GD_SNAP_STATUS_NONE, + GD_SNAP_STATUS_INIT, + GD_SNAP_STATUS_IN_USE, + GD_SNAP_STATUS_DECOMMISSION, + GD_SNAP_STATUS_RESTORED, +} gd_snap_status_t; + +struct glusterd_snap_ { + gf_lock_t lock; + struct list_head volumes; + struct list_head snap_list; + char snapname[GLUSTERD_MAX_SNAP_NAME]; + uuid_t snap_id; + char *description; + time_t time_stamp; + gf_boolean_t snap_restored; + gd_snap_status_t snap_status; + gf_store_handle_t *shandle; +}; + +typedef struct glusterd_snap_op_ { + int32_t brick_num; + char *brick_path; + int32_t op; + int32_t status; + struct list_head snap_ops_list; +} glusterd_snap_op_t; + +typedef struct glusterd_missed_snap_ { + char *node_snap_info; + struct list_head missed_snaps; + struct list_head snap_ops; +} glusterd_missed_snap_info; + typedef enum gd_node_type_ { GD_NODE_NONE, GD_NODE_BRICK, @@ -339,6 +415,12 @@ typedef enum gd_node_type_ { GD_NODE_QUOTAD, } gd_node_type; +typedef enum missed_snap_stat { + GD_MISSED_SNAP_NONE, + GD_MISSED_SNAP_PENDING, + GD_MISSED_SNAP_DONE, +} missed_snap_stat; + typedef struct glusterd_pending_node_ { struct list_head list; void *node; @@ -371,12 +453,19 @@ enum glusterd_vol_comp_status_ { #define GLUSTERD_VOLUME_DIR_PREFIX "vols" #define GLUSTERD_PEER_DIR_PREFIX "peers" #define GLUSTERD_VOLUME_INFO_FILE "info" +#define GLUSTERD_SNAP_INFO_FILE "info" #define GLUSTERD_VOLUME_RBSTATE_FILE "rbstate" #define GLUSTERD_BRICK_INFO_DIR "bricks" #define GLUSTERD_CKSUM_FILE "cksum" #define GLUSTERD_VOL_QUOTA_CKSUM_FILE "quota.cksum" #define GLUSTERD_TRASH "trash" #define GLUSTERD_NODE_STATE_FILE "node_state.info" +#define GLUSTERD_MISSED_SNAPS_LIST_FILE "missed_snaps_list" +#define GLUSTERD_VOL_SNAP_DIR_PREFIX "snaps" + +#define GLUSTERD_DEFAULT_SNAPS_BRICK_DIR "/gluster/snaps" +#define GLUSTERD_VAR_RUN_DIR "/var/run" +#define GLUSTERD_RUN_DIR "/run" /* definitions related to replace brick */ #define RB_CLIENT_MOUNTPOINT "rb_mount" @@ -389,14 +478,29 @@ enum glusterd_vol_comp_status_ { typedef ssize_t (*gd_serialize_t) (struct iovec outmsg, void *args); -#define GLUSTERD_GET_VOLUME_DIR(path, volinfo, priv) \ - snprintf (path, PATH_MAX, "%s/vols/%s", priv->workdir,\ - volinfo->volname); +#define GLUSTERD_GET_VOLUME_DIR(path, volinfo, priv) \ + if (volinfo->is_snap_volume) { \ + snprintf (path, PATH_MAX, "%s/snaps/%s/%s", priv->workdir, \ + volinfo->snapshot->snapname, volinfo->volname); \ + } else { \ + snprintf (path, PATH_MAX, "%s/vols/%s", priv->workdir, \ + volinfo->volname); \ + } -#define GLUSTERD_GET_BRICK_DIR(path, volinfo, priv) \ - snprintf (path, PATH_MAX, "%s/%s/%s/%s", priv->workdir,\ - GLUSTERD_VOLUME_DIR_PREFIX, volinfo->volname, \ - GLUSTERD_BRICK_INFO_DIR); +#define GLUSTERD_GET_SNAP_DIR(path, snap, priv) \ + snprintf (path, PATH_MAX, "%s/snaps/%s", priv->workdir, \ + snap->snapname); + +#define GLUSTERD_GET_BRICK_DIR(path, volinfo, priv) \ + if (volinfo->is_snap_volume) { \ + snprintf (path, PATH_MAX, "%s/snaps/%s/%s/%s", priv->workdir, \ + volinfo->snapshot->snapname, volinfo->volname, \ + GLUSTERD_BRICK_INFO_DIR); \ + } else { \ + snprintf (path, PATH_MAX, "%s/%s/%s/%s", priv->workdir, \ + GLUSTERD_VOLUME_DIR_PREFIX, volinfo->volname, \ + GLUSTERD_BRICK_INFO_DIR); \ + } #define GLUSTERD_GET_NFS_DIR(path, priv) \ snprintf (path, PATH_MAX, "%s/nfs", priv->workdir); @@ -423,7 +527,7 @@ typedef ssize_t (*gd_serialize_t) (struct iovec outmsg, void *args); GLUSTERD_GET_VOLUME_DIR (volpath, volinfo, priv); \ GLUSTERD_REMOVE_SLASH_FROM_PATH (brickinfo->path, exp_path); \ snprintf (pidfile, PATH_MAX, "%s/run/%s-%s.pid", \ - volpath, brickinfo->hostname, exp_path); \ + volpath, brickinfo->hostname, exp_path); \ } while (0) #define GLUSTERD_GET_NFS_PIDFILE(pidfile,nfspath) { \ @@ -472,6 +576,20 @@ typedef ssize_t (*gd_serialize_t) (struct iovec outmsg, void *args); DEFAULT_VAR_RUN_DIRECTORY"/%s.pid", volname); \ } +#define GLUSTERD_GET_UUID_NOHYPHEN(ret_string, uuid) do { \ + char *snap_volname_ptr = ret_string; \ + char *snap_volid_ptr = uuid_utoa(uuid); \ + while (*snap_volid_ptr) { \ + if (*snap_volid_ptr == '-') { \ + snap_volid_ptr++; \ + } else { \ + (*snap_volname_ptr++) = \ + (*snap_volid_ptr++); \ + } \ + } \ + *snap_volname_ptr = '\0'; \ + } while (0) + int glusterd_uuid_init(); int glusterd_uuid_generate_save (); @@ -533,12 +651,12 @@ int glusterd_op_unlock_send_resp (rpcsvc_request_t *req, int32_t status); int -glusterd_op_volume_lock_send_resp (rpcsvc_request_t *req, - uuid_t *txn_id, int32_t status); +glusterd_op_mgmt_v3_lock_send_resp (rpcsvc_request_t *req, + uuid_t *txn_id, int32_t status); int -glusterd_op_volume_unlock_send_resp (rpcsvc_request_t *req, - uuid_t *txn_id, int32_t status); +glusterd_op_mgmt_v3_unlock_send_resp (rpcsvc_request_t *req, + uuid_t *txn_id, int32_t status); int glusterd_op_stage_send_resp (rpcsvc_request_t *req, @@ -756,6 +874,9 @@ int glusterd_handle_cli_heal_volume (rpcsvc_request_t *req); int glusterd_handle_cli_list_volume (rpcsvc_request_t *req); +int +glusterd_handle_snapshot (rpcsvc_request_t *req); + /* op-sm functions */ int glusterd_op_stage_heal_volume (dict_t *dict, char **op_errstr); int glusterd_op_heal_volume (dict_t *dict, char **op_errstr); @@ -808,6 +929,9 @@ int glusterd_op_statedump_volume_args_get (dict_t *dict, char **volname, int glusterd_op_gsync_args_get (dict_t *dict, char **op_errstr, char **master, char **slave, char **host_uuid); + +int glusterd_stop_volume (glusterd_volinfo_t *volinfo); + /* Synctask part */ int32_t glusterd_op_begin_synctask (rpcsvc_request_t *req, glusterd_op_t op, void *dict); @@ -823,4 +947,50 @@ glusterd_txn_opinfo_dict_fini (); void glusterd_txn_opinfo_init (); +/* snapshot */ +glusterd_snap_t* +glusterd_new_snap_object(); + +int32_t +glusterd_list_add_snapvol (glusterd_volinfo_t *origin_vol, + glusterd_volinfo_t *snap_vol); + +glusterd_snap_t* +glusterd_remove_snap_by_id (uuid_t snap_id); + +glusterd_snap_t* +glusterd_remove_snap_by_name (char *snap_name); + +glusterd_snap_t* +glusterd_find_snap_by_name (char *snap_name); + +glusterd_snap_t* +glusterd_find_snap_by_id (uuid_t snap_id); + +int +glusterd_snapshot_prevalidate (dict_t *dict, char **op_errstr, + dict_t *rsp_dict); +int +glusterd_snapshot_brickop (dict_t *dict, char **op_errstr, dict_t *rsp_dict); +int +glusterd_snapshot (dict_t *dict, char **op_errstr, dict_t *rsp_dict); +int +glusterd_snapshot_postvalidate (dict_t *dict, int32_t op_ret, char **op_errstr, + dict_t *rsp_dict); +char * +glusterd_build_snap_device_path (char *device, char *snapname); +int32_t +glusterd_snap_remove (dict_t *rsp_dict, glusterd_snap_t *snap, + gf_boolean_t remove_lvm, gf_boolean_t force); +int32_t +glusterd_snapshot_cleanup (dict_t *dict, char **op_errstr, dict_t *rsp_dict); + +int32_t +glusterd_add_missed_snaps_to_list (dict_t *dict, int32_t missed_snap_count); + +int32_t +glusterd_store_missed_snaps_list (char *missed_info, int32_t brick_num, + char *brick_path, int32_t snap_op, + int32_t snap_status); + #endif diff --git a/xlators/protocol/server/src/server-helpers.c b/xlators/protocol/server/src/server-helpers.c index c11011abf..76c0036e0 100644 --- a/xlators/protocol/server/src/server-helpers.c +++ b/xlators/protocol/server/src/server-helpers.c @@ -1092,3 +1092,327 @@ out: return ret; } + +int32_t +gf_barrier_transmit (server_conf_t *conf, gf_barrier_payload_t *payload) +{ + gf_barrier_t *barrier = NULL; + int32_t ret = -1; + client_t *client = NULL; + gf_boolean_t lk_heal = _gf_false; + call_frame_t *frame = NULL; + server_state_t *state = NULL; + + GF_VALIDATE_OR_GOTO ("barrier", conf, out); + GF_VALIDATE_OR_GOTO ("barrier", conf->barrier, out); + GF_VALIDATE_OR_GOTO ("barrier", payload, out); + + barrier = conf->barrier; + + frame = payload->frame; + if (frame) { + state = CALL_STATE (frame); + frame->local = NULL; + client = frame->root->client; + } + /* currently lk fops are not barrier'ed. This is reflecting code in + * server_submit_reply */ + if (client) + lk_heal = ((server_conf_t *) client->this->private)->lk_heal; + + ret = rpcsvc_submit_generic (payload->req, &payload->rsp, 1, + payload->payload, payload->payload_count, + payload->iobref); + iobuf_unref (payload->iob); + if (ret == -1) { + gf_log_callingfn ("", GF_LOG_ERROR, "Reply submission failed"); + if (frame && client && !lk_heal) { + server_connection_cleanup (frame->this, client, + INTERNAL_LOCKS | POSIX_LOCKS); + } else { + /* TODO: Failure of open(dir), create, inodelk, entrylk + or lk fops send failure must be handled specially. */ + } + goto ret; + } + + ret = 0; +ret: + if (state) { + free_state (state); + } + + if (frame) { + gf_client_unref (client); + STACK_DESTROY (frame->root); + } + + if (payload->free_iobref) { + iobref_unref (payload->iobref); + } +out: + return ret; +} + +gf_barrier_payload_t * +gf_barrier_dequeue (gf_barrier_t *barrier) +{ + gf_barrier_payload_t *payload = NULL; + + if (!barrier || list_empty (&barrier->queue)) + return NULL; + + payload = list_entry (barrier->queue.next, + gf_barrier_payload_t, list); + if (payload) { + list_del_init (&payload->list); + barrier->cur_size--; + } + + return payload; +} + + +void* +gf_barrier_dequeue_start (void *data) +{ + server_conf_t *conf = NULL; + gf_barrier_t *barrier = NULL; + gf_barrier_payload_t *payload = NULL; + + conf = (server_conf_t *)data; + if (!conf || !conf->barrier) + return NULL; + barrier = conf->barrier; + + LOCK (&barrier->lock); + { + while (barrier->cur_size) { + payload = gf_barrier_dequeue (barrier); + if (payload) { + if (gf_barrier_transmit (conf, payload)) { + gf_log ("server", GF_LOG_WARNING, + "Failed to transmit"); + } + GF_FREE (payload); + } + } + } + UNLOCK (&barrier->lock); + return NULL; +} + +void +gf_barrier_timeout (void *data) +{ + server_conf_t *conf = NULL; + gf_barrier_t *barrier = NULL; + gf_boolean_t need_dequeue = _gf_false; + + conf = (server_conf_t *)data; + if (!conf || !conf->barrier) + goto out; + barrier = conf->barrier; + + gf_log ("", GF_LOG_INFO, "barrier timed-out"); + LOCK (&barrier->lock); + { + need_dequeue = barrier->on; + barrier->on = _gf_false; + } + UNLOCK (&barrier->lock); + + if (need_dequeue == _gf_true) + gf_barrier_dequeue_start (data); +out: + return; +} + + +int32_t +gf_barrier_start (xlator_t *this) +{ + server_conf_t *conf = NULL; + gf_barrier_t *barrier = NULL; + int32_t ret = -1; + struct timespec time = {0,}; + + conf = this->private; + + GF_VALIDATE_OR_GOTO ("server", this, out); + GF_VALIDATE_OR_GOTO (this->name, conf, out); + GF_VALIDATE_OR_GOTO (this->name, conf->barrier, out); + + barrier = conf->barrier; + + gf_log (this->name, GF_LOG_INFO, "barrier start called"); + LOCK (&barrier->lock); + { + /* if barrier is on, reset timer */ + if (barrier->on == _gf_true) { + ret = gf_timer_call_cancel (this->ctx, barrier->timer); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "unset timer, failing barrier start"); + goto unlock; + } + } + + barrier->on = _gf_true; + time.tv_sec = barrier->time_out; + time.tv_nsec = 0; + + barrier->timer = gf_timer_call_after (this->ctx, time, + gf_barrier_timeout, + (void *)conf); + if (!barrier->timer) { + gf_log (this->name, GF_LOG_ERROR, "Failed to set " + "timer, failing barrier start"); + barrier->on = _gf_false; + } + } +unlock: + UNLOCK (&barrier->lock); + + ret = 0; +out: + return ret; +} + +int32_t +gf_barrier_stop (xlator_t *this) +{ + server_conf_t *conf = NULL; + gf_barrier_t *barrier = NULL; + int32_t ret = -1; + gf_boolean_t need_dequeue = _gf_false; + + conf = this->private; + + GF_VALIDATE_OR_GOTO ("server", this, out); + GF_VALIDATE_OR_GOTO (this->name, conf, out); + GF_VALIDATE_OR_GOTO (this->name, conf->barrier, out); + + barrier = conf->barrier; + + gf_log (this->name, GF_LOG_INFO, "barrier stop called"); + LOCK (&barrier->lock); + { + need_dequeue = barrier->on; + barrier->on = _gf_false; + } + UNLOCK (&barrier->lock); + + if (need_dequeue == _gf_true) { + gf_timer_call_cancel (this->ctx, barrier->timer); + ret = gf_thread_create (&conf->barrier_th, NULL, + gf_barrier_dequeue_start, + conf); + if (ret) { + gf_log (this->name, GF_LOG_CRITICAL, + "Failed to start un-barriering"); + goto out; + } + } + ret = 0; +out: + return ret; +} + +int32_t +gf_barrier_fops_configure (xlator_t *this, gf_barrier_t *barrier, char *str) +{ + int32_t ret = -1; + char *dup_str = NULL; + char *str_tok = NULL; + char *save_ptr = NULL; + uint64_t fops = 0; + + /* by defaul fsync & flush needs to be barriered */ + + fops |= 1 << GFS3_OP_FSYNC; + fops |= 1 << GFS3_OP_FLUSH; + + if (!str) + goto done; + + dup_str = gf_strdup (str); + if (!dup_str) + goto done; + + str_tok = strtok_r (dup_str, ",", &save_ptr); + if (!str_tok) + goto done; + + fops = 0; + while (str_tok) { + if (!strcmp(str_tok, "writev")) { + fops |= ((uint64_t)1 << GFS3_OP_WRITE); + } else if (!strcmp(str_tok, "fsync")) { + fops |= ((uint64_t)1 << GFS3_OP_FSYNC); + } else if (!strcmp(str_tok, "read")) { + fops |= ((uint64_t)1 << GFS3_OP_READ); + } else if (!strcmp(str_tok, "rename")) { + fops |= ((uint64_t)1 << GFS3_OP_RENAME); + } else if (!strcmp(str_tok, "flush")) { + fops |= ((uint64_t)1 << GFS3_OP_FLUSH); + } else if (!strcmp(str_tok, "ftruncate")) { + fops |= ((uint64_t)1 << GFS3_OP_FTRUNCATE); + } else if (!strcmp(str_tok, "fallocate")) { + fops |= ((uint64_t)1 << GFS3_OP_FALLOCATE); + } else if (!strcmp(str_tok, "rmdir")) { + fops |= ((uint64_t)1 << GFS3_OP_RMDIR); + } else { + gf_log ("barrier", GF_LOG_ERROR, + "Invalid barrier fop %s", str_tok); + } + + str_tok = strtok_r (NULL, ",", &save_ptr); + } +done: + LOCK (&barrier->lock); + { + barrier->fops = fops; + } + UNLOCK (&barrier->lock); + ret = 0; + + GF_FREE (dup_str); + return ret; +} + +void +gf_barrier_enqueue (gf_barrier_t *barrier, gf_barrier_payload_t *payload) +{ + list_add_tail (&payload->list, &barrier->queue); + barrier->cur_size++; +} + +gf_barrier_payload_t * +gf_barrier_payload (rpcsvc_request_t *req, struct iovec *rsp, + call_frame_t *frame, struct iovec *payload_orig, + int payloadcount, struct iobref *iobref, + struct iobuf *iob, gf_boolean_t free_iobref) +{ + gf_barrier_payload_t *payload = NULL; + + if (!rsp) + return NULL; + + payload = GF_CALLOC (1, sizeof (*payload),1); + if (!payload) + return NULL; + + INIT_LIST_HEAD (&payload->list); + + payload->req = req; + memcpy (&payload->rsp, rsp, sizeof (struct iovec)); + payload->frame = frame; + payload->payload = payload_orig; + payload->payload_count = payloadcount; + payload->iobref = iobref; + payload->iob = iob; + payload->free_iobref = free_iobref; + + return payload; +} diff --git a/xlators/protocol/server/src/server-helpers.h b/xlators/protocol/server/src/server-helpers.h index 3c257b3bc..486048b8a 100644 --- a/xlators/protocol/server/src/server-helpers.h +++ b/xlators/protocol/server/src/server-helpers.h @@ -29,6 +29,10 @@ #define IS_NOT_ROOT(pathlen) ((pathlen > 2)? 1 : 0) +#define is_fop_barriered(fops, procnum) (fops & ((uint64_t)1 << procnum)) + +#define barrier_add_to_queue(barrier) (barrier->on || barrier->cur_size) + void free_state (server_state_t *state); void server_loc_wipe (loc_t *loc); @@ -58,4 +62,15 @@ int auth_set_username_passwd (dict_t *input_params, dict_t *config_params, server_ctx_t *server_ctx_get (client_t *client, xlator_t *xlator); +int32_t gf_barrier_start (xlator_t *this); +int32_t gf_barrier_stop (xlator_t *this); +int32_t gf_barrier_fops_configure (xlator_t *this, gf_barrier_t *barrier, + char *str); +void gf_barrier_enqueue (gf_barrier_t *barrier, gf_barrier_payload_t *stub); +gf_barrier_payload_t * +gf_barrier_payload (rpcsvc_request_t *req, struct iovec *rsp, + call_frame_t *frame, struct iovec *payload, + int payloadcount, struct iobref *iobref, + struct iobuf *iob, gf_boolean_t free_iobref); + #endif /* !_SERVER_HELPERS_H */ diff --git a/xlators/protocol/server/src/server.c b/xlators/protocol/server/src/server.c index 378aa393a..785517058 100644 --- a/xlators/protocol/server/src/server.c +++ b/xlators/protocol/server/src/server.c @@ -130,8 +130,6 @@ ret: return iob; } - - int server_submit_reply (call_frame_t *frame, rpcsvc_request_t *req, void *arg, struct iovec *payload, int payloadcount, @@ -144,6 +142,10 @@ server_submit_reply (call_frame_t *frame, rpcsvc_request_t *req, void *arg, char new_iobref = 0; client_t *client = NULL; gf_boolean_t lk_heal = _gf_false; + server_conf_t *conf = NULL; + gf_barrier_t *barrier = NULL; + gf_barrier_payload_t *stub = NULL; + gf_boolean_t barriered = _gf_false; GF_VALIDATE_OR_GOTO ("server", req, ret); @@ -151,6 +153,7 @@ server_submit_reply (call_frame_t *frame, rpcsvc_request_t *req, void *arg, state = CALL_STATE (frame); frame->local = NULL; client = frame->root->client; + conf = (server_conf_t *) client->this->private; } if (client) @@ -173,6 +176,32 @@ server_submit_reply (call_frame_t *frame, rpcsvc_request_t *req, void *arg, iobref_add (iobref, iob); + if (conf) + barrier = conf->barrier; + if (barrier) { + /* todo: write's with fd flags set to O_SYNC and O_DIRECT */ + LOCK (&barrier->lock); + { + if (is_fop_barriered (barrier->fops, req->procnum) && + (barrier_add_to_queue (barrier))) { + stub = gf_barrier_payload (req, &rsp, frame, + payload, + payloadcount, iobref, + iob, new_iobref); + if (stub) { + gf_barrier_enqueue (barrier, stub); + barriered = _gf_true; + } else { + gf_log ("", GF_LOG_ERROR, "Failed to " + " barrier fop %"PRIu64, + ((uint64_t)1 << req->procnum)); + } + } + } + UNLOCK (&barrier->lock); + if (barriered == _gf_true) + goto out; + } /* Then, submit the message for transmission. */ ret = rpcsvc_submit_generic (req, &rsp, 1, payload, payloadcount, iobref); @@ -214,7 +243,7 @@ ret: if (new_iobref) { iobref_unref (iobref); } - +out: return ret; } @@ -798,6 +827,8 @@ init (xlator_t *this) server_conf_t *conf = NULL; rpcsvc_listener_t *listener = NULL; char *statedump_path = NULL; + gf_barrier_t *barrier = NULL; + char *str = NULL; GF_VALIDATE_OR_GOTO ("init", this, out); if (this->children == NULL) { @@ -946,6 +977,37 @@ init (xlator_t *this) } } #endif + /* barrier related */ + barrier = GF_CALLOC (1, sizeof (*barrier),1); + if (!barrier) { + gf_log (this->name, GF_LOG_WARNING, + "WARNING: Failed to allocate barrier"); + ret = -1; + goto out; + } + + LOCK_INIT (&barrier->lock); + INIT_LIST_HEAD (&barrier->queue); + barrier->on = _gf_false; + + GF_OPTION_INIT ("barrier-queue-length", barrier->max_size, + int64, out); + GF_OPTION_INIT ("barrier-timeout", barrier->time_out, + uint64, out); + + ret = dict_get_str (this->options, "barrier-fops", &str); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, + "setting barrier fops to default value"); + } + ret = gf_barrier_fops_configure (this, barrier, str); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "invalid barrier fops specified"); + goto out; + } + + conf->barrier = barrier; this->private = conf; ret = 0; @@ -999,12 +1061,48 @@ int notify (xlator_t *this, int32_t event, void *data, ...) { int ret = 0; + int32_t val = 0; + dict_t *dict = NULL; + dict_t *output = NULL; + va_list ap; + + dict = data; + va_start (ap, data); + output = va_arg (ap, dict_t*); + va_end (ap); + switch (event) { + case GF_EVENT_VOLUME_BARRIER_OP: + ret = dict_get_int32 (dict, "barrier", &val); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "Wrong BARRIER event"); + goto out; + } + /* !val un-barrier, if val, barrier */ + if (val) { + ret = gf_barrier_start (this); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "Barrier start failed"); + } else { + ret = gf_barrier_stop (this); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "Barrier stop failed"); + } + ret = dict_set_int32 (output, "barrier-status", ret); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "Failed to set barrier-status in dict"); + break; + + /* todo: call default_notify to make other xlators handle it.*/ default: default_notify (this, event, data); break; } - +out: return ret; } @@ -1128,6 +1226,23 @@ struct volume_options options[] = { "overrides the auth.allow option. By default, all" " connections are allowed." }, - + {.key = {"barrier-timeout"}, + .type = GF_OPTION_TYPE_INT, + .default_value = "60", + .min = 0, + .max = 360, + .description = "Barrier timeout in seconds", + }, + {.key = {"barrier-queue-length"}, + .type = GF_OPTION_TYPE_INT, + .default_value = "4096", + .min = 0, + .max = 16384, + .description = "Barrier queue length", + }, + {.key = {"barrier-fops"}, + .type = GF_OPTION_TYPE_STR, + .description = "Allow a comma seperated fop lists", + }, { .key = {NULL} }, }; diff --git a/xlators/protocol/server/src/server.h b/xlators/protocol/server/src/server.h index 43e84921c..782327d77 100644 --- a/xlators/protocol/server/src/server.h +++ b/xlators/protocol/server/src/server.h @@ -28,6 +28,34 @@ #define GF_MAX_SOCKET_WINDOW_SIZE (1 * GF_UNIT_MB) #define GF_MIN_SOCKET_WINDOW_SIZE (0) +struct _gf_barrier_payload { + rpcsvc_request_t *req; + struct iovec rsp; + call_frame_t *frame; + struct iovec *payload; + struct iobref *iobref; + struct iobuf *iob; + int payload_count; + gf_boolean_t free_iobref; + struct list_head list; +}; + +typedef struct _gf_barrier_payload gf_barrier_payload_t; + +struct _gf_barrier { + gf_lock_t lock; + gf_boolean_t on; + gf_boolean_t force; + size_t cur_size; + int64_t max_size; + uint64_t fops; + gf_timer_t *timer; + uint64_t time_out; + struct list_head queue; +}; + +typedef struct _gf_barrier gf_barrier_t; + typedef enum { INTERNAL_LOCKS = 1, POSIX_LOCKS = 2, @@ -56,7 +84,9 @@ struct server_conf { struct timespec grace_ts; dict_t *auth_modules; pthread_mutex_t mutex; + gf_barrier_t *barrier; struct list_head xprt_list; + pthread_t barrier_th; }; typedef struct server_conf server_conf_t; -- cgit From 0e7f8af0db8201ee892979713ac86d5548f5ec73 Mon Sep 17 00:00:00 2001 From: Kaushal M Date: Thu, 11 Jul 2013 19:42:16 +0530 Subject: cli,glusterd: Improve detach check validation This patch improves the validation for the 'peer detach' command. A check for if volumes exist with some bricks on the peer being detached validation is added in peer detach code flow (even force would have this validation). This patch also gurantees that peer detach doesn't fail for a volume with all its brick on the peer which is getting detached and there are no other bricks on this peer. The following steps need to be followed for removing a downed and unrecoverable peer. * If a replacement system is available - add it to the cluster - use replace-brick to migrate bricks of the downed peer to the new peer (since data cannot be recovered anyway use the 'replace-brick commit force' command) or, If no replacement system is available, - remove bricks of the downed peer using 'remove-brick' Change-Id: Ie85ac5b66e87bec365fdedd8352b645bb25e1c33 BUG: 983590 Signed-off-by: Kaushal M Signed-off-by: Atin Mukherjee Reviewed-on: http://review.gluster.org/5325 Reviewed-by: Krishnan Parthasarathi Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- xlators/mgmt/glusterd/src/glusterd-handler.c | 39 +++++++++++++++++----------- xlators/mgmt/glusterd/src/glusterd-sm.c | 9 +++++++ xlators/mgmt/glusterd/src/glusterd-utils.c | 30 ++++++++++++++------- xlators/mgmt/glusterd/src/glusterd-utils.h | 2 +- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/xlators/mgmt/glusterd/src/glusterd-handler.c b/xlators/mgmt/glusterd/src/glusterd-handler.c index 58c030ca0..53c402136 100644 --- a/xlators/mgmt/glusterd/src/glusterd-handler.c +++ b/xlators/mgmt/glusterd/src/glusterd-handler.c @@ -1167,6 +1167,8 @@ __glusterd_handle_cli_deprobe (rpcsvc_request_t *req) char *hostname = NULL; int port = 0; int flags = 0; + glusterd_volinfo_t *volinfo = NULL; + glusterd_volinfo_t *tmp = NULL; this = THIS; GF_ASSERT (this); @@ -1207,7 +1209,6 @@ __glusterd_handle_cli_deprobe (rpcsvc_request_t *req) gf_log (this->name, GF_LOG_ERROR, "Failed to get port"); goto out; } - ret = dict_get_int32 (dict, "flags", &flags); if (ret) { gf_log (this->name, GF_LOG_ERROR, "Failed to get flags"); @@ -1227,22 +1228,30 @@ __glusterd_handle_cli_deprobe (rpcsvc_request_t *req) } if (!(flags & GF_CLI_FLAG_OP_FORCE)) { - if (!uuid_is_null (uuid)) { - /* Check if peers are connected, except peer being detached*/ - if (!glusterd_chk_peers_connected_befriended (uuid)) { - ret = -1; - op_errno = GF_DEPROBE_FRIEND_DOWN; - goto out; - } - ret = glusterd_all_volume_cond_check ( - glusterd_friend_brick_belongs, - -1, &uuid); - if (ret) { - op_errno = GF_DEPROBE_BRICK_EXIST; - goto out; - } + /* Check if peers are connected, except peer being + * detached*/ + if (!glusterd_chk_peers_connected_befriended (uuid)) { + ret = -1; + op_errno = GF_DEPROBE_FRIEND_DOWN; + goto out; } + } + /* Check for if volumes exist with some bricks on the peer being + * detached. It's not a problem if a volume contains none or all + * of its bricks on the peer being detached + */ + list_for_each_entry_safe (volinfo, tmp, &priv->volumes, + vol_list) { + ret = glusterd_friend_contains_vol_bricks (volinfo, + uuid); + if (ret == 1) { + op_errno = GF_DEPROBE_BRICK_EXIST; + goto out; + } + } + + if (!(flags & GF_CLI_FLAG_OP_FORCE)) { if (glusterd_is_any_volume_in_server_quorum (this) && !does_gd_meet_server_quorum (this)) { gf_log (this->name, GF_LOG_ERROR, "Quorum does not " diff --git a/xlators/mgmt/glusterd/src/glusterd-sm.c b/xlators/mgmt/glusterd/src/glusterd-sm.c index c671edf68..fd56e5abf 100644 --- a/xlators/mgmt/glusterd/src/glusterd-sm.c +++ b/xlators/mgmt/glusterd/src/glusterd-sm.c @@ -527,6 +527,9 @@ out: return ret; } +/* Clean up stale volumes on the peer being detached. The volumes which have + * bricks on other peers are stale with respect to the detached peer. + */ static int glusterd_peer_detach_cleanup (glusterd_conf_t *priv) { @@ -538,6 +541,12 @@ glusterd_peer_detach_cleanup (glusterd_conf_t *priv) list_for_each_entry_safe (volinfo,tmp_volinfo, &priv->volumes, vol_list) { + /* The peer detach checks make sure that, at this point in the + * detach process, there are only volumes contained completely + * within or completely outside the detached peer. + * The only stale volumes at this point are the ones + * completely outside the peer and can be safely deleted. + */ if (!glusterd_friend_contains_vol_bricks (volinfo, MY_UUID)) { gf_log (THIS->name, GF_LOG_INFO, diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.c b/xlators/mgmt/glusterd/src/glusterd-utils.c index 721ffe27f..eb6fb6757 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-utils.c @@ -7387,29 +7387,41 @@ out: return ret; } -/* Checks if the given peer contains all the bricks belonging to the - * given volume. Returns true if it does else returns false +/* Checks if the given peer contains bricks belonging to the given volume. + * Returns, + * 2 - if peer contains all the bricks + * 1 - if peer contains at least 1 brick + * 0 - if peer contains no bricks */ -gf_boolean_t +int glusterd_friend_contains_vol_bricks (glusterd_volinfo_t *volinfo, uuid_t friend_uuid) { - gf_boolean_t ret = _gf_true; + int ret = 0; glusterd_brickinfo_t *brickinfo = NULL; + int count = 0; GF_ASSERT (volinfo); list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) { - if (uuid_compare (friend_uuid, brickinfo->uuid)) { - ret = _gf_false; - break; + if (!uuid_compare (brickinfo->uuid, friend_uuid)) { + count++; } } + + if (count) { + if (count == volinfo->brick_count) + ret = 2; + else + ret = 1; + } gf_log (THIS->name, GF_LOG_DEBUG, "Returning %d", ret); return ret; } -/* Remove all volumes which completely belong to given friend +/* Cleanup the stale volumes left behind in the cluster. The volumes which are + * contained completely within the detached peer are stale with respect to the + * cluster. */ int glusterd_friend_remove_cleanup_vols (uuid_t uuid) @@ -7424,7 +7436,7 @@ glusterd_friend_remove_cleanup_vols (uuid_t uuid) list_for_each_entry_safe (volinfo, tmp_volinfo, &priv->volumes, vol_list) { - if (glusterd_friend_contains_vol_bricks (volinfo, uuid)) { + if (glusterd_friend_contains_vol_bricks (volinfo, uuid) == 2) { gf_log (THIS->name, GF_LOG_INFO, "Deleting stale volume %s", volinfo->volname); ret = glusterd_delete_volume (volinfo); diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.h b/xlators/mgmt/glusterd/src/glusterd-utils.h index 1964c88c5..23f8ad7f6 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.h +++ b/xlators/mgmt/glusterd/src/glusterd-utils.h @@ -453,7 +453,7 @@ glusterd_is_volume_replicate (glusterd_volinfo_t *volinfo); gf_boolean_t glusterd_is_brick_decommissioned (glusterd_volinfo_t *volinfo, char *hostname, char *path); -gf_boolean_t +int glusterd_friend_contains_vol_bricks (glusterd_volinfo_t *volinfo, uuid_t friend_uuid); int -- cgit From 1c079acf4e9ef121e5e22e12243f15b080ae5f65 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Fri, 11 Apr 2014 12:56:24 +0200 Subject: doc: add documentation for the "Brick Failure Detection" feature The documentation from the feature page should be included in the sources. Change-Id: I4fd67ce1c56afc5236c00de8be9110dfa6bbe91f BUG: 1086700 Feature-page: http://www.gluster.org/community/documentation/index.php/Features/Brick_Failure_Detection Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7449 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- doc/features/brick-failure-detection.md | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 doc/features/brick-failure-detection.md diff --git a/doc/features/brick-failure-detection.md b/doc/features/brick-failure-detection.md new file mode 100644 index 000000000..24f2a18f3 --- /dev/null +++ b/doc/features/brick-failure-detection.md @@ -0,0 +1,67 @@ +# Brick Failure Detection + +This feature attempts to identify storage/file system failures and disable the failed brick without disrupting the remainder of the node's operation. + +## Description + +Detecting failures on the filesystem that a brick uses makes it possible to handle errors that are caused from outside of the Gluster environment. + +There have been hanging brick processes when the underlying storage of a brick went unavailable. A hanging brick process can still use the network and repond to clients, but actual I/O to the storage is impossible and can cause noticible delays on the client side. + +Provide better detection of storage subsytem failures and prevent bricks from hanging. It should prevent hanging brick processes when storage-hardware or the filesystem fails. + +A health-checker (thread) has been added to the posix xlator. This thread periodically checks the status of the filesystem (implies checking of functional storage-hardware). + +`glusterd` can detect that the brick process has exited, `gluster volume status` will show that the brick process is not running anymore. System administrators checking the logs should be able to triage the cause. + +## Usage and Configuration + +The health-checker is enabled by default and runs a check every 30 seconds. This interval can be changed per volume with: + + # gluster volume set storage.health-check-interval + +If `SECONDS` is set to 0, the health-checker will be disabled. + +## Failure Detection + +Error are logged to the standard syslog (mostly `/var/log/messages`): + + Jun 24 11:31:49 vm130-32 kernel: XFS (dm-2): metadata I/O error: block 0x0 ("xfs_buf_iodone_callbacks") error 5 buf count 512 + Jun 24 11:31:49 vm130-32 kernel: XFS (dm-2): I/O Error Detected. Shutting down filesystem + Jun 24 11:31:49 vm130-32 kernel: XFS (dm-2): Please umount the filesystem and rectify the problem(s) + Jun 24 11:31:49 vm130-32 kernel: VFS:Filesystem freeze failed + Jun 24 11:31:50 vm130-32 GlusterFS[1969]: [2013-06-24 10:31:50.500674] M [posix-helpers.c:1114:posix_health_check_thread_proc] 0-failing_xfs-posix: health-check failed, going down + Jun 24 11:32:09 vm130-32 kernel: XFS (dm-2): xfs_log_force: error 5 returned. + Jun 24 11:32:20 vm130-32 GlusterFS[1969]: [2013-06-24 10:32:20.508690] M [posix-helpers.c:1119:posix_health_check_thread_proc] 0-failing_xfs-posix: still alive! -> SIGTERM + +The messages labelled with `GlusterFS` in the above output are also written to the logs of the brick process. + +## Recovery after a failure + +When a brick process detects that the underlaying storage is not responding anymore, the process will exit. There is no automated way that the brick process gets restarted, the sysadmin will need to fix the problem with the storage first. + +After correcting the storage (hardware or filesystem) issue, the following command will start the brick process again: + + # gluster volume start force + +## How To Test + +The health-checker thread that is part of each brick process will get started automatically when a volume has been started. Verifying its functionality can be done in different ways. + +On virtual hardware: + +* disconnect the disk from the VM that holds the brick + +On real hardware: + +* simulate a RAID-card failure by unplugging the card or cables + +On a system that uses LVM for the bricks: + +* use device-mapper to load an error-table for the disk, see [this description](http://review.gluster.org/5176). + +On any system (writing to random offsets of the block device, more difficult to trigger): + +1. cause corruption on the filesystem that holds the brick +2. read contents from the brick, hoping to hit the corrupted area +3. the filsystem should abort after hitting a bad spot, the health-checker should notice that shortly afterwards -- cgit From d69c1bd9b4caf633745e08be61921ac8636d5991 Mon Sep 17 00:00:00 2001 From: M S Vishwanath Bhat Date: Thu, 10 Apr 2014 22:21:22 +0530 Subject: Adding admin-guide for distributed-geo-rep Change-Id: Icbbc9d3fc14fc1a5ae9550f096eb69dda41e92a4 BUG: 1086796 Signed-off-by: M S Vishwanath Bhat Reviewed-on: http://review.gluster.org/7437 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- .../en-US/markdown/admin_distributed_geo_rep.md | 114 +++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 doc/admin-guide/en-US/markdown/admin_distributed_geo_rep.md diff --git a/doc/admin-guide/en-US/markdown/admin_distributed_geo_rep.md b/doc/admin-guide/en-US/markdown/admin_distributed_geo_rep.md new file mode 100644 index 000000000..1ec4f28ae --- /dev/null +++ b/doc/admin-guide/en-US/markdown/admin_distributed_geo_rep.md @@ -0,0 +1,114 @@ +# Distributed Geo-Replication in glusterfs-3.5 + +This is a admin how-to guide for new dustributed-geo-replication being released as part of glusterfs-3.5 + +##### Note: +This article is targeted towards users/admins who want to try new geo-replication, without going much deeper into internals and technology used. + +### How is it different from earlier geo-replication? + +- Up until now, in geo-replication, only one of the nodes in master volume would participate in geo-replication. This meant that all the data syncing is taken care by only one node while other nodes in the cluster would sit idle (not participate in data syncing). With distributed-geo-replication, each node of the master volume takes the repsonsibility of syncing the data present in that node. In case of replicate configuration, one of them would 'Active'ly sync the data while other node of the replica pair would be 'Passive'. The 'Passive' node only becomes 'Active' when the 'Active' pair goes down. This way new geo-rep leverages all the nodes in the volume and remove the bottleneck of syncing from one single node. +- New change detection mechanism is the other thing which has been improved with new geo-rep. So far geo-rep used to crawl through glusterfs file system to figure out the files that need to synced. And because crawling filesystem can be an expensive operation, this used to be a major bottleneck for performance. With distributed geo-rep, all the files that need to be synced are identified through changelog xlator. Changelog xlator journals all the fops that modifes the file and these journals are then consumed by geo-rep to effectively identify the files that need to be synced. +- A new syncing method tar+ssh, has been introduced to improve the performance of few specific data sets. You can switch between rsync and tar+ssh syncing method via CLI to suite your data set needs. This tar+ssh is better suited for data sets which have large number of small files. + + +### Using Distributed geo-replication: + +#### Prerequisites: +- There should be a password-less ssh setup between at least one node in master volume to one node in slave volume. The geo-rep create command should be executed from this node which has password-less ssh setup to slave. + +- Unlike previous version, slave **must** be a gluster volume. Slave can not be a directory. And both the master and slave volumes should have been created and started before creating geo-rep session. + +#### Creating secret pem pub file +- Execute the below command from the node where you setup the password-less ssh to slave. This will create the secret pem pub file which would have information of RSA key of all the nodes in the master volume. And when geo-rep create command is executed, glusterd uses this file to establish a geo-rep specific ssh connections. +```sh +gluster system:: execute gsec_create +``` + +#### Creating geo-replication session. +Create a geo-rep session between master and slave volume using the following command. The node in which this command is executed and the specified in the command should have password less ssh setup between them. The push-pem option actually uses the secret pem pub file created earlier and establishes geo-rep specific password less ssh between each node in master to each node of slave. +```sh +gluster volume geo-replication :: create push-pem [force] +``` + +If the total available size in slave volume is more than the total size of master, the command will throw error message. In such cases 'force' option can be used. + +#### Starting a geo-rep session +There is no change in this command from previous versions to this version. +```sh +gluster volume geo-replication :: start +``` +This command actually starts the session. Meaning the gsyncd monitor process will be started, which in turn spawns gsync worker processes whenever required. This also turns on changelog xlator (if not in ON state already), which starts recording all the changes on each of the glusterfs bricks. And if master is empty during geo-rep start, the change detection mechanism will be changelog. Else it’ll be xsync (the changes are identified by crawling through filesystem). Later when the initial data is syned to slave, change detection mechanism will be set to changelog + +#### Status of geo-replication + +gluster now has variants of status command. + +```sh +gluster volume geo-replication :: status +``` + +This displays the status of session from each brick of the master to each brick of the slave node. + +If you want more detailed status, then run 'status detail' + +```sh +gluster volume geo-replication :: status detail +``` + +This command displays extra information like, total files synced, files that needs to be synced, deletes pending etc. + +#### Stopping geo-replication session + +This command stops all geo-rep relates processes i.e. gsyncd monitor and works processes. Note that changelog will **not** be turned off with this command. + +```sh +gluster volume geo-replication :: stop [force] +``` +Force option is to be used, when one of the node (or glusterd in one of the node) is down. Once stopped, the session can be restarted any time. Note that upon restarting of the session, the change detection mechanism falls back to xsync mode. This happens even though you have changelog generating journals, while the geo-rep session is stopped. + +#### Deleting geo-replication session + +Now you can delete the glusterfs geo-rep session. This will delete all the config data associated with the geo-rep session. + +```sh +gluster volume geo-replication :: delete +``` + +This deletes all the gsync conf files in each of the nodes. This returns failure, if any of the node is down. And unlike geo-rep stop, there is 'force' option with this. + +#### Changing the config values + +There are some configuration values which can be changed using the CLI. And you can see all the current config values with following command. + +```sh +gluster volume geo-replication :: config +``` + +But you can check only one of them, like log_file or change-detector + +```sh +gluster volume geo-replication :: config log-file +``` +```sh +gluster volume geo-replication :: config change-detector +``` +```sh +gluster volume geo-replication :: config working-dir +``` +To set a new value to this, just provide a new value. Note that, not all the config values are allowed to change. Some can not be modified. + +```sh +gluster volume geo-replication :: config change-detector xsync +``` +Make sure you provide the proper value to the config value. And if you have large number of small files data set, then you can use tar+ssh as syncing method. Note that, if geo-rep session is running, this restarts the gsyncd. + +```sh +gluster volume geo-replication :: config use-tarssh true +``` +Resetting these value to default is also simple. + +```sh +gluster volume geo-replication :: config \!use-tarssh +``` +That makes the config key (tar-ssh in this case) to fall back to it’s default value. -- cgit From e73af16885f6d11ebcb225edb4d12214ceeba74c Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 10 Apr 2014 22:09:53 +0200 Subject: mount.glusterfs: return an error when mounting failed When mounting fails, mount.glusterfs incorrectly returns 0 for some error cases, it should return 1 instead. Also make sure that error messages are redirected to /dev/stderr and not printed to stdout. Unfortunately it is not possible with the current test-scripts to test commands like 'mount -t glusterfs ...'. Any mounting of Gluster volumes is done directly with the 'glusterfs' command instead. Change-Id: Ica9d45b6d5ae537de869a1fa0f6c3edab47225d1 BUG: 1031973 Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7441 Reviewed-by: Harshavardhana Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- xlators/mount/fuse/utils/mount.glusterfs.in | 47 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/xlators/mount/fuse/utils/mount.glusterfs.in b/xlators/mount/fuse/utils/mount.glusterfs.in index b7718b35f..235246c39 100755 --- a/xlators/mount/fuse/utils/mount.glusterfs.in +++ b/xlators/mount/fuse/utils/mount.glusterfs.in @@ -8,6 +8,11 @@ # later), or the GNU General Public License, version 2 (GPLv2), in all # cases as published by the Free Software Foundation. +warn () +{ + echo "$@" >/dev/stderr +} + _init () { # log level definitions @@ -29,7 +34,7 @@ _init () export PATH getfattr=$(which getfattr 2>/dev/null); if [ $? -ne 0 ]; then - echo "WARNING: getfattr not found, certain checks will be skipped.." + warn "WARNING: getfattr not found, certain checks will be skipped.." fi alias lsL='ls -L' @@ -125,7 +130,7 @@ start_glusterfs () log_level=$LOG_NONE; ;; *) - echo "invalid log level $log_level_str, using INFO"; + warn "invalid log level $log_level_str, using INFO"; log_level=$LOG_INFO; ;; esac @@ -241,7 +246,7 @@ start_glusterfs () cmd_line=$(echo "$cmd_line --volfile-server=$i"); done else - echo "ERROR: No valid servers found on command line.. exiting" + warn "ERROR: No valid servers found on command line.. exiting" print_usage exit 1 fi @@ -250,7 +255,7 @@ start_glusterfs () if [ -z "$backup_volfile_servers" ]; then is_valid_hostname ${backupvolfile_server}; if [ $? -eq 1 ]; then - echo "ERROR: Invalid backup server specified.. exiting" + warn "ERROR: Invalid backup server specified.. exiting" exit 1 fi cmd_line=$(echo "$cmd_line --volfile-server=$backupvolfile_server"); @@ -297,7 +302,7 @@ start_glusterfs () inode=$( ${getinode} $mount_point 2>/dev/null); # this is required if the stat returns error if [ $? -ne 0 ]; then - echo "Mount failed. Please check the log file for more details." + warn "Mount failed. Please check the log file for more details." umount $mount_point > /dev/null 2>&1; exit 1; fi @@ -317,7 +322,7 @@ EOF check_recursive_mount () { if [ $1 = "/" ]; then - echo "Cannot mount over root"; + warn "Cannot mount over root"; exit 2; fi @@ -328,7 +333,7 @@ check_recursive_mount () if [ -n ${getfattr} ]; then ${getfattr} -n trusted.gfid $mnt_dir 2>/dev/null | grep -iq "trusted.gfid="; if [ $? -eq 0 ]; then - echo "ERROR: $mnt_dir is in use as a brick of a gluster volume"; + warn "ERROR: $mnt_dir is in use as a brick of a gluster volume"; exit 2; fi fi @@ -364,7 +369,7 @@ check_recursive_mount () brick_inode=`${lgetinode} $brick`; if [ "$mnt_inode" -eq "$brick_inode" \ -a "$mnt_dev" -eq "$brick_dev" ]; then - echo "ERROR: ${mnt_dir} is a parent of the brick ${tmp_brick}"; + warn "ERROR: ${mnt_dir} is a parent of the brick ${tmp_brick}"; exit 2; fi [ "$root_inode" -ne "$brick_inode" \ @@ -461,8 +466,8 @@ with_options() no_root_squash=1; fi ;; *) - echo "Invalid option: $key" - exit 0 + warn "Invalid option: $key" + exit 1 ;; esac } @@ -507,8 +512,8 @@ without_options() "_netdev") ;; *) - echo "Invalid option $option"; - exit 0 + warn "Invalid option $option"; + exit 1 ;; esac } @@ -580,43 +585,43 @@ main () } [ -z "$volume_id" -o -z "$server_ip" ] && { - cat </dev/stderr ERROR: Server name/volume name unspecified cannot proceed further.. Please specify correct format Usage: man 8 $0 EOF - exit 0; + exit 1; } grep_ret=$(echo ${mount_point} | grep '^\-o'); [ "x" != "x${grep_ret}" ] && { - cat </dev/stderr ERROR: -o options cannot be specified in either first two arguments.. Please specify correct style Usage: man 8 $0 EOF - exit 0; + exit 1; } # No need to do a ! -d test, it is taken care while initializing the # variable mount_point [ -z "$mount_point" -o ! -d "$mount_point" ] && { - cat </dev/stderr ERROR: Mount point does not exist Please specify a mount point Usage: man 8 $0 EOF - exit 0; + exit 1; } # Simple check to avoid multiple identical mounts if grep -q "[[:space:]+]${mount_point}[[:space:]+]fuse" $mounttab; then - echo -n "$0: according to mtab, GlusterFS is already mounted on " - echo "$mount_point" - exit 0; + warn -n "$0: according to mtab, GlusterFS is already mounted on " + warn "$mount_point" + exit 1; fi check_recursive_mount "$mount_point"; -- cgit From 712541cdefcc9614e5a79f628358f205ceda6e55 Mon Sep 17 00:00:00 2001 From: Krutika Dhananjay Date: Tue, 11 Mar 2014 15:05:44 +0530 Subject: logging: Make logger and log format configurable through cli Change-Id: Ic4b701a6621578848ff67ae4ecb5a10b5f32f93b BUG: 1075611 Signed-off-by: Krutika Dhananjay Reviewed-on: http://review.gluster.org/7372 Tested-by: Gluster Build System Reviewed-by: Krishnan Parthasarathi Reviewed-by: Vijay Bellur --- glusterfsd/src/glusterfsd.c | 32 +++++++++ glusterfsd/src/glusterfsd.h | 2 + libglusterfs/src/common-utils.c | 35 ++++++++++ libglusterfs/src/common-utils.h | 6 ++ libglusterfs/src/glusterfs.h | 2 + libglusterfs/src/logging.h | 12 ++++ xlators/debug/io-stats/src/io-stats.c | 67 +++++++++++++++++++ xlators/mgmt/glusterd/src/glusterd-volgen.c | 87 +++++++++++++++++++++++-- xlators/mgmt/glusterd/src/glusterd-volume-set.c | 22 +++++++ 9 files changed, 259 insertions(+), 6 deletions(-) diff --git a/glusterfsd/src/glusterfsd.c b/glusterfsd/src/glusterfsd.c index 9c9fad96e..531932619 100644 --- a/glusterfsd/src/glusterfsd.c +++ b/glusterfsd/src/glusterfsd.c @@ -110,6 +110,11 @@ static struct argp_option gf_options[] = { {"log-file", ARGP_LOG_FILE_KEY, "LOGFILE", 0, "File to use for logging [default: " DEFAULT_LOG_FILE_DIRECTORY "/" PACKAGE_NAME ".log" "]"}, + {"logger", ARGP_LOGGER, "LOGGER", 0, "Set which logging sub-system to " + "log to, valid options are: gluster-log and syslog, " + "[default: \"gluster-log\"]"}, + {"log-format", ARGP_LOG_FORMAT, "LOG-FORMAT", 0, "Set log format, valid" + " options are: no-msg-id and with-msg-id, [default: \"with-msg-id\"]"}, {0, 0, 0, 0, "Advanced Options:"}, {"volfile-server-port", ARGP_VOLFILE_SERVER_PORT_KEY, "PORT", 0, @@ -1084,6 +1089,27 @@ parse_opts (int key, char *arg, struct argp_state *state) "unknown use-readdirp setting \"%s\"", arg); break; + case ARGP_LOGGER: + if (strcasecmp (arg, GF_LOGGER_GLUSTER_LOG) == 0) + cmd_args->logger = gf_logger_glusterlog; + else if (strcasecmp (arg, GF_LOGGER_SYSLOG) == 0) + cmd_args->logger = gf_logger_syslog; + else + argp_failure (state, -1, 0, "unknown logger %s", arg); + + break; + + case ARGP_LOG_FORMAT: + if (strcasecmp (arg, GF_LOG_FORMAT_NO_MSG_ID) == 0) + cmd_args->log_format = gf_logformat_traditional; + else if (strcasecmp (arg, GF_LOG_FORMAT_WITH_MSG_ID) == 0) + cmd_args->log_format = gf_logformat_withmsgid; + else + argp_failure (state, -1, 0, "unknown log format %s", + arg); + + break; + } return 0; @@ -1297,6 +1323,8 @@ glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx) /* parsing command line arguments */ cmd_args->log_level = DEFAULT_LOG_LEVEL; + cmd_args->logger = gf_logger_glusterlog; + cmd_args->log_format = gf_logformat_withmsgid; cmd_args->mac_compat = GF_OPTION_DISABLE; #ifdef GF_DARWIN_HOST_OS @@ -1365,6 +1393,10 @@ logging_init (glusterfs_ctx_t *ctx, const char *progpath) /* finish log set parameters before init */ gf_log_set_loglevel (cmd_args->log_level); + gf_log_set_logger (cmd_args->logger); + + gf_log_set_logformat (cmd_args->log_format); + if (gf_log_init (ctx, cmd_args->log_file, cmd_args->log_ident) == -1) { fprintf (stderr, "ERROR: failed to open logfile %s\n", cmd_args->log_file); diff --git a/glusterfsd/src/glusterfsd.h b/glusterfsd/src/glusterfsd.h index ad4c3699b..24487b4d4 100644 --- a/glusterfsd/src/glusterfsd.h +++ b/glusterfsd/src/glusterfsd.h @@ -85,6 +85,8 @@ enum argp_option_keys { ARGP_FUSE_USE_READDIRP_KEY = 165, ARGP_AUX_GFID_MOUNT_KEY = 166, ARGP_FUSE_NO_ROOT_SQUASH_KEY = 167, + ARGP_LOGGER = 168, + ARGP_LOG_FORMAT = 169, }; struct _gfd_vol_top_priv_t { diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c index 1ccb27d16..bfc2fc6ad 100644 --- a/libglusterfs/src/common-utils.c +++ b/libglusterfs/src/common-utils.c @@ -3048,3 +3048,38 @@ dht_is_linkfile (struct iatt *buf, dict_t *dict) return linkfile_key_found; } +int +gf_check_log_format (const char *value) +{ + int log_format = -1; + + if (!strcasecmp (value, GF_LOG_FORMAT_NO_MSG_ID)) + log_format = gf_logformat_traditional; + else if (!strcasecmp (value, GF_LOG_FORMAT_WITH_MSG_ID)) + log_format = gf_logformat_withmsgid; + + if (log_format == -1) + gf_log (THIS->name, GF_LOG_ERROR, "Invalid log-format. " + "possible values are " + GF_LOG_FORMAT_NO_MSG_ID "|" GF_LOG_FORMAT_WITH_MSG_ID); + + return log_format; +} + +int +gf_check_logger (const char *value) +{ + int logger = -1; + + if (!strcasecmp (value, GF_LOGGER_GLUSTER_LOG)) + logger = gf_logger_glusterlog; + else if (!strcasecmp (value, GF_LOGGER_SYSLOG)) + logger = gf_logger_syslog; + + if (logger == -1) + gf_log (THIS->name, GF_LOG_ERROR, "Invalid logger. " + "possible values are " + GF_LOGGER_GLUSTER_LOG "|" GF_LOGGER_SYSLOG); + + return logger; +} diff --git a/libglusterfs/src/common-utils.h b/libglusterfs/src/common-utils.h index ee4e5b7e6..26dcd3147 100644 --- a/libglusterfs/src/common-utils.h +++ b/libglusterfs/src/common-utils.h @@ -629,4 +629,10 @@ struct _dict; inline gf_boolean_t dht_is_linkfile (struct iatt *buf, struct _dict *dict); +int +gf_check_log_format (const char *value); + +int +gf_check_logger (const char *value); + #endif /* _COMMON_UTILS_H */ diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h index 6a8e38a21..c419308f9 100644 --- a/libglusterfs/src/glusterfs.h +++ b/libglusterfs/src/glusterfs.h @@ -346,6 +346,8 @@ struct _cmd_args { gf_loglevel_t log_level; char *log_file; char *log_ident; + gf_log_logger_t logger; + gf_log_format_t log_format; int32_t max_connect_attempts; /* advanced options */ uint32_t volfile_server_port; diff --git a/libglusterfs/src/logging.h b/libglusterfs/src/logging.h index b6b13fbd0..210602c32 100644 --- a/libglusterfs/src/logging.h +++ b/libglusterfs/src/logging.h @@ -48,6 +48,12 @@ #define LOG_DEBUG 7 /* debug-level messages */ #endif +#define GF_LOG_FORMAT_NO_MSG_ID "no-msg-id" +#define GF_LOG_FORMAT_WITH_MSG_ID "with-msg-id" + +#define GF_LOGGER_GLUSTER_LOG "gluster-log" +#define GF_LOGGER_SYSLOG "syslog" + typedef enum { GF_LOG_NONE, GF_LOG_EMERG, @@ -254,6 +260,12 @@ void set_sys_log_level (gf_loglevel_t level); int gf_log_fini(void *data); +void +gf_log_set_logger (gf_log_logger_t logger); + +void +gf_log_set_logformat (gf_log_format_t format); + #define GF_DEBUG(xl, format, args...) \ gf_log ((xl)->name, GF_LOG_DEBUG, format, ##args) #define GF_INFO(xl, format, args...) \ diff --git a/xlators/debug/io-stats/src/io-stats.c b/xlators/debug/io-stats/src/io-stats.c index 9e48a7c6e..d63fbb26c 100644 --- a/xlators/debug/io-stats/src/io-stats.c +++ b/xlators/debug/io-stats/src/io-stats.c @@ -2666,9 +2666,13 @@ reconfigure (xlator_t *this, dict_t *options) struct ios_conf *conf = NULL; int ret = -1; char *sys_log_str = NULL; + char *log_format_str = NULL; + char *logger_str = NULL; int sys_log_level = -1; char *log_str = NULL; int log_level = -1; + int log_format = -1; + int logger = -1; if (!this || !this->private) goto out; @@ -2696,6 +2700,18 @@ reconfigure (xlator_t *this, dict_t *options) gf_log_set_loglevel (log_level); } + GF_OPTION_RECONF ("logger", logger_str, options, str, out); + if (logger_str) { + logger = gf_check_logger (logger_str); + gf_log_set_logger (logger); + } + + GF_OPTION_RECONF ("log-format", log_format_str, options, str, out); + if (log_format_str) { + log_format = gf_check_log_format (log_format_str); + gf_log_set_logformat (log_format); + } + ret = 0; out: gf_log (this->name, GF_LOG_DEBUG, "reconfigure returning %d", ret); @@ -2727,6 +2743,10 @@ init (xlator_t *this) { struct ios_conf *conf = NULL; char *sys_log_str = NULL; + char *logger_str = NULL; + char *log_format_str = NULL; + int logger = -1; + int log_format = -1; int sys_log_level = -1; char *log_str = NULL; int log_level = -1; @@ -2785,6 +2805,19 @@ init (xlator_t *this) gf_log_set_loglevel (log_level); } + GF_OPTION_INIT ("logger", logger_str, str, out); + if (logger_str) { + logger = gf_check_logger (logger_str); + gf_log_set_logger (logger); + } + + GF_OPTION_INIT ("log-format", log_format_str, str, out); + if (log_format_str) { + log_format = gf_check_log_format (log_format_str); + gf_log_set_logformat (log_format); + } + + this->private = conf; ret = 0; out: @@ -3027,6 +3060,40 @@ struct volume_options options[] = { .value = { "DEBUG", "WARNING", "ERROR", "INFO", "CRITICAL", "NONE", "TRACE"} }, + { .key = {"logger"}, + .type = GF_OPTION_TYPE_STR, + .value = { GF_LOGGER_GLUSTER_LOG, GF_LOGGER_SYSLOG} + }, + { .key = {"client-logger"}, + .type = GF_OPTION_TYPE_STR, + .default_value = GF_LOGGER_GLUSTER_LOG, + .description = "Changes the logging sub-system to log to, for the " + "clients", + .value = { GF_LOGGER_GLUSTER_LOG, GF_LOGGER_SYSLOG} + }, + { .key = {"brick-logger"}, + .type = GF_OPTION_TYPE_STR, + .default_value = GF_LOGGER_GLUSTER_LOG, + .description = "Changes the logging sub-system to log to, for the " + "bricks", + .value = { GF_LOGGER_GLUSTER_LOG, GF_LOGGER_SYSLOG} + }, + { .key = {"log-format"}, + .type = GF_OPTION_TYPE_STR, + .value = { GF_LOG_FORMAT_NO_MSG_ID, GF_LOG_FORMAT_WITH_MSG_ID} + }, + { .key = {"client-log-format"}, + .type = GF_OPTION_TYPE_STR, + .default_value = GF_LOG_FORMAT_WITH_MSG_ID, + .description = "Changes log format for the clients", + .value = { GF_LOG_FORMAT_NO_MSG_ID, GF_LOG_FORMAT_WITH_MSG_ID} + }, + { .key = {"brick-log-format"}, + .type = GF_OPTION_TYPE_STR, + .default_value = GF_LOG_FORMAT_WITH_MSG_ID, + .description = "Changes the log format for the bricks", + .value = { GF_LOG_FORMAT_NO_MSG_ID, GF_LOG_FORMAT_WITH_MSG_ID} + }, { .key = {NULL} }, }; diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c index 783958279..0b3a8d099 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.c +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c @@ -1327,6 +1327,44 @@ sys_loglevel_option_handler (volgen_graph_t *graph, return basic_option_handler (graph, &vme2, NULL); } +static int +logger_option_handler (volgen_graph_t *graph, struct volopt_map_entry *vme, + void *param) +{ + char *role = NULL; + struct volopt_map_entry vme2 = {0,}; + + role = (char *) param; + + if (strcmp (vme->option, "!logger") != 0 || + !strstr (vme->key, role)) + return 0; + + memcpy (&vme2, vme, sizeof (vme2)); + vme2.option = "logger"; + + return basic_option_handler (graph, &vme2, NULL); +} + +static int +log_format_option_handler (volgen_graph_t *graph, struct volopt_map_entry *vme, + void *param) +{ + char *role = NULL; + struct volopt_map_entry vme2 = {0,}; + + role = (char *) param; + + if (strcmp (vme->option, "!log-format") != 0 || + !strstr (vme->key, role)) + return 0; + + memcpy (&vme2, vme, sizeof (vme2)); + vme2.option = "log-format"; + + return basic_option_handler (graph, &vme2, NULL); +} + static int volgen_graph_set_xl_options (volgen_graph_t *graph, dict_t *dict) { @@ -1379,6 +1417,12 @@ server_spec_option_handler (volgen_graph_t *graph, if (!ret) ret = sys_loglevel_option_handler (graph, vme, "brick"); + if (!ret) + ret = logger_option_handler (graph, vme, "brick"); + + if (!ret) + ret = log_format_option_handler (graph, vme, "brick"); + return ret; } @@ -2536,7 +2580,9 @@ client_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, char *tmp = NULL; gf_boolean_t var = _gf_false; gf_boolean_t ob = _gf_false; + xlator_t *this = THIS; + GF_ASSERT (this); GF_ASSERT (conf); volname = volinfo->volname; @@ -2613,7 +2659,7 @@ client_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, ob = _gf_false; ret = gf_string2boolean (tmp, &ob); if (!ret && ob) { - gf_log (THIS->name, GF_LOG_WARNING, + gf_log (this->name, GF_LOG_WARNING, "root-squash is enabled. Please turn it" " off to change read-after-open " "option"); @@ -2682,7 +2728,7 @@ client_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, ret = 0; } if (ret) { - gf_log (THIS->name, GF_LOG_WARNING, "setting " + gf_log (this->name, GF_LOG_WARNING, "setting " "open behind option as part of root " "squash failed"); goto out; @@ -2708,14 +2754,28 @@ client_graph_builder (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, &loglevel_option_handler); if (ret) - gf_log (THIS->name, GF_LOG_WARNING, "changing client log level" + gf_log (this->name, GF_LOG_WARNING, "changing client log level" " failed"); ret = volgen_graph_set_options_generic (graph, set_dict, "client", &sys_loglevel_option_handler); if (ret) - gf_log (THIS->name, GF_LOG_WARNING, "changing client syslog " + gf_log (this->name, GF_LOG_WARNING, "changing client syslog " "level failed"); + + ret = volgen_graph_set_options_generic (graph, set_dict, "client", + &logger_option_handler); + + if (ret) + gf_log (this->name, GF_LOG_WARNING, "changing client logger" + " failed"); + + ret = volgen_graph_set_options_generic (graph, set_dict, "client", + &log_format_option_handler); + if (ret) + gf_log (this->name, GF_LOG_WARNING, "changing client log format" + " failed"); + out: return ret; } @@ -3052,16 +3112,31 @@ build_shd_graph (volgen_graph_t *graph, dict_t *mod_dict) &loglevel_option_handler); if (ret) - gf_log (THIS->name, GF_LOG_WARNING, "changing loglevel " + gf_log (this->name, GF_LOG_WARNING, "changing loglevel " "of self-heal daemon failed"); ret = volgen_graph_set_options_generic (graph, set_dict, "client", &sys_loglevel_option_handler); if (ret) - gf_log (THIS->name, GF_LOG_WARNING, "changing syslog " + gf_log (this->name, GF_LOG_WARNING, "changing syslog " "level of self-heal daemon failed"); + ret = volgen_graph_set_options_generic (graph, set_dict, + "client", + &logger_option_handler); + + if (ret) + gf_log (this->name, GF_LOG_WARNING, "changing logger " + "of self-heal daemon failed"); + + ret = volgen_graph_set_options_generic (graph, set_dict, + "client", + &log_format_option_handler); + if (ret) + gf_log (this->name, GF_LOG_WARNING, "changing log " + "format level of self-heal daemon failed"); + ret = dict_reset (set_dict); if (ret) goto out; diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-set.c b/xlators/mgmt/glusterd/src/glusterd-volume-set.c index 7f094c439..29e9f2b87 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c +++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c @@ -610,6 +610,28 @@ struct volopt_map_entry glusterd_volopt_map[] = { .op_version = 1, .flags = OPT_FLAG_CLIENT_OPT }, + { .key = "diagnostics.brick-logger", + .voltype = "debug/io-stats", + .option = "!logger", + .op_version = 4 + }, + { .key = "diagnostics.client-logger", + .voltype = "debug/io-stats", + .option = "!logger", + .op_version = 4, + .flags = OPT_FLAG_CLIENT_OPT + }, + { .key = "diagnostics.brick-log-format", + .voltype = "debug/io-stats", + .option = "!log-format", + .op_version = 4 + }, + { .key = "diagnostics.client-log-format", + .voltype = "debug/io-stats", + .option = "!log-format", + .op_version = 4, + .flags = OPT_FLAG_CLIENT_OPT + }, /* IO-cache xlator options */ { .key = "performance.cache-max-file-size", -- cgit From 493e7c1934e65f5b7de5e710528f503e083c49e4 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 10 Apr 2014 10:54:01 -0700 Subject: pkg-version: Use /bin/bash instead of /bin/sh `function` is reserved word only available in /bin/bash, Using `/bin/sh` with `function` keyword fails on Ubuntu. Change-Id: Ia0fff8f13ace2a0eb1dac50093ff42294d960814 BUG: 1074919 Reviewed-on: http://review.gluster.org/7439 Reviewed-by: Bala FA Reviewed-by: Niels de Vos Tested-by: Gluster Build System --- build-aux/pkg-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-aux/pkg-version b/build-aux/pkg-version index 2be2a9756..15efb154f 100755 --- a/build-aux/pkg-version +++ b/build-aux/pkg-version @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # To override version/release from git, # create VERSION file containing text with version/release -- cgit From c193996c94901d81a2a623799d83ef96f595d3e2 Mon Sep 17 00:00:00 2001 From: "Bala.FA" Date: Thu, 10 Apr 2014 14:03:55 +0530 Subject: build: set run levels for glusterd init script properly 'chkconfig --add glusterd' does not set run levels for start/stop. This patch fixes it. Change-Id: I8962f5207d034b6875353e15eb60c4d08c5a4fe4 BUG: 1086141 Signed-off-by: Bala.FA Reviewed-on: http://review.gluster.org/7432 Reviewed-by: Niels de Vos Tested-by: Gluster Build System --- extras/init.d/glusterd-Redhat.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/init.d/glusterd-Redhat.in b/extras/init.d/glusterd-Redhat.in index e320708ae..c6254a066 100755 --- a/extras/init.d/glusterd-Redhat.in +++ b/extras/init.d/glusterd-Redhat.in @@ -11,8 +11,8 @@ # Required-Stop: $local_fs $network # Should-Start: # Should-Stop: -# Default-Start: -# Default-Stop: 0 1 2 3 4 5 6 +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 # Short-Description: glusterfs server # Description: Clustered file-system server ### END INIT INFO -- cgit From 09d2dcac3a238cfe6bde1a7aed06facfa16caf20 Mon Sep 17 00:00:00 2001 From: "Kaleb S. KEITHLEY" Date: Thu, 10 Apr 2014 14:43:47 -0400 Subject: configure: argp-standalone is always built, even when not needed This patch still configures argp, i.e. creates Makefile in /contrib/argp-standalone, for the `make dist`, but there's no reason to compile it when it's not going to be linked into anything. Change-Id: Id0fcb717b5821d8a4c176e6274339f46b3a4a249 BUG: 1086773 Signed-off-by: Kaleb S. KEITHLEY Reviewed-on: http://review.gluster.org/7440 Tested-by: Gluster Build System Reviewed-by: Harshavardhana Reviewed-by: Niels de Vos Reviewed-by: Anand Avati --- Makefile.am | 17 +++++++++-------- configure.ac | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Makefile.am b/Makefile.am index 030a30cdd..b42071db2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,9 +5,10 @@ EXTRA_DIST = autogen.sh \ error-codes.json gf-error-codes.h.template \ gen-headers.py run-tests.sh \ build-aux/pkg-version \ + contrib/argp-standalone \ $(shell find $(top_srcdir)/tests -type f -print) -SUBDIRS = contrib/argp-standalone libglusterfs rpc api xlators glusterfsd \ +SUBDIRS = $(ARGP_STANDALONE_DIR) libglusterfs rpc api xlators glusterfsd \ $(FUSERMOUNT_SUBDIR) doc extras cli @SYNCDAEMON_SUBDIR@ pkgconfigdir = @pkgconfigdir@ @@ -21,13 +22,13 @@ gitclean: distclean find . -name mount.glusterfs -exec rm -f {} \; rm -fr autom4te.cache rm -f missing aclocal.m4 config.h.in config.guess config.sub ltmain.sh install-sh configure depcomp - rm -fr $(CONTRIBDIR)/argp-standalone/autom4te.cache - rm -f $(CONTRIBDIR)/argp-standalone/aclocal.m4 - rm -f $(CONTRIBDIR)/argp-standalone/config.h.in - rm -f $(CONTRIBDIR)/argp-standalone/configure - rm -f $(CONTRIBDIR)/argp-standalone/depcomp - rm -f $(CONTRIBDIR)/argp-standalone/install-sh - rm -f $(CONTRIBDIR)/argp-standalone/missing + -rm -fr $(CONTRIBDIR)/argp-standalone/autom4te.cache + -rm -f $(CONTRIBDIR)/argp-standalone/aclocal.m4 + -rm -f $(CONTRIBDIR)/argp-standalone/config.h.in + -rm -f $(CONTRIBDIR)/argp-standalone/configure + -rm -f $(CONTRIBDIR)/argp-standalone/depcomp + -rm -f $(CONTRIBDIR)/argp-standalone/install-sh + -rm -f $(CONTRIBDIR)/argp-standalone/missing dist-hook: gen-VERSION gen-ChangeLog .PHONY: gen-VERSION gen-ChangeLog diff --git a/configure.ac b/configure.ac index d146b38c6..65e8cb753 100644 --- a/configure.ac +++ b/configure.ac @@ -656,6 +656,7 @@ if test "x${ac_cv_header_argp_h}" = "xno"; then BUILD_ARGP_STANDALONE=yes ARGP_STANDALONE_CPPFLAGS='-I${top_srcdir}/contrib/argp-standalone' ARGP_STANDALONE_LDADD='${top_builddir}/contrib/argp-standalone/libargp.a' + ARGP_STANDALONE_DIR='${top_builddir}/contrib/argp-standalone' fi AC_SUBST(ARGP_STANDALONE_CPPFLAGS) -- cgit From a9d4d369efc978511e3cb69e5643945710cc9416 Mon Sep 17 00:00:00 2001 From: Krishnan Parthasarathi Date: Wed, 2 Apr 2014 11:36:20 +0530 Subject: dict: add dict_set_dynstr_with_alloc There is an overwhelming no. of instances of the following pattern in glusterd module. ... char *dynstr = gf_strdup (str); if (!dynstr) goto err; ret = dict_set_dynstr (dict, key, dynstr); if (ret) goto err; ... With this changes it would look as below, ret = dict_set_dynstr_with_alloc (dict, key, str); if (ret) goto err; Change-Id: I6a47b1cbab4834badadc48c56d0b5c8c06c6dd4d Signed-off-by: Krishnan Parthasarathi Reviewed-on: http://review.gluster.org/7379 Tested-by: Gluster Build System Reviewed-by: Jeff Darcy --- libglusterfs/src/dict.c | 17 ++++++++++++++ libglusterfs/src/dict.h | 1 + xlators/mgmt/glusterd/src/glusterd-utils.c | 36 ++++++++++-------------------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/libglusterfs/src/dict.c b/libglusterfs/src/dict.c index 7bc5d57b0..1bed8bf9b 100644 --- a/libglusterfs/src/dict.c +++ b/libglusterfs/src/dict.c @@ -2066,6 +2066,23 @@ err: return ret; } +int +dict_set_dynstr_with_alloc (dict_t *this, char *key, const char *str) +{ + char *alloc_str = NULL; + int ret = -1; + + alloc_str = gf_strdup (str); + if (!alloc_str) + return -1; + + ret = dict_set_dynstr (this, key, alloc_str); + if (ret) + GF_FREE (alloc_str); + + return ret; +} + int dict_set_dynstr (dict_t *this, char *key, char *str) { diff --git a/libglusterfs/src/dict.h b/libglusterfs/src/dict.h index 6e5d8aa06..a92fd2cb6 100644 --- a/libglusterfs/src/dict.h +++ b/libglusterfs/src/dict.h @@ -228,6 +228,7 @@ GF_MUST_CHECK int dict_set_static_bin (dict_t *this, char *key, void *ptr, size_ GF_MUST_CHECK int dict_set_str (dict_t *this, char *key, char *str); GF_MUST_CHECK int dict_set_dynmstr (dict_t *this, char *key, char *str); GF_MUST_CHECK int dict_set_dynstr (dict_t *this, char *key, char *str); +GF_MUST_CHECK int dict_set_dynstr_with_alloc (dict_t *this, char *key, const char *str); GF_MUST_CHECK int dict_get_str (dict_t *this, char *key, char **str); GF_MUST_CHECK int dict_get_str_boolean (dict_t *this, char *key, int default_val); diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.c b/xlators/mgmt/glusterd/src/glusterd-utils.c index eb6fb6757..1822be1ec 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-utils.c @@ -5671,11 +5671,8 @@ glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, char key[1024] = {0}; char base_key[1024] = {0}; char *mnt_pt = NULL; - char *fs_name = NULL; - char *mnt_options = NULL; - char *device = NULL; - struct mntent *entry = NULL; FILE *mtab = NULL; + struct mntent *entry = NULL; snprintf (base_key, sizeof (base_key), "brick%d", count); @@ -5693,8 +5690,7 @@ glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "%s.device", base_key); - device = gf_strdup (entry->mnt_fsname); - ret = dict_set_dynstr (dict, key, device); + ret = dict_set_dynstr_with_alloc (dict, key, entry->mnt_fsname); if (ret) goto out; @@ -5702,8 +5698,7 @@ glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "%s.fs_name", base_key); - fs_name = gf_strdup (entry->mnt_type); - ret = dict_set_dynstr (dict, key, fs_name); + ret = dict_set_dynstr_with_alloc (dict, key, entry->mnt_type); if (ret) goto out; @@ -5711,8 +5706,7 @@ glusterd_add_brick_mount_details (glusterd_brickinfo_t *brickinfo, memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "%s.mnt_options", base_key); - mnt_options = gf_strdup (entry->mnt_opts); - ret = dict_set_dynstr (dict, key, mnt_options); + ret = dict_set_dynstr_with_alloc (dict, key, entry->mnt_opts); out: GF_FREE (mnt_pt); @@ -5857,7 +5851,6 @@ glusterd_add_brick_to_dict (glusterd_volinfo_t *volinfo, int ret = -1; int32_t pid = -1; int32_t brick_online = -1; - char *peer_id_str = NULL; char key[1024] = {0}; char base_key[1024] = {0}; char pidfile[PATH_MAX] = {0}; @@ -5888,16 +5881,11 @@ glusterd_add_brick_to_dict (glusterd_volinfo_t *volinfo, goto out; /* add peer uuid */ - peer_id_str = gf_strdup (uuid_utoa (brickinfo->uuid)); - if (!peer_id_str) { - ret = -1; - goto out; - } memset (key, 0, sizeof (key)); snprintf (key, sizeof (key), "%s.peerid", base_key); - ret = dict_set_dynstr (dict, key, peer_id_str); + ret = dict_set_dynstr_with_alloc (dict, key, + uuid_utoa (brickinfo->uuid)); if (ret) { - GF_FREE (peer_id_str); goto out; } @@ -6559,8 +6547,7 @@ glusterd_sm_tr_log_transition_add_to_dict (dict_t *dict, snprintf (key, sizeof (key), "log%d-time", count); gf_time_fmt (timestr, sizeof timestr, log->transitions[i].time, gf_timefmt_FT); - str = gf_strdup (timestr); - ret = dict_set_dynstr (dict, key, str); + ret = dict_set_dynstr_with_alloc (dict, key, timestr); if (ret) goto out; @@ -7979,7 +7966,7 @@ glusterd_append_gsync_status (dict_t *dst, dict_t *src) goto out; } - ret = dict_set_dynstr (dst, "gsync-status", gf_strdup (stop_msg)); + ret = dict_set_dynstr_with_alloc (dst, "gsync-status", stop_msg); if (ret) { gf_log ("glusterd", GF_LOG_WARNING, "Unable to set the stop" "message in the ctx dictionary"); @@ -8083,8 +8070,8 @@ glusterd_gsync_use_rsp_dict (dict_t *aggr, dict_t *rsp_dict, char *op_errstr) ret = dict_get_str (rsp_dict, "conf_path", &conf_path); if (!ret && conf_path) { - ret = dict_set_dynstr (ctx, "conf_path", - gf_strdup(conf_path)); + ret = dict_set_dynstr_with_alloc (ctx, "conf_path", + conf_path); if (ret) { gf_log ("", GF_LOG_ERROR, "Unable to store conf path."); @@ -8093,7 +8080,8 @@ glusterd_gsync_use_rsp_dict (dict_t *aggr, dict_t *rsp_dict, char *op_errstr) } } if ((op_errstr) && (strcmp ("", op_errstr))) { - ret = dict_set_dynstr (ctx, "errstr", gf_strdup(op_errstr)); + ret = dict_set_dynstr_with_alloc (ctx, "errstr", + op_errstr); if (ret) goto out; } -- cgit From c29a9d63b3f36866f1c138fe9b5dd5da48ec720f Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Tue, 15 Apr 2014 10:26:31 +0530 Subject: doc: Add doc for network compression xlator Change-Id: Iaed94f1bfe7c97eade95923759980a1d10805663 BUG: 1086752 Signed-off-by: Prashanth Pai Reviewed-on: http://review.gluster.org/7479 Reviewed-by: Humble Devassy Chirammal Reviewed-by: Niels de Vos Tested-by: Gluster Build System --- doc/network_compression.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 doc/network_compression.md diff --git a/doc/network_compression.md b/doc/network_compression.md new file mode 100644 index 000000000..7327591ef --- /dev/null +++ b/doc/network_compression.md @@ -0,0 +1,71 @@ +#On-Wire Compression + Decompression + +The 'compression translator' compresses and decompresses data in-flight +between client and bricks. + +###Working +When a writev call occurs, the client compresses the data before sending it to +brick. On the brick, compressed data is decompressed. Similarly, when a readv +call occurs, the brick compresses the data before sending it to client. On the +client, the compressed data is decompressed. Thus, the amount of data sent over +the wire is minimized. Compression/Decompression is done using Zlib library. + +During normal operation, this is the format of data sent over wire: + +~~~ + + trailer(8 bytes) +~~~ + +The trailer contains the CRC32 checksum and length of original uncompressed +data. This is used for validation. + +###Usage + +Turning on compression xlator: + +~~~ +gluster volume set network.compression on +~~~ + +###Configurable parameters (optional) + +**Compression level** +~~~ +gluster volume set network.compression.compression-level 8 +~~~ + +~~~ +0 : no compression +1 : best speed +9 : best compression +-1 : default compression +~~~ + +**Minimum file size** + +~~~ +gluster volume set network.compression.min-size 50 +~~~ + +Data is compressed only when its size exceeds the above value in bytes. + +**Other paramaters** + +Other less frequently used parameters include `network.compression.mem-level` +and `network.compression.window-size`. More details can about these options +can be found by running `gluster volume set help` command. + +###Known Issues and Limitations + +* Compression translator cannot work with striped volumes. +* Mount point hangs when writing a file with write-behind xlator turned on. To +overcome this, turn off `performance.write-behind` entirely OR +set`performance.strict-write-ordering` to on. +* For glusterfs versions <= 3.5, compression traslator can ONLY work with pure +distribute volumes. This limitation is caused by AFR not being able to +propagate xdata. This issue has been fixed in glusterfs versions > 3.5 + +###TODO +Although zlib offers high compression ratio, it is very slow. We can make the +translator pluggable to add support for other compression methods such as +[lz4 compression](https://code.google.com/p/lz4/) -- cgit From f7e19ce46782fd62c50894ed541cebdcf72ead82 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Tue, 15 Apr 2014 11:24:31 +0200 Subject: scripts: add new email aliases Two developers started to use new email addresses, marking them as aliases. Also assign @gmail.com addresses to the "(unknown)" employer. Change-Id: Ic5722c1611b003182c0288ba530d6ab275c2ca1b BUG: 1087771 Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7484 Reviewed-by: Prashanth Pai Reviewed-by: Vijay Bellur Tested-by: Vijay Bellur --- .mailmap | 4 ++++ extras/who-wrote-glusterfs/gitdm.aliases | 2 ++ extras/who-wrote-glusterfs/gitdm.domain-map | 1 + 3 files changed, 7 insertions(+) diff --git a/.mailmap b/.mailmap index 6bcd95dea..d4a13b400 100644 --- a/.mailmap +++ b/.mailmap @@ -1,5 +1,7 @@ # .mailmap, see 'git short-log --help' for details # +# This file needs to match extras/who-wrote-glusterfs/gitdm.aliases. +# # Listing of contributors that filed patches with different email addresses. # Format: [ ...] # @@ -9,6 +11,7 @@ Anand Avati Csaba Henk Harshavardhana +Justin Clift Kaleb S. KEITHLEY Kaushal M Kaushik BV @@ -19,6 +22,7 @@ M S Vishwanath Bhat Pete Zaitcev Pranith Kumar K +Prashanth Pai Raghavendra Bhat Raghavendra G Rahul C S diff --git a/extras/who-wrote-glusterfs/gitdm.aliases b/extras/who-wrote-glusterfs/gitdm.aliases index 784a3e3bc..e19b99c79 100644 --- a/extras/who-wrote-glusterfs/gitdm.aliases +++ b/extras/who-wrote-glusterfs/gitdm.aliases @@ -20,6 +20,7 @@ harsha@gluster.com fharshav@redhat.com harsha@zresearch.com fharshav@redhat.com harsha@dev.gluster.com fharshav@redhat.com harsha@harshavardhana.net fharshav@redhat.com +jclift@redhat.com jclift@gluster.org kkeithle@f16node1.kkeithle.usersys.redhat.com kkeithle@redhat.com kaushal@gluster.com kaushal@redhat.com kaushikbv@gluster.com kbudiger@redhat.com @@ -29,6 +30,7 @@ krishna@guest-laptop ksriniva@redhat.com kp@gluster.com kparthas@redhat.com me@louiszuckerman.com louiszuckerman@gmail.com msvbhat@gmail.com vbhat@redhat.com +nullpai@gmail.com ppai@redhat.com vishwanath@gluster.com vbhat@redhat.com pavan@dev.gluster.com pavan@gluster.com zaitcev@yahoo.com zaitcev@kotori.zaitcev.us diff --git a/extras/who-wrote-glusterfs/gitdm.domain-map b/extras/who-wrote-glusterfs/gitdm.domain-map index f1c305898..39526f0f9 100644 --- a/extras/who-wrote-glusterfs/gitdm.domain-map +++ b/extras/who-wrote-glusterfs/gitdm.domain-map @@ -4,6 +4,7 @@ active.by ActiveCloud cern.ch CERN gluster.com Red Hat +gmail.com (unknown) gooddata.com GoodData hastexo.com hastexo ibm.com IBM -- cgit From 686ee24a80c6ee7a5135aace8c2536f2afbcf367 Mon Sep 17 00:00:00 2001 From: Jeff Darcy Date: Tue, 15 Apr 2014 20:12:04 +0000 Subject: doc: add documentation for server-quorum feature Change-Id: Ifaf5c18a320270a9b98c7b25536281fa42b64ad3 BUG: 1086765 Signed-off-by: Jeff Darcy Reviewed-on: http://review.gluster.org/7491 Reviewed-by: Vijay Bellur Tested-by: Vijay Bellur --- doc/features/server-quorum.md | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 doc/features/server-quorum.md diff --git a/doc/features/server-quorum.md b/doc/features/server-quorum.md new file mode 100644 index 000000000..7b20084ce --- /dev/null +++ b/doc/features/server-quorum.md @@ -0,0 +1,44 @@ +# Server Quorum + +Server quorum is a feature intended to reduce the occurrence of "split brain" +after a brick failure or network partition. Split brain happens when different +sets of servers are allowed to process different sets of writes, leaving data +in a state that can not be reconciled automatically. The key to avoiding split +brain is to ensure that there can be only one set of servers - a quorum - that +can continue handling writes. Server quorum does this by the brutal but +effective means of forcing down all brick daemons on cluster nodes that can no +longer reach enough of their peers to form a majority. Because there can only +be one majority, there can be only one set of bricks remaining, and thus split +brain can not occur. + +## Options + +Server quorum is controlled by two parameters: + + * **cluster.server-quorum-type** + + This value may be "server" to indicate that server quorum is enabled, or + "none" to mean it's disabled. + + * **cluster.server-quorum-ratio** + + This is the percentage of cluster nodes that must be up to maintain quorum. + More precisely, this percentage of nodes *plus one* must be up. + +Note that these are cluster-wide flags. All volumes served by the cluster will +be affected. Once these values are set, quorum actions - starting or stopping +brick daemons in response to node or network events - will be automatic. + +## Best Practices + +If a cluster with an even number of nodes is split exactly down the middle, +neither half can have quorum (which requires **more than** half of the total). +This is particularly important when N=2, in which case the loss of either node +leads to loss of quorum. Therefore, it is highly advisable to ensure that the +cluster size is three or greater. The "extra" node in this case need not have +any bricks or serve any data. It need only be present to preserve the notion +of a quorum majority less than the entire cluster membership, allowing the +cluster to survive the loss of a single node without losing quorum. + + + -- cgit From d6ad57239d396f31ae0cd21e2e8e5db64aae5714 Mon Sep 17 00:00:00 2001 From: Jeff Darcy Date: Tue, 15 Apr 2014 18:06:24 +0000 Subject: doc: add documentation for NUFA feature Change-Id: I00f1c669d4ebc61f09c50f8de4c893907f6d75c2 BUG: 1086745 Signed-off-by: Jeff Darcy Reviewed-on: http://review.gluster.org/7489 Reviewed-by: Niels de Vos Reviewed-by: Vijay Bellur Tested-by: Vijay Bellur --- doc/features/nufa.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 doc/features/nufa.md diff --git a/doc/features/nufa.md b/doc/features/nufa.md new file mode 100644 index 000000000..03b8194b4 --- /dev/null +++ b/doc/features/nufa.md @@ -0,0 +1,20 @@ +# NUFA Translator + +The NUFA ("Non Uniform File Access") is a variant of the DHT ("Distributed Hash +Table") translator, intended for use with workloads that have a high locality +of reference. Instead of placing new files pseudo-randomly, it places them on +the same nodes where they are created so that future accesses can be made +locally. For replicated volumes, this means that one copy will be local and +others will be remote; the read-replica selection mechanisms will then favor +the local copy for reads. For non-replicated volumes, the only copy will be +local. + +## Interface + +Use of NUFA is controlled by a volume option, as follows. + + gluster volume set myvolume cluster.nufa on + +This will cause the NUFA translator to be used wherever the DHT translator +otherwise would be. The rest is all automatic. + -- cgit From 1dc3331d93ea9a2bb17a6dfa54f24ff57ecd6047 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 16 Apr 2014 12:05:14 -0700 Subject: scripts: Use `/bin/bash` not `/bin/sh` If using `/bin/sh` this is what we see $ ./who-wrote-glusterfs.sh ./who-wrote-glusterfs.sh: 39: shift: can't shift that many Use `/bin/bash` instead where 'shift' without an argument is supported. Change-Id: I2be05f0062eb8456631c1cee859757052f1b6dc7 BUG: 1087771 Signed-off-by: Harshavardhana Reviewed-on: http://review.gluster.org/7496 Reviewed-by: Niels de Vos Tested-by: Gluster Build System --- extras/who-wrote-glusterfs/who-wrote-glusterfs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh b/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh index 487f5874b..7a1896e6a 100755 --- a/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh +++ b/extras/who-wrote-glusterfs/who-wrote-glusterfs.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Gather statistics on "Who wrote GlusterFS". The idea comes from the excellent # articles on http://lwn.net/ named "Who wrote ?". -- cgit From 36ae3edd207bf173ba4d106024368df4c0d18c7e Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Mon, 14 Apr 2014 12:36:50 +0200 Subject: mount.glusterfs: return 32 in case of double mounting The mount.nfs helper returns 32 for this particular error case. It makes sense for mount.glusterfs to return the same error value. Change-Id: I628f4c93bc796bb096e91857195ffd3d296eaae9 BUG: 1031973 Reported-by: Deepak C Shetty Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7469 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- xlators/mount/fuse/utils/mount.glusterfs.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xlators/mount/fuse/utils/mount.glusterfs.in b/xlators/mount/fuse/utils/mount.glusterfs.in index 235246c39..71ea66c3c 100755 --- a/xlators/mount/fuse/utils/mount.glusterfs.in +++ b/xlators/mount/fuse/utils/mount.glusterfs.in @@ -619,9 +619,9 @@ EOF # Simple check to avoid multiple identical mounts if grep -q "[[:space:]+]${mount_point}[[:space:]+]fuse" $mounttab; then - warn -n "$0: according to mtab, GlusterFS is already mounted on " - warn "$mount_point" - exit 1; + warn "$0: according to mtab, GlusterFS is already mounted on" \ + "$mount_point" + exit 32; fi check_recursive_mount "$mount_point"; -- cgit From c7d59dc00860ac6c355845bf79ba787d9e92e036 Mon Sep 17 00:00:00 2001 From: Krutika Dhananjay Date: Wed, 18 Sep 2013 14:12:58 +0530 Subject: doc: Upgrade steps for quota Change-Id: Ib007029f74f52d28e424a15950d46504336bde10 BUG: 969461 Signed-off-by: Krutika Dhananjay Reviewed-on: http://review.gluster.org/7419 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- doc/upgrade/quota-upgrade-steps.md | 79 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 doc/upgrade/quota-upgrade-steps.md diff --git a/doc/upgrade/quota-upgrade-steps.md b/doc/upgrade/quota-upgrade-steps.md new file mode 100644 index 000000000..402fbdf65 --- /dev/null +++ b/doc/upgrade/quota-upgrade-steps.md @@ -0,0 +1,79 @@ +Upgrade Steps For Quota +======================= + +The upgrade process for quota involves executing two upgrade scripts: +1. pre-upgrade-script-for-quota.sh, and +2. post-upgrade-script-for-quota.sh + +Pre-Upgrade Script: +================== + +###What it does: + +The pre-upgrade script (pre-upgrade-script-for-quota.sh) iterates over the list of volumes that have quota enabled and captures the configured quota limits for each such volume in a file under /var/tmp/glusterfs/quota-config-backup/vol_<VOLNAME> by executing 'quota list' command on each one of them. + +###Pre-requisites for running Pre-Upgrade Script: + +1. Make sure glusterd and the brick processes are running on all nodes in the cluster. +2. The pre-upgrade script must be run prior to upgradation. +3. The pre-upgrade script must be run on only one of the nodes in the cluster. + +###Location: +pre-upgrade-script-for-quota.sh must be retrieved from the source tree under the 'extras' directory. + +###Invocation: +Invoke the script by executing `./pre-upgrade-script-for-quota.sh` from the shell on any one of the nodes in the cluster. + +* Example: + + [root@server1 extras]#./pre-upgrade-script-for-quota.sh + + +Post-Upgrade Script: +=================== + +###What it does: +The post-upgrade script (post-upgrade-script-for-quota.sh) picks the volumes that have quota enabled. + +Because the cluster must be operating at op-version 3 for quota to work, the 'default-soft-limit' for each of these volumes is set to 80% (which is its default value) via `volume set` operation as an explicit trigger to bump up the op-version of the cluster and also to trigger a re-write of volfiles which knocks quota off client volume file. + +Once this is done, these volumes are started forcefully using `volume start force` to launch the Quota Daemon on all the nodes. + +Thereafter, for each of these volumes, the paths and the limits configured on them are retrieved from the backed up file /var/tmp/glusterfs/quota-config-backup/vol_<VOLNAME> and limits are set on them via the `quota limit-usage` interface. + +####Note: +In the new version of quota, the command `quota limit-usage` will fail if the directory on which quota limit is to be set for a given volume does not exist. Therefore, it is advised that you create these directories first before running post-upgrade-script-for-quota.sh if you want limits to be set on these directories. + +###Pre-requisites for running Post-Upgrade Script: +1. The post-upgrade script must be executed after all the nodes in the cluster have upgraded. +2. Also, all the clients accessing the given volume must also be upgraded before the script is run. +3. Make sure glusterd and the brick processes are running on all nodes in the cluster post upgrade. +4. The script must be run from the same node where the pre-upgrade script was run. + + +###Location: +post-upgrade-script-for-quota.sh can be found under the 'extras' directory of the source tree for glusterfs. + +###Invocation: +post-upgrade-script-for-quota.sh takes one command line argument. This argument could be one of the following: +1. the name of the volume which has quota enabled; or +2. 'all'. + +In the first case, invoke post-upgrade-script-for-quota.sh from the shell for each volume with quota enabled, with the name of the volume passed as an argument in the command-line: + +* Example: + For a volume "vol1" on which quota is enabled, invoke the script in the following way: + + [root@server1 extras]#./post-upgrade-script-for-quota.sh vol1 + + +In the second case, the post-upgrade script picks on its own, the volumes on which quota is enabled, and executes the post-upgrade procedure on each one of them. In this case, invoke post-upgrade-script-for-quota.sh from the shell with 'all' passed as an argument in the command-line: + +* Example: + + [root@server1 extras]#./post-upgrade-script-for-quota.sh all + + +####Note: +1. In the second case, post-upgrade-script-for-quota.sh exits prematurely upon failure to ugprade any given volume. In that case, you may run post-upgrade-script-for-quota.sh individually (using the volume name as command line argument) on this volume and also on all volumes appearing after this volume in the output of `gluster volume list`, that have quota enabled. +2. The backed up files under /var/tmp/glusterfs/quota-config-backup/ are retained after the post-upgrade procedure for reference. -- cgit From e55f3fe25a4fd2cc9021fad97faedc5e94aa4e7c Mon Sep 17 00:00:00 2001 From: Krutika Dhananjay Date: Tue, 17 Sep 2013 17:01:07 +0530 Subject: Add upgrade scripts for quota Change-Id: I289662300d32f75e2fc6e789037c3224054a38a5 BUG: 969461 Signed-off-by: Krutika Dhananjay Reviewed-on: http://review.gluster.org/7418 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- extras/Makefile.am | 5 ++- extras/post-upgrade-script-for-quota.sh | 65 +++++++++++++++++++++++++++++++++ extras/pre-upgrade-script-for-quota.sh | 18 +++++++++ glusterfs.spec.in | 2 + 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100755 extras/post-upgrade-script-for-quota.sh create mode 100755 extras/pre-upgrade-script-for-quota.sh diff --git a/extras/Makefile.am b/extras/Makefile.am index faa91cddf..525fa9738 100644 --- a/extras/Makefile.am +++ b/extras/Makefile.am @@ -9,11 +9,14 @@ conf_DATA = glusterfs-logrotate gluster-rsyslog-7.2.conf gluster-rsyslog-5.8.con voldir = $(sysconfdir)/glusterfs vol_DATA = glusterd.vol +scriptsdir = $(datadir)/glusterfs/scripts +scripts_DATA = post-upgrade-script-for-quota.sh pre-upgrade-script-for-quota.sh EXTRA_DIST = $(conf_DATA) specgen.scm MacOSX/Portfile glusterfs-mode.el \ glusterfs.vim migrate-unify-to-distribute.sh backend-xattr-sanitize.sh \ backend-cleanup.sh disk_usage_sync.sh clear_xattrs.sh \ - glusterd-sysconfig glusterd.vol + glusterd-sysconfig glusterd.vol post-upgrade-script-for-quota.sh \ + pre-upgrade-script-for-quota.sh install-data-local: $(mkdir_p) $(DESTDIR)$(localstatedir)/lib/glusterd/groups diff --git a/extras/post-upgrade-script-for-quota.sh b/extras/post-upgrade-script-for-quota.sh new file mode 100755 index 000000000..c22668793 --- /dev/null +++ b/extras/post-upgrade-script-for-quota.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +#The post-upgrade script must be executed after all the nodes in the cluster +#have upgraded. +#Also, all the clients accessing the given volume must also be upgraded +#before the script is run. +#Make sure glusterd and the brick processes are running on all nodes in the +#cluster post upgrade. +#Execute this script on the node where the pre-upgrade script for quota was run. + +VOL=$1; + +BACKUP_DIR=/var/tmp/glusterfs/quota-config-backup + +function set_limits { + local var=$(gluster volume info $1 | grep 'features.quota'| cut -d" " -f2); + + if [ -z "$var" ] || [ "$var" == "off" ]; then + if [ $2 -eq '0' ]; then + echo "Volume $1 does not have quota enabled. " \ + "Exiting ..." + exit 1 + fi + else + gluster volume set $1 default-soft-limit 80% + if [ $? -ne '0' ]; then + echo "Post-upgrade process failed." \ + "Please address the error and run " \ + "post-upgrade-script.sh on volume $VOL again." + exit 1 + fi + + gluster volume start $1 force + sleep 3; + + local path_array=( $(cat $BACKUP_DIR/vol_$1 | tail -n +3 | awk '{print $1}') ) + local limit_array=( $(cat $BACKUP_DIR/vol_$1 | tail -n +3 | awk '{print $2}') ) + local len=${#path_array[@]} + + for ((j=0; j<$len; j++)) + do + gluster volume quota $1 limit-usage ${path_array[$j]} ${limit_array[j]}; + if [ $? -eq 0 ]; then + echo "Setting limit (${limit_array[j]}) on " \ + "path ${path_array[$j]} has been " \ + "successful" + fi + done + fi; +} + +if [ -z $1 ]; then + echo "Please provide volume name or 'all'"; + exit 1; +fi + +if [ "$1" == "all" ]; then + for VOL in `gluster volume list`; + do + set_limits $VOL '1'; + done + +else + set_limits $1 '0'; +fi diff --git a/extras/pre-upgrade-script-for-quota.sh b/extras/pre-upgrade-script-for-quota.sh new file mode 100755 index 000000000..a8001e9e1 --- /dev/null +++ b/extras/pre-upgrade-script-for-quota.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +#Make sure glusterd and the brick processes are running on all nodes in the +#cluster. +#This script must be run prior to upgradation, that too on +#only one of the nodes in the cluster. + +BACKUP_DIR=/var/tmp/glusterfs/quota-config-backup + +mkdir -p $BACKUP_DIR +for i in `gluster volume list`; do + var=$(gluster volume info $i | grep 'features.quota'| cut -d" " -f2); + if [ -z "$var" ] || [ "$var" == "off" ]; then + continue + else + gluster volume quota $i list > $BACKUP_DIR/vol_$i; + fi; +done diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 5fa8ededc..fbaabea41 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -829,6 +829,8 @@ fi %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/encryption/rot-13* %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/mac-compat* %exclude %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/testing/performance/symlink-cache* +%{_datadir}/glusterfs/scripts/post-upgrade-script-for-quota.sh +%{_datadir}/glusterfs/scripts/pre-upgrade-script-for-quota.sh %files api %exclude %{_libdir}/*.so -- cgit From 02ac5fa4b5ed4c1cb531724c85ff10c83f221541 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Tue, 1 Apr 2014 17:28:55 -0700 Subject: cluster/dht: force set dir inode ctx cached time in setattr() In setattr, the inode times may have been explicitly set "back in time". In such cases, if the inode ctx times are not force set, then they continue to be higher and continue serving the higher/older value in future calls to dht_inode_ctx_time_update() Change-Id: I9cbfa7cf7c4069b0106d1f462de08c5d59bc91b5 BUG: 1083324 Signed-off-by: Anand Avati Reviewed-on: http://review.gluster.org/7378 Reviewed-by: Harshavardhana Tested-by: Harshavardhana Tested-by: Gluster Build System Reviewed-by: Raghavendra G Reviewed-by: Vijay Bellur --- xlators/cluster/dht/src/dht-common.h | 1 + xlators/cluster/dht/src/dht-helper.c | 28 ++++++++++++++++++++++++++++ xlators/cluster/dht/src/dht-inode-write.c | 6 +++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/xlators/cluster/dht/src/dht-common.h b/xlators/cluster/dht/src/dht-common.h index d391b87d5..d74d0dfd4 100644 --- a/xlators/cluster/dht/src/dht-common.h +++ b/xlators/cluster/dht/src/dht-common.h @@ -753,6 +753,7 @@ dht_inode_ctx_layout_set (inode_t *inode, xlator_t *this, int dht_inode_ctx_time_update (inode_t *inode, xlator_t *this, struct iatt *stat, int32_t update_ctx); +void dht_inode_ctx_time_set (inode_t *inode, xlator_t *this, struct iatt *stat); int dht_inode_ctx_get (inode_t *inode, xlator_t *this, dht_inode_ctx_t **ctx); int dht_inode_ctx_set (inode_t *inode, xlator_t *this, dht_inode_ctx_t *ctx); diff --git a/xlators/cluster/dht/src/dht-helper.c b/xlators/cluster/dht/src/dht-helper.c index 18a501f04..f1dc5072f 100644 --- a/xlators/cluster/dht/src/dht-helper.c +++ b/xlators/cluster/dht/src/dht-helper.c @@ -1109,6 +1109,34 @@ dht_inode_ctx_layout_set (inode_t *inode, xlator_t *this, return ret; } + +void +dht_inode_ctx_time_set (inode_t *inode, xlator_t *this, struct iatt *stat) +{ + dht_inode_ctx_t *ctx = NULL; + dht_stat_time_t *time = 0; + int ret = -1; + + ret = dht_inode_ctx_get (inode, this, &ctx); + + if (ret) + return; + + time = &ctx->time; + + time->mtime = stat->ia_mtime; + time->mtime_nsec = stat->ia_mtime_nsec; + + time->ctime = stat->ia_ctime; + time->ctime_nsec = stat->ia_ctime_nsec; + + time->atime = stat->ia_atime; + time->atime_nsec = stat->ia_atime_nsec; + + return; +} + + int dht_inode_ctx_time_update (inode_t *inode, xlator_t *this, struct iatt *stat, int32_t post) diff --git a/xlators/cluster/dht/src/dht-inode-write.c b/xlators/cluster/dht/src/dht-inode-write.c index 363bff3bf..576f007e5 100644 --- a/xlators/cluster/dht/src/dht-inode-write.c +++ b/xlators/cluster/dht/src/dht-inode-write.c @@ -862,9 +862,13 @@ unlock: UNLOCK (&frame->lock); this_call_cnt = dht_frame_return (frame); - if (is_last_call (this_call_cnt)) + if (is_last_call (this_call_cnt)) { + if (local->op_ret == 0) + dht_inode_ctx_time_set (local->loc.inode, this, + &local->stbuf); DHT_STACK_UNWIND (setattr, frame, local->op_ret, local->op_errno, &local->prebuf, &local->stbuf, xdata); + } return 0; } -- cgit From 580ac71d3b91a10c3264eddc7600dd75dfb0ad88 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 17 Apr 2014 12:07:04 +0200 Subject: rdma: correct some spelling mistakes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib7018aa8a79d36ab942516457a79039cb3e67355 BUG: 1088849 Reported-by: Patrick Matthäi Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7498 Reviewed-by: Vikhyat Umrao Tested-by: Gluster Build System --- rpc/rpc-transport/rdma/src/rdma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpc/rpc-transport/rdma/src/rdma.c b/rpc/rpc-transport/rdma/src/rdma.c index c09067230..701abdba7 100644 --- a/rpc/rpc-transport/rdma/src/rdma.c +++ b/rpc/rpc-transport/rdma/src/rdma.c @@ -997,7 +997,7 @@ gf_rdma_cm_handle_event_established (struct rdma_cm_event *event) } gf_log (this->name, GF_LOG_TRACE, - "recieved event RDMA_CM_EVENT_ESTABLISHED (me:%s peer:%s)", + "received event RDMA_CM_EVENT_ESTABLISHED (me:%s peer:%s)", this->myinfo.identifier, this->peerinfo.identifier); return ret; @@ -1085,7 +1085,7 @@ gf_rdma_cm_event_handler (void *data) this = event->id->context; gf_log (this->name, GF_LOG_DEBUG, - "recieved disconnect (me:%s peer:%s)\n", + "received disconnect (me:%s peer:%s)\n", this->myinfo.identifier, this->peerinfo.identifier); @@ -3775,12 +3775,12 @@ gf_rdma_async_event_thread (void *context) switch (event.event_type) { case IBV_EVENT_SRQ_LIMIT_REACHED: gf_log (GF_RDMA_LOG_NAME, GF_LOG_WARNING, - "recieved srq_limit reached"); + "received srq_limit reached"); break; default: gf_log (GF_RDMA_LOG_NAME, GF_LOG_DEBUG, - "event (%d) recieved", event.event_type); + "event (%d) received", event.event_type); break; } -- cgit From 99d86b1a1afe62c06f7aa2e3d6bb68df7762ce48 Mon Sep 17 00:00:00 2001 From: Soumya Koduri Date: Fri, 21 Mar 2014 12:57:14 +0530 Subject: Added Handle-based ops to get/set/remove extended attributes in the libgfapi. Change-Id: I1a8e666018d7b93e0bba2d9882935681da909980 BUG: 1089414 Signed-off-by: Soumya Koduri Reviewed-on: http://review.gluster.org/7308 Reviewed-by: Vijay Bellur Tested-by: Vijay Bellur --- api/src/glfs-handleops.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++ api/src/glfs-handles.h | 11 ++++ api/src/glfs-internal.h | 3 + 3 files changed, 174 insertions(+) diff --git a/api/src/glfs-handleops.c b/api/src/glfs-handleops.c index 49e060015..7fb202973 100644 --- a/api/src/glfs-handleops.c +++ b/api/src/glfs-handleops.c @@ -217,6 +217,59 @@ out: return ret; } +int +glfs_h_getxattrs (struct glfs *fs, struct glfs_object *object, const char *name, + void *value, size_t size) +{ + int ret = 0; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = {0, }; + dict_t *xattr = NULL; + + /* validate in args */ + if ((fs == NULL) || (object == NULL)) { + errno = EINVAL; + return -1; + } + + __glfs_entry_fs (fs); + + /* get the active volume */ + subvol = glfs_active_subvol (fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode (fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE (inode, loc, out); + + ret = syncop_getxattr (subvol, &loc, &xattr, name); + DECODE_SYNCOP_ERR (ret); + + if (ret) + goto out; + + ret = glfs_getxattr_process (value, size, xattr, name); + +out: + if (inode) + inode_unref (inode); + + glfs_subvol_done (fs, subvol); + + return ret; +} + int glfs_h_setattrs (struct glfs *fs, struct glfs_object *object, struct stat *stat, int valid) @@ -271,6 +324,113 @@ out: return ret; } +int +glfs_h_setxattrs (struct glfs *fs, struct glfs_object *object, const char *name, + const void *value, size_t size, int flags) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = {0, }; + dict_t *xattr = NULL; + + /* validate in args */ + if ((fs == NULL) || (object == NULL) || (stat == NULL)) { + errno = EINVAL; + return -1; + } + + __glfs_entry_fs (fs); + + /* get the active volume */ + subvol = glfs_active_subvol (fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode (fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + xattr = dict_for_key_value (name, value, size); + if (!xattr) { + ret = -1; + errno = ENOMEM; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE (inode, loc, out); + + /* fop/op */ + ret = syncop_setxattr (subvol, &loc, xattr, flags); + DECODE_SYNCOP_ERR (ret); + +out: + loc_wipe (&loc); + + if (inode) + inode_unref (inode); + + glfs_subvol_done (fs, subvol); + + return ret; +} + +int +glfs_h_removexattrs (struct glfs *fs, struct glfs_object *object, const char *name) +{ + int ret = -1; + xlator_t *subvol = NULL; + inode_t *inode = NULL; + loc_t loc = {0, }; + + /* validate in args */ + if ((fs == NULL) || (object == NULL) || (stat == NULL)) { + errno = EINVAL; + return -1; + } + + __glfs_entry_fs (fs); + + /* get the active volume */ + subvol = glfs_active_subvol (fs); + if (!subvol) { + ret = -1; + errno = EIO; + goto out; + } + + /* get/refresh the in arg objects inode in correlation to the xlator */ + inode = glfs_resolve_inode (fs, subvol, object); + if (!inode) { + errno = ESTALE; + goto out; + } + + /* populate loc */ + GLFS_LOC_FILL_INODE (inode, loc, out); + + /* fop/op */ + ret = syncop_removexattr (subvol, &loc, name, 0); + DECODE_SYNCOP_ERR (ret); + +out: + loc_wipe (&loc); + + if (inode) + inode_unref (inode); + + glfs_subvol_done (fs, subvol); + + return ret; +} + struct glfs_fd * glfs_h_open (struct glfs *fs, struct glfs_object *object, int flags) { diff --git a/api/src/glfs-handles.h b/api/src/glfs-handles.h index bc26618c4..027089760 100644 --- a/api/src/glfs-handles.h +++ b/api/src/glfs-handles.h @@ -114,9 +114,17 @@ int glfs_h_stat(struct glfs *fs, struct glfs_object *object, int glfs_h_getattrs (struct glfs *fs, struct glfs_object *object, struct stat *stat) __THROW; +int glfs_h_getxattrs (struct glfs *fs, struct glfs_object *object, + const char *name, void *value, + size_t size) __THROW; + int glfs_h_setattrs (struct glfs *fs, struct glfs_object *object, struct stat *sb, int valid) __THROW; +int glfs_h_setxattrs (struct glfs *fs, struct glfs_object *object, + const char *name, const void *value, + size_t size, int flags) __THROW; + int glfs_h_readlink (struct glfs *fs, struct glfs_object *object, char *buf, size_t bufsiz) __THROW; @@ -127,6 +135,9 @@ int glfs_h_rename (struct glfs *fs, struct glfs_object *olddir, const char *oldname, struct glfs_object *newdir, const char *newname) __THROW; +int glfs_h_removexattrs (struct glfs *fs, struct glfs_object *object, + const char *name) __THROW; + /* Operations enabling opaque invariant handle to object transitions */ ssize_t glfs_h_extract_handle (struct glfs_object *object, unsigned char *handle, int len) __THROW; diff --git a/api/src/glfs-internal.h b/api/src/glfs-internal.h index f04557323..d7d675e81 100644 --- a/api/src/glfs-internal.h +++ b/api/src/glfs-internal.h @@ -216,5 +216,8 @@ int glfs_loc_touchup (loc_t *loc); void glfs_iatt_to_stat (struct glfs *fs, struct iatt *iatt, struct stat *stat); int glfs_loc_link (loc_t *loc, struct iatt *iatt); int glfs_loc_unlink (loc_t *loc); +dict_t * dict_for_key_value (const char *name, const char *value, size_t size); +int glfs_getxattr_process (void *value, size_t size, dict_t *xattr, + const char *name); #endif /* !_GLFS_INTERNAL_H */ -- cgit From a67630b3970f9ad035a1d1463d9c2a6c6e7de736 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Wed, 26 Mar 2014 10:59:27 -0700 Subject: defaults: add new symbol default_fops with members filled with pointers to appropriate default methods Change-Id: I6cdc43e4f6776e2ad45cd5cbca5642e0c639ffde BUG: 1089216 Signed-off-by: Anand Avati Reviewed-on: http://review.gluster.org/7504 Tested-by: Gluster Build System Reviewed-by: Pranith Kumar Karampuri --- libglusterfs/src/defaults.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ libglusterfs/src/defaults.h | 2 ++ 2 files changed, 53 insertions(+) diff --git a/libglusterfs/src/defaults.c b/libglusterfs/src/defaults.c index 8a1c281a5..599f9477d 100644 --- a/libglusterfs/src/defaults.c +++ b/libglusterfs/src/defaults.c @@ -2200,6 +2200,57 @@ default_getspec (call_frame_t *frame, xlator_t *this, const char *key, return 0; } + +struct xlator_fops _default_fops = { + .create = default_create, + .open = default_open, + .stat = default_stat, + .readlink = default_readlink, + .mknod = default_mknod, + .mkdir = default_mkdir, + .unlink = default_unlink, + .rmdir = default_rmdir, + .symlink = default_symlink, + .rename = default_rename, + .link = default_link, + .truncate = default_truncate, + .readv = default_readv, + .writev = default_writev, + .statfs = default_statfs, + .flush = default_flush, + .fsync = default_fsync, + .setxattr = default_setxattr, + .getxattr = default_getxattr, + .fsetxattr = default_fsetxattr, + .fgetxattr = default_fgetxattr, + .removexattr = default_removexattr, + .fremovexattr = default_fremovexattr, + .opendir = default_opendir, + .readdir = default_readdir, + .readdirp = default_readdirp, + .fsyncdir = default_fsyncdir, + .access = default_access, + .ftruncate = default_ftruncate, + .fstat = default_fstat, + .lk = default_lk, + .inodelk = default_inodelk, + .finodelk = default_finodelk, + .entrylk = default_entrylk, + .fentrylk = default_fentrylk, + .lookup = default_lookup, + .rchecksum = default_rchecksum, + .xattrop = default_xattrop, + .fxattrop = default_fxattrop, + .setattr = default_setattr, + .fsetattr = default_fsetattr, + .fallocate = default_fallocate, + .discard = default_discard, + .zerofill = default_zerofill, + + .getspec = default_getspec, +}; +struct xlator_fops *default_fops = &_default_fops; + /* notify */ int default_notify (xlator_t *this, int32_t event, void *data, ...) diff --git a/libglusterfs/src/defaults.h b/libglusterfs/src/defaults.h index 1b33e8099..9bd5eb842 100644 --- a/libglusterfs/src/defaults.h +++ b/libglusterfs/src/defaults.h @@ -34,6 +34,8 @@ int32_t default_release (xlator_t *this, fd_t *fd); int32_t default_releasedir (xlator_t *this, fd_t *fd); +extern struct xlator_fops *default_fops; + /* Management Operations */ int32_t default_getspec (call_frame_t *frame, -- cgit From bac02a359f86a742fbf750cf4f056160324d0746 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Thu, 27 Mar 2014 23:53:58 -0700 Subject: fuse-resolve: loc_wipe() after inode_link() the inode to be linked may have the last ref. loc_wipe() can destroy it before inode_link() gets to ref it. Change-Id: Ic2d44084e6e9c8289f35cae82c8e4575af105398 BUG: 1089216 Signed-off-by: Anand Avati Reviewed-on: http://review.gluster.org/7505 Tested-by: Gluster Build System Reviewed-by: Pranith Kumar Karampuri --- xlators/mount/fuse/src/fuse-resolve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xlators/mount/fuse/src/fuse-resolve.c b/xlators/mount/fuse/src/fuse-resolve.c index 17d76d46b..76b1d9a72 100644 --- a/xlators/mount/fuse/src/fuse-resolve.c +++ b/xlators/mount/fuse/src/fuse-resolve.c @@ -163,10 +163,10 @@ fuse_resolve_gfid_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - loc_wipe (&resolve->resolve_loc); - link_inode = inode_link (inode, NULL, NULL, buf); + loc_wipe (&resolve->resolve_loc); + if (!link_inode) goto out; -- cgit From 81b32ab6e1c950577d0f2779db67283303e3e705 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Thu, 27 Mar 2014 06:35:34 -0700 Subject: fuse: allow xlators to request for direct-io-mode on virtual files Translators like meta, create virtual files with dynamic content generated only at the time of open(). Therefore the file size returned in lookup or stat is 0 (just like files in /proc). However the VFS does not read beyond the size, and if the size is 0, no READ ever reaches gluster for that file -- unless direct-io-mode is enabled. This patch allows translators to return "direct-io-mode" flag for such 0-byte virtual files in xdata of open_cbk/create_cbk. Change-Id: I3fe3312cd96baa4eecfe1247ab7255b4f455f049 BUG: 1089216 Signed-off-by: Anand Avati Reviewed-on: http://review.gluster.org/7506 Tested-by: Gluster Build System Reviewed-by: Pranith Kumar Karampuri --- xlators/mount/fuse/src/fuse-bridge.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index d9055468e..cbf256e8b 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -869,6 +869,16 @@ out: return ret; } + +gf_boolean_t +direct_io_mode (dict_t *xdata) +{ + if (xdata && dict_get (xdata, "direct-io-mode")) + return _gf_true; + return _gf_false; +} + + static int fuse_fd_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata) @@ -892,7 +902,8 @@ fuse_fd_cbk (call_frame_t *frame, void *cookie, xlator_t *this, if (!IA_ISDIR (fd->inode->ia_type)) { if (((priv->direct_io_mode == 2) && ((state->flags & O_ACCMODE) != O_RDONLY)) - || (priv->direct_io_mode == 1)) + || (priv->direct_io_mode == 1) + || (direct_io_mode (xdata))) foo.open_flags |= FOPEN_DIRECT_IO; #ifdef GF_DARWIN_HOST_OS /* In Linux: by default, buffer cache @@ -1840,7 +1851,8 @@ fuse_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, if (((priv->direct_io_mode == 2) && ((state->flags & O_ACCMODE) != O_RDONLY)) - || (priv->direct_io_mode == 1)) + || (priv->direct_io_mode == 1) + || direct_io_mode (xdata)) foo.open_flags |= FOPEN_DIRECT_IO; gf_log ("glusterfs-fuse", GF_LOG_TRACE, -- cgit From 11473a6a10aaa5e62050e7e52115e2f8f10de124 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Thu, 27 Mar 2014 08:03:52 -0700 Subject: common-utils: new dirent compatible time fmt Introduce a new time format without "/" and spaces, in order to be used in (virtual) filenames. Change-Id: I468be54f1ec7f45265add4c458e19d95567439f7 BUG: 1089216 Signed-off-by: Anand Avati Reviewed-on: http://review.gluster.org/7507 Tested-by: Gluster Build System Reviewed-by: Pranith Kumar Karampuri --- libglusterfs/src/common-utils.c | 6 ++++-- libglusterfs/src/common-utils.h | 1 + libglusterfs/src/graph.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c index bfc2fc6ad..80d9d2940 100644 --- a/libglusterfs/src/common-utils.c +++ b/libglusterfs/src/common-utils.c @@ -2300,14 +2300,16 @@ static const char *__gf_timefmts[] = { "%F %T", "%Y/%m/%d-%T", "%b %d %T", - "%F %H%M%S" + "%F %H%M%S", + "%Y-%m-%d-%T", }; static const char *__gf_zerotimes[] = { "0000-00-00 00:00:00", "0000/00/00-00:00:00", "xxx 00 00:00:00", - "0000-00-00 000000" + "0000-00-00 000000", + "0000-00-00-00:00:00", }; void diff --git a/libglusterfs/src/common-utils.h b/libglusterfs/src/common-utils.h index 26dcd3147..e17029dba 100644 --- a/libglusterfs/src/common-utils.h +++ b/libglusterfs/src/common-utils.h @@ -480,6 +480,7 @@ typedef enum { gf_timefmt_Ymd_T, /* YYYY/MM-DD-hh:mm:ss */ gf_timefmt_bdT, /* ddd DD hh:mm:ss */ gf_timefmt_F_HMS, /* YYYY-MM-DD hhmmss */ + gf_timefmt_dirent, gf_timefmt_last } gf_timefmts; diff --git a/libglusterfs/src/graph.c b/libglusterfs/src/graph.c index e76df1ca5..b4eddd826 100644 --- a/libglusterfs/src/graph.c +++ b/libglusterfs/src/graph.c @@ -367,7 +367,7 @@ fill_uuid (char *uuid, int size) strerror (errno)); } - gf_time_fmt (now_str, sizeof now_str, tv.tv_sec, gf_timefmt_Ymd_T); + gf_time_fmt (now_str, sizeof now_str, tv.tv_sec, gf_timefmt_dirent); snprintf (uuid, size, "%s-%d-%s:%"GF_PRI_SUSECONDS, hostname, getpid(), now_str, tv.tv_usec); -- cgit From 2da51737c49f7917a974bdf9e6e566307583ad16 Mon Sep 17 00:00:00 2001 From: Anand Avati Date: Thu, 27 Mar 2014 21:21:12 -0700 Subject: strfd: memory backed file descriptor A file descriptor like interface, backed by a string, on which fprintf() like IO can be performed. Internally the backing string is grown on demand. Useful in generating virtual file content on the fly (used in meta) Change-Id: I60d8751c4c750f3f06aa454a4ccd9909b3ac8ac7 BUG: 1089216 Signed-off-by: Anand Avati Reviewed-on: http://review.gluster.org/7508 Reviewed-by: Pranith Kumar Karampuri Tested-by: Gluster Build System --- libglusterfs/src/Makefile.am | 4 +- libglusterfs/src/mem-types.h | 4 +- libglusterfs/src/strfd.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ libglusterfs/src/strfd.h | 28 ++++++++++++++ 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 libglusterfs/src/strfd.c create mode 100644 libglusterfs/src/strfd.h diff --git a/libglusterfs/src/Makefile.am b/libglusterfs/src/Makefile.am index b1a853687..8934b35f2 100644 --- a/libglusterfs/src/Makefile.am +++ b/libglusterfs/src/Makefile.am @@ -26,7 +26,7 @@ libglusterfs_la_SOURCES = dict.c xlator.c logging.c \ graph-print.c trie.c run.c options.c fd-lk.c circ-buff.c \ event-history.c gidcache.c ctx.c client_t.c event-poll.c event-epoll.c \ $(CONTRIBDIR)/libgen/basename_r.c $(CONTRIBDIR)/libgen/dirname_r.c \ - $(CONTRIBDIR)/stdlib/gf_mkostemp.c + $(CONTRIBDIR)/stdlib/gf_mkostemp.c strfd.c nodist_libglusterfs_la_SOURCES = y.tab.c graph.lex.c gf-error-codes.h @@ -43,7 +43,7 @@ noinst_HEADERS = common-utils.h defaults.h dict.h glusterfs.h hashfn.h timespec. $(CONTRIB_BUILDDIR)/uuid/uuid_types.h syncop.h graph-utils.h trie.h \ run.h options.h lkowner.h fd-lk.h circ-buff.h event-history.h \ gidcache.h client_t.h glusterfs-acl.h glfs-message-id.h \ - template-component-messages.h + template-component-messages.h strfd.h EXTRA_DIST = graph.l graph.y diff --git a/libglusterfs/src/mem-types.h b/libglusterfs/src/mem-types.h index c01dfa75e..c07d1387b 100644 --- a/libglusterfs/src/mem-types.h +++ b/libglusterfs/src/mem-types.h @@ -122,6 +122,8 @@ enum gf_common_mem_types_ { gf_common_mt_uuid_t = 106, gf_common_mt_mgmt_v3_lock_obj_t = 107, gf_common_mt_txn_opinfo_obj_t = 108, - gf_common_mt_end = 109 + gf_common_mt_strfd_t = 109, + gf_common_mt_strfd_data_t = 110, + gf_common_mt_end }; #endif diff --git a/libglusterfs/src/strfd.c b/libglusterfs/src/strfd.c new file mode 100644 index 000000000..8c97670d4 --- /dev/null +++ b/libglusterfs/src/strfd.c @@ -0,0 +1,91 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include + +#include "mem-types.h" +#include "mem-pool.h" +#include "strfd.h" +#include "common-utils.h" + + +strfd_t * +strfd_open () +{ + strfd_t *strfd = NULL; + + strfd = GF_CALLOC(1, sizeof(*strfd), gf_common_mt_strfd_t); + + return strfd; +} + + +int +strprintf (strfd_t *strfd, const char *fmt, ...) +{ + va_list ap; + char *str = NULL; + int size = 0; + + va_start (ap, fmt); + + size = vasprintf (&str, fmt, ap); + + if (size < 0) + return size; + + if (!strfd->alloc_size) { + strfd->data = GF_CALLOC (max(size + 1, 4096), 1, + gf_common_mt_strfd_data_t); + if (!strfd->data) { + free (str); /* NOT GF_FREE */ + return -1; + } + strfd->alloc_size = max(size + 1, 4096); + } + + if (strfd->alloc_size <= (strfd->size + size)) { + char *tmp_ptr = NULL; + int new_size = max ((strfd->alloc_size * 2), + gf_roundup_next_power_of_two (strfd->size + size + 1)); + tmp_ptr = GF_REALLOC (strfd->data, new_size); + if (!tmp_ptr) { + free (str); /* NOT GF_FREE */ + return -1; + } + strfd->alloc_size = new_size; + strfd->data = tmp_ptr; + } + + // Copy the trailing '\0', but do not account for it in ->size. + // This allows safe use of strfd->data as a string. + memcpy (strfd->data + strfd->size, str, size + 1); + strfd->size += size; + + free (str); /* NOT GF_FREE */ + + return size; +} + + +int +strfd_close (strfd_t *strfd) +{ + GF_FREE (strfd->data); + GF_FREE (strfd); + + return 0; +} + diff --git a/libglusterfs/src/strfd.h b/libglusterfs/src/strfd.h new file mode 100644 index 000000000..e386c8432 --- /dev/null +++ b/libglusterfs/src/strfd.h @@ -0,0 +1,28 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _STRFD_H +#define _STRFD_H + +typedef struct { + void *data; + size_t alloc_size; + size_t size; + off_t pos; +} strfd_t; + +strfd_t *strfd_open(); + +int strprintf(strfd_t *strfd, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + +int strfd_close(strfd_t *strfd); + +#endif -- cgit From 20e317011af7c0f075819bf0648b225f6dc42350 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 27 Mar 2014 20:34:44 +0100 Subject: fuse: prevent READDIR(P) from writing to much data to /dev/fuse In an environment with mixed architectures (32-bit servers, 64-bit client), it is possible that the on-wire Reply on a READDIR(P) procedure contains more direntries than the client can fit in the maximum size that the fuse-request indicated. A direntry is a dynamically sized structure, because the structure contains the name of the entry. The client sends a maximum size in the READDIR(P) Call to the server, and the server uses this size to limit the number of direntries to return. In case the server can pack more direntries in the requested maximum size (due to alignment differences between the architectures), it can happen that the client unpacks the list of direntries into a buffer that exceeds the maximum size that was given in the initial fuse-request. This change introduces a check for the maximum requested size of the fuse-response in fuse_readdir_cbk() and fuse_readdirp_cbk(). When the conversion from gluster-direntries to the fuse-direntry format takes place, the maximum size is checked, and the 'extra' direntries are discarded. The next readdir()/getdents() that is done, will fetch the just discarded direntries again. In addition to this bugfix, some extra logging in send_fuse_iov() and send_fuse_data() has been added to help diagnosing similar issues. Change-Id: If2eecfcdf9c248f3820035601446d2c89ff9d1a1 BUG: 1074023 Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/7278 Tested-by: Gluster Build System Reviewed-by: Xavier Hernandez Reviewed-by: Anand Avati --- xlators/mount/fuse/src/fuse-bridge.c | 60 +++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index cbf256e8b..fd44c4fb5 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -186,6 +186,8 @@ send_fuse_iov (xlator_t *this, fuse_in_header_t *finh, struct iovec *iov_out, fouh->unique = finh->unique; res = writev (priv->fd, iov_out, count); + gf_log ("glusterfs-fuse", GF_LOG_TRACE, "writev() result %d/%d %s", + res, fouh->len, res == -1 ? strerror (errno) : ""); if (res == -1) return errno; @@ -215,13 +217,19 @@ send_fuse_data (xlator_t *this, fuse_in_header_t *finh, void *data, size_t size) { struct fuse_out_header fouh = {0, }; struct iovec iov_out[2]; + int ret = 0; fouh.error = 0; iov_out[0].iov_base = &fouh; iov_out[1].iov_base = data; iov_out[1].iov_len = size; - return send_fuse_iov (this, finh, iov_out, 2); + ret = send_fuse_iov (this, finh, iov_out, 2); + if (ret != 0) + gf_log ("glusterfs-fuse", GF_LOG_ERROR, "send_fuse_iov() " + "failed: %s", strerror (ret)); + + return ret; } #define send_fuse_obj(this, finh, obj) \ @@ -2524,6 +2532,7 @@ fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; int size = 0; + int max_size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; struct fuse_dirent *fde = NULL; @@ -2549,16 +2558,23 @@ fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, frame->root->unique, op_ret, state->size, state->off); list_for_each_entry (entry, &entries->list, list) { - size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + - strlen (entry->d_name)); + max_size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + + strlen (entry->d_name)); + + if (max_size > state->size) { + /* we received to many entries to fit in the request */ + max_size -= FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + + strlen (entry->d_name)); + break; + } } - if (size <= 0) { - send_fuse_data (this, finh, 0, 0); - goto out; - } + if (max_size <= 0) { + send_fuse_data (this, finh, 0, 0); + goto out; + } - buf = GF_CALLOC (1, size, gf_fuse_mt_char); + buf = GF_CALLOC (1, max_size, gf_fuse_mt_char); if (!buf) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": READDIR => -1 (%s)", frame->root->unique, @@ -2572,6 +2588,9 @@ fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, fde = (struct fuse_dirent *)(buf + size); gf_fuse_fill_dirent (entry, fde, priv->enable_ino32); size += FUSE_DIRENT_SIZE (fde); + + if (size == max_size) + break; } send_fuse_data (this, finh, buf, size); @@ -2625,6 +2644,7 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; + int max_size = 0; int size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; @@ -2650,16 +2670,24 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, frame->root->unique, op_ret, state->size, state->off); list_for_each_entry (entry, &entries->list, list) { - size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + - strlen (entry->d_name)); + max_size += FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + + strlen (entry->d_name)); + + if (max_size > state->size) { + /* we received to many entries to fit in the reply */ + max_size -= FUSE_DIRENT_ALIGN ( + FUSE_NAME_OFFSET_DIRENTPLUS + + strlen (entry->d_name)); + break; + } } - if (size <= 0) { + if (max_size <= 0) { send_fuse_data (this, finh, 0, 0); goto out; } - buf = GF_CALLOC (1, size, gf_fuse_mt_char); + buf = GF_CALLOC (1, max_size, gf_fuse_mt_char); if (!buf) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": READDIRP => -1 (%s)", frame->root->unique, @@ -2682,7 +2710,7 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, size += FUSE_DIRENTPLUS_SIZE (fde); if (!entry->inode) - continue; + goto next_entry; entry->d_stat.ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (&entry->d_stat, &feo->attr, priv->enable_ino32); @@ -2690,7 +2718,7 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, linked_inode = inode_link (entry->inode, state->fd->inode, entry->d_name, &entry->d_stat); if (!linked_inode) - continue; + goto next_entry; inode_lookup (linked_inode); @@ -2708,6 +2736,10 @@ fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, calc_timeout_sec (priv->attribute_timeout); feo->attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); + +next_entry: + if (size == max_size) + break; } send_fuse_data (this, finh, buf, size); -- cgit From 31c9cd4631a25ab635f38b2e83800975ff212688 Mon Sep 17 00:00:00 2001 From: Krishnan Parthasarathi Date: Mon, 21 Apr 2014 13:53:29 +0530 Subject: glusterd: pass the right argument for perf subgraph Change-Id: Ic292dcd8e477066c1079f0f1e170f5153459b029 Signed-off-by: Krishnan Parthasarathi Reviewed-on: http://review.gluster.org/7514 Tested-by: Gluster Build System Reviewed-by: Anand Avati --- xlators/mgmt/glusterd/src/glusterd-volgen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c index 0b3a8d099..ca799aefc 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.c +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c @@ -2561,7 +2561,7 @@ static int client_graph_set_perf_options(volgen_graph_t *graph, tmp_data = dict_get (set_dict, "nfs-volume-file"); if (!tmp_data) return volgen_graph_set_options_generic(graph, set_dict, - volname, + volinfo, &perfxl_option_handler); else return volgen_graph_set_options_generic(graph, set_dict, -- cgit From 29e183b41b3c915de997464d0c137ad30559afd2 Mon Sep 17 00:00:00 2001 From: Kasturi Narra Date: Tue, 15 Apr 2014 18:45:12 +0530 Subject: doc: Adding documentation for file-snapshot Change-Id: I52b70a2fb24f5e3f802f0ce8b1c50102c267e218 BUG: 1086750 Signed-off-by: Kasturi Narra Reviewed-on: http://review.gluster.org/7487 Reviewed-by: Vijay Bellur Tested-by: Vijay Bellur --- doc/features/file-snapshot.md | 91 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 doc/features/file-snapshot.md diff --git a/doc/features/file-snapshot.md b/doc/features/file-snapshot.md new file mode 100644 index 000000000..7f7c419fc --- /dev/null +++ b/doc/features/file-snapshot.md @@ -0,0 +1,91 @@ +#File Snapshot +This feature gives the ability to take snapshot of files. + +##Descritpion +This feature adds file snapshotting support to glusterfs. Snapshots can be created , deleted and reverted. + +To take a snapshot of a file, file should be in QCOW2 format as the code for the block layer snapshot has been taken from Qemu and put into gluster as a translator. + +With this feature, glusterfs will have better integration with Openstack Cinder, and in general ability to take snapshots of files (typically VM images). + +New extended attribute (xattr) will be added to identify files which are 'snapshot managed' vs raw files. + +##Volume Options +Following volume option needs to be set on the volume for taking file snapshot. + + # features.file-snapshot on +##CLI parameters +Following cli parameters needs to be passed with setfattr command to create, delete and revert file snapshot. + + # trusted.glusterfs.block-format + # trusted.glusterfs.block-snapshot-create + # trusted.glusterfs.block-snapshot-goto +##Fully loaded Example +Download glusterfs3.5 rpms from download.gluster.org +Install these rpms. + +start glusterd by using the command + + # service glusterd start +Now create a volume by using the command + + # gluster volume create +Run the command below to make sure that volume is created. + + # gluster volume info +Now turn on the snapshot feature on the volume by using the command + + # gluster volume set features.file-snapshot on +Verify that the option is set by using the command + + # gluster volume info +User should be able to see another option in the volume info + + # features.file-snapshot: on +Now mount the volume using fuse mount + + # mount -t glusterfs +cd into the mount point + # cd + # touch +Size of the file can be set and format of the file can be changed to QCOW2 by running the command below. File size can be in KB/MB/GB + + # setfattr -n trusted.glusterfs.block-format -v qcow2: +Now create another file and send data to that file by running the command + + # echo 'ABCDEFGHIJ' > +copy the data to the one file to another by running the command + + # dd if=data-file1 of=big-file conv=notrunc +Now take the `snapshot of the file` by running the command + + # setfattr -n trusted.glusterfs.block-snapshot-create -v +Add some more contents to the file and take another file snaphot by doing the following steps + + # echo '1234567890' > + # dd if= of= conv=notrunc + # setfattr -n trusted.glusterfs.block-snapshot-create -v +Now `revert` both the file snapshots and write data to some files so that data can be compared. + + # setfattr -n trusted.glusterfs.block-snapshot-goto -v + # dd if= of= bs=11 count=1 + # setfattr -n trusted.glusterfs.block-snapshot-goto -v + # dd if= of= bs=11 count=1 +Now read the contents of the files and compare as below: + + # cat , and compare contents. + # cat , and compare contents. +##one line description for the variables used +file_name = File which will be creating in the mount point intially. + +data_file1 = File which contains data 'ABCDEFGHIJ' + +image1 = First file snapshot which has 'ABCDEFGHIJ' + some null values. + +data_file2 = File which contains data '1234567890' + +image2 = second file snapshot which has '1234567890' + some null values. + +out_file1 = After reverting image1 this contains 'ABCDEFGHIJ' + +out_file2 = After reverting image2 this contians '1234567890' -- cgit From 99bfc2a2a1689da1e173cb2f8ef54d2b09ef3a5d Mon Sep 17 00:00:00 2001 From: Ravishankar N Date: Mon, 14 Apr 2014 12:39:36 +0000 Subject: snapshot: use volume's brick_ids for the snaps brickinfo->brick_id was introduced to establish persistence of client xlator names and AFR chanelog attributes (http://review.gluster.org/7155). The snapshot volumes must also use the same IDs during snapshot create and restore to maintain persistence. Change-Id: I13d66d19b63520061ba9ec5f0ce661cf3b9eeafe BUG: 1066778 Signed-off-by: Ravishankar N Reviewed-on: http://review.gluster.org/7477 Reviewed-by: Vijaikumar Mallikarjuna Reviewed-by: Avra Sengupta Reviewed-by: Pranith Kumar Karampuri Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- xlators/mgmt/glusterd/src/glusterd-snapshot.c | 7 +++---- xlators/mgmt/glusterd/src/glusterd-utils.c | 6 +----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/xlators/mgmt/glusterd/src/glusterd-snapshot.c b/xlators/mgmt/glusterd/src/glusterd-snapshot.c index e19ee78ec..d11abee70 100644 --- a/xlators/mgmt/glusterd/src/glusterd-snapshot.c +++ b/xlators/mgmt/glusterd/src/glusterd-snapshot.c @@ -2973,6 +2973,9 @@ glusterd_add_bricks_to_snap_volume (dict_t *dict, dict_t *rsp_dict, strcpy (snap_brickinfo->hostname, original_brickinfo->hostname); strcpy (snap_brickinfo->path, snap_brick_path); uuid_copy (snap_brickinfo->uuid, original_brickinfo->uuid); + /* AFR changelog names are based on brick_id and hence the snap + * volume's bricks must retain the same ID */ + strcpy (snap_brickinfo->brick_id, original_brickinfo->brick_id); list_add_tail (&snap_brickinfo->brick_list, &snap_vol->bricks); out: @@ -3201,10 +3204,6 @@ glusterd_do_snap_vol (glusterd_volinfo_t *origin_vol, glusterd_snap_t *snap, goto out; } - /*Update the brickid for the new brick in new volume*/ - GLUSTERD_ASSIGN_BRICKID_TO_BRICKINFO (snap_brickinfo, snap_vol, - brick_count); - /* Take snapshot of the brick */ if ((uuid_compare (brickinfo->uuid, MY_UUID)) || (snap_brickinfo->snap_status == -1)) { diff --git a/xlators/mgmt/glusterd/src/glusterd-utils.c b/xlators/mgmt/glusterd/src/glusterd-utils.c index 1822be1ec..bb8028003 100644 --- a/xlators/mgmt/glusterd/src/glusterd-utils.c +++ b/xlators/mgmt/glusterd/src/glusterd-utils.c @@ -626,6 +626,7 @@ glusterd_brickinfo_dup (glusterd_brickinfo_t *brickinfo, goto out; } } + strcpy (dup_brickinfo->brick_id, brickinfo->brick_id); dup_brickinfo->status = brickinfo->status; dup_brickinfo->snap_status = brickinfo->snap_status; out: @@ -678,11 +679,6 @@ glusterd_snap_volinfo_restore (dict_t *rsp_dict, goto out; } - /*Update the brickid for the new brick in new volume*/ - GLUSTERD_ASSIGN_BRICKID_TO_BRICKINFO (new_brickinfo, - new_volinfo, - brick_count); - /* If the brick is not of this peer, or snapshot is missed * * for the brick do not replace the xattr for it */ if ((!uuid_compare (brickinfo->uuid, MY_UUID)) && -- cgit