This file is indexed.

/usr/lib/drbd/rhcs_fence is in drbd-utils 8.9.10-2.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/bin/bash

Author="Lars Ellenberg <lars.ellenberg@linbit.com>"
Date="2014-09-18"
Version="1.0"
License="GPL v2+"
#
# Inspired by the rhcs_fence perl version
# by Digimer (digimer@alteeve.ca)
# Alteeve's Niche! - https://alteeve.ca/w/
# As found at https://github.com/digimer/rhcs_fence/releases/tag/0.2.8
#
# This program ties Linbit's DRBD into Red Hat's RHCS's fence daemon via the
# 'fence_node' shell call.

# from environment
# if tools do not yet export the plural,
# use the (deprecated) singular.
: ${DRBD_PEERS:=$DRBD_PEER}

PROG=${0##*/}
: ${DEBUG:=1}
LOG_PRIO=daemon.warning
LOG_TAG="$PROG[$$] $DRBD_PEERS: $DRBD_RESOURCE(minor $DRBD_MINOR)"

##############################################################################
# helper functions
##############################################################################

die() { echo "$*"; exit 1; }

all_minors_up_to_date()
{
	set -- $DRBD_MINOR
	local n_minors=$#

	[[ $n_minors != 0 ]] ||
		die "Resource minor numbers unknown! Unable to proceed."

	# build a "grep extended regex"
	local _OLDIFS=$IFS
	IFS="|"
	local minor_regex="^ *($*): cs:"
	IFS=$_OLDIFS

	# grep -c -Ee '^ *(m|i|n|o|r|s): cs:.* ds:UpToDate' /proc/drbd
	local proc_drbd=$(</proc/drbd)
	local minors_of_resource=$(echo "$proc_drbd" | grep -E -e "$minor_regex")
	local n_up_to_date=$(echo "$minors_of_resource" | grep -c -e "ds:UpToDate")

	debug "n_minors: $n_minors; n_up_to_date: $n_up_to_date"
	[[ $n_up_to_date = $n_minors ]] # return code is propagated
}

wait_for_fence_domain_state_change()
{
	local retries=$1 i
	for (( i=0; $i < $retries; i++ )); do
		sleep 1

		# canonicalize white space by word splitting
		# append one space (last line is "members")
		# for easier pattern matching on member id)
		set -- $(fence_tool ls)
		fence_tool_ls="$* "
		debug "$i: fence_tool ls: $fence_tool_ls"

		wait_condition && return 0
	done
	echo "still not met wait condition after $i retries"
	return 1	# timed out
}

wait_for_id_to_become_victim()
{
	wait_condition() [[ $fence_tool_ls = *"victim now $id "* ]]
	echo "waiting for $id to become victim"
	wait_for_fence_domain_state_change 30 # return value propagates
}

wait_for_id_to_drop_out_of_membership()
{
	wait_condition() [[ $fence_tool_ls != *"members"*" $id "* ]]
	echo "waiting for $id to drop out of membership"
	wait_for_fence_domain_state_change 240 || return 1
	echo "successfully fenced $name (by fenced)"
	return 0
}

eject_target()
{
	# tell cman to eject this node
	debug "call: cman_tool kill -n $name"
	cman_tool kill -n $name

	# if it was member in the fence domain,
	# wait for it to become victim,
	# wait for it to drop out of the membership.
	if [[ $fence_tool_ls = *"members"*" $id "* ]]; then
		wait_for_id_to_become_victim &&
		wait_for_id_to_drop_out_of_membership &&
		return 0 # Yes!
	fi
	return 1
}

##############################################################################
# logging preparations
##############################################################################

# Funky redirection to avoid logger feeding its own output to itself accidentally.
# Funky double exec to avoid an intermediate sub-shell.
# Sometimes, the sub-shell lingers around, keeps file descriptors open,
# and logger then won't notice the main script has finished,
# forever waiting for further input.
# The second exec replaces the subshell, and logger will notice directly
# when its stdin is closed once the main script exits.
# This avoids the spurious logger processes.
if test -t 2 ; then
	[[ $DEBUG != 0 ]] &&
	exec 3> >( exec 1>&- logger -s -p ${LOG_PRIO%.*}.debug -t "$LOG_TAG: DEBUG" )
	exec 1> >( exec 1>&- logger -s -p $LOG_PRIO            -t "$LOG_TAG" )
else
	[[ $DEBUG != 0 ]] &&
	exec 3> >( exec 1>&- 2>&- logger -p ${LOG_PRIO%.*}.debug -t "$LOG_TAG: DEBUG" )
	exec 1> >( exec 1>&- 2>&- logger -p $LOG_PRIO            -t "$LOG_TAG" )
fi
# and now point stderr to logger, too
exec 2>&1
if [[ $DEBUG = 0 ]]; then
	debug() { :; }
else
	debug() { echo >&3 "$*" ; }
	if [[ $DEBUG -gt 1 ]]; then
		BASH_XTRACEFD=3
		set -x
	fi
fi

##############################################################################
# "main"
##############################################################################

[[ $DRBD_PEERS ]] || die "No target list specified. You need to pass DRBD_PEERS via environment."

all_minors_up_to_date || die "some minor device is NOT 'UpToDate', will not fence peer"

for peer in $DRBD_PEERS; do
	for name in $peer ${peer%%.*}; do
		set -- $(cman_tool -F id,type nodes -n $name)
		id=$1 state=$2
		[[ $id ]] && break
	done
	if [[ -z $id ]] || [[ $id = *[!0-9]* ]] ; then
		die "could not resolve cman node id of $peer, giving up"
	fi
	echo "resolved $peer as cman node $name, id $id, state $state"

	# record fence domain state now
	set -- $(fence_tool ls)
	fence_tool_ls="$* "
	debug "fence_tool ls: $fence_tool_ls"

	if [[ $state = M ]] ; then
		eject_target && continue # with next peer, if any
	else
		# maybe cman noticed before the handler triggered,
		# and fencing is already active anyways.
		if [[ $fence_tool_ls = *"victim now $id "* ]]; then
			wait_for_id_to_drop_out_of_membership && continue # with next peer, if any
		fi
	fi

	# apparently it was not in the member list.
	# or we timed out waiting for fenced
	debug "trying direct fence of $name"
	dash_v=-v
	[[ $DEBUG -gt 1 ]] && dash_v=-vv
	echo "fence_node $dash_v $name"
	if fence_node $dash_v $name ; then
		echo "successfully fenced $name"
		continue # with next peer, if any
	else
		die "fencing $name failed, giving up"
	fi
done

# if we fenced more than one peer,
# add an other log line
[[ $peer != $DRBD_PEERS ]] &&
echo "SUCCESSFULLY FENCED $DRBD_PEERS"
exit 7