#!/bin/sh
#
# Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com>
# Copyright (c) 2015 ungleich GmbH <http://www.ungleich.ch>
#
# 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.
warn ()
{
echo "$@" >&2
}
_init ()
{
# log level definitions
LOG_NONE=NONE;
LOG_CRITICAL=CRITICAL;
LOG_ERROR=ERROR;
LOG_WARNING=WARNING;
LOG_INFO=INFO
LOG_DEBUG=DEBUG;
LOG_TRACE=TRACE;
HOST_NAME_MAX=64;
prefix="@prefix@";
exec_prefix=@exec_prefix@;
cmd_line=$(echo "@sbindir@/glusterfs");
# check whether getfattr exists
export PATH
getfattr=$(command -v getfattr 2>/dev/null)
if [ $? -ne 0 ]; then
warn "WARNING: getfattr not found, certain checks will be skipped.."
fi
mounttab=/proc/mounts
uname_s=`uname -s`
case ${uname_s} in
NetBSD)
getinode="stat -f %i"
getdev="stat -f %d"
lgetinode="${getinode} -L"
lgetdev="${getdev} -L"
;;
Linux)
getinode="stat -c %i"
getdev="stat -c %d"
lgetinode="${getinode} -L"
lgetdev="${getdev} -L"
;;
esac
UPDATEDBCONF=/etc/updatedb.conf
}
is_valid_hostname ()
{
local server=$1
length=$(echo $server | wc -c)
if [ ${length} -gt ${HOST_NAME_MAX} ]; then
return 1
fi
}
parse_backup_volfile_servers ()
{
local server_list=$1
local servers=""
local new_servers=""
servers=$(echo ${server_list} | sed 's/\:/ /g')
for server in ${servers}; do
is_valid_hostname ${server}
if [ $? -eq 1 ]; then
continue
fi
new_servers=$(echo "${new_servers} ${server}")
done
echo ${new_servers}
}
parse_volfile_servers ()
{
local server_list=$1
local servers=""
local new_servers=""
servers=$(echo ${server_list} | sed 's/,/ /g')
for server in ${servers}; do
is_valid_hostname ${server}
if [ $? -eq 1 ]; then
continue
fi
new_servers=$(echo "${new_servers} ${server}")
done
echo ${new_servers}
}
start_glusterfs ()
{
if [ -n "$log_level_str" ]; then
case "$( echo $log_level_str | awk '{print toupper($0)}')" in
"ERROR")
log_level=$LOG_ERROR;
;;
"INFO")
log_level=$LOG_INFO;
;;
"DEBUG")
log_level=$LOG_DEBUG;
;;
"CRITICAL")
log_level=$LOG_CRITICAL;
;;
"WARNING")
log_level=$LOG_WARNING;
;;
"TRACE")
log_level=$LOG_TRACE;
;;
"NONE")
log_level=$LOG_NONE;
;;
*)
warn "invalid log level $log_level_str, using INFO";
log_level=$LOG_INFO;
;;
esac
fi
# options without values start here
if [ -n "$read_only" ]; then
cmd_line=$(echo "$cmd_line --read-only");
fi
if [ -n "$acl" ]; then
cmd_line=$(echo "$cmd_line --acl");
fi
if [ -n "$selinux" ]; then
cmd_line=$(echo "$cmd_line --selinux");
fi
if [ -n "$enable_ino32" ]; then
cmd_line=$(echo "$cmd_line --enable-ino32");
fi
if [ -n "$worm" ]; then
cmd_line=$(echo "$cmd_line --worm");
fi
if [ -n "$volfile_max_fetch_attempts" ]; then
cmd_line=$(echo "$cmd_line --volfile-max-fetch-attempts=$volfile_max_fetch_attempts")
fi
if [ -n "$fopen_keep_cache" ]; then
cmd_line=$(echo "$cmd_line --fopen-keep-cache");
fi
if [ -n "$volfile_check" ]; then
cmd_line=$(echo "$cmd_line --volfile-check");
fi
if [ -n "$mem_accounting" ]; then
cmd_line=$(echo "$cmd_line --mem-accounting");
fi
if [ -n "$aux_gfid_mount" ]; then
cmd_line=$(echo "$cmd_line --aux-gfid-mount");
fi
if [ -n "$resolve_gids" ]; then
cmd_line=$(echo "$cmd_line --resolve-gids");
fi
if [ -n "$no_root_squash" ]; then
cmd_line=$(echo "$cmd_line --no-root-squash");
fi
if [ -n "$thin_client" ]; then
cmd_line=$(echo "$cmd_line --thin-client");
fi
#options with values start here
if [ -n "$halo_max_latency" ]; then
cmd_line=$(echo "$cmd_line --xlator-option \
*replicate*.halo-max-latency=$halo_max_latency");
fi
if [ -n "$halo_max_replicas" ]; then
cmd_line=$(echo "$cmd_line --xlator-option \
*replicate*.halo-max-replicas=$halo_max_replicas");
fi
if [ -n "$halo_min_replicas" ]; then
cmd_line=$(echo "$cmd_line --xlator-option \
*replicate*.halo-min-replicas=$halo_min_replicas");
fi
if [ -n "$log_level" ]; then
cmd_line=$(echo "$cmd_line --log-level=$log_level");
fi
if [ -n "$log_file" ]; then
cmd_line=$(echo "$cmd_line --log-file=$log_file");
fi
if [ -n "$direct_io_mode" ]; then
cmd_line=$(echo "$cmd_line --direct-io-mode=$direct_io_mode");
fi
if [ -n "$use_readdirp" ]; then
cmd_line=$(echo "$cmd_line --use-readdirp=$use_readdirp");
fi
if [ -n "$event_history" ]; then
cmd_line=$(echo "$cmd_line --event-history=$event_history");
fi
if [ -n "$reader_thread_count" ]; then
cmd_line=$(echo "$cmd_line --reader-thread-count=$reader_thread_count");
fi
if [ -n "$fuse_auto_invalidation" ]; then
cmd_line=$(echo "$cmd_line --auto-invalidation=$fuse_auto_invalidation");
fi
if [ -n "$volume_name" ]; then
cmd_line=$(echo "$cmd_line --volume-name=$volume_name");
fi
if [ -n "$attribute_timeout" ]; then
cmd_line=$(echo "$cmd_line --attribute-timeout=$attribute_timeout");
fi
if [ -n "$entry_timeout" ]; then
cmd_line=$(echo "$cmd_line --entry-timeout=$entry_timeout");
fi
if [ -n "$negative_timeout" ]; then
cmd_line=$(echo "$cmd_line --negative-timeout=$negative_timeout");
fi
if [ -n "$gid_timeout" ]; then
cmd_line=$(echo "$cmd_line --gid-timeout=$gid_timeout");
fi
if [ -n "$lru_limit" ]; then
cmd_line=$(echo "$cmd_line --lru-limit=$lru_limit");
fi
if [ -n "$invalidate_limit" ]; then
cmd_line=$(echo "$cmd_line --invalidate-limit=$invalidate_limit");
fi
if [ -n "$bg_qlen" ]; then
cmd_line=$(echo "$cmd_line --background-qlen=$bg_qlen");
fi
if [ -n "$cong_threshold" ]; then
cmd_line=$(echo "$cmd_line --congestion-threshold=$cong_threshold");
fi
if [ -n "$oom_score_adj" ]; then
cmd_line=$(echo "$cmd_line --oom-score-adj=$oom_score_adj");
fi
if [ -n "$fuse_mountopts" ]; then
cmd_line=$(echo "$cmd_line --fuse-mountopts=$fuse_mountopts");
fi
if [ -n "$xlator_option" ]; then
cmd_line=$(echo "$cmd_line --xlator-option=$xlator_option");
fi
if [ -n "$kernel_writeback_cache" ]; then
cmd_line=$(echo "$cmd_line --kernel-writeback-cache=$kernel_writeback_cache");
fi
if [ -n "$attr_times_granularity" ]; then
cmd_line=$(echo "$cmd_line --attr-times-granularity=$attr_times_granularity");
fi
if [ -n "$dump_fuse" ]; then
cmd_line=$(echo "$cmd_line --dump-fuse=$dump_fuse");
fi
if [ -n "$fuse_flush_handle_interrupt" ]; then
cmd_line=$(echo "$cmd_line --fuse-flush-handle-interrupt=$fuse_flush_handle_interrupt");
fi
if [ -n "$process_name" ]; then
cmd_line=$(echo "$cmd_line --process-name fuse.$process_name");
else
cmd_line=$(echo "$cmd_line --process-name fuse");
fi
# if trasnport type is specified, we have to append it to
# volume name, so that it fetches the right client vol file
if [ -z "$volfile_loc" ]; then
if [ -n "$server_ip" ]; then
servers=$(parse_volfile_servers ${server_ip});
if [ -n "$servers" ]; then
for i in $(echo ${servers}); do
cmd_line=$(echo "$cmd_line --volfile-server=$i");
done
else
warn "ERROR: No valid servers found on command line.. exiting"
print_usage
exit 1
fi
if [ -n "$backupvolfile_server" ]; then
if [ -z "$backup_volfile_servers" ]; then
is_valid_hostname ${backupvolfile_server};
if [ $? -eq 1 ]; then
warn "ERROR: Invalid backup server specified.. exiting"
exit 1
fi
cmd_line=$(echo "$cmd_line --volfile-server=$backupvolfile_server");
fi
fi
if [ -n "$backup_volfile_servers" ]; then
backup_servers=$(parse_backup_volfile_servers ${backup_volfile_servers})
for i in $(echo ${backup_servers}); do
cmd_line=$(echo "$cmd_line --volfile-server=$i");
done
fi
if [ -n "$server_port" ]; then
cmd_line=$(echo "$cmd_line --volfile-server-port=$server_port");
fi
if [ -n "$volume_id" ]; then
if [ -n "$transport" ]; then
volume_id="$volume_id.$transport";
cmd_line=$(echo "$cmd_line --volfile-server-transport=$transport");
fi
cmd_line=$(echo "$cmd_line --volfile-id=$volume_id");
fi
fi
else
cmd_line=$(echo "$cmd_line --volfile=$volfile_loc");
fi
if [ -n "$fuse_mountopts" ]; then
cmd_line=$(echo "$cmd_line --fuse-mountopts=$fuse_mountopts");
fi
if [ -n "$subdir_mount" ]; then
cmd_line=$(echo "$cmd_line --subdir-mount=/$subdir_mount");
fi
cmd_line=$(echo "$cmd_line $mount_point");
$cmd_line;
if [ $? -ne 0 ]; then
# If this is true, then glusterfs process returned error without
# getting daemonized. We have made sure the logs are posted to
# 'stderr', so no need to point them to logfile.
warn "Mounting glusterfs on $mount_point failed."
exit 1;
fi
inode=$( ${getinode} $mount_point 2>/dev/null);
# this is required if the stat returns error
if [ $? -ne 0 ]; then
# At this time, glusterfs got daemonized, and then later exited.
# These failures are only logged in log file.
warn "Mount failed. Check the log file ${log_file} for more details."
umount $mount_point > /dev/null 2>&1;
exit 1;
fi
}
print_usage ()
{
cat << EOF
Usage: $0 <server>:<volume/subdir> <mountpoint> -o<options>
Options:
man 8 $(basename $0)
To display the version number of the mount helper: $0 -V
EOF
}
# check for recursive mounts. i.e, mounting over an existing brick
check_recursive_mount ()
{
if [ $1 = "/" ]; then
warn "Cannot mount over root";
exit 2;
fi
# GFID check first
# remove trailing / from mount point
mnt_dir=${1%/};
if [ -n "${getfattr}" ]; then
${getfattr} -n trusted.gfid $mnt_dir 2>/dev/null | grep -iq "trusted.gfid=";
if [ $? -eq 0 ]; then
warn "ERROR: $mnt_dir is in use as a brick of a gluster volume";
exit 2;
fi
fi
# check if the mount point is a brick's parent directory
GLUSTERD_WORKDIR="@GLUSTERD_WORKDIR@";
ls -L "${GLUSTERD_WORKDIR}"/vols/*/bricks/* > /dev/null 2>&1;
if [ $? -ne 0 ]; then
return;
fi
brick_path=`grep ^path "$GLUSTERD_WORKDIR"/vols/*/bricks/* 2>/dev/null | cut -d "=" -f 2`;
root_inode=`${lgetinode} /`;
root_dev=`${lgetdev} /`;
mnt_inode=`${lgetinode} $mnt_dir`;
mnt_dev=`${lgetdev} $mnt_dir`;
for brick in "$brick_path"; do
# evaluate brick path to see if this is local, if non-local, skip iteration
ls $brick > /dev/null 2>&1;
if [ $? -ne 0 ]; then
continue;
fi
if [ -n "${getfattr}" ]; then
${getfattr} -n trusted.gfid "$brick" 2>/dev/null | grep -iq "trusted.gfid=";
if [ $? -eq 0 ]; then
# brick is local
while [ 1 ]; do
tmp_brick="$brick";
brick="$brick"/..;
brick_dev=`${lgetdev} $brick`;
brick_inode=`${lgetinode} $brick`;
if [ "$mnt_inode" -eq "$brick_inode" \
-a "$mnt_dev" -eq "$brick_dev" ]; then
warn "ERROR: ${mnt_dir} is a parent of the brick ${tmp_brick}";
exit 2;
fi
[ "$root_inode" -ne "$brick_inode" \
-o "$root_dev" -ne "$brick_dev" ] || break;
done;
else
continue;
fi
else
continue;
fi
done;
}
with_options()
{
local key=$1
local value=$2
# Handle options with values.
case "$key" in
"log-level")
log_level_str=$value
;;
"log-file")
log_file=$value
;;
"transport")
transport=$value
;;
"direct-io-mode")
direct_io_mode=$value
;;
"volume-name")
volume_name=$value
;;
"volume-id")
volume_id=$value
;;
"subdir-mount")
subdir_mount=$value
;;
"volfile-check")
volfile_check=$value
;;
"server-port")
server_port=$value
;;
"attribute-timeout")
attribute_timeout=$value
;;
"entry-timeout")
entry_timeout=$value
;;
"negative-timeout")
negative_timeout=$value
;;
"gid-timeout")
gid_timeout=$value
;;
"lru-limit")
lru_limit=$value
;;
"invalidate-limit")
invalidate_limit=$value
;;
"background-qlen")
bg_qlen=$value
;;
"backup-volfile-servers")
backup_volfile_servers=$value
;;
"backupvolfile-server")
backupvolfile_server=$value
;;
"fetch-attempts")
volfile_max_fetch_attempts=$value
;;
"congestion-threshold")
cong_threshold=$value
;;
"oom-score-adj")
oom_score_adj=$value
;;
"xlator-option")
xlator_option=$value
;;
"fuse-mountopts")
fuse_mountopts=$value
;;
"use-readdirp")
use_readdirp=$value
;;
"event-history")
event_history=$value
;;
"reader-thread-count")
reader_thread_count=$value
;;
"auto-invalidation")
fuse_auto_invalidation=$value
;;
"no-root-squash")
if [ $value = "yes" ] ||
[ $value = "on" ] ||
[ $value = "enable" ] ||
[ $value = "true" ] ; then
no_root_squash=1;
fi ;;
"root-squash")
if [ $value = "no" ] ||
[ $value = "off" ] ||
[ $value = "disable" ] ||
[ $value = "false" ] ; then
no_root_squash=1;
fi ;;
"kernel-writeback-cache")
kernel_writeback_cache=$value
;;
"attr-times-granularity")
attr_times_granularity=$value
;;
"dump-fuse")
dump_fuse=$value
;;
"fuse-flush-handle-interrupt")
fuse_flush_handle_interrupt=$value
;;
"context"|"fscontext"|"defcontext"|"rootcontext")
# standard SElinux mount options to pass to the kernel
[ -z "$fuse_mountopts" ] || fuse_mountopts="$fuse_mountopts,"
fuse_mountopts="${fuse_mountopts}$key=\"$value\""
;;
"halo-max-latency")
halo_max_latency=$value
;;
"halo-max-replicas")
halo_max_replicas=$value
;;
"halo-min-replicas")
halo_min_replicas=$value
;;
"process-name")
process_name=$value
;;
x-*)
# comments or userspace application-specific options, drop them
;;
*)
warn "Invalid option: $key"
exit 1
;;
esac
}
without_options()
{
local option=$1
# Handle options without values.
case "$option" in
"ro")
read_only=1
;;
"acl")
acl=1
;;
"selinux")
selinux=1
;;
"worm")
worm=1
;;
"fopen-keep-cache")
fopen_keep_cache=1
;;
"enable-ino32")
enable_ino32=1
;;
"mem-accounting")
mem_accounting=1
;;
"aux-gfid-mount")
if [ ${uname_s} = "Linux" ]; then
aux_gfid_mount=1
fi
;;
"thin-client")
thin_client=1
;;
"resolve-gids")
resolve_gids=1
;;
# "mount -t glusterfs" sends this, but it's useless.
"rw")
;;
# TODO: not sure how to handle this yet
"async"|"sync"|"dirsync"|\
"mand"|"nomand"|\
"silent"|"loud"|\
"iversion"|"noiversion"|\
"nofail")
warn "mount option '${option}' is not handled (yet?)"
;;
# standard mount options to pass to the kernel
"atime"|"noatime"|"diratime"|"nodiratime"|\
"relatime"|"norelatime"|\
"strictatime"|"nostrictatime"|"lazyatime"|"nolazyatime"|\
"dev"|"nodev"|"exec"|"noexec"|"suid"|"nosuid"|"auto_unmount")
[ -z "$fuse_mountopts" ] || fuse_mountopts="$fuse_mountopts,"
fuse_mountopts="${fuse_mountopts}${option}"
;;
# these ones are interpreted during system initialization
"auto"|"noauto")
;;
"_netdev")
;;
x-*)
# comments or userspace application-specific options, drop them
;;
*)
warn "Invalid option $option";
exit 1
;;
esac
}
parse_options()
{
local optarg=${1}
for pair in $(echo ${optarg}|sed 's/,/ /g'); do
key=$(echo "$pair" | cut -f1 -d'=');
value=$(echo "$pair" | cut -f2- -d'=');
if [ "$key" = "$value" ]; then
without_options $pair;
else
with_options $key $value;
fi
done
}
update_updatedb()
{
# Append fuse.glusterfs to PRUNEFS variable in updatedb.conf(5).
# updatedb(8) should not index files under GlusterFS, indexing
# GlusterFS is not necessary and should be avoided.
# Following code disables updatedb crawl on 'glusterfs'
test -f $UPDATEDBCONF && {
if ! grep -q 'glusterfs' $UPDATEDBCONF; then
sed 's/\(PRUNEFS.*\)"/\1 fuse.glusterfs"/' $UPDATEDBCONF \
> ${UPDATEDBCONF}.bak
mv -f ${UPDATEDBCONF}.bak $UPDATEDBCONF
fi
}
}
main ()
{
if [ "x${uname_s}" = "xLinux" -a $# -ge 2 ] ; then
volfile_loc=$1
mount_point=$2
## `mount` specifies options as a last argument
shift 2;
fi
while getopts "Vo:hns" opt; do
case "${opt}" in
o)
parse_options ${OPTARG};
shift 2;
;;
n)
;;
s)
# accept+ignore sloppy mount, passed by autofs
;;
V)
${cmd_line} -V;
exit 0;
;;
h)
print_usage;
exit 0;
;;
?)
print_usage;
exit 0;
;;
esac
done
if [ "x${uname_s}" = "xNetBSD" ] ; then
volfile_loc=$1
mount_point=$2
fi
[ -r "$volfile_loc" ] || {
# '%' included to support ipv6 link local addresses
server_ip=$(echo "$volfile_loc" | sed -n 's/\([a-zA-Z0-9:%.\-]*\):.*/\1/p');
volume_str=$(echo "$volfile_loc" | sed -n 's/.*:\([^ ]*\).*/\1/p');
[ -n "$volume_str" ] && {
volume_id=$volume_str
volume_str_temp=$volume_str
first_char=$(echo "$volume_str" | cut -c 1)
[ ${first_char} = '/' ] && {
volume_str_temp=$(echo "$volume_str" | cut -c 2-)
}
volume_id_temp=$(echo "$volume_str_temp" | cut -f1 -d '/');
[ $(echo $volume_str_temp | grep -c "/") -eq 1 ] &&
[ "$volume_id_temp" != "snaps" ] && {
volume_id=$volume_id_temp;
subdir_mount=$(echo "$volume_str_temp" | cut -f2- -d '/');
}
}
volfile_loc="";
[ -z "$volume_id" -o -z "$server_ip" ] && {
cat <<EOF >&2
ERROR: Server name/volume name unspecified cannot proceed further..
Please specify correct format
Usage:
man 8 $0
EOF
exit 1;
}
}
grep_ret=$(echo ${mount_point} | grep '^\-o');
[ "x" != "x${grep_ret}" ] && {
cat <<EOF >&2
ERROR: -o options cannot be specified in either first two arguments..
Please specify correct style
Usage:
man 8 $0
EOF
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 <<EOF >&2
ERROR: Mount point does not exist
Please specify a mount point
Usage:
man 8 $0
EOF
exit 1;
}
# Simple check to avoid multiple identical mounts
if grep -q "[[:space:]+]${mount_point}[[:space:]+]fuse.glusterfs" $mounttab; then
warn "$0: according to mtab, GlusterFS is already mounted on" \
"$mount_point"
exit 32;
fi
#Snapshot volumes are mounted read only
case $volume_id in
/snaps/* ) read_only=1
esac
check_recursive_mount "$mount_point";
update_updatedb;
start_glusterfs;
}
_init "$@" && main "$@";