diff options
Diffstat (limited to 'geo-replication/src')
| -rw-r--r-- | geo-replication/src/Makefile.am | 33 | ||||
| -rw-r--r-- | geo-replication/src/gsyncd.c | 408 | ||||
| -rwxr-xr-x | geo-replication/src/gverify.sh | 160 | ||||
| -rw-r--r-- | geo-replication/src/peer_add_secret_pub.in | 9 | ||||
| -rwxr-xr-x | geo-replication/src/peer_gsec_create.in | 12 | ||||
| -rw-r--r-- | geo-replication/src/procdiggy.c | 121 | ||||
| -rw-r--r-- | geo-replication/src/procdiggy.h | 20 |
7 files changed, 763 insertions, 0 deletions
diff --git a/geo-replication/src/Makefile.am b/geo-replication/src/Makefile.am new file mode 100644 index 000000000..324d8869f --- /dev/null +++ b/geo-replication/src/Makefile.am @@ -0,0 +1,33 @@ + +gsyncddir = $(libexecdir)/glusterfs + +gsyncd_SCRIPTS = gverify.sh peer_add_secret_pub peer_gsec_create + +# peer_gsec_create and peer_add_secret_pub are not added to +# EXTRA_DIST as it's derived from a .in file +EXTRA_DIST = gverify.sh + +gsyncd_PROGRAMS = gsyncd + +gsyncd_SOURCES = gsyncd.c procdiggy.c + +gsyncd_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ + $(GF_GLUSTERFS_LIBS) + +gsyncd_LDFLAGS = $(GF_LDFLAGS) + +noinst_HEADERS = procdiggy.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) \ + -I$(top_srcdir)/libglusterfs/src\ + -DGSYNCD_PREFIX=\"$(libexecdir)/glusterfs\"\ + -DUSE_LIBGLUSTERFS\ + -DSBIN_DIR=\"$(sbindir)\" -DPYTHON=\"$(PYTHON)\" + +AM_CFLAGS = -Wall $(GF_CFLAGS) + + +CLEANFILES = + +$(top_builddir)/libglusterfs/src/libglusterfs.la: + $(MAKE) -C $(top_builddir)/libglusterfs/src/ all diff --git a/geo-replication/src/gsyncd.c b/geo-replication/src/gsyncd.c new file mode 100644 index 000000000..0830e7f9b --- /dev/null +++ b/geo-replication/src/gsyncd.c @@ -0,0 +1,408 @@ +/* + Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com> + 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <sys/param.h> /* for PATH_MAX */ + +/* NOTE (USE_LIBGLUSTERFS): + * ------------------------ + * When USE_LIBGLUSTERFS debugging sumbol is passed; perform + * glusterfs translator like initialization so that glusterfs + * globals, contexts are valid when glustefs api's are invoked. + * We unconditionally pass then while building gsyncd binary. + */ +#ifdef USE_LIBGLUSTERFS +#include "glusterfs.h" +#include "globals.h" +#endif + +#include "common-utils.h" +#include "run.h" +#include "procdiggy.h" + +#define _GLUSTERD_CALLED_ "_GLUSTERD_CALLED_" +#define _GSYNCD_DISPATCHED_ "_GSYNCD_DISPATCHED_" +#define GSYNCD_CONF_TEMPLATE "geo-replication/gsyncd_template.conf" +#define GSYNCD_PY "gsyncd.py" +#define RSYNC "rsync" + +int restricted = 0; + +static int +duplexpand (void **buf, size_t tsiz, size_t *len) +{ + size_t osiz = tsiz * *len; + char *p = realloc (*buf, osiz << 1); + if (!p) { + free(*buf); + return -1; + } + + memset (p + osiz, 0, osiz); + *buf = p; + *len <<= 1; + + return 0; +} + +static int +str2argv (char *str, char ***argv) +{ + char *p = NULL; + char *savetok = NULL; + int argc = 0; + size_t argv_len = 32; + int ret = 0; + + assert (str); + str = strdup (str); + if (!str) + return -1; + + *argv = calloc (argv_len, sizeof (**argv)); + if (!*argv) + goto error; + + while ((p = strtok_r (str, " ", &savetok))) { + str = NULL; + + argc++; + if (argc == argv_len) { + ret = duplexpand ((void *)argv, + sizeof (**argv), + &argv_len); + if (ret == -1) + goto error; + } + (*argv)[argc - 1] = p; + } + + return argc; + + error: + fprintf (stderr, "out of memory\n"); + return -1; +} + +static int +invoke_gsyncd (int argc, char **argv) +{ + char config_file[PATH_MAX] = {0,}; + size_t gluster_workdir_len = 0; + runner_t runner = {0,}; + int i = 0; + int j = 0; + char *nargv[argc + 4]; + char *python = NULL; + + if (restricted) { + size_t len; + /* in restricted mode we forcibly use the system-wide config */ + runinit (&runner); + runner_add_args (&runner, SBIN_DIR"/gluster", + "--log-file=-", "system::", "getwd", + NULL); + runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); + if (runner_start (&runner) == 0 && + fgets (config_file, PATH_MAX, + runner_chio (&runner, STDOUT_FILENO)) != NULL && + (len = strlen (config_file)) && + config_file[len - 1] == '\n' && + runner_end (&runner) == 0) + gluster_workdir_len = len - 1; + + if (gluster_workdir_len) { + if (gluster_workdir_len + 1 + strlen (GSYNCD_CONF_TEMPLATE) + 1 > + PATH_MAX) + goto error; + config_file[gluster_workdir_len] = '/'; + strcat (config_file, GSYNCD_CONF_TEMPLATE); + } else + goto error; + + if (setenv ("_GSYNCD_RESTRICTED_", "1", 1) == -1) + goto error; + } + + if (chdir ("/") == -1) + goto error; + + j = 0; + python = getenv("PYTHON"); + if(!python) + python = PYTHON; + nargv[j++] = python; + nargv[j++] = GSYNCD_PREFIX"/python/syncdaemon/"GSYNCD_PY; + for (i = 1; i < argc; i++) + nargv[j++] = argv[i]; + if (config_file[0]) { + nargv[j++] = "-c"; + nargv[j++] = config_file; + } + nargv[j++] = NULL; + + execvp (python, nargv); + + fprintf (stderr, "exec of '%s' failed\n", python); + return 127; + + error: + fprintf (stderr, "gsyncd initializaion failed\n"); + return 1; +} + + +static int +find_gsyncd (pid_t pid, pid_t ppid, char *name, void *data) +{ + char buf[NAME_MAX * 2] = {0,}; + char path[PATH_MAX] = {0,}; + char *p = NULL; + int zeros = 0; + int ret = 0; + int fd = -1; + pid_t *pida = (pid_t *)data; + + if (ppid != pida[0]) + return 0; + + sprintf (path, PROC"/%d/cmdline", pid); + fd = open (path, O_RDONLY); + if (fd == -1) + return 0; + ret = read (fd, buf, sizeof (buf)); + close (fd); + if (ret == -1) + return 0; + for (zeros = 0, p = buf; zeros < 2 && p < buf + ret; p++) + zeros += !*p; + + ret = 0; + switch (zeros) { + case 2: + if ((strcmp (basename (buf), basename (PYTHON)) || + strcmp (basename (buf + strlen (buf) + 1), GSYNCD_PY)) == 0) { + ret = 1; + break; + } + /* fallthrough */ + case 1: + if (strcmp (basename (buf), GSYNCD_PY) == 0) + ret = 1; + } + + if (ret == 1) { + if (pida[1] != -1) { + fprintf (stderr, GSYNCD_PY" sibling is not unique"); + return -1; + } + pida[1] = pid; + } + + return 0; +} + +static int +invoke_rsync (int argc, char **argv) +{ + int i = 0; + char path[PATH_MAX] = {0,}; + pid_t pid = -1; + pid_t ppid = -1; + pid_t pida[] = {-1, -1}; + char *name = NULL; + char buf[PATH_MAX + 1] = {0,}; + int ret = 0; + + assert (argv[argc] == NULL); + + if (argc < 2 || strcmp (argv[1], "--server") != 0) + goto error; + + for (i = 2; i < argc && argv[i][0] == '-'; i++); + + if (!(i == argc - 2 && strcmp (argv[i], ".") == 0 && argv[i + 1][0] == '/')) { + fprintf (stderr, "need an rsync invocation without protected args\n"); + goto error; + } + + /* look up sshd we are spawned from */ + for (pid = getpid () ;; pid = ppid) { + ppid = pidinfo (pid, &name); + if (ppid < 0) { + fprintf (stderr, "sshd ancestor not found\n"); + goto error; + } + if (strcmp (name, "sshd") == 0) { + GF_FREE (name); + break; + } + GF_FREE (name); + } + /* look up "ssh-sibling" gsyncd */ + pida[0] = pid; + ret = prociter (find_gsyncd, pida); + if (ret == -1 || pida[1] == -1) { + fprintf (stderr, "gsyncd sibling not found\n"); + goto error; + } + /* check if rsync target matches gsyncd target */ + sprintf (path, PROC"/%d/cwd", pida[1]); + ret = readlink (path, buf, sizeof (buf)); + if (ret == -1 || ret == sizeof (buf)) + goto error; + if (strcmp (argv[argc - 1], "/") == 0 /* root dir cannot be a target */ || + (strcmp (argv[argc - 1], path) /* match against gluster target */ && + strcmp (argv[argc - 1], buf) /* match against file target */) != 0) { + fprintf (stderr, "rsync target does not match "GEOREP" session\n"); + goto error; + } + + argv[0] = RSYNC; + + execvp (RSYNC, argv); + + fprintf (stderr, "exec of "RSYNC" failed\n"); + return 127; + + error: + fprintf (stderr, "disallowed "RSYNC" invocation\n"); + return 1; +} + +static int +invoke_gluster (int argc, char **argv) +{ + int i = 0; + int j = 0; + int optsover = 0; + char *ov = NULL; + + for (i = 1; i < argc; i++) { + ov = strtail (argv[i], "--"); + if (ov && !optsover) { + if (*ov == '\0') + optsover = 1; + continue; + } + switch (++j) { + case 1: + if (strcmp (argv[i], "volume") != 0) + goto error; + break; + case 2: + if (strcmp (argv[i], "info") != 0) + goto error; + break; + case 3: + break; + default: + goto error; + } + } + + argv[0] = "gluster"; + execvp (SBIN_DIR"/gluster", argv); + fprintf (stderr, "exec of gluster failed\n"); + return 127; + + error: + fprintf (stderr, "disallowed gluster invocation\n"); + return 1; +} + +struct invocable { + char *name; + int (*invoker) (int argc, char **argv); +}; + +struct invocable invocables[] = { + { "rsync", invoke_rsync }, + { "gsyncd", invoke_gsyncd }, + { "gluster", invoke_gluster }, + { NULL, NULL} +}; + +int +main (int argc, char **argv) +{ + char *evas = NULL; + struct invocable *i = NULL; + char *b = NULL; + char *sargv = NULL; + +#ifdef USE_LIBGLUSTERFS + glusterfs_ctx_t *ctx = NULL; + + ctx = glusterfs_ctx_new (); + if (!ctx) + return ENOMEM; + + if (glusterfs_globals_init (ctx)) + return 1; + + THIS->ctx = ctx; +#endif + + evas = getenv (_GLUSTERD_CALLED_); + if (evas && strcmp (evas, "1") == 0) + /* OK, we know glusterd called us, no need to look for further config + * ... altough this conclusion should not inherit to our children + */ + unsetenv (_GLUSTERD_CALLED_); + else { + /* we regard all gsyncd invocations unsafe + * that do not come from glusterd and + * therefore restrict it + */ + restricted = 1; + + if (!getenv (_GSYNCD_DISPATCHED_)) { + evas = getenv ("SSH_ORIGINAL_COMMAND"); + if (evas) + sargv = evas; + else { + evas = getenv ("SHELL"); + if (evas && strcmp (basename (evas), "gsyncd") == 0 && + argc == 3 && strcmp (argv[1], "-c") == 0) + sargv = argv[2]; + } + } + + } + + if (!(sargv && restricted)) + return invoke_gsyncd (argc, argv); + + argc = str2argv (sargv, &argv); + if (argc == -1 || setenv (_GSYNCD_DISPATCHED_, "1", 1) == -1) { + fprintf (stderr, "internal error\n"); + return 1; + } + + b = basename (argv[0]); + for (i = invocables; i->name; i++) { + if (strcmp (b, i->name) == 0) + return i->invoker (argc, argv); + } + + fprintf (stderr, "invoking %s in restricted SSH session is not allowed\n", + b); + + return 1; +} diff --git a/geo-replication/src/gverify.sh b/geo-replication/src/gverify.sh new file mode 100755 index 000000000..bd1b25f24 --- /dev/null +++ b/geo-replication/src/gverify.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +# Script to verify the Master and Slave Gluster compatibility. +# To use ./gverify <master volume> <slave host> <slave volume> +# Returns 0 if master and slave compatible. + +# Considering buffer_size 100MB +BUFFER_SIZE=104857600; +slave_log_file=`gluster --print-logdir`/geo-replication-slaves/slave.log + +function SSHM() +{ + ssh -q \ + -oPasswordAuthentication=no \ + -oStrictHostKeyChecking=no \ + -oControlMaster=yes \ + "$@"; +} + +function cmd_master() +{ + VOL=$1; + local cmd_line; + cmd_line=$(cat <<EOF +function do_verify() { +v=\$1; +d=\$(mktemp -d 2>/dev/null); +glusterfs -s localhost --xlator-option="*dht.lookup-unhashed=off" --volfile-id \$v -l $slave_log_file \$d; +i=\$(stat -c "%i" \$d); +if [[ "\$i" -ne "1" ]]; then +echo 0:0; +exit 1; +fi; +cd \$d; +available_size=\$(df \$d | tail -1 | awk "{print \\\$2}"); +umount -l \$d; +rmdir \$d; +ver=\$(gluster --version | head -1 | cut -f2 -d " "); +echo \$available_size:\$ver; +}; +cd /tmp; +[ x$VOL != x ] && do_verify $VOL; +EOF +); + +echo $cmd_line; +} + +function cmd_slave() +{ + VOL=$1; + local cmd_line; + cmd_line=$(cat <<EOF +function do_verify() { +v=\$1; +d=\$(mktemp -d 2>/dev/null); +glusterfs -s localhost --xlator-option="*dht.lookup-unhashed=off" --volfile-id \$v -l $slave_log_file \$d; +i=\$(stat -c "%i" \$d); +if [[ "\$i" -ne "1" ]]; then +echo 0:0; +exit 1; +fi; +cd \$d; +available_size=\$(df \$d | tail -1 | awk "{print \\\$4}"); +no_of_files=\$(find \$d -maxdepth 0 -empty); +umount -l \$d; +rmdir \$d; +ver=\$(gluster --version | head -1 | cut -f2 -d " "); +echo \$available_size:\$ver:\$no_of_files:; +}; +cd /tmp; +[ x$VOL != x ] && do_verify $VOL; +EOF +); + +echo $cmd_line; +} + +function master_stats() +{ + MASTERVOL=$1; + local cmd_line; + cmd_line=$(cmd_master $MASTERVOL); + bash -c "$cmd_line"; +} + + +function slave_stats() +{ + SLAVEHOST=$1; + SLAVEVOL=$2; + local cmd_line; + cmd_line=$(cmd_slave $SLAVEVOL); + SSHM $SLAVEHOST bash -c "'$cmd_line'"; +} + + +function main() +{ + log_file=$4 + > $log_file + + # Use FORCE_BLOCKER flag in the error message to differentiate + # between the errors which the force command should bypass + + ping -w 5 $2; + if [ $? -ne 0 ]; then + echo "FORCE_BLOCKER|$2 not reachable." > $log_file + exit 1; + fi; + + ssh -oNumberOfPasswordPrompts=0 $2 "echo Testing_Passwordless_SSH"; + if [ $? -ne 0 ]; then + echo "FORCE_BLOCKER|Passwordless ssh login has not been setup with $2." > $log_file + exit 1; + fi; + + ERRORS=0; + master_data=$(master_stats $1); + slave_data=$(slave_stats $2 $3); + master_size=$(echo $master_data | cut -f1 -d':'); + slave_size=$(echo $slave_data | cut -f1 -d':'); + master_version=$(echo $master_data | cut -f2 -d':'); + slave_version=$(echo $slave_data | cut -f2 -d':'); + slave_no_of_files=$(echo $slave_data | cut -f3 -d':'); + + if [[ "x$master_size" = "x" || "x$master_version" = "x" || "$master_size" -eq "0" ]]; then + echo "FORCE_BLOCKER|Unable to fetch master volume details. Please check the master cluster and master volume." > $log_file; + exit 1; + fi; + + if [[ "x$slave_size" = "x" || "x$slave_version" = "x" || "$slave_size" -eq "0" ]]; then + echo "FORCE_BLOCKER|Unable to fetch slave volume details. Please check the slave cluster and slave volume." > $log_file; + exit 1; + fi; + + # The above checks are mandatory and force command should be blocked + # if they fail. The checks below can be bypassed if force option is + # provided hence no FORCE_BLOCKER flag. + + if [ ! $slave_size -ge $(($master_size - $BUFFER_SIZE )) ]; then + echo "Total size of master is greater than available size of slave." >> $log_file; + ERRORS=$(($ERRORS + 1)); + fi; + + if [ -z $slave_no_of_files ]; then + echo "$2::$3 is not empty. Please delete existing files in $2::$3 and retry, or use force to continue without deleting the existing files." >> $log_file; + ERRORS=$(($ERRORS + 1)); + fi; + + if [[ $master_version > $slave_version ]]; then + echo "Gluster version mismatch between master and slave." >> $log_file; + ERRORS=$(($ERRORS + 1)); + fi; + + exit $ERRORS; +} + + +main "$@"; diff --git a/geo-replication/src/peer_add_secret_pub.in b/geo-replication/src/peer_add_secret_pub.in new file mode 100644 index 000000000..c036cf334 --- /dev/null +++ b/geo-replication/src/peer_add_secret_pub.in @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ ! -d ~/.ssh ]; then + mkdir ~/.ssh; + chmod 700 ~/.ssh + chown root:root ~/.ssh +fi + +cat "$GLUSTERD_WORKING_DIR"/geo-replication/common_secret.pem.pub >> ~/.ssh/authorized_keys diff --git a/geo-replication/src/peer_gsec_create.in b/geo-replication/src/peer_gsec_create.in new file mode 100755 index 000000000..ef630bd44 --- /dev/null +++ b/geo-replication/src/peer_gsec_create.in @@ -0,0 +1,12 @@ +#!/bin/bash + +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +if [ ! -f "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem.pub ]; then + \rm -rf "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem* + ssh-keygen -N '' -f "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem > /dev/null +fi + +output=`echo command=\"@libexecdir@/glusterfs/gsyncd\" " "``cat "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem.pub` +echo $output diff --git a/geo-replication/src/procdiggy.c b/geo-replication/src/procdiggy.c new file mode 100644 index 000000000..1eba414c1 --- /dev/null +++ b/geo-replication/src/procdiggy.c @@ -0,0 +1,121 @@ +/* + Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com> + 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <sys/param.h> /* for PATH_MAX */ + +#include "common-utils.h" +#include "procdiggy.h" + +pid_t +pidinfo (pid_t pid, char **name) +{ + char buf[NAME_MAX * 2] = {0,}; + FILE *f = NULL; + char path[PATH_MAX] = {0,}; + char *p = NULL; + int ret = 0; + + sprintf (path, PROC"/%d/status", pid); + + f = fopen (path, "r"); + if (!f) + return -1; + + if (name) + *name = NULL; + for (;;) { + size_t len; + memset (buf, 0, sizeof (buf)); + if (fgets (buf, sizeof (buf), f) == NULL || + (len = strlen (buf)) == 0 || + buf[len - 1] != '\n') { + pid = -1; + goto out; + } + buf[len - 1] = '\0'; + + if (name && !*name) { + p = strtail (buf, "Name:"); + if (p) { + while (isspace (*++p)); + *name = gf_strdup (p); + if (!*name) { + pid = -2; + goto out; + } + continue; + } + } + + p = strtail (buf, "PPid:"); + if (p) + break; + } + + while (isspace (*++p)); + ret = gf_string2int (p, &pid); + if (ret == -1) + pid = -1; + + out: + fclose (f); + if (pid == -1 && name && *name) + GF_FREE (name); + if (pid == -2) + fprintf (stderr, "out of memory\n"); + return pid; +} + +int +prociter (int (*proch) (pid_t pid, pid_t ppid, char *tmpname, void *data), + void *data) +{ + char *name = NULL; + DIR *d = NULL; + struct dirent *de = NULL; + pid_t pid = -1; + pid_t ppid = -1; + int ret = 0; + + d = opendir (PROC); + if (!d) + return -1; + while (errno = 0, de = readdir (d)) { + if (gf_string2int (de->d_name, &pid) != -1 && pid >= 0) { + ppid = pidinfo (pid, &name); + switch (ppid) { + case -1: continue; + case -2: ret = -1; break; + } + ret = proch (pid, ppid, name, data); + GF_FREE (name); + if (ret) + break; + } + } + closedir (d); + if (!de && errno) { + fprintf (stderr, "failed to traverse "PROC" (%s)\n", + strerror (errno)); + ret = -1; + } + + return ret; +} diff --git a/geo-replication/src/procdiggy.h b/geo-replication/src/procdiggy.h new file mode 100644 index 000000000..56dfc4eb2 --- /dev/null +++ b/geo-replication/src/procdiggy.h @@ -0,0 +1,20 @@ +/* + Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com> + 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. +*/ +#ifdef __NetBSD__ +#include <sys/syslimits.h> +#endif /* __NetBSD__ */ + +#define PROC "/proc" + +pid_t pidinfo (pid_t pid, char **name); + +int prociter (int (*proch) (pid_t pid, pid_t ppid, char *name, void *data), + void *data); + |
