1# 2# 3# Common helper functions for the OCF Resource Agents supplied by 4# heartbeat. 5# 6# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée 7# All Rights Reserved. 8# 9# 10# This library is free software; you can redistribute it and/or 11# modify it under the terms of the GNU Lesser General Public 12# License as published by the Free Software Foundation; either 13# version 2.1 of the License, or (at your option) any later version. 14# 15# This library is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18# Lesser General Public License for more details. 19# 20# You should have received a copy of the GNU Lesser General Public 21# License along with this library; if not, write to the Free Software 22# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23# 24 25# Build version: 55a4e2c91cb846b2f35490645a1ebe832b7bdde9 26 27# TODO: Some of this should probably split out into a generic OCF 28# library for shell scripts, but for the time being, we'll just use it 29# ourselves... 30# 31 32# TODO wish-list: 33# - Generic function for evaluating version numbers 34# - Generic function(s) to extract stuff from our own meta-data 35# - Logging function which automatically adds resource identifier etc 36# prefixes 37# TODO: Move more common functionality for OCF RAs here. 38# 39 40# This was common throughout all legacy Heartbeat agents 41unset LC_ALL; export LC_ALL 42unset LANGUAGE; export LANGUAGE 43 44: ${HA_SBIN_DIR:=@sbindir@} 45 46__SCRIPT_NAME=`basename $0` 47 48if [ -z "$OCF_ROOT" ]; then 49 : ${OCF_ROOT=@OCF_ROOT_DIR@} 50fi 51 52if [ "$OCF_FUNCTIONS_DIR" = ${OCF_ROOT}/resource.d/heartbeat ]; then # old 53 unset OCF_FUNCTIONS_DIR 54fi 55 56: ${OCF_FUNCTIONS_DIR:=${OCF_ROOT}/lib/heartbeat} 57 58. ${OCF_FUNCTIONS_DIR}/ocf-binaries 59. ${OCF_FUNCTIONS_DIR}/ocf-returncodes 60. ${OCF_FUNCTIONS_DIR}/ocf-directories 61. ${OCF_FUNCTIONS_DIR}/ocf-rarun 62. ${OCF_FUNCTIONS_DIR}/ocf-distro 63 64# Define OCF_RESKEY_CRM_meta_interval in case it isn't already set, 65# to make sure that ocf_is_probe() always works 66: ${OCF_RESKEY_CRM_meta_interval=0} 67 68ocf_is_root() { 69 if [ X`id -u` = X0 ]; then 70 true 71 else 72 false 73 fi 74} 75 76ocf_maybe_random() { 77 if test -c /dev/urandom; then 78 od -An -N4 -tu4 /dev/urandom | tr -d '[:space:]' 79 else 80 awk -v pid=$$ 'BEGIN{srand(pid); print rand()}' | sed 's/^.*[.]//' 81 fi 82} 83 84# Portability comments: 85# o The following rely on Bourne "sh" pattern-matching, which is usually 86# that for filename generation (note: not regexp). 87# o The "*) true ;;" clause is probably unnecessary, but is included 88# here for completeness. 89# o The negation in the pattern uses "!". This seems to be common 90# across many OSes (whereas the alternative "^" fails on some). 91# o If an OS is encountered where this negation fails, then a possible 92# alternative would be to replace the function contents by (e.g.): 93# [ -z "`echo $1 | tr -d '[0-9]'`" ] 94# 95ocf_is_decimal() { 96 case "$1" in 97 ""|*[!0-9]*) # empty, or at least one non-decimal 98 false ;; 99 *) 100 true ;; 101 esac 102} 103 104ocf_is_true() { 105 case "$1" in 106 yes|true|1|YES|TRUE|True|ja|on|ON) true ;; 107 *) false ;; 108 esac 109} 110 111ocf_is_hex() { 112 case "$1" in 113 ""|*[!0-9a-fA-F]*) # empty, or at least one non-hex 114 false ;; 115 *) 116 true ;; 117 esac 118} 119 120ocf_is_octal() { 121 case "$1" in 122 ""|*[!0-7]*) # empty, or at least one non-octal 123 false ;; 124 *) 125 true ;; 126 esac 127} 128 129__ocf_set_defaults() { 130 __OCF_ACTION="$1" 131 132 # Return to sanity for the agents... 133 unset LANG 134 LC_ALL=C 135 export LC_ALL 136 137 # TODO: Review whether we really should source this. Or rewrite 138 # to match some emerging helper function syntax...? This imports 139 # things which no OCF RA should be using... 140 141 # Strip the OCF_RESKEY_ prefix from this particular parameter 142 if [ -z "$OCF_RESKEY_OCF_CHECK_LEVEL" ]; then 143 : ${OCF_CHECK_LEVEL:=0} 144 else 145 : ${OCF_CHECK_LEVEL:=$OCF_RESKEY_OCF_CHECK_LEVEL} 146 fi 147 148 if [ ! -d "$OCF_ROOT" ]; then 149 ha_log "ERROR: OCF_ROOT points to non-directory $OCF_ROOT." 150 exit $OCF_ERR_GENERIC 151 fi 152 153 if [ -z "$OCF_RESOURCE_TYPE" ]; then 154 : ${OCF_RESOURCE_TYPE:=$__SCRIPT_NAME} 155 fi 156 157 if [ "x$__OCF_ACTION" = "xmeta-data" ]; then 158 : ${OCF_RESOURCE_INSTANCE:="RESOURCE_ID"} 159 fi 160 161 if [ -z "$OCF_RA_VERSION_MAJOR" ]; then 162 : We are being invoked as an init script. 163 : Fill in some things with reasonable values. 164 : ${OCF_RESOURCE_INSTANCE:="default"} 165 return 0 166 fi 167 168 if [ -z "$OCF_RESOURCE_INSTANCE" ]; then 169 ha_log "ERROR: Need to tell us our resource instance name." 170 exit $OCF_ERR_ARGS 171 fi 172} 173 174hadate() { 175 date "+${HA_DATEFMT}" 176} 177 178set_logtag() { 179 if [ -z "$HA_LOGTAG" ]; then 180 if [ -n "$OCF_RESOURCE_INSTANCE" ]; then 181 HA_LOGTAG="$__SCRIPT_NAME($OCF_RESOURCE_INSTANCE)[$$]" 182 else 183 HA_LOGTAG="$__SCRIPT_NAME[$$]" 184 fi 185 fi 186} 187 188__ha_log() { 189 local ignore_stderr=false 190 local loglevel 191 192 [ "x$1" = "x--ignore-stderr" ] && ignore_stderr=true && shift 193 194 [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" 195 # if we're connected to a tty, then output to stderr 196 if tty >/dev/null; then 197 if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then 198 return 0 199 elif [ "$ignore_stderr" = "true" ]; then 200 # something already printed this error to stderr, so ignore 201 return 0 202 fi 203 if [ "$HA_LOGTAG" ]; then 204 echo "$HA_LOGTAG: $*" 205 else 206 echo "$*" 207 fi >&2 208 return 0 209 fi 210 211 set_logtag 212 213 if [ "x${HA_LOGD}" = "xyes" ] ; then 214 ha_logger -t "${HA_LOGTAG}" "$@" 215 if [ "$?" -eq "0" ] ; then 216 return 0 217 fi 218 fi 219 220 if 221 [ -n "$HA_LOGFACILITY" ] 222 then 223 : logging through syslog 224 # loglevel is unknown, use 'notice' for now 225 loglevel=notice 226 case "${*}" in 227 *ERROR*) loglevel=err;; 228 *WARN*) loglevel=warning;; 229 *INFO*|info) loglevel=info;; 230 esac 231 logger -t "$HA_LOGTAG" -p ${HA_LOGFACILITY}.${loglevel} "${*}" 232 fi 233 if 234 [ -n "$HA_LOGFILE" ] 235 then 236 : appending to $HA_LOGFILE 237 echo `hadate`" $HA_LOGTAG: ${*}" >> $HA_LOGFILE 238 fi 239 if 240 [ -z "$HA_LOGFACILITY" -a -z "$HA_LOGFILE" ] && ! [ "$ignore_stderr" = "true" ] 241 then 242 : appending to stderr 243 echo `hadate`"${*}" >&2 244 fi 245 if 246 [ -n "$HA_DEBUGLOG" ] 247 then 248 : appending to $HA_DEBUGLOG 249 if [ "$HA_LOGFILE"x != "$HA_DEBUGLOG"x ]; then 250 echo "$HA_LOGTAG: "`hadate`"${*}" >> $HA_DEBUGLOG 251 fi 252 fi 253} 254 255ha_log() 256{ 257 __ha_log "$@" 258} 259 260ha_debug() { 261 262 if [ "x${HA_debug}" = "x0" ] || [ -z "${HA_debug}" ] ; then 263 return 0 264 fi 265 if tty >/dev/null; then 266 if [ "$HA_LOGTAG" ]; then 267 echo "$HA_LOGTAG: $*" 268 else 269 echo "$*" 270 fi >&2 271 return 0 272 fi 273 274 set_logtag 275 276 if [ "x${HA_LOGD}" = "xyes" ] ; then 277 ha_logger -t "${HA_LOGTAG}" -D "ha-debug" "$@" 278 if [ "$?" -eq "0" ] ; then 279 return 0 280 fi 281 fi 282 283 [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" 284 285 if 286 [ -n "$HA_LOGFACILITY" ] 287 then 288 : logging through syslog 289 logger -t "$HA_LOGTAG" -p "${HA_LOGFACILITY}.debug" "${*}" 290 fi 291 if 292 [ -n "$HA_DEBUGLOG" ] 293 then 294 : appending to $HA_DEBUGLOG 295 echo "$HA_LOGTAG: "`hadate`"${*}" >> $HA_DEBUGLOG 296 fi 297 if 298 [ -z "$HA_LOGFACILITY" -a -z "$HA_DEBUGLOG" ] 299 then 300 : appending to stderr 301 echo "$HA_LOGTAG: `hadate`${*}: ${HA_LOGFACILITY}" >&2 302 fi 303} 304 305ha_parameter() { 306 local VALUE 307 VALUE=`sed -e 's%[ ][ ]*% %' -e 's%^ %%' -e 's%#.*%%' $HA_CF | grep -i "^$1 " | sed 's%[^ ]* %%'` 308 if 309 [ "X$VALUE" = X ] 310 then 311 312 case $1 in 313 keepalive) VALUE=2;; 314 deadtime) 315 ka=`ha_parameter keepalive` 316 VALUE=`expr $ka '*' 2 '+' 1`;; 317 esac 318 fi 319 echo $VALUE 320} 321 322ocf_log() { 323 # TODO: Revisit and implement internally. 324 if 325 [ $# -lt 2 ] 326 then 327 ocf_log err "Not enough arguments [$#] to ocf_log." 328 fi 329 __OCF_PRIO="$1" 330 shift 331 __OCF_MSG="$*" 332 333 case "${__OCF_PRIO}" in 334 crit) __OCF_PRIO="CRIT";; 335 err) __OCF_PRIO="ERROR";; 336 warn) __OCF_PRIO="WARNING";; 337 info) __OCF_PRIO="INFO";; 338 debug)__OCF_PRIO="DEBUG";; 339 *) __OCF_PRIO=`echo ${__OCF_PRIO}| tr '[a-z]' '[A-Z]'`;; 340 esac 341 342 if [ "${__OCF_PRIO}" = "DEBUG" ]; then 343 ha_debug "${__OCF_PRIO}: $__OCF_MSG" 344 else 345 ha_log "${__OCF_PRIO}: $__OCF_MSG" 346 fi 347} 348 349# 350# ocf_exit_reason: print exit error string to stderr 351# Usage: Allows the OCF script to provide a string 352# describing why the exit code was returned. 353# Arguments: reason - required, The string that represents why the error 354# occured. 355# 356ocf_exit_reason() 357{ 358 local cookie="$OCF_EXIT_REASON_PREFIX" 359 local fmt 360 local msg 361 362 # No argument is likely not intentional. 363 # Just one argument implies a printf format string of just "%s". 364 # "Least surprise" in case some interpolated string from variable 365 # expansion or other contains a percent sign. 366 # More than one argument: first argument is going to be the format string. 367 case $# in 368 0) ocf_log err "Not enough arguments to ocf_log_exit_msg." ;; 369 1) fmt="%s" ;; 370 371 *) fmt=$1 372 shift 373 case $fmt in 374 *%*) : ;; # ok, does look like a format string 375 *) ocf_log warn "Does not look like format string: [$fmt]" ;; 376 esac ;; 377 esac 378 379 if [ -z "$cookie" ]; then 380 # use a default prefix 381 cookie="ocf-exit-reason:" 382 fi 383 384 msg=$(printf "${fmt}" "$@") 385 printf >&2 "%s%s\n" "$cookie" "$msg" 386 __ha_log --ignore-stderr "ERROR: $msg" 387} 388 389# 390# ocf_deprecated: Log a deprecation warning 391# Usage: ocf_deprecated [param-name] 392# Arguments: param-name optional, name of a boolean resource 393# parameter that can be used to suppress 394# the warning (default 395# "ignore_deprecation") 396ocf_deprecated() { 397 local param 398 param=${1:-ignore_deprecation} 399 # don't use ${!param} here, it's a bashism 400 if ! ocf_is_true $(eval echo \$OCF_RESKEY_$param); then 401 ocf_log warn "This resource agent is deprecated" \ 402 "and may be removed in a future release." \ 403 "See the man page for details." \ 404 "To suppress this warning, set the \"${param}\"" \ 405 "resource parameter to true." 406 fi 407} 408 409# 410# Ocf_run: Run a script, and log its output. 411# Usage: ocf_run [-q] [-info|-warn|-err] <command> 412# -q: don't log the output of the command if it succeeds 413# -info|-warn|-err: log the output of the command at given 414# severity if it fails (defaults to err) 415# 416ocf_run() { 417 local rc 418 local output 419 local verbose=1 420 local loglevel=err 421 local var 422 423 for var in 1 2 424 do 425 case "$1" in 426 "-q") 427 verbose="" 428 shift 1;; 429 "-info"|"-warn"|"-err") 430 loglevel=`echo $1 | sed -e s/-//g` 431 shift 1;; 432 *) 433 ;; 434 esac 435 done 436 437 output=`"$@" 2>&1` 438 rc=$? 439 [ -n "$output" ] && output="$(echo "$output" | tr -s ' \t\r\n' ' ')" 440 if [ $rc -eq 0 ]; then 441 if [ "$verbose" -a ! -z "$output" ]; then 442 ocf_log info "$output" 443 fi 444 else 445 if [ ! -z "$output" ]; then 446 ocf_log $loglevel "$output" 447 else 448 ocf_log $loglevel "command failed: $*" 449 fi 450 fi 451 452 return $rc 453} 454 455ocf_pidfile_status() { 456 local pid pidfile=$1 457 if [ ! -e $pidfile ]; then 458 # Not exists 459 return 2 460 fi 461 pid=`cat $pidfile` 462 kill -0 $pid > /dev/null 2>&1 463 if [ $? = 0 ]; then 464 return 0 465 fi 466 467 # Stale 468 return 1 469} 470 471# mkdir(1) based locking 472# first the directory is created with the name given as $1 473# then a file named "pid" is created within that directory with 474# the process PID 475# stale locks are handled carefully, the inode of a directory 476# needs to match before and after test if the process is running 477# empty directories are also handled appropriately 478# we relax (sleep) occasionally to allow for other processes to 479# finish managing the lock in case they are in the middle of the 480# business 481 482relax() { sleep 0.5; } 483ocf_get_stale_pid() { 484 local piddir pid dir_inode 485 486 piddir="$1" 487 [ -z "$piddir" ] && return 2 488 dir_inode="`ls -di $piddir 2>/dev/null`" 489 [ -z "$dir_inode" ] && return 1 490 pid=`cat $piddir/pid 2>/dev/null` 491 if [ -z "$pid" ]; then 492 # empty directory? 493 relax 494 if [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then 495 echo $dir_inode 496 else 497 return 1 498 fi 499 elif kill -0 $pid >/dev/null 2>&1; then 500 return 1 501 elif relax && [ -e "$piddir/pid" ] && [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then 502 echo $pid 503 else 504 return 1 505 fi 506} 507 508# There is a race when the following two functions to manage the 509# lock file (mk and rm) are invoked in parallel by different 510# instances. It is up to the caller to reduce probability of that 511# taking place (see ocf_take_lock() below). 512 513ocf_mk_pid() { 514 mkdir $1 2>/dev/null && echo $$ > $1/pid 515} 516ocf_rm_pid() { 517 rm -f $1/pid 518 rmdir $1 2>/dev/null 519} 520 521# Testing and subsequently removing a stale lock (containing the 522# process pid) is inherently difficult to do in such a way as to 523# prevent a race between creating a pid file and removing it and 524# its directory. We reduce the probability of that happening by 525# checking if the stale lock persists over a random period of 526# time. 527 528ocf_take_lock() { 529 local lockdir=$1 530 local rnd 531 local stale_pid 532 533 # we don't want it too short, so strip leading zeros 534 rnd=$(ocf_maybe_random | sed 's/^0*//') 535 stale_pid=`ocf_get_stale_pid $lockdir` 536 if [ -n "$stale_pid" ]; then 537 sleep 0.$rnd 538 # remove "stale pid" only if it persists 539 [ "$stale_pid" = "`ocf_get_stale_pid $lockdir`" ] && 540 ocf_rm_pid $lockdir 541 fi 542 while ! ocf_mk_pid $lockdir; do 543 ocf_log info "Sleeping until $lockdir is released..." 544 sleep 0.$rnd 545 done 546} 547 548ocf_release_lock_on_exit() { 549 trap "ocf_rm_pid $1" EXIT 550} 551 552# returns true if the CRM is currently running a probe. A probe is 553# defined as a monitor operation with a monitoring interval of zero. 554ocf_is_probe() { 555 [ "$__OCF_ACTION" = "monitor" -a "$OCF_RESKEY_CRM_meta_interval" = 0 ] 556} 557 558# returns true if the resource is configured as a clone. This is 559# defined as a resource where the clone-max meta attribute is present. 560ocf_is_clone() { 561 [ ! -z "${OCF_RESKEY_CRM_meta_clone_max}" ] 562} 563 564# returns true if the resource is configured as a multistate 565# (master/slave) resource. This is defined as a resource where the 566# master-max meta attribute is present, and set to greater than zero. 567ocf_is_ms() { 568 [ "${OCF_RESKEY_CRM_meta_promotable}" = "true" ] || { [ ! -z "${OCF_RESKEY_CRM_meta_master_max}" ] && [ "${OCF_RESKEY_CRM_meta_master_max}" -gt 0 ]; } 569} 570 571# version check functions 572# allow . and - to delimit version numbers 573# max version number is 999 574# 575ocf_is_ver() { 576 echo $1 | grep '^[0-9][0-9.-]*[0-9A-Za-z.\+-]*$' >/dev/null 2>&1 577} 578 579# usage: ocf_version_cmp VER1 VER2 580# version strings can contain digits, dots, and dashes 581# must start and end with a digit 582# returns: 583# 0: VER1 smaller (older) than VER2 584# 1: versions equal 585# 2: VER1 greater (newer) than VER2 586# 3: bad format 587ocf_version_cmp() { 588 ocf_is_ver "$1" || return 3 589 ocf_is_ver "$2" || return 3 590 local v1=$1 591 local v2=$2 592 593 sort_version="sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n" 594 older=$( (echo "$v1"; echo "$v2") | $sort_version | head -1 ) 595 596 if [ "$v1" = "$v2" ]; then 597 return 1 598 elif [ "$v1" = "$older" ]; then 599 return 0 600 else 601 return 2 # -1 would look funny in shell ;-) 602 fi 603} 604 605ocf_local_nodename() { 606 # use crm_node -n for pacemaker > 1.1.8 607 which pacemakerd > /dev/null 2>&1 608 if [ $? -eq 0 ]; then 609 local version=$(pacemakerd -$ | grep "Pacemaker .*" | awk '{ print $2 }') 610 version=$(echo $version | awk -F- '{ print $1 }') 611 ocf_version_cmp "$version" "1.1.8" 612 if [ $? -eq 2 ]; then 613 which crm_node > /dev/null 2>&1 614 if [ $? -eq 0 ]; then 615 crm_node -n 616 return 617 fi 618 fi 619 fi 620 621 # otherwise use uname -n 622 uname -n 623} 624 625# usage: dirname DIR 626dirname() 627{ 628 local a 629 local b 630 631 [ $# = 1 ] || return 1 632 a="$1" 633 while [ 1 ]; do 634 b="${a%/}" 635 [ "$a" = "$b" ] && break 636 a="$b" 637 done 638 b=${a%/*} 639 [ -z "$b" -o "$a" = "$b" ] && b="." 640 641 echo "$b" 642 return 0 643} 644 645# usage: systemd_is_running 646# returns: 647# 0 PID 1 is systemd 648# 1 otherwise 649systemd_is_running() 650{ 651 [ "$(cat /proc/1/comm 2>/dev/null)" = "systemd" ] 652} 653 654# usage: systemd_drop_in <name> <After|Before> <dependency.service> 655systemd_drop_in() 656{ 657 local conf_file 658 if [ $# -ne 3 ]; then 659 ocf_log err "Incorrect number of arguments [$#] for systemd_drop_in." 660 fi 661 662 systemdrundir="/run/systemd/system/resource-agents-deps.target.d" 663 mkdir -p "$systemdrundir" 664 conf_file="$systemdrundir/$1.conf" 665 cat >"$conf_file" <<EOF 666[Unit] 667$2=$3 668EOF 669 # The information is accessible through systemd API and systemd would 670 # complain about improper permissions. 671 chmod o+r "$conf_file" 672 systemctl daemon-reload 673} 674 675# usage: crm_mon_no_validation args... 676# run crm_mon without any cib schema validation 677# This is useful when an agent runs in a bundle to avoid potential 678# schema validation errors when host and bundle are not perfectly aligned 679# To be used, your shell must support on process substitution (e.g. bash) 680# returns: 681# <crm_mon error codes> 682crm_mon_no_validation() 683{ 684 # The subshell prevents parsing error with incompatible shells 685 "$SHELL" -c "CIB_file=<(${HA_SBIN_DIR}/cibadmin -Q | sed 's/validate-with=\"[^\"]*\"/validate-with=\"none\"/') \ 686 ${HA_SBIN_DIR}/crm_mon \$*" -- $* 687} 688 689# 690# pseudo_resource status tracking function... 691# 692# This allows pseudo resources to give correct status information. As we add 693# resource monitoring, and better resource tracking in general, this will 694# become essential. 695# 696# These scripts work because ${HA_RSCTMP} is cleaned on node reboot. 697# 698# We create "resource-string" tracking files under ${HA_RSCTMP} in a 699# very simple way: 700# 701# Existence of "${HA_RSCTMP}/resource-string" means that we consider 702# the resource named by "resource-string" to be running. 703# 704# Note that "resource-string" needs to be unique. Using the resource type 705# plus the resource instance arguments to make up the resource string 706# is probably sufficient... 707# 708# usage: ha_pseudo_resource resource-string op [tracking_file] 709# where op is {start|stop|monitor|status|restart|reload|print} 710# print is a special op which just prints the tracking file location 711# user can override our choice of the tracking file location by 712# specifying it as the third arg 713# Note that all operations are silent... 714# 715ha_pseudo_resource() 716{ 717 local ha_resource_tracking_file="${3:-${HA_RSCTMP}/$1}" 718 case $2 in 719 start|restart|reload) touch "$ha_resource_tracking_file";; 720 stop) rm -f "$ha_resource_tracking_file";; 721 status|monitor) 722 if 723 [ -f "$ha_resource_tracking_file" ] 724 then 725 return 0 726 else 727 case $2 in 728 status) return 3;; 729 *) return 7;; 730 esac 731 fi;; 732 print) echo "$ha_resource_tracking_file";; 733 *) return 3;; 734 esac 735} 736 737# usage: rmtempdir TMPDIR 738rmtempdir() 739{ 740 [ $# = 1 ] || return 1 741 if [ -e "$1" ]; then 742 rmdir "$1" || return 1 743 fi 744 return 0 745} 746 747# usage: maketempfile [-d] 748maketempfile() 749{ 750 if [ $# = 1 -a "$1" = "-d" ]; then 751 mktemp -d 752 return 0 753 elif [ $# != 0 ]; then 754 return 1 755 fi 756 757 mktemp 758 return 0 759} 760 761# usage: rmtempfile TMPFILE 762rmtempfile () 763{ 764 [ $# = 1 ] || return 1 765 if [ -e "$1" ]; then 766 rm "$1" || return 1 767 fi 768 return 0 769} 770 771# echo the first lower supported check level 772# pass set of levels supported by the agent 773# (in increasing order, 0 is optional) 774ocf_check_level() 775{ 776 local lvl prev 777 lvl=0 778 prev=0 779 if ocf_is_decimal "$OCF_CHECK_LEVEL"; then 780 # the level list should be very short 781 for lvl; do 782 if [ "$lvl" -eq "$OCF_CHECK_LEVEL" ]; then 783 break 784 elif [ "$lvl" -gt "$OCF_CHECK_LEVEL" ]; then 785 lvl=$prev # the previous one 786 break 787 fi 788 prev=$lvl 789 done 790 fi 791 echo $lvl 792} 793 794# usage: ocf_stop_processes SIGNALS WAIT_TIME PIDS 795# 796# we send signals (use quotes for more than one!) in the order 797# given; if one or more processes are still running we try KILL; 798# the wait_time is the _total_ time we'll spend in this function 799# this time may be slightly exceeded if the processes won't leave 800# 801# returns: 802# 0: all processes left 803# 1: some processes still running 804# 805# example: 806# 807# ocf_stop_processes TERM 5 $pids 808# 809ocf_stop_processes() { 810 local signals="$1" 811 local wait_time="$(($2/`echo $signals|wc -w`))" 812 shift 2 813 local pids="$*" 814 local sig i 815 test -z "$pids" && 816 return 0 817 for sig in $signals KILL; do 818 kill -s $sig $pids 2>/dev/null 819 # try to leave early, and yet leave processes time to exit 820 sleep 0.2 821 for i in `seq $wait_time`; do 822 kill -s 0 $pids 2>/dev/null || 823 return 0 824 sleep 1 825 done 826 done 827 return 1 828} 829 830# 831# create a given status directory 832# if the directory path doesn't start with $HA_VARRUN, then 833# we return with error (most of the calls would be with the user 834# supplied configuration, hence we need to do necessary 835# protection) 836# used mostly for PID files 837# 838# usage: ocf_mkstatedir owner permissions path 839# 840# owner: user.group 841# permissions: permissions 842# path: directory path 843# 844# example: 845# ocf_mkstatedir named 755 `dirname $pidfile` 846# 847ocf_mkstatedir() 848{ 849 local owner 850 local perms 851 local path 852 853 owner=$1 854 perms=$2 855 path=$3 856 857 test -d $path && return 0 858 [ $(id -u) = 0 ] || return 1 859 860 case $path in 861 ${HA_VARRUN%/}/*) : this path is ok ;; 862 *) ocf_log err "cannot create $path (does not start with $HA_VARRUN)" 863 return 1 864 ;; 865 esac 866 867 mkdir -p $path && 868 chown $owner $path && 869 chmod $perms $path 870} 871 872# 873# create a unique status directory in $HA_VARRUN 874# used mostly for PID files 875# the directory is by default set to 876# $HA_VARRUN/$OCF_RESOURCE_INSTANCE 877# the directory name is printed to stdout 878# 879# usage: ocf_unique_rundir owner permissions name 880# 881# owner: user.group (default: "root") 882# permissions: permissions (default: "755") 883# name: some unique string (default: "$OCF_RESOURCE_INSTANCE") 884# 885# to use the default either don't set the parameter or set it to 886# empty string ("") 887# example: 888# 889# STATEDIR=`ocf_unique_rundir named "" myownstatedir` 890# 891ocf_unique_rundir() 892{ 893 local path 894 local owner 895 local perms 896 local name 897 898 owner=${1:-"root"} 899 perms=${2:-"755"} 900 name=${3:-"$OCF_RESOURCE_INSTANCE"} 901 path=$HA_VARRUN/$name 902 if [ ! -d $path ]; then 903 [ $(id -u) = 0 ] || return 1 904 mkdir -p $path && 905 chown $owner $path && 906 chmod $perms $path || return 1 907 fi 908 echo $path 909} 910 911# 912# RA tracing may be turned on by setting OCF_TRACE_RA 913# the trace output will be saved to OCF_TRACE_FILE, if set, or 914# by default to 915# $HA_VARLIB/trace_ra/<type>/<id>.<action>.<timestamp> 916# e.g. $HA_VARLIB/trace_ra/oracle/db.start.2012-11-27.08:37:08 917# 918# OCF_TRACE_FILE: 919# - FD (small integer [3-9]) in that case it is up to the callers 920# to capture output; the FD _must_ be open for writing 921# - absolute path 922# 923# NB: FD 9 may be used for tracing with bash >= v4 in case 924# OCF_TRACE_FILE is set to a path. 925# 926ocf_bash_has_xtracefd() { 927 [ -n "$BASH_VERSION" ] && [ ${BASH_VERSINFO[0]} -ge 4 ] 928} 929# for backwards compatibility 930ocf_is_bash4() { 931 ocf_bash_has_xtracefd 932} 933ocf_trace_redirect_to_file() { 934 local dest=$1 935 if ocf_bash_has_xtracefd; then 936 exec 9>$dest 937 BASH_XTRACEFD=9 938 else 939 exec 2>$dest 940 fi 941} 942ocf_trace_redirect_to_fd() { 943 local fd=$1 944 if ocf_bash_has_xtracefd; then 945 BASH_XTRACEFD=$fd 946 else 947 exec 2>&$fd 948 fi 949} 950__ocf_test_trc_dest() { 951 local dest=$1 952 if ! touch $dest; then 953 ocf_log warn "$dest not writable, trace not going to happen" 954 __OCF_TRC_DEST="" 955 __OCF_TRC_MANAGE="" 956 return 1 957 fi 958 return 0 959} 960ocf_default_trace_dest() { 961 tty >/dev/null && return 962 if [ -n "$OCF_RESOURCE_TYPE" -a \ 963 -n "$OCF_RESOURCE_INSTANCE" -a -n "$__OCF_ACTION" ]; then 964 local ts=`date +%F.%T` 965 __OCF_TRC_DEST=$HA_VARLIB/trace_ra/${OCF_RESOURCE_TYPE}/${OCF_RESOURCE_INSTANCE}.${__OCF_ACTION}.$ts 966 __OCF_TRC_MANAGE="1" 967 fi 968} 969 970ocf_start_trace() { 971 export __OCF_TRC_DEST="" __OCF_TRC_MANAGE="" 972 case "$OCF_TRACE_FILE" in 973 [3-9]) ocf_trace_redirect_to_fd "$OCF_TRACE_FILE" ;; 974 /*/*) __OCF_TRC_DEST=$OCF_TRACE_FILE ;; 975 "") ocf_default_trace_dest ;; 976 *) 977 ocf_log warn "OCF_TRACE_FILE must be set to either FD (open for writing) or absolute file path" 978 ocf_default_trace_dest 979 ;; 980 esac 981 if [ "$__OCF_TRC_DEST" ]; then 982 mkdir -p `dirname $__OCF_TRC_DEST` 983 __ocf_test_trc_dest $__OCF_TRC_DEST || 984 return 985 ocf_trace_redirect_to_file "$__OCF_TRC_DEST" 986 fi 987 if [ -n "$BASH_VERSION" ]; then 988 PS4='+ `date +"%T"`: ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: ' 989 fi 990 set -x 991 env=$( echo; printenv | sort ) 992} 993ocf_stop_trace() { 994 set +x 995} 996 997# Helper functions to map from nodename/bundle-name and physical hostname 998# list_index_for_word "node0 node1 node2 node3 node4 node5" node4 --> 5 999# list_word_at_index "NA host1 host2 host3 host4 host5" 3 --> host2 1000 1001# list_index_for_word "node1 node2 node3 node4 node5" node7 --> "" 1002# list_word_at_index "host1 host2 host3 host4 host5" 8 --> "" 1003 1004# attribute_target node1 --> host1 1005list_index_for_word() { 1006 echo $1 | tr ' ' '\n' | awk -v x="$2" '$0~x {print NR}' 1007} 1008 1009list_word_at_index() { 1010 echo $1 | tr ' ' '\n' | awk -v n="$2" 'n == NR' 1011} 1012 1013ocf_attribute_target() { 1014 if [ x$1 = x ]; then 1015 if [ x$OCF_RESKEY_CRM_meta_container_attribute_target = xhost -a x$OCF_RESKEY_CRM_meta_physical_host != x ]; then 1016 echo $OCF_RESKEY_CRM_meta_physical_host 1017 else 1018 if [ x$OCF_RESKEY_CRM_meta_on_node != x ]; then 1019 echo $OCF_RESKEY_CRM_meta_on_node 1020 else 1021 ocf_local_nodename 1022 fi 1023 fi 1024 return 1025 elif [ x"$OCF_RESKEY_CRM_meta_notify_all_uname" != x ]; then 1026 index=$(list_index_for_word "$OCF_RESKEY_CRM_meta_notify_all_uname" $1) 1027 mapping="" 1028 if [ x$index != x ]; then 1029 mapping=$(list_word_at_index "$OCF_RESKEY_CRM_meta_notify_all_hosts" $index) 1030 fi 1031 if [ x$mapping != x -a x$mapping != xNA ]; then 1032 echo $mapping 1033 return 1034 fi 1035 fi 1036 echo $1 1037} 1038 1039__ocf_set_defaults "$@" 1040 1041: ${OCF_TRACE_RA:=$OCF_RESKEY_trace_ra} 1042ocf_is_true "$OCF_TRACE_RA" && ocf_start_trace 1043 1044# pacemaker sets HA_use_logd, some others use HA_LOGD :/ 1045if ocf_is_true "$HA_use_logd"; then 1046 : ${HA_LOGD:=yes} 1047fi 1048