summaryrefslogtreecommitdiffstats
path: root/heartbeat/ocf-shellfuncs.in
blob: a617e00f96234eed6143befe0f8bec829af5a2dd (plain)
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
#
#
# 	Common helper functions for the OCF Resource Agents supplied by
# 	heartbeat.
#
# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
#                    All Rights Reserved.
#
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
# 

# Build version: 72b4771db9437c36931c0f673210f6fe6ed4f8ed

# TODO: Some of this should probably split out into a generic OCF
# library for shell scripts, but for the time being, we'll just use it
# ourselves...
#

# TODO wish-list:
# - Generic function for evaluating version numbers
# - Generic function(s) to extract stuff from our own meta-data
# - Logging function which automatically adds resource identifier etc
#   prefixes
# TODO: Move more common functionality for OCF RAs here.
#

# This was common throughout all legacy Heartbeat agents
unset LC_ALL; export LC_ALL
unset LANGUAGE; export LANGUAGE

: ${HA_SBIN_DIR:=@sbindir@}

__SCRIPT_NAME=`basename $0`

if [ -z "$OCF_ROOT" ]; then
    : ${OCF_ROOT=@OCF_ROOT_DIR@}
fi

if [ "$OCF_FUNCTIONS_DIR" = ${OCF_ROOT}/resource.d/heartbeat ]; then  # old
	unset OCF_FUNCTIONS_DIR
fi

: ${OCF_FUNCTIONS_DIR:=${OCF_ROOT}/lib/heartbeat}

. ${OCF_FUNCTIONS_DIR}/ocf-binaries
. ${OCF_FUNCTIONS_DIR}/ocf-returncodes
. ${OCF_FUNCTIONS_DIR}/ocf-directories
. ${OCF_FUNCTIONS_DIR}/ocf-rarun
. ${OCF_FUNCTIONS_DIR}/ocf-distro

# Define OCF_RESKEY_CRM_meta_interval in case it isn't already set,
# to make sure that ocf_is_probe() always works
: ${OCF_RESKEY_CRM_meta_interval=0}

ocf_is_root() {
	if [ X`id -u` = X0 ]; then
		true
	else
		false
	fi
}

ocf_maybe_random() {
	if test -c /dev/urandom; then
		od -An -N4 -tu4 /dev/urandom | tr -d '[:space:]'
	else
		awk -v pid=$$ 'BEGIN{srand(pid); print rand()}' | sed 's/^.*[.]//'
	fi
}

# Portability comments:
# o The following rely on Bourne "sh" pattern-matching, which is usually
#   that for filename generation (note: not regexp).
# o The "*) true ;;" clause is probably unnecessary, but is included
#   here for completeness.
# o The negation in the pattern uses "!".  This seems to be common
#   across many OSes (whereas the alternative "^" fails on some).
# o If an OS is encountered where this negation fails, then a possible
#   alternative would be to replace the function contents by (e.g.):
#	[ -z "`echo $1 | tr -d '[0-9]'`" ]
#
ocf_is_decimal() {
	case "$1" in
	""|*[!0-9]*)	# empty, or at least one non-decimal
		false ;;
	*)
		true ;;
	esac
}

ocf_is_true() {
	case "$1" in
	yes|true|1|YES|TRUE|True|ja|on|ON) true ;;
	*)	false ;;
	esac
}

ocf_is_hex() {
	case "$1" in
        ""|*[!0-9a-fA-F]*)	# empty, or at least one non-hex
		false ;;
	*)
		true ;;
	esac
}

ocf_is_octal() {
	case "$1" in
        ""|*[!0-7]*)	# empty, or at least one non-octal
		false ;;
	*)
		true ;;
	esac
}

__ocf_set_defaults() {
	__OCF_ACTION="$1"

	# Return to sanity for the agents...
	unset LANG
	LC_ALL=C
	export LC_ALL

	# TODO: Review whether we really should source this. Or rewrite
	# to match some emerging helper function syntax...? This imports
	# things which no OCF RA should be using...

	# Strip the OCF_RESKEY_ prefix from this particular parameter
	if [ -z "$OCF_RESKEY_OCF_CHECK_LEVEL" ]; then
		: ${OCF_CHECK_LEVEL:=0}
	else
		: ${OCF_CHECK_LEVEL:=$OCF_RESKEY_OCF_CHECK_LEVEL}
	fi

	if [ ! -d "$OCF_ROOT" ]; then
		ha_log "ERROR: OCF_ROOT points to non-directory $OCF_ROOT."
		exit $OCF_ERR_GENERIC
	fi

	if [ -z "$OCF_RESOURCE_TYPE" ]; then
		: ${OCF_RESOURCE_TYPE:=$__SCRIPT_NAME}
	fi

	if [ "x$__OCF_ACTION" = "xmeta-data" ]; then
		: ${OCF_RESOURCE_INSTANCE:="RESOURCE_ID"}
	fi

	if [ -z "$OCF_RA_VERSION_MAJOR" ]; then
		: We are being invoked as an init script.
		: Fill in some things with reasonable values.
		: ${OCF_RESOURCE_INSTANCE:="default"}
		return 0
        fi

	if [ -z "$OCF_RESOURCE_INSTANCE" ]; then
		ha_log "ERROR: Need to tell us our resource instance name."
		exit $OCF_ERR_ARGS
	fi
}

hadate() {
  date "+${HA_DATEFMT}"
}

set_logtag() {
	if [ -z "$HA_LOGTAG" ]; then
		if [ -n "$OCF_RESOURCE_INSTANCE" ]; then
			HA_LOGTAG="$__SCRIPT_NAME($OCF_RESOURCE_INSTANCE)[$$]"
		else
			HA_LOGTAG="$__SCRIPT_NAME[$$]"
		fi
	fi
}

__ha_log() {
	local ignore_stderr=false
	local loglevel

	[ "x$1" = "x--ignore-stderr" ] && ignore_stderr=true && shift

	[ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""
	# if we're connected to a tty, then output to stderr
	if tty >/dev/null; then
		if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
			return 0
		elif [ "$ignore_stderr" = "true" ]; then
			# something already printed this error to stderr, so ignore
			return 0
		fi
		if [ "$HA_LOGTAG" ]; then
			echo "$HA_LOGTAG: $*"
		else
			echo "$*"
		fi >&2
		return 0
	fi

	set_logtag

	if [ "x${HA_LOGD}" = "xyes" ] ; then 
		ha_logger -t "${HA_LOGTAG}" "$@"
		if [ "$?" -eq "0" ] ; then
			return 0
		fi
	fi

	if
	  [ -n "$HA_LOGFACILITY" ]
        then
	  : logging through syslog
	  # loglevel is unknown, use 'notice' for now
          loglevel=notice
          case "${*}" in
            *ERROR*)		loglevel=err;;
            *WARN*)		loglevel=warning;;
            *INFO*|info)	loglevel=info;;
	  esac
	  logger -t "$HA_LOGTAG" -p ${HA_LOGFACILITY}.${loglevel} "${*}"
        fi	
        if
	  [ -n "$HA_LOGFILE" ]
	then
	  : appending to $HA_LOGFILE
	  echo `hadate`" $HA_LOGTAG:    ${*}" >> $HA_LOGFILE
	fi
	if
	  [ -z "$HA_LOGFACILITY" -a -z "$HA_LOGFILE" ] && ! [ "$ignore_stderr" = "true" ]
	then
	  : appending to stderr
	  echo `hadate`"${*}" >&2
	fi
        if
          [ -n "$HA_DEBUGLOG" ]
        then
          : appending to $HA_DEBUGLOG
		  if [ "$HA_LOGFILE"x != "$HA_DEBUGLOG"x ]; then
            echo "$HA_LOGTAG:	"`hadate`"${*}" >> $HA_DEBUGLOG
          fi
        fi
}

ha_log()
{
	__ha_log "$@"
}

ha_debug() {

        if [ "x${HA_debug}" = "x0" ] || [ -z "${HA_debug}" ] ; then
                return 0
        fi
	if tty >/dev/null; then
		if [ "$HA_LOGTAG" ]; then
			echo "$HA_LOGTAG: $*"
		else
			echo "$*"
		fi >&2
		return 0
	fi

	set_logtag

        if [ "x${HA_LOGD}" = "xyes" ] ; then  
		ha_logger -t "${HA_LOGTAG}" -D "ha-debug" "$@"
                if [ "$?" -eq "0" ] ; then
                        return 0
                fi
        fi

	[ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""

	if
	  [ -n "$HA_LOGFACILITY" ]
	then
	  : logging through syslog
	  logger -t "$HA_LOGTAG" -p "${HA_LOGFACILITY}.debug" "${*}"
	fi
        if
	  [ -n "$HA_DEBUGLOG" ]
	then
	  : appending to $HA_DEBUGLOG
	  echo "$HA_LOGTAG:	"`hadate`"${*}" >> $HA_DEBUGLOG
	fi
	if
	  [ -z "$HA_LOGFACILITY" -a -z "$HA_DEBUGLOG" ]
	then
	  : appending to stderr
	  echo "$HA_LOGTAG:	`hadate`${*}:	${HA_LOGFACILITY}" >&2
	fi
}

ha_parameter() {
	local VALUE
    VALUE=`sed -e 's%[	][	]*% %' -e 's%^ %%' -e 's%#.*%%'   $HA_CF | grep -i "^$1 " | sed 's%[^ ]* %%'`
    if
	[ "X$VALUE" = X ]
    then
	
	case $1 in
	    keepalive)	VALUE=2;;
	    deadtime)
		ka=`ha_parameter keepalive`
		VALUE=`expr $ka '*' 2 '+' 1`;;
	esac
    fi
    echo $VALUE
}

ocf_log() {
	# TODO: Revisit and implement internally.
	if
          [ $# -lt 2 ]
        then
          ocf_log err "Not enough arguments [$#] to ocf_log."
        fi
        __OCF_PRIO="$1"
        shift
        __OCF_MSG="$*"

        case "${__OCF_PRIO}" in
          crit)	__OCF_PRIO="CRIT";;
          err)	__OCF_PRIO="ERROR";;
          warn)	__OCF_PRIO="WARNING";;
          info)	__OCF_PRIO="INFO";;
          debug)__OCF_PRIO="DEBUG";;
          *)	__OCF_PRIO=`echo ${__OCF_PRIO}| tr '[a-z]' '[A-Z]'`;;
	esac

	if [ "${__OCF_PRIO}" = "DEBUG" ]; then
		ha_debug "${__OCF_PRIO}: $__OCF_MSG"
	else
		ha_log "${__OCF_PRIO}: $__OCF_MSG"
	fi
}

#
# ocf_exit_reason: print exit error string to stderr
# Usage:           Allows the OCF script to provide a string
#                  describing why the exit code was returned.
# Arguments:   reason - required, The string that represents why the error
#                       occured.
#
ocf_exit_reason()
{
	local cookie="$OCF_EXIT_REASON_PREFIX"
	local fmt
	local msg

	# No argument is likely not intentional.
	# Just one argument implies a printf format string of just "%s".
	# "Least surprise" in case some interpolated string from variable
	# expansion or other contains a percent sign.
	# More than one argument: first argument is going to be the format string.
	case $# in
	0)	ocf_log err "Not enough arguments to ocf_log_exit_msg." ;;
	1)	fmt="%s" ;;

	*)	fmt=$1
		shift
		case $fmt in
		*%*) : ;; # ok, does look like a format string
		*) ocf_log warn "Does not look like format string: [$fmt]" ;;
		esac ;;
	esac

	if [ -z "$cookie" ]; then
		# use a default prefix
		cookie="ocf-exit-reason:"
	fi

	msg=$(printf "${fmt}" "$@")
	printf >&2 "%s%s\n" "$cookie" "$msg"
	__ha_log --ignore-stderr "ERROR: $msg"
}

#
# ocf_deprecated: Log a deprecation warning
# Usage:          ocf_deprecated [param-name]
# Arguments:      param-name optional, name of a boolean resource
#                            parameter that can be used to suppress
#                            the warning (default
#                            "ignore_deprecation")
ocf_deprecated() {
    local param
    param=${1:-ignore_deprecation}
    # don't use ${!param} here, it's a bashism
    if ! ocf_is_true $(eval echo \$OCF_RESKEY_$param); then
	ocf_log warn "This resource agent is deprecated" \
	    "and may be removed in a future release." \
	    "See the man page for details." \
	    "To suppress this warning, set the \"${param}\"" \
	    "resource parameter to true."
    fi
}

#
# Ocf_run: Run a script, and log its output.
# Usage:   ocf_run [-q] [-info|-warn|-err] <command>
#	-q: don't log the output of the command if it succeeds
#	-info|-warn|-err: log the output of the command at given
#		severity if it fails (defaults to err)
#
ocf_run() {
	local rc
	local output
	local verbose=1
	local loglevel=err
	local var

	for var in 1 2
	do
	    case "$1" in
		"-q")
		    verbose=""
		    shift 1;;
		"-info"|"-warn"|"-err")
		    loglevel=`echo $1 | sed -e s/-//g`
		    shift 1;;
		*)
		    ;;		
	    esac
	done

	output=`"$@" 2>&1`
	rc=$?
	[ -n "$output" ] && output="$(echo "$output" | tr -s ' \t\r\n' ' ')"
	if [ $rc -eq 0 ]; then 
	    if [ "$verbose" -a ! -z "$output" ]; then
		ocf_log info "$output"
	    fi
	else
	    if [ ! -z "$output" ]; then
		ocf_log $loglevel "$output"
	    else
		ocf_log $loglevel "command failed: $*"
	    fi
	fi

	return $rc
}

ocf_pidfile_status() {
	local pid pidfile="$1"
	if [ ! -e "$pidfile" ]; then
		# Not exists
		return 2
	fi
	pid=$(cat "$pidfile")
	kill -0 "$pid" > /dev/null 2>&1
	if [ $? = 0 ]; then
		return 0
	fi

	# Stale
	return 1
}

# mkdir(1) based locking
# first the directory is created with the name given as $1
# then a file named "pid" is created within that directory with
# the process PID
# stale locks are handled carefully, the inode of a directory
# needs to match before and after test if the process is running
# empty directories are also handled appropriately
# we relax (sleep) occasionally to allow for other processes to
# finish managing the lock in case they are in the middle of the
# business

relax() { sleep 0.5; }
ocf_get_stale_pid() {
	local piddir pid dir_inode

	piddir="$1"
	[ -z "$piddir" ] && return 2
	dir_inode="`ls -di $piddir 2>/dev/null`"
	[ -z "$dir_inode" ] && return 1
	pid=`cat $piddir/pid 2>/dev/null`
	if [ -z "$pid" ]; then
		# empty directory?
		relax
		if [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
			echo $dir_inode
		else
			return 1
		fi
	elif kill -0 $pid >/dev/null 2>&1; then
		return 1
	elif relax && [ -e "$piddir/pid" ] && [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
		echo $pid
	else
		return 1
	fi
}

# There is a race when the following two functions to manage the
# lock file (mk and rm) are invoked in parallel by different
# instances. It is up to the caller to reduce probability of that
# taking place (see ocf_take_lock() below).

ocf_mk_pid() {
	mkdir $1 2>/dev/null && echo $$ > $1/pid
}
ocf_rm_pid() {
	rm -f $1/pid
	rmdir $1 2>/dev/null
}

# Testing and subsequently removing a stale lock (containing the
# process pid) is inherently difficult to do in such a way as to
# prevent a race between creating a pid file and removing it and
# its directory. We reduce the probability of that happening by
# checking if the stale lock persists over a random period of
# time.

ocf_take_lock() {
	local lockdir=$1
	local rnd
	local stale_pid

	# we don't want it too short, so strip leading zeros
	rnd=$(ocf_maybe_random | sed 's/^0*//')
	stale_pid=`ocf_get_stale_pid $lockdir`
	if [ -n "$stale_pid" ]; then
		sleep 0.$rnd
		# remove "stale pid" only if it persists
		[ "$stale_pid" = "`ocf_get_stale_pid $lockdir`" ] &&
			ocf_rm_pid $lockdir
	fi
	while ! ocf_mk_pid $lockdir; do
		ocf_log info "Sleeping until $lockdir is released..."
		sleep 0.$rnd
	done
}

ocf_release_lock_on_exit() {
	trap "ocf_rm_pid $1" EXIT
}

# returns true if the CRM is currently running a probe. A probe is
# defined as a monitor operation with a monitoring interval of zero.
ocf_is_probe() {
    [ "$__OCF_ACTION" = "monitor" -a "$OCF_RESKEY_CRM_meta_interval" = 0 ]
}

# returns true if the resource is configured as a clone. This is
# defined as a resource where the clone-max meta attribute is present.
ocf_is_clone() {
    [ ! -z "${OCF_RESKEY_CRM_meta_clone_max}" ]
}

# returns true if the resource is configured as a multistate
# (master/slave) resource. This is defined as a resource where the
# master-max meta attribute is present, and set to greater than zero.
ocf_is_ms() {
    [ "${OCF_RESKEY_CRM_meta_promotable}" = "true" ] || { [ ! -z "${OCF_RESKEY_CRM_meta_master_max}" ] && [ "${OCF_RESKEY_CRM_meta_master_max}" -gt 0 ]; }
}

# version check functions
# allow . and - to delimit version numbers
# max version number is 999
#
ocf_is_ver() {
	echo $1 | grep '^[0-9][0-9.-]*[0-9A-Za-z.\+-]*$' >/dev/null 2>&1
}

# usage: ocf_version_cmp VER1 VER2
#     version strings can contain digits, dots, and dashes
#     must start and end with a digit
# returns:
#     0: VER1 smaller (older) than VER2
#     1: versions equal
#     2: VER1 greater (newer) than VER2
#     3: bad format
ocf_version_cmp() {
	ocf_is_ver "$1" || return 3
	ocf_is_ver "$2" || return 3
	local v1=$1
	local v2=$2

	sort_version="sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n"
	older=$( (echo "$v1"; echo "$v2") | $sort_version | head -1 )

	if [ "$v1" = "$v2" ]; then
		return 1
	elif [ "$v1" = "$older" ]; then
		return 0
	else
		return 2 # -1 would look funny in shell ;-)
	fi
}

ocf_local_nodename() {
	# use crm_node -n for pacemaker > 1.1.8
	which pacemakerd > /dev/null 2>&1
	if [ $? -eq 0 ]; then
		local version=$(pacemakerd -$ | grep "Pacemaker .*" | awk '{ print $2 }')
		version=$(echo $version | awk -F- '{ print $1 }')
		ocf_version_cmp "$version" "1.1.8"
		if [ $? -eq 2 ]; then
			which crm_node > /dev/null 2>&1
			if [ $? -eq 0 ]; then
				crm_node -n
				return
			fi
		fi
	fi

	# otherwise use uname -n
	uname -n
}

# usage: dirname DIR
dirname()
{
	local a
	local b

	[ $# = 1 ] || return 1
	a="$1"
	while [ 1 ]; do
		b="${a%/}"
		[ "$a" = "$b" ] && break
		a="$b"
	done
	b=${a%/*}
	[ -z "$b" -o "$a" = "$b"  ] && b="."

	echo "$b"
	return 0
}

# usage: systemd_is_running
# returns:
#    0  PID 1 is systemd
#    1  otherwise
systemd_is_running()
{
	[ "$(cat /proc/1/comm 2>/dev/null)" = "systemd" ]
}

# usage: systemd_drop_in <name> <After|Before> <dependency.service>
systemd_drop_in()
{
	local conf_file
	if [ $# -ne 3 ]; then
          ocf_log err "Incorrect number of arguments [$#] for systemd_drop_in."
        fi

	systemdrundir="/run/systemd/system/resource-agents-deps.target.d"
	mkdir -p "$systemdrundir"
	conf_file="$systemdrundir/$1.conf"
	cat >"$conf_file" <<EOF
[Unit]
$2=$3
EOF
	# The information is accessible through systemd API and systemd would
	# complain about improper permissions.
	chmod o+r "$conf_file"
	systemctl daemon-reload
}

# move process to root cgroup if realtime scheduling is enabled
ocf_move_to_root_cgroup_if_rt_enabled()
{
	if [ -e "/sys/fs/cgroup/cpu/cpu.rt_runtime_us" ]; then
		echo $$ >> /sys/fs/cgroup/cpu/tasks

		if [ "$?" -ne "0" ]; then
			ocf_log warn "Unable to move PID $$ to the root cgroup"
		fi
	fi
}

# usage: crm_mon_no_validation args...
# run crm_mon without any cib schema validation
# This is useful when an agent runs in a bundle to avoid potential
# schema validation errors when host and bundle are not perfectly aligned
# To be used, your shell must support on process substitution (e.g. bash)
# returns:
#    <crm_mon error codes>
crm_mon_no_validation()
{
	# The subshell prevents parsing error with incompatible shells
	"$SHELL" -c "CIB_file=<(${HA_SBIN_DIR}/cibadmin -Q | sed 's/validate-with=\"[^\"]*\"/validate-with=\"none\"/') \
	${HA_SBIN_DIR}/crm_mon \$*" -- $*
}

#
# pseudo_resource status tracking function...
#
# This allows pseudo resources to give correct status information.  As we add
# resource monitoring, and better resource tracking in general, this will
# become essential.
#
# These scripts work because ${HA_RSCTMP} is cleaned on node reboot.
#
# We create "resource-string" tracking files under ${HA_RSCTMP} in a
# very simple way:
#
#	Existence of "${HA_RSCTMP}/resource-string" means that we consider
#	the resource named by "resource-string" to be running.
#
# Note that "resource-string" needs to be unique.  Using the resource type
# plus the resource instance arguments to make up the resource string
# is probably sufficient...
#
# usage: ha_pseudo_resource resource-string op [tracking_file]
# 	where op is {start|stop|monitor|status|restart|reload|print}
#	print is a special op which just prints the tracking file location
#	user can override our choice of the tracking file location by
#		specifying it as the third arg
#	Note that all operations are silent...
#
ha_pseudo_resource()
{
  local ha_resource_tracking_file="${3:-${HA_RSCTMP}/$1}"
  case $2 in
    start|restart|reload)  touch "$ha_resource_tracking_file";;
    stop) rm -f "$ha_resource_tracking_file";;
    status|monitor)
           if
             [ -f "$ha_resource_tracking_file" ]
           then
             return 0
           else
             case $2 in
               status)	return 3;;
               *)	return 7;;
             esac
           fi;;
    print)  echo "$ha_resource_tracking_file";;
    *)	return 3;;
  esac
}

# usage: rmtempdir TMPDIR
rmtempdir()
{
	[ $# = 1 ] || return 1
	if [ -e "$1" ]; then
		rmdir "$1" || return 1
	fi
	return 0
}

# usage: maketempfile [-d]
maketempfile()
{
	if [ $# = 1 -a "$1" = "-d" ]; then
		mktemp -d
		return 0
	elif [ $# != 0 ]; then
		return 1
	fi

	mktemp
	return 0
}

# usage: rmtempfile TMPFILE
rmtempfile ()
{
	[ $# = 1 ] || return 1
	if [ -e "$1" ]; then
		rm "$1" || return 1
	fi
	return 0
}

# echo the first lower supported check level
# pass set of levels supported by the agent
# (in increasing order, 0 is optional)
ocf_check_level()
{
	local lvl prev
	lvl=0
	prev=0
	if ocf_is_decimal "$OCF_CHECK_LEVEL"; then
		# the level list should be very short
		for lvl; do
			if [ "$lvl" -eq "$OCF_CHECK_LEVEL" ]; then
				break
			elif [ "$lvl" -gt "$OCF_CHECK_LEVEL" ]; then
				lvl=$prev # the previous one
				break
			fi
			prev=$lvl
		done
	fi
	echo $lvl
}

# usage: ocf_stop_processes SIGNALS WAIT_TIME PIDS
#
# we send signals (use quotes for more than one!) in the order
# given; if one or more processes are still running we try KILL;
# the wait_time is the _total_ time we'll spend in this function
# this time may be slightly exceeded if the processes won't leave
# 
# returns:
#     0: all processes left
#     1: some processes still running
#
# example:
#
# ocf_stop_processes TERM 5 $pids
# 
ocf_stop_processes() {
	local signals="$1"
	local wait_time="$(($2/`echo $signals|wc -w`))"
	shift 2
	local pids="$*"
	local sig i
	test -z "$pids" &&
		return 0
	for sig in $signals KILL; do
		kill -s $sig $pids 2>/dev/null
		# try to leave early, and yet leave processes time to exit
		sleep 0.2
		for i in `seq $wait_time`; do
			kill -s 0 $pids 2>/dev/null ||
				return 0
			sleep 1
		done
	done
	return 1
}

#
# create a given status directory
# if the directory path doesn't start with $HA_VARRUN, then
# we return with error (most of the calls would be with the user
# supplied configuration, hence we need to do necessary
# protection)
# used mostly for PID files
#
# usage: ocf_mkstatedir owner permissions path
#
# owner: user.group
# permissions: permissions
# path: directory path
#
# example:
#	ocf_mkstatedir named 755 `dirname $pidfile`
#
ocf_mkstatedir()
{
	local owner
	local perms
	local path

	owner=$1
	perms=$2
	path=$3

	test -d $path && return 0
	[ $(id -u) = 0 ] || return 1

	case $path in
	${HA_VARRUN%/}/*) : this path is ok ;;
	*) ocf_log err "cannot create $path (does not start with $HA_VARRUN)"
		return 1
	;;
	esac

	mkdir -p $path &&
	chown $owner $path &&
	chmod $perms $path
}

#
# create a unique status directory in $HA_VARRUN
# used mostly for PID files
# the directory is by default set to
#   $HA_VARRUN/$OCF_RESOURCE_INSTANCE
# the directory name is printed to stdout
#
# usage: ocf_unique_rundir owner permissions name
#
# owner: user.group (default: "root")
# permissions: permissions (default: "755")
# name: some unique string (default: "$OCF_RESOURCE_INSTANCE")
#
# to use the default either don't set the parameter or set it to
# empty string ("")
# example:
#
#	STATEDIR=`ocf_unique_rundir named "" myownstatedir`
#
ocf_unique_rundir()
{
	local path
	local owner
	local perms
	local name

	owner=${1:-"root"}
	perms=${2:-"755"}
	name=${3:-"$OCF_RESOURCE_INSTANCE"}
	path=$HA_VARRUN/$name
	if [ ! -d $path ]; then
		[ $(id -u) = 0 ] || return 1
		mkdir -p $path &&
		chown $owner $path &&
		chmod $perms $path || return 1
	fi
	echo $path
}

#
# RA tracing may be turned on by setting OCF_TRACE_RA
# the trace output will be saved to OCF_TRACE_FILE, if set, or
# by default to
#   $HA_VARLIB/trace_ra/<type>/<id>.<action>.<timestamp>
#   e.g. $HA_VARLIB/trace_ra/oracle/db.start.2012-11-27.08:37:08
#
# OCF_TRACE_FILE:
# - FD (small integer [3-9]) in that case it is up to the callers
#   to capture output; the FD _must_ be open for writing
# - absolute path
#
# NB: FD 9 may be used for tracing with bash >= v4 in case
# OCF_TRACE_FILE is set to a path.
#
ocf_bash_has_xtracefd() {
	[ -n "$BASH_VERSION" ] && [ ${BASH_VERSINFO[0]} -ge 4 ]
}
# for backwards compatibility
ocf_is_bash4() {
	ocf_bash_has_xtracefd
}
ocf_trace_redirect_to_file() {
	local dest=$1
	if ocf_bash_has_xtracefd; then
		exec 9>$dest
		BASH_XTRACEFD=9
	else
		exec 2>$dest
	fi
}
ocf_trace_redirect_to_fd() {
	local fd=$1
	if ocf_bash_has_xtracefd; then
		BASH_XTRACEFD=$fd
	else
		exec 2>&$fd
	fi
}
__ocf_test_trc_dest() {
	local dest=$1
	if ! touch $dest; then
		ocf_log warn "$dest not writable, trace not going to happen"
		__OCF_TRC_DEST=""
		__OCF_TRC_MANAGE=""
		return 1
	fi
	return 0
}
ocf_default_trace_dest() {
	tty >/dev/null && return
	if [ -n "$OCF_RESOURCE_TYPE" -a \
			-n "$OCF_RESOURCE_INSTANCE" -a -n "$__OCF_ACTION" ]; then
		local ts=`date +%F.%T`
		__OCF_TRC_DEST=${OCF_RESKEY_trace_dir}/${OCF_RESOURCE_TYPE}/${OCF_RESOURCE_INSTANCE}.${__OCF_ACTION}.$ts
		__OCF_TRC_MANAGE="1"
	fi
}

ocf_start_trace() {
	export __OCF_TRC_DEST="" __OCF_TRC_MANAGE=""
	case "$OCF_TRACE_FILE" in
	[3-9]) ocf_trace_redirect_to_fd "$OCF_TRACE_FILE" ;;
	/*/*) __OCF_TRC_DEST=$OCF_TRACE_FILE ;;
	"") ocf_default_trace_dest ;;
	*)
		ocf_log warn "OCF_TRACE_FILE must be set to either FD (open for writing) or absolute file path"
		ocf_default_trace_dest
		;;
	esac
	if [ "$__OCF_TRC_DEST" ]; then
		mkdir -p `dirname $__OCF_TRC_DEST`
		__ocf_test_trc_dest $__OCF_TRC_DEST ||
			return
		ocf_trace_redirect_to_file "$__OCF_TRC_DEST"
	fi
	if [ -n "$BASH_VERSION" ]; then
		PS4='+ `date +"%T"`: ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: '
	fi
	set -x
	env=$( echo; printenv | sort )
}
ocf_stop_trace() {
	set +x
}

# Helper functions to map from nodename/bundle-name and physical hostname
# list_index_for_word "node0 node1 node2 node3 node4 node5" node4 --> 5
# list_word_at_index "NA host1 host2 host3 host4 host5" 3      --> host2

# list_index_for_word "node1 node2 node3 node4 node5" node7 --> ""
# list_word_at_index "host1 host2 host3 host4 host5" 8      --> ""

# attribute_target node1                                    --> host1
list_index_for_word() {
	echo $1 | tr ' ' '\n' | awk -v x="$2" '$0~x {print NR}'
}

list_word_at_index() {
	echo $1 | tr ' ' '\n' | awk -v n="$2" 'n == NR'
}

ocf_attribute_target() {
	if [ x$1 = x ]; then
		if [ x$OCF_RESKEY_CRM_meta_container_attribute_target = xhost -a x$OCF_RESKEY_CRM_meta_physical_host != x ]; then
			echo $OCF_RESKEY_CRM_meta_physical_host
		else
			if [ x$OCF_RESKEY_CRM_meta_on_node != x ]; then
				echo $OCF_RESKEY_CRM_meta_on_node
			else
				ocf_local_nodename
			fi
		fi
		return
	elif [ x"$OCF_RESKEY_CRM_meta_notify_all_uname" != x ]; then
		index=$(list_index_for_word "$OCF_RESKEY_CRM_meta_notify_all_uname" $1)
		mapping=""
		if [ x$index != x ]; then
			mapping=$(list_word_at_index "$OCF_RESKEY_CRM_meta_notify_all_hosts" $index)
		fi
		if [ x$mapping != x -a x$mapping != xNA ]; then
			echo $mapping
			return
		fi
	fi
	echo $1
}

ocf_promotion_score() {
	ocf_version_cmp "$OCF_RESKEY_crm_feature_set" "3.10.0"
	res=$?
	if [ $res -eq 2 ] || [ $res -eq 1 ] || ! have_binary "crm_master"; then
		${HA_SBIN_DIR}/crm_attribute -p ${OCF_RESOURCE_INSTANCE} $@
	else
		${HA_SBIN_DIR}/crm_master -l reboot $@
	fi
}

__ocf_set_defaults "$@"

: ${OCF_TRACE_RA:=$OCF_RESKEY_trace_ra}
: ${OCF_RESKEY_trace_dir:="$HA_VARLIB/trace_ra"}
ocf_is_true "$OCF_TRACE_RA" && ocf_start_trace

# pacemaker sets HA_use_logd, some others use HA_LOGD :/
if ocf_is_true "$HA_use_logd"; then
	: ${HA_LOGD:=yes}
fi