summaryrefslogtreecommitdiffstats
path: root/heartbeat/Filesystem
diff options
context:
space:
mode:
Diffstat (limited to 'heartbeat/Filesystem')
-rwxr-xr-xheartbeat/Filesystem1128
1 files changed, 1128 insertions, 0 deletions
diff --git a/heartbeat/Filesystem b/heartbeat/Filesystem
new file mode 100755
index 0000000..0665628
--- /dev/null
+++ b/heartbeat/Filesystem
@@ -0,0 +1,1128 @@
+#!/bin/sh
+#
+# Support: users@clusterlabs.org
+# License: GNU General Public License (GPL)
+#
+# Filesystem
+# Description: Manages a Filesystem on a shared storage medium.
+# Original Author: Eric Z. Ayers (eric.ayers@compgen.com)
+# Original Release: 25 Oct 2000
+#
+# usage: ./Filesystem {start|stop|status|monitor|validate-all|meta-data}
+#
+# OCF parameters are as below:
+# OCF_RESKEY_device
+# OCF_RESKEY_directory
+# OCF_RESKEY_fstype
+# OCF_RESKEY_options
+# OCF_RESKEY_statusfile_prefix
+# OCF_RESKEY_run_fsck
+# OCF_RESKEY_fast_stop
+# OCF_RESKEY_force_clones
+#
+#OCF_RESKEY_device : name of block device for the filesystem. e.g. /dev/sda1, /dev/md0
+# Or a -U or -L option for mount, or an NFS mount specification
+#OCF_RESKEY_directory : the mount point for the filesystem
+#OCF_RESKEY_fstype : optional name of the filesystem type. e.g. ext2
+#OCF_RESKEY_options : options to be given to the mount command via -o
+#OCF_RESKEY_statusfile_prefix : the prefix used for a status file for monitoring
+#OCF_RESKEY_run_fsck : fsck execution mode: auto(default)/force/no
+#OCF_RESKEY_fast_stop : fast stop: yes(default)/no
+#OCF_RESKEY_force_clones : allow running the resource as clone. e.g. local xfs mounts
+# for each brick in a glusterfs setup
+#
+#
+# This assumes you want to manage a filesystem on a shared (SCSI) bus,
+# on a replicated device (such as DRBD), or a network filesystem (such
+# as NFS or Samba).
+#
+# Do not put this filesystem in /etc/fstab. This script manages all of
+# that for you.
+#
+# NOTE: If 2 or more nodes mount the same file system read-write, and
+# that file system is not designed for that specific purpose
+# (such as GFS or OCFS2), and is not a network file system like
+# NFS or Samba, then the filesystem is going to become
+# corrupted.
+#
+# As a result, you should use this together with the stonith
+# option and redundant, independent communications paths.
+#
+# If you don't do this, don't blame us when you scramble your
+# disk.
+
+#######################################################################
+# Initialization:
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+
+# Defaults
+DFLT_STATUSDIR=".Filesystem_status/"
+
+# Parameter defaults
+
+OCF_RESKEY_device_default=""
+OCF_RESKEY_directory_default=""
+OCF_RESKEY_fstype_default=""
+OCF_RESKEY_options_default=""
+OCF_RESKEY_statusfile_prefix_default="${DFLT_STATUSDIR}"
+OCF_RESKEY_run_fsck_default="auto"
+OCF_RESKEY_fast_stop_default="no"
+OCF_RESKEY_force_clones_default="false"
+OCF_RESKEY_force_unmount_default="true"
+OCF_RESKEY_term_signals_default="TERM"
+OCF_RESKEY_kill_signals_default="KILL"
+OCF_RESKEY_signal_delay_default="1"
+
+# RHEL specific defaults
+if is_redhat_based; then
+ get_os_ver
+ ocf_version_cmp "$VER" "9.0" 2>/dev/null
+
+ case "$?" in
+ # RHEL >= 9
+ 1|2)
+ OCF_RESKEY_force_unmount_default="safe";;
+ # RHEL < 9 and fallback if ocf_version_cmp() fails
+ *)
+ OCF_RESKEY_fast_stop_default="yes";;
+ esac
+fi
+
+
+: ${OCF_RESKEY_device=${OCF_RESKEY_device_default}}
+: ${OCF_RESKEY_directory=${OCF_RESKEY_directory_default}}
+: ${OCF_RESKEY_fstype=${OCF_RESKEY_fstype_default}}
+: ${OCF_RESKEY_options=${OCF_RESKEY_options_default}}
+: ${OCF_RESKEY_statusfile_prefix=${OCF_RESKEY_statusfile_prefix_default}}
+: ${OCF_RESKEY_run_fsck=${OCF_RESKEY_run_fsck_default}}
+if [ -z "${OCF_RESKEY_fast_stop}" ]; then
+ case "$OCF_RESKEY_fstype" in
+ gfs2)
+ OCF_RESKEY_fast_stop="no";;
+ *)
+ OCF_RESKEY_fast_stop=${OCF_RESKEY_fast_stop_default};;
+ esac
+fi
+: ${OCF_RESKEY_force_clones=${OCF_RESKEY_force_clones_default}}
+: ${OCF_RESKEY_force_unmount=${OCF_RESKEY_force_unmount_default}}
+: ${OCF_RESKEY_term_signals=${OCF_RESKEY_term_signals_default}}
+: ${OCF_RESKEY_kill_signals=${OCF_RESKEY_kill_signals_default}}
+: ${OCF_RESKEY_signal_delay=${OCF_RESKEY_signal_delay_default}}
+
+# Variables used by multiple methods
+HOSTOS=$(uname)
+TAB=' '
+
+# The status file is going to an extra directory, by default
+#
+prefix=${OCF_RESKEY_statusfile_prefix}
+: ${prefix:=$DFLT_STATUSDIR}
+suffix="${OCF_RESOURCE_INSTANCE}"
+[ "$OCF_RESKEY_CRM_meta_clone" ] &&
+ suffix="${suffix}_$OCF_RESKEY_CRM_meta_clone"
+suffix="${suffix}_$(uname -n)"
+STATUSFILE="${OCF_RESKEY_directory}/$prefix$suffix"
+
+#######################################################################
+
+usage() {
+ cat <<-EOT
+ usage: $0 {start|stop|status|monitor|validate-all|meta-data}
+ EOT
+}
+
+meta_data() {
+ cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="Filesystem" version="1.1">
+<version>1.0</version>
+
+<longdesc lang="en">
+Resource script for Filesystem. It manages a Filesystem on a
+shared storage medium.
+
+The standard monitor operation of depth 0 (also known as probe)
+checks if the filesystem is mounted. If you want deeper tests,
+set OCF_CHECK_LEVEL to one of the following values:
+
+10: read first 16 blocks of the device (raw read)
+
+This doesn't exercise the filesystem at all, but the device on
+which the filesystem lives. This is noop for non-block devices
+such as NFS, SMBFS, or bind mounts.
+
+20: test if a status file can be written and read
+
+The status file must be writable by root. This is not always the
+case with an NFS mount, as NFS exports usually have the
+"root_squash" option set. In such a setup, you must either use
+read-only monitoring (depth=10), export with "no_root_squash" on
+your NFS server, or grant world write permissions on the
+directory where the status file is to be placed.
+</longdesc>
+<shortdesc lang="en">Manages filesystem mounts</shortdesc>
+
+<parameters>
+<parameter name="device" required="1">
+<longdesc lang="en">
+The name of block device for the filesystem, or -U, -L options for mount, or NFS mount specification.
+
+NOTE: On Linux /dev/disk/by-{uuid,label}/ are preferred to -U/-L.
+</longdesc>
+<shortdesc lang="en">block device</shortdesc>
+<content type="string" default="${OCF_RESKEY_device_default}" />
+</parameter>
+
+<parameter name="directory" required="1">
+<longdesc lang="en">
+The mount point for the filesystem.
+</longdesc>
+<shortdesc lang="en">mount point</shortdesc>
+<content type="string" default="${OCF_RESKEY_directory_default}" />
+</parameter>
+
+<parameter name="fstype" required="1">
+<longdesc lang="en">
+The type of filesystem to be mounted.
+</longdesc>
+<shortdesc lang="en">filesystem type</shortdesc>
+<content type="string" default="${OCF_RESKEY_fstype_default}" />
+</parameter>
+
+<parameter name="options">
+<longdesc lang="en">
+Any extra options to be given as -o options to mount.
+
+For bind mounts, add "bind" here and set fstype to "none".
+We will do the right thing for options such as "bind,ro".
+</longdesc>
+<shortdesc lang="en">options</shortdesc>
+<content type="string" default="${OCF_RESKEY_options_default}" />
+</parameter>
+
+<parameter name="statusfile_prefix">
+<longdesc lang="en">
+The prefix to be used for a status file for resource monitoring
+with depth 20. If you don't specify this parameter, all status
+files will be created in a separate directory.
+</longdesc>
+<shortdesc lang="en">status file prefix</shortdesc>
+<content type="string" default="${OCF_RESKEY_statusfile_prefix_default}" />
+</parameter>
+
+<parameter name="run_fsck">
+<longdesc lang="en">
+Specify how to decide whether to run fsck or not.
+
+"auto" : decide to run fsck depending on the fstype(default)
+"force" : always run fsck regardless of the fstype
+"no" : do not run fsck ever.
+</longdesc>
+<shortdesc lang="en">run_fsck</shortdesc>
+<content type="string" default="${OCF_RESKEY_run_fsck_default}" />
+</parameter>
+
+<parameter name="fast_stop">
+<longdesc lang="en">
+Normally, we expect no users of the filesystem and the stop
+operation to finish quickly. If you cannot control the filesystem
+users easily and want to prevent the stop action from failing,
+then set this parameter to "no" and add an appropriate timeout
+for the stop operation.
+
+This defaults to "no" for GFS2 filesystems.
+</longdesc>
+<shortdesc lang="en">fast stop</shortdesc>
+<content type="boolean" default="${OCF_RESKEY_fast_stop_default}" />
+</parameter>
+
+<parameter name="force_clones">
+<longdesc lang="en">
+The use of a clone setup for local filesystems is forbidden
+by default. For special setups like glusterfs, cloning a mount
+of a local device with a filesystem like ext4 or xfs independently
+on several nodes is a valid use case.
+
+Only set this to "true" if you know what you are doing!
+</longdesc>
+<shortdesc lang="en">allow running as a clone, regardless of filesystem type</shortdesc>
+<content type="boolean" default="${OCF_RESKEY_force_clones_default}" />
+</parameter>
+
+<parameter name="force_unmount">
+<longdesc lang="en">
+This option allows specifying how to handle processes that are
+currently accessing the mount directory.
+
+"true" : Kill processes accessing mount point
+"safe" : Kill processes accessing mount point using methods that
+ avoid functions that could potentially block during process
+ detection
+"false" : Do not kill any processes.
+
+The 'safe' option uses shell logic to walk the /procs/ directory
+for pids using the mount point while the default option uses the
+fuser cli tool. fuser is known to perform operations that can potentially
+block if unresponsive nfs mounts are in use on the system.
+</longdesc>
+<shortdesc lang="en">Kill processes before unmount</shortdesc>
+<content type="string" default="${OCF_RESKEY_force_unmount_default}" />
+</parameter>
+
+<parameter name="term_signals">
+<longdesc lang="en">
+Signals (names or numbers, whitespace separated) to send processes during graceful termination phase in stop-action.
+</longdesc>
+<shortdesc lang="en">Signals (names or numbers, whitespace separated) to send processes during graceful termination phase in stop-action</shortdesc>
+<content type="string" default="${OCF_RESKEY_term_signals_default}" />
+</parameter>
+
+<parameter name="kill_signals">
+<longdesc lang="en">
+Signals (names or numbers, whitespace separated) to send processes during forceful killing phase in stop-action.
+</longdesc>
+<shortdesc lang="en">Signals (names or numbers, whitespace separated) to send processes during forceful killing phase in stop-action</shortdesc>
+<content type="string" default="${OCF_RESKEY_kill_signals_default}" />
+</parameter>
+
+<parameter name="signal_delay">
+<longdesc lang="en">
+How many seconds to wait after sending term/kill signals to processes in stop-action.
+</longdesc>
+<shortdesc lang="en">How many seconds to wait after sending term/kill signals to processes in stop-action</shortdesc>
+<content type="string" default="${OCF_RESKEY_signal_delay_default}" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="60s" />
+<action name="stop" timeout="60s" />
+<action name="monitor" depth="0" timeout="40s" interval="20s" />
+<action name="validate-all" timeout="5s" />
+<action name="meta-data" timeout="5s" />
+</actions>
+</resource-agent>
+END
+}
+
+#
+# Make sure the kernel does the right thing with the FS buffers
+# This function should be called after unmounting and before mounting
+# It may not be necessary in 2.4 and later kernels, but it shouldn't hurt
+# anything either...
+#
+# It's really a bug that you have to do this at all...
+#
+flushbufs() {
+ if have_binary $BLOCKDEV ; then
+ if [ "$blockdevice" = "yes" ] ; then
+ $BLOCKDEV --flushbufs $1
+ return $?
+ fi
+ fi
+ return 0
+}
+
+# Take advantage of /etc/mtab if present, use portable mount command
+# otherwise. Normalize format to "dev mountpoint fstype".
+is_bind_mount() {
+ echo "$options" | grep -w bind >/dev/null 2>&1
+}
+
+list_mounts() {
+ local inpf=""
+ local mount_list=""
+ local check_list="x"
+
+ if [ -e "/proc/mounts" ] && ! is_bind_mount; then
+ inpf=/proc/mounts
+ elif [ -f "/etc/mtab" -a -r "/etc/mtab" ]; then
+ inpf=/etc/mtab
+ fi
+
+ # Make sure that the mount list has not been changed while reading.
+ while [ "$mount_list" != "$check_list" ]; do
+ check_list="$mount_list"
+ if [ "$inpf" ]; then
+ # <device> <mountpoint> <fstype> ...
+ # Spaces in device or mountpoint are octal \040 in $inpf
+ # Convert literal spaces (field separators) to tabs
+ mount_list=$(cut -d' ' -f1,2,3 < $inpf | tr ' ' "$TAB")
+ else
+ # <device> on <mountpoint> type <fstype> ...
+ # Use tabs as field separators
+ match_string='\(.*\) on \(.*\) type \([^[:space:]]\+\) .*'
+ replace_string="\\1${TAB}\\2${TAB}\\3"
+ mount_list=$($MOUNT | sed "s/$match_string/$replace_string/g")
+ fi
+ done
+
+ # Convert octal \040 to space characters
+ printf "$mount_list"
+}
+
+determine_blockdevice() {
+ if [ $blockdevice = "yes" ]; then
+ return
+ fi
+
+ # Get the current real device name, if possible.
+ # (specified devname could be -L or -U...)
+ case "$FSTYPE" in
+ nfs4|nfs|efs|smbfs|cifs|glusterfs|ceph|tmpfs|overlay|overlayfs|rozofs|zfs|cvfs|none|lustre)
+ : ;;
+ *)
+ match_string="${TAB}${CANONICALIZED_MOUNTPOINT}${TAB}"
+ DEVICE=$(list_mounts | grep "$match_string" | cut -d"$TAB" -f1)
+ if [ -b "$DEVICE" ]; then
+ blockdevice=yes
+ fi
+ ;;
+ esac
+}
+
+# Lists all filesystems potentially mounted under a given path,
+# excluding the path itself.
+list_submounts() {
+ list_mounts | grep "${TAB}${1}/" | cut -d"$TAB" -f2 | sort -r
+}
+
+# Lists all bind mounts of a given file system,
+# excluding the path itself.
+list_bindmounts() {
+ if is_bind_mount; then
+ # skip bind mount
+ # we should not umount the original file system via a bind mount
+ return
+ fi
+
+ match_string="${TAB}${1}${TAB}"
+ if list_mounts | grep "$match_string" >/dev/null 2>&1; then
+ mount_disk=$(list_mounts | grep "$match_string" | cut -d"$TAB" -f1)
+ else
+ return
+ fi
+
+ if [ -b "$mount_disk" ]; then
+ list_mounts | grep "$mount_disk" | grep -v "$match_string" | cut -d"$TAB" -f2 | sort -r
+ fi
+}
+
+# kernels < 2.6.26 can't handle bind remounts
+bind_kernel_check() {
+ echo "$options" | grep -w ro >/dev/null 2>&1 ||
+ return
+ uname -r | awk -F. '
+ $1==2 && $2==6 {
+ sub("[^0-9].*","",$3);
+ if ($3<26)
+ exit(1);
+ }'
+ [ $? -ne 0 ] &&
+ ocf_log warn "kernel $(uname -r) cannot handle read only bind mounts"
+}
+
+bind_root_mount_check() {
+ if [ "$(df -P "$1" | awk 'END{print $6}')" = "/" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+bind_mount() {
+ if is_bind_mount && [ "$options" != "-o bind" ]
+ then
+ bind_kernel_check
+ bind_opts=$(echo "$options" | sed 's/bind/remount/')
+ $MOUNT $bind_opts "$MOUNTPOINT"
+ else
+ true # make sure to return OK
+ fi
+}
+
+is_option() {
+ echo "$OCF_RESKEY_options" | grep -w "$1" >/dev/null 2>&1
+}
+
+is_fsck_needed() {
+ case $OCF_RESKEY_run_fsck in
+ force) true;;
+ no) false;;
+ ""|auto)
+ case "$FSTYPE" in
+ ext4|ext4dev|ext3|reiserfs|reiser4|nss|xfs|jfs|vfat|fat|nfs4|nfs|efs|cifs|smbfs|ocfs2|gfs2|none|lustre|glusterfs|ceph|tmpfs|overlay|overlayfs|rozofs|zfs|cvfs)
+ false;;
+ *)
+ true;;
+ esac;;
+ *)
+ ocf_log warn "Invalid parameter value for fsck: '$OCF_RESKEY_run_fsck'; setting to 'auto'"
+ OCF_RESKEY_run_fsck="auto"
+ is_fsck_needed;;
+ esac
+}
+
+fstype_supported()
+{
+ local support="$FSTYPE"
+ local rc
+
+ if [ "X${HOSTOS}" = "XOpenBSD" ];then
+ # skip checking /proc/filesystems for obsd
+ return $OCF_SUCCESS
+ fi
+
+ if [ -z "$FSTYPE" -o "$FSTYPE" = none ]; then
+ : No FSTYPE specified, rely on the system has the right file-system support already
+ return $OCF_SUCCESS
+ fi
+
+ # support fuse-filesystems (e.g. GlusterFS) and Amazon Elastic File
+ # System (EFS)
+ case "$FSTYPE" in
+ fuse.*|glusterfs|rozofs) support="fuse";;
+ efs) check_binary "mount.efs"; support="nfs4";;
+ esac
+
+ if [ "$support" != "$FSTYPE" ]; then
+ ocf_log info "Checking support for $FSTYPE as \"$support\""
+ fi
+
+ grep -w "$support"'$' /proc/filesystems >/dev/null
+ if [ $? -eq 0 ]; then
+ # found the fs type
+ return $OCF_SUCCESS
+ fi
+
+ # if here, we should attempt to load the module and then
+ # check the if the filesystem support exists again.
+ $MODPROBE $support >/dev/null
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Couldn't find filesystem $support in /proc/filesystems and failed to load kernel module"
+ return $OCF_ERR_INSTALLED
+ fi
+
+ # It is possible for the module to load and not be complete initialized
+ # before we check /proc/filesystems again. Give this a few trys before
+ # giving up entirely.
+ for try in $(seq 5); do
+ grep -w "$support"'$' /proc/filesystems >/dev/null
+ if [ $? -eq 0 ] ; then
+ # yes. found the filesystem after doing the modprobe
+ return $OCF_SUCCESS
+ fi
+ ocf_log debug "Unable to find support for $support in /proc/filesystems after modprobe, trying again"
+ sleep 1
+ done
+
+ ocf_exit_reason "Couldn't find filesystem $support in /proc/filesystems"
+ return $OCF_ERR_INSTALLED
+}
+
+
+#
+# In the case a fresh filesystem is just created from another
+# node on the shared storage, and is not visible yet. Then try
+# partprobe to refresh /dev/disk/by-{label,uuid}/* up to date.
+#
+# DEVICE can be /dev/xxx, -U, -L
+#
+trigger_udev_rules_if_needed()
+{
+ local refresh_flag="no"
+ local tmp
+ local timeout
+
+ if [ $blockdevice = "yes" ]; then
+ tmp="$DEVICE"
+ if [ "$DEVICE" != "/dev/null" -a ! -b "$DEVICE" ] ; then
+ refresh_flag="yes"
+ fi
+ else
+ tmp="$(echo $DEVICE|awk '{$1=""; print substr($0,2)}')"
+ case "$DEVICE" in
+ -U*|--uuid*)
+ tmp="/dev/disk/by-uuid/$tmp"
+ ;;
+ -L*|--label*)
+ tmp="/dev/disk/by-label/$tmp"
+ ;;
+ *)
+ # bind mount?
+ return ;;
+ esac
+ [ ! -b "$tmp" ] && refresh_flag="yes"
+ fi
+
+ [ "$refresh_flag" = "no" ] && return
+
+ have_binary partprobe && partprobe >/dev/null 2>&1
+ timeout=${OCF_RESKEY_CRM_meta_timeout:="60000"}
+ timeout=$((timeout/1000))
+ have_binary udevadm && udevadm settle -t $timeout --exit-if-exists=$tmp
+
+ return $?
+}
+
+#
+# START: Start up the filesystem
+#
+Filesystem_start()
+{
+ # Check if there are any mounts mounted under the mountpoint
+ match_string="${TAB}${CANONICALIZED_MOUNTPOINT}"
+ if list_mounts | grep -E "$match_string/\w+" >/dev/null 2>&1; then
+ ocf_log err "There is one or more mounts mounted under $MOUNTPOINT."
+ return $OCF_ERR_CONFIGURED
+ fi
+
+ # See if the device is already mounted.
+ if Filesystem_status >/dev/null 2>&1 ; then
+ ocf_log info "Filesystem $MOUNTPOINT is already mounted."
+ return $OCF_SUCCESS
+ fi
+
+ fstype_supported || exit $OCF_ERR_INSTALLED
+
+ # Check the filesystem & auto repair.
+ # NOTE: Some filesystem types don't need this step... Please modify
+ # accordingly
+
+ trigger_udev_rules_if_needed
+
+ if [ $blockdevice = "yes" ]; then
+ if [ "$DEVICE" != "/dev/null" -a ! -b "$DEVICE" ] ; then
+ ocf_exit_reason "Couldn't find device [$DEVICE]. Expected /dev/??? to exist"
+ exit $OCF_ERR_INSTALLED
+ fi
+
+ if is_fsck_needed; then
+ ocf_log info "Starting filesystem check on $DEVICE"
+ if [ -z "$FSTYPE" ]; then
+ $FSCK -p "$DEVICE"
+ else
+ $FSCK -t "$FSTYPE" -p "$DEVICE"
+ fi
+
+ # NOTE: if any errors at all are detected, it returns non-zero
+ # if the error is >= 4 then there is a big problem
+ if [ $? -ge 4 ]; then
+ ocf_exit_reason "Couldn't successfully fsck filesystem for $DEVICE"
+ return $OCF_ERR_GENERIC
+ fi
+ fi
+ fi
+
+ [ -d "$MOUNTPOINT" ] ||
+ ocf_run mkdir -p "$MOUNTPOINT"
+ if [ ! -d "$MOUNTPOINT" ] ; then
+ ocf_exit_reason "Couldn't find directory [$MOUNTPOINT] to use as a mount point"
+ exit $OCF_ERR_INSTALLED
+ fi
+
+ flushbufs "$DEVICE"
+ # Mount the filesystem.
+ case "$FSTYPE" in
+ none) $MOUNT $options $device_opt "$DEVICE" "$MOUNTPOINT" &&
+ bind_mount
+ ;;
+ "") $MOUNT $options $device_opt "$DEVICE" "$MOUNTPOINT" ;;
+ *) $MOUNT -t "$FSTYPE" $options $device_opt "$DEVICE" "$MOUNTPOINT" ;;
+ esac
+
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Couldn't mount device [$DEVICE] as $MOUNTPOINT"
+ return $OCF_ERR_GENERIC
+ fi
+ return $OCF_SUCCESS
+}
+# end of Filesystem_start
+
+get_pids()
+{
+ local dir=$1
+ local procs
+ local mmap_procs
+
+ if is_bind_mount && ocf_is_true "$FORCE_UNMOUNT" && ! bind_root_mount_check "$DEVICE"; then
+ ocf_log debug "Change force_umount from '$FORCE_UNMOUNT' to 'safe'"
+ FORCE_UNMOUNT=safe
+ fi
+
+ if ocf_is_true "$FORCE_UNMOUNT"; then
+ if [ "X${HOSTOS}" = "XOpenBSD" ];then
+ fstat | grep $dir | awk '{print $3}'
+ else
+ $FUSER -m $dir 2>/dev/null
+ fi
+ elif [ "$FORCE_UNMOUNT" = "safe" ]; then
+ procs=$(find /proc/[0-9]*/ -type l -lname "${dir}/*" -or -lname "${dir}" 2>/dev/null | awk -F/ '{print $3}')
+ mmap_procs=$(grep " ${dir}/" /proc/[0-9]*/maps | awk -F/ '{print $3}')
+ printf "${procs}\n${mmap_procs}" | sort | uniq
+ fi
+}
+
+signal_processes() {
+ local dir=$1
+ local sig=$2
+ local pids pid
+ # fuser returns a non-zero return code if none of the
+ # specified files is accessed or in case of a fatal
+ # error.
+ pids=$(get_pids "$dir")
+ if [ -z "$pids" ]; then
+ ocf_log info "No processes on $dir were signalled. force_unmount is set to '$FORCE_UNMOUNT'"
+ return
+ fi
+ for pid in $pids; do
+ ocf_log info "sending signal $sig to: $(ps -f $pid | tail -1)"
+ kill -s $sig $pid
+ done
+}
+try_umount() {
+ local SUB="$1"
+ $UMOUNT $umount_force "$SUB"
+ list_mounts | grep "${TAB}${SUB}${TAB}" >/dev/null 2>&1 || {
+ ocf_log info "unmounted $SUB successfully"
+ return $OCF_SUCCESS
+ }
+ return $OCF_ERR_GENERIC
+}
+timeout_child() {
+ local pid="$1" timeout="$2" killer ret
+
+ # start job in the background that will KILL the given process after timeout expires
+ sleep $timeout && kill -s KILL $pid &
+ killer=$!
+
+ # block until the child process either exits on its own or gets killed by the above killer pipeline
+ wait $pid
+ ret=$?
+
+ # ret would be 127 + child exit code if the timeout expired
+ [ $ret -lt 128 ] && kill -s KILL $killer
+ return $ret
+}
+fs_stop_loop() {
+ local SUB="$1" signals="$2" sig
+ while true; do
+ for sig in $signals; do
+ signal_processes "$SUB" $sig
+ done
+ sleep $OCF_RESKEY_signal_delay
+ try_umount "$SUB" && return $OCF_SUCCESS
+ done
+}
+fs_stop() {
+ local SUB="$1" timeout=$2 grace_time ret
+ grace_time=$((timeout/2))
+
+ # try gracefully terminating processes for up to half of the configured timeout
+ fs_stop_loop "$SUB" "$OCF_RESKEY_term_signals" &
+ timeout_child $! $grace_time
+ ret=$?
+ [ $ret -eq $OCF_SUCCESS ] && return $ret
+
+ # try killing them for the rest of the timeout
+ fs_stop_loop "$SUB" "$OCF_RESKEY_kill_signals" &
+ timeout_child $! $grace_time
+ ret=$?
+ [ $ret -eq $OCF_SUCCESS ] && return $ret
+
+ # timeout expired
+ ocf_exit_reason "Couldn't unmount $SUB within given timeout"
+ return $OCF_ERR_GENERIC
+}
+
+#
+# STOP: Unmount the filesystem
+#
+Filesystem_stop()
+{
+ # See if the device is currently mounted
+ Filesystem_status >/dev/null 2>&1
+ if [ $? -eq $OCF_NOT_RUNNING ]; then
+ # Already unmounted, wonderful.
+ rc=$OCF_SUCCESS
+ else
+ # Wipe the status file, but continue with a warning if
+ # removal fails -- the file system might be read only
+ if [ $OCF_CHECK_LEVEL -eq 20 ]; then
+ rm -f "${STATUSFILE}"
+ if [ $? -ne 0 ]; then
+ ocf_log warn "Failed to remove status file ${STATUSFILE}."
+ fi
+ fi
+
+ # Determine the real blockdevice this is mounted on (if
+ # possible) prior to unmounting.
+ determine_blockdevice
+
+ # For networked filesystems, there's merit in trying -f:
+ case "$FSTYPE" in
+ nfs4|nfs|efs|cifs|smbfs) umount_force="-f" ;;
+ esac
+
+ # Umount all sub-filesystems mounted under $MOUNTPOINT/ too.
+ local timeout
+ while read SUB; do
+ ocf_log info "Trying to unmount $SUB"
+ if ocf_is_true "$FAST_STOP"; then
+ timeout=6
+ else
+ timeout=${OCF_RESKEY_CRM_meta_timeout:="20000"}
+ timeout=$((timeout/1000))
+ fi
+ fs_stop "$SUB" $timeout
+ rc=$?
+ if [ $rc -ne $OCF_SUCCESS ]; then
+ ocf_exit_reason "Couldn't unmount $SUB, giving up!"
+ fi
+ done <<-EOF
+ $(list_submounts "$CANONICALIZED_MOUNTPOINT"; \
+ list_bindmounts "$CANONICALIZED_MOUNTPOINT"; \
+ echo $CANONICALIZED_MOUNTPOINT)
+ EOF
+ fi
+
+ flushbufs "$DEVICE"
+
+ return $rc
+}
+# end of Filesystem_stop
+
+#
+# STATUS: is the filesystem mounted or not?
+#
+Filesystem_status()
+{
+ match_string="${TAB}${CANONICALIZED_MOUNTPOINT}${TAB}"
+ if list_mounts | grep "$match_string" >/dev/null 2>&1; then
+ rc=$OCF_SUCCESS
+ msg="$MOUNTPOINT is mounted (running)"
+ else
+ rc=$OCF_NOT_RUNNING
+ msg="$MOUNTPOINT is unmounted (stopped)"
+ fi
+
+ # Special case "monitor" to check whether the UUID cached and
+ # on-disk still match?
+ case "$OP" in
+ status) ocf_log info "$msg";;
+ esac
+
+ return $rc
+}
+# end of Filesystem_status
+
+
+# Note: the read/write tests below will stall in case the
+# underlying block device (or in the case of a NAS mount, the
+# NAS server) has gone away. In that case, if I/O does not
+# return to normal in time, the operation hits its timeout
+# and it is up to the CRM to initiate appropriate recovery
+# actions (such as fencing the node).
+#
+# MONITOR 10: read the device
+#
+Filesystem_monitor_10()
+{
+ if [ "$blockdevice" = "no" ] ; then
+ ocf_log warn "$DEVICE is not a block device, monitor 10 is noop"
+ return $OCF_SUCCESS
+ fi
+ dd_opts="iflag=direct bs=4k count=1"
+ err_output=$(dd if="$DEVICE" $dd_opts 2>&1 >/dev/null)
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Failed to read device $DEVICE"
+ ocf_log err "dd said: $err_output"
+ return $OCF_ERR_GENERIC
+ fi
+ return $OCF_SUCCESS
+}
+#
+# MONITOR 20: write and read a status file
+#
+Filesystem_monitor_20()
+{
+ if [ "$blockdevice" = "no" ] ; then
+ # O_DIRECT not supported on cifs/smbfs
+ dd_opts="oflag=sync bs=4k conv=fsync,sync"
+ else
+ # Writing to the device in O_DIRECT mode is imperative
+ # to bypass caches.
+ dd_opts="oflag=direct,sync bs=4k conv=fsync,sync"
+ fi
+ status_dir=$(dirname "$STATUSFILE")
+ [ -d "$status_dir" ] || mkdir -p "$status_dir"
+ err_output=$(echo "${OCF_RESOURCE_INSTANCE}" | dd of="${STATUSFILE}" $dd_opts 2>&1)
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Failed to write status file ${STATUSFILE}"
+ ocf_log err "dd said: $err_output"
+ return $OCF_ERR_GENERIC
+ fi
+ test -f "${STATUSFILE}"
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Cannot stat the status file ${STATUSFILE}"
+ return $OCF_ERR_GENERIC
+ fi
+ cat "${STATUSFILE}" > /dev/null
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Cannot read the status file ${STATUSFILE}"
+ return $OCF_ERR_GENERIC
+ fi
+ return $OCF_SUCCESS
+}
+Filesystem_monitor()
+{
+ Filesystem_status
+ rc=$?
+ if [ $rc -ne $OCF_SUCCESS ]; then
+ return $rc
+ fi
+
+ if [ $rc -eq $OCF_SUCCESS -a $OCF_CHECK_LEVEL -gt 0 ]; then
+ case "$OCF_CHECK_LEVEL" in
+ 10) Filesystem_monitor_10; rc=$?;;
+ 20) Filesystem_monitor_20; rc=$?;;
+ *)
+ ocf_exit_reason "unsupported monitor level $OCF_CHECK_LEVEL"
+ rc=$OCF_ERR_CONFIGURED
+ ;;
+ esac
+ fi
+ return $rc
+}
+# end of Filesystem_monitor
+
+
+#
+# VALIDATE_ALL: Are the instance parameters valid?
+# FIXME!! The only part that's useful is the return code.
+# This code always returns $OCF_SUCCESS (!)
+# FIXME!! Needs some tuning to match fstype_supported() (e.g., for
+# fuse). Can we just call fstype_supported() with a flag like
+# "no_modprobe" instead?
+#
+Filesystem_validate_all()
+{
+ # Check if the $FSTYPE is workable
+ # NOTE: Without inserting the $FSTYPE module, this step may be imprecise
+ # TODO: This is Linux specific crap.
+ if [ ! -z "$FSTYPE" -a "$FSTYPE" != none ]; then
+ cut -f2 /proc/filesystems | grep "^${FSTYPE}$" >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ modpath=/lib/modules/$(uname -r)
+ moddep=$modpath/modules.dep
+ # Do we have $FSTYPE in modules.dep?
+ cut -d' ' -f1 $moddep \
+ | grep "^${modpath}.*${FSTYPE}\.k\?o:$" >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ ocf_log info "It seems we do not have $FSTYPE support"
+ fi
+ fi
+ fi
+
+ # If we are supposed to do monitoring with status files, then
+ # we need a utility to write in O_DIRECT mode.
+ if [ $OCF_CHECK_LEVEL -gt 0 ]; then
+ check_binary dd
+ # Note: really old coreutils version do not support
+ # the "oflag" option for dd. We don't check for that
+ # here. In case dd does not support oflag, monitor is
+ # bound to fail, with dd spewing an error message to
+ # the logs. On such systems, we must do without status
+ # file monitoring.
+ fi
+
+ #TODO: How to check the $options ?
+ return $OCF_SUCCESS
+}
+
+#
+# set the blockdevice variable to "no" or "yes"
+#
+set_blockdevice_var() {
+ blockdevice=no
+
+ # these are definitely not block devices
+ case "$FSTYPE" in
+ nfs4|nfs|efs|smbfs|cifs|none|glusterfs|ceph|tmpfs|overlay|overlayfs|rozofs|zfs|cvfs|lustre) return;;
+ esac
+
+ if $(is_option "loop"); then
+ return
+ fi
+
+ case "$DEVICE" in
+ --uuid=*|--uuid\ *|--label=*|--label\ *)
+ device_opt=$(echo $DEVICE | sed "s/\([[:blank:]]\|=\).*//")
+ DEVICE=$(echo $DEVICE | sed -E "s/$device_opt([[:blank:]]*|=)//")
+ ;;
+ -U*|-L*) # short versions of --uuid/--label
+ device_opt=$(echo $DEVICE | cut -c1-2)
+ DEVICE=$(echo $DEVICE | sed "s/$device_opt[[:blank:]]*//")
+ ;;
+ /dev/null) # Special case for BSC
+ blockdevice=yes
+ ;;
+ *)
+ if [ ! -b "$DEVICE" -a ! -d "$DEVICE" -a "X$OP" != Xstart ] ; then
+ ocf_log warn "Couldn't find device [$DEVICE]. Expected /dev/??? to exist"
+ fi
+ if [ ! -d "$DEVICE" ]; then
+ blockdevice=yes
+ fi
+ ;;
+ esac
+}
+
+# Check the arguments passed to this script
+if [ $# -ne 1 ]; then
+ usage
+ exit $OCF_ERR_ARGS
+fi
+
+# Check the OCF_RESKEY_ environment variables...
+FORCE_UNMOUNT="yes"
+if [ -n "${OCF_RESKEY_force_unmount}" ]; then
+ FORCE_UNMOUNT=$OCF_RESKEY_force_unmount
+fi
+
+DEVICE="$OCF_RESKEY_device"
+FSTYPE=$OCF_RESKEY_fstype
+if [ ! -z "$OCF_RESKEY_options" ]; then
+ options="-o $OCF_RESKEY_options"
+fi
+FAST_STOP=${OCF_RESKEY_fast_stop:="yes"}
+
+OP=$1
+
+# These operations do not require instance parameters
+case $OP in
+ meta-data) meta_data
+ exit $OCF_SUCCESS
+ ;;
+ usage) usage
+ exit $OCF_SUCCESS
+ ;;
+esac
+
+if [ x = x"$DEVICE" ]; then
+ ocf_exit_reason "Please set OCF_RESKEY_device to the device to be managed"
+ exit $OCF_ERR_CONFIGURED
+fi
+
+set_blockdevice_var
+
+# Normalize instance parameters:
+
+# It is possible that OCF_RESKEY_directory has one or even multiple trailing "/".
+# But the output of `mount` and /proc/mounts do not.
+if [ -z "$OCF_RESKEY_directory" ]; then
+ if [ X$OP = "Xstart" -o $blockdevice = "no" ]; then
+ ocf_exit_reason "Please specify the directory"
+ exit $OCF_ERR_CONFIGURED
+ fi
+else
+ MOUNTPOINT="$(echo "$OCF_RESKEY_directory" | sed 's/\/*$//')"
+ : ${MOUNTPOINT:=/}
+ if [ -e "$MOUNTPOINT" ] ; then
+ CANONICALIZED_MOUNTPOINT="$(readlink -f "$MOUNTPOINT")"
+ if [ $? -ne 0 ]; then
+ ocf_exit_reason "Could not canonicalize $MOUNTPOINT because readlink failed"
+ exit $OCF_ERR_GENERIC
+ fi
+ else
+ CANONICALIZED_MOUNTPOINT="$MOUNTPOINT"
+ fi
+ # At this stage, $MOUNTPOINT does not contain trailing "/" unless it is "/"
+ # TODO: / mounted via Filesystem sounds dangerous. On stop, we'll
+ # kill the whole system. Is that a good idea?
+fi
+
+# Check to make sure the utilites are found
+if [ "X${HOSTOS}" != "XOpenBSD" ];then
+check_binary $MODPROBE
+check_binary $FUSER
+fi
+check_binary $FSCK
+check_binary $MOUNT
+check_binary $UMOUNT
+
+if [ "$OP" != "monitor" ]; then
+ ocf_log info "Running $OP for $DEVICE on $MOUNTPOINT"
+fi
+
+case $OP in
+ status) Filesystem_status
+ exit $?
+ ;;
+ monitor) Filesystem_monitor
+ exit $?
+ ;;
+ validate-all) Filesystem_validate_all
+ exit $?
+ ;;
+ stop) Filesystem_stop
+ exit $?
+ ;;
+esac
+
+CLUSTERSAFE=0
+is_option "ro" &&
+ CLUSTERSAFE=2
+
+case "$FSTYPE" in
+nfs4|nfs|efs|smbfs|cifs|none|gfs2|glusterfs|ceph|ocfs2|overlay|overlayfs|tmpfs|cvfs|lustre)
+ CLUSTERSAFE=1 # this is kind of safe too
+ systemd_drop_in "99-Filesystem-remote" "After" "remote-fs.target"
+ ;;
+# add here CLUSTERSAFE=0 for all filesystems which are not
+# cluster aware and which, even if when mounted read-only,
+# could still modify parts of it such as journal/metadata
+ext4|ext4dev|ext3|reiserfs|reiser4|xfs|jfs)
+ if ocf_is_true "$OCF_RESKEY_force_clones"; then
+ CLUSTERSAFE=2
+ systemd_drop_in "99-Filesystem-remote" "After" "remote-fs.target"
+ else
+ CLUSTERSAFE=0 # these are not allowed
+ fi
+ ;;
+esac
+
+if ocf_is_clone; then
+ case $CLUSTERSAFE in
+ 0)
+ ocf_exit_reason "DANGER! $FSTYPE on $DEVICE is NOT cluster-aware!"
+ ocf_log err "DO NOT RUN IT AS A CLONE!"
+ ocf_log err "Politely refusing to proceed to avoid data corruption."
+ exit $OCF_ERR_CONFIGURED
+ ;;
+ 2)
+ ocf_log warn "$FSTYPE on $DEVICE is NOT cluster-aware!"
+ if ocf_is_true "$OCF_RESKEY_force_clones"; then
+ ocf_log warn "But we'll let it run because we trust _YOU_ verified it's safe to do so."
+ else
+ ocf_log warn "But we'll let it run because it is mounted read-only."
+ ocf_log warn "Please make sure that it's meta data is read-only too!"
+ fi
+ ;;
+ esac
+fi
+
+case $OP in
+ start) Filesystem_start
+ ;;
+ *) usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+ esac
+exit $?
+
+