summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:40:13 +0000
commite9be59e1502a41bab9891d96d753102a7dafef0b (patch)
treec3b2da87c414881f4b53d0964f407c83492d813e
parentInitial commit. (diff)
downloadcluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.tar.xz
cluster-glue-e9be59e1502a41bab9891d96d753102a7dafef0b.zip
Adding upstream version 1.0.12.upstream/1.0.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.hg_archival.txt2
-rw-r--r--.hgignore92
-rw-r--r--.hgsigs4
-rw-r--r--.hgtags68
-rw-r--r--AUTHORS19
-rw-r--r--COPYING339
-rw-r--r--COPYING.LIB504
-rw-r--r--ChangeLog262
-rw-r--r--GNUmakefile47
-rw-r--r--Makefile.am39
-rw-r--r--NEWS1
-rw-r--r--README1
-rwxr-xr-xautogen.sh193
-rw-r--r--cluster-glue-fedora.spec249
-rw-r--r--cluster-glue-suse.spec307
-rw-r--r--config/Makefile.am19
-rw-r--r--config/byteorder_test.c15
-rw-r--r--configure.ac1439
-rw-r--r--doc/Makefile.am53
-rw-r--r--doc/ha_logd.xml.in134
-rw-r--r--doc/ha_logger.xml.in110
-rw-r--r--doc/hb_report.8.txt478
-rw-r--r--doc/meatclient.xml.in77
-rw-r--r--doc/stonith.xml.in315
-rw-r--r--doc/stonith/Makefile.am37
-rw-r--r--doc/stonith/README.bladehpi101
-rw-r--r--doc/stonith/README.cyclades61
-rw-r--r--doc/stonith/README.drac318
-rw-r--r--doc/stonith/README.dracmc87
-rw-r--r--doc/stonith/README.external90
-rw-r--r--doc/stonith/README.ibmrsa9
-rw-r--r--doc/stonith/README.ibmrsa-telnet55
-rw-r--r--doc/stonith/README.ipmilan131
-rw-r--r--doc/stonith/README.ippower925868
-rw-r--r--doc/stonith/README.meatware26
-rw-r--r--doc/stonith/README.rackpdu21
-rw-r--r--doc/stonith/README.rcd_serial186
-rw-r--r--doc/stonith/README.riloe36
-rw-r--r--doc/stonith/README.vacm40
-rw-r--r--doc/stonith/README.vcenter90
-rw-r--r--doc/stonith/README.wti_mpc85
-rw-r--r--doc/stonith/README_kdumpcheck.txt151
-rw-r--r--hb_report/Makefile.am26
-rw-r--r--hb_report/ha_cf_support.sh83
-rwxr-xr-xhb_report/hb_report.in1445
-rw-r--r--hb_report/openais_conf_support.sh97
-rw-r--r--hb_report/utillib.sh752
-rw-r--r--include/Makefile.am25
-rw-r--r--include/clplumbing/GSource.h236
-rw-r--r--include/clplumbing/GSource_internal.h111
-rw-r--r--include/clplumbing/Gmain_timeout.h44
-rw-r--r--include/clplumbing/Makefile.am59
-rw-r--r--include/clplumbing/apphb_cs.h75
-rw-r--r--include/clplumbing/base64.h50
-rw-r--r--include/clplumbing/cl_log.h99
-rw-r--r--include/clplumbing/cl_misc.h31
-rw-r--r--include/clplumbing/cl_pidfile.h25
-rw-r--r--include/clplumbing/cl_plugin.h29
-rw-r--r--include/clplumbing/cl_poll.h46
-rw-r--r--include/clplumbing/cl_quorum.h44
-rw-r--r--include/clplumbing/cl_quorumd.h48
-rw-r--r--include/clplumbing/cl_random.h81
-rw-r--r--include/clplumbing/cl_reboot.h6
-rw-r--r--include/clplumbing/cl_signal.h91
-rw-r--r--include/clplumbing/cl_syslog.h32
-rw-r--r--include/clplumbing/cl_tiebreaker.h35
-rw-r--r--include/clplumbing/cl_uuid.h40
-rw-r--r--include/clplumbing/coredumps.h36
-rw-r--r--include/clplumbing/cpulimits.h66
-rw-r--r--include/clplumbing/ipc.h788
-rw-r--r--include/clplumbing/loggingdaemon.h32
-rw-r--r--include/clplumbing/longclock.h143
-rw-r--r--include/clplumbing/lsb_exitcodes.h92
-rw-r--r--include/clplumbing/md5.h49
-rw-r--r--include/clplumbing/mkstemp_mode.h34
-rw-r--r--include/clplumbing/netstring.h48
-rw-r--r--include/clplumbing/proctrack.h120
-rw-r--r--include/clplumbing/realtime.h65
-rw-r--r--include/clplumbing/replytrack.h208
-rw-r--r--include/clplumbing/setproctitle.h64
-rw-r--r--include/clplumbing/timers.h23
-rw-r--r--include/clplumbing/uids.h32
-rw-r--r--include/compress.h50
-rw-r--r--include/glue_config.h.in104
-rw-r--r--include/ha_msg.h464
-rw-r--r--include/lha_internal.h182
-rw-r--r--include/lrm/Makefile.am22
-rw-r--r--include/lrm/lrm_api.h455
-rw-r--r--include/lrm/lrm_msg.h160
-rw-r--r--include/lrm/racommon.h29
-rw-r--r--include/lrm/raexec.h157
-rw-r--r--include/pils/Makefile.am25
-rw-r--r--include/pils/generic.h118
-rw-r--r--include/pils/interface.h159
-rw-r--r--include/pils/plugin.h.in736
-rw-r--r--include/replace_uuid.h50
-rw-r--r--include/stonith/Makefile.am25
-rw-r--r--include/stonith/expect.h61
-rw-r--r--include/stonith/st_ttylock.h21
-rw-r--r--include/stonith/stonith.h187
-rw-r--r--include/stonith/stonith_plugin.h125
-rw-r--r--lib/Makefile.am20
-rw-r--r--lib/clplumbing/GSource.c1864
-rw-r--r--lib/clplumbing/Makefile.am99
-rw-r--r--lib/clplumbing/base64.c422
-rw-r--r--lib/clplumbing/base64_md5_test.c113
-rw-r--r--lib/clplumbing/cl_compress.c500
-rw-r--r--lib/clplumbing/cl_log.c1261
-rw-r--r--lib/clplumbing/cl_malloc.c1044
-rw-r--r--lib/clplumbing/cl_misc.c179
-rw-r--r--lib/clplumbing/cl_msg.c2537
-rw-r--r--lib/clplumbing/cl_msg_types.c1736
-rw-r--r--lib/clplumbing/cl_netstring.c570
-rw-r--r--lib/clplumbing/cl_pidfile.c294
-rw-r--r--lib/clplumbing/cl_plugin.c140
-rw-r--r--lib/clplumbing/cl_poll.c809
-rw-r--r--lib/clplumbing/cl_random.c164
-rw-r--r--lib/clplumbing/cl_reboot.c59
-rw-r--r--lib/clplumbing/cl_signal.c209
-rw-r--r--lib/clplumbing/cl_syslog.c149
-rw-r--r--lib/clplumbing/cl_uuid.c180
-rw-r--r--lib/clplumbing/coredumps.c309
-rw-r--r--lib/clplumbing/cpulimits.c219
-rw-r--r--lib/clplumbing/ipcsocket.c2767
-rw-r--r--lib/clplumbing/ipctest.c1377
-rw-r--r--lib/clplumbing/ipctransient.h50
-rw-r--r--lib/clplumbing/ipctransientclient.c222
-rw-r--r--lib/clplumbing/ipctransientlib.c97
-rw-r--r--lib/clplumbing/ipctransientserver.c204
-rw-r--r--lib/clplumbing/longclock.c275
-rw-r--r--lib/clplumbing/md5.c335
-rw-r--r--lib/clplumbing/mkstemp_mode.c56
-rw-r--r--lib/clplumbing/netstring_test.c255
-rw-r--r--lib/clplumbing/ocf_ipc.c594
-rw-r--r--lib/clplumbing/proctrack.c515
-rw-r--r--lib/clplumbing/realtime.c354
-rw-r--r--lib/clplumbing/replytrack.c643
-rw-r--r--lib/clplumbing/setproctitle.c235
-rw-r--r--lib/clplumbing/timers.c119
-rwxr-xr-xlib/clplumbing/transient-test.sh120
-rw-r--r--lib/clplumbing/uids.c140
-rw-r--r--lib/lrm/Makefile.am36
-rw-r--r--lib/lrm/clientlib.c1612
-rw-r--r--lib/lrm/lrm_msg.c212
-rw-r--r--lib/lrm/racommon.c178
-rw-r--r--lib/pils/Makefile.am57
-rw-r--r--lib/pils/main.c122
-rw-r--r--lib/pils/pils.c2152
-rw-r--r--lib/pils/test.c107
-rw-r--r--lib/plugins/InterfaceMgr/HBauth.c171
-rw-r--r--lib/plugins/InterfaceMgr/Makefile.am33
-rw-r--r--lib/plugins/InterfaceMgr/generic.c452
-rw-r--r--lib/plugins/Makefile.am20
-rw-r--r--lib/plugins/compress/Makefile.am52
-rw-r--r--lib/plugins/compress/bz2.c142
-rw-r--r--lib/plugins/compress/zlib.c135
-rw-r--r--lib/plugins/lrm/Makefile.am58
-rw-r--r--lib/plugins/lrm/dbus/Makefile.am16
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml45
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml71
-rw-r--r--lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml57
-rw-r--r--lib/plugins/lrm/raexechb.c416
-rw-r--r--lib/plugins/lrm/raexeclsb.c609
-rw-r--r--lib/plugins/lrm/raexecocf.c496
-rw-r--r--lib/plugins/lrm/raexecupstart.c222
-rw-r--r--lib/plugins/lrm/upstart-dbus.c406
-rw-r--r--lib/plugins/lrm/upstart-dbus.h36
-rw-r--r--lib/plugins/stonith/Makefile.am216
-rw-r--r--lib/plugins/stonith/apcmaster.c822
-rw-r--r--lib/plugins/stonith/apcmastersnmp.c890
-rw-r--r--lib/plugins/stonith/apcmastersnmp.cfg.example39
-rw-r--r--lib/plugins/stonith/apcsmart.c1028
-rw-r--r--lib/plugins/stonith/apcsmart.cfg.example1
-rw-r--r--lib/plugins/stonith/baytech.c924
-rw-r--r--lib/plugins/stonith/bladehpi.c1101
-rw-r--r--lib/plugins/stonith/cyclades.c650
-rw-r--r--lib/plugins/stonith/drac3.c359
-rw-r--r--lib/plugins/stonith/drac3_command.c342
-rw-r--r--lib/plugins/stonith/drac3_command.h29
-rw-r--r--lib/plugins/stonith/drac3_hash.c106
-rw-r--r--lib/plugins/stonith/drac3_hash.h28
-rw-r--r--lib/plugins/stonith/external.c868
-rw-r--r--lib/plugins/stonith/external/Makefile.am33
-rw-r--r--lib/plugins/stonith/external/drac5.in113
-rw-r--r--lib/plugins/stonith/external/dracmc-telnet377
-rwxr-xr-xlib/plugins/stonith/external/hetzner139
-rw-r--r--lib/plugins/stonith/external/hmchttp218
-rw-r--r--lib/plugins/stonith/external/ibmrsa157
-rw-r--r--lib/plugins/stonith/external/ibmrsa-telnet320
-rw-r--r--lib/plugins/stonith/external/ipmi276
-rwxr-xr-xlib/plugins/stonith/external/ippower9258.in316
-rw-r--r--lib/plugins/stonith/external/kdumpcheck.in274
-rw-r--r--lib/plugins/stonith/external/libvirt298
-rw-r--r--lib/plugins/stonith/external/nut302
-rw-r--r--lib/plugins/stonith/external/rackpdu280
-rw-r--r--lib/plugins/stonith/external/riloe530
-rw-r--r--lib/plugins/stonith/external/ssh.in176
-rwxr-xr-xlib/plugins/stonith/external/vcenter280
-rw-r--r--lib/plugins/stonith/external/vmware216
-rw-r--r--lib/plugins/stonith/external/xen0253
-rwxr-xr-xlib/plugins/stonith/external/xen0-ha-dom0-stonith-helper72
-rwxr-xr-xlib/plugins/stonith/external/xen0-ha.in96
-rw-r--r--lib/plugins/stonith/ibmhmc.c1261
-rw-r--r--lib/plugins/stonith/ipmi_os_handler.c257
-rw-r--r--lib/plugins/stonith/ipmilan.c587
-rw-r--r--lib/plugins/stonith/ipmilan.h41
-rw-r--r--lib/plugins/stonith/ipmilan_command.c399
-rw-r--r--lib/plugins/stonith/ipmilan_test.c63
-rw-r--r--lib/plugins/stonith/meatware.c351
-rw-r--r--lib/plugins/stonith/null.c260
-rw-r--r--lib/plugins/stonith/nw_rpc100s.c779
-rw-r--r--lib/plugins/stonith/rcd_serial.c602
-rw-r--r--lib/plugins/stonith/rhcs.c1035
-rw-r--r--lib/plugins/stonith/ribcl.py.in101
-rw-r--r--lib/plugins/stonith/riloe.c338
-rw-r--r--lib/plugins/stonith/rps10.c1070
-rw-r--r--lib/plugins/stonith/ssh.c351
-rw-r--r--lib/plugins/stonith/stonith_config_xml.h157
-rw-r--r--lib/plugins/stonith/stonith_expect_helpers.h120
-rw-r--r--lib/plugins/stonith/stonith_plugin_common.h127
-rw-r--r--lib/plugins/stonith/stonith_signal.h68
-rw-r--r--lib/plugins/stonith/suicide.c274
-rw-r--r--lib/plugins/stonith/vacm.c485
-rw-r--r--lib/plugins/stonith/wti_mpc.c856
-rw-r--r--lib/plugins/stonith/wti_nps.c813
-rw-r--r--lib/stonith/Makefile.am54
-rw-r--r--lib/stonith/README31
-rw-r--r--lib/stonith/expect.c539
-rwxr-xr-xlib/stonith/ha_log.sh114
-rw-r--r--lib/stonith/main.c727
-rw-r--r--lib/stonith/meatclient.c152
-rw-r--r--lib/stonith/st_ttylock.c225
-rw-r--r--lib/stonith/stonith.c636
-rw-r--r--logd/Makefile.am56
-rw-r--r--logd/ha_logd.c1085
-rw-r--r--logd/ha_logger.138
-rw-r--r--logd/ha_logger.c142
-rw-r--r--logd/logd.cf66
-rwxr-xr-xlogd/logd.in101
-rw-r--r--logd/logd.service.in13
-rw-r--r--logd/logtest.c129
-rw-r--r--lrm/Makefile.am20
-rw-r--r--lrm/admin/Makefile.am40
-rwxr-xr-xlrm/admin/cibsecret.in350
-rw-r--r--lrm/admin/lrmadmin.c1129
-rw-r--r--lrm/admin/lrmadmin.txt60
-rw-r--r--lrm/lrmd/Makefile.am42
-rw-r--r--lrm/lrmd/audit.c191
-rw-r--r--lrm/lrmd/cib_secrets.c205
-rw-r--r--lrm/lrmd/lrmd.c4053
-rw-r--r--lrm/lrmd/lrmd.h282
-rw-r--r--lrm/lrmd/lrmd_fdecl.h111
-rwxr-xr-xlrm/test/LRMBasicSanityCheck.in55
-rw-r--r--lrm/test/Makefile.am48
-rw-r--r--lrm/test/README.regression164
-rw-r--r--lrm/test/apitest.c317
-rw-r--r--lrm/test/apitest.exp122
-rw-r--r--lrm/test/callbacktest.c204
-rw-r--r--lrm/test/defaults9
-rw-r--r--lrm/test/descriptions55
-rwxr-xr-xlrm/test/evaltest.sh171
-rw-r--r--lrm/test/language16
-rw-r--r--lrm/test/lrmadmin-interface43
-rw-r--r--lrm/test/lrmregtest-lsb54
-rw-r--r--lrm/test/lrmregtest.in220
-rw-r--r--lrm/test/plugintest.c84
-rwxr-xr-xlrm/test/regression.sh.in248
-rw-r--r--lrm/test/testcases/BSC4
-rw-r--r--lrm/test/testcases/Makefile.am27
-rw-r--r--lrm/test/testcases/basicset6
-rwxr-xr-xlrm/test/testcases/common.filter27
-rw-r--r--lrm/test/testcases/flood19
-rw-r--r--lrm/test/testcases/flood.exp1354
-rw-r--r--lrm/test/testcases/metadata29
-rw-r--r--lrm/test/testcases/metadata.exp31
-rwxr-xr-xlrm/test/testcases/ra-list.sh12
-rw-r--r--lrm/test/testcases/rscexec48
-rw-r--r--lrm/test/testcases/rscexec.exp117
-rw-r--r--lrm/test/testcases/rscmgmt29
-rw-r--r--lrm/test/testcases/rscmgmt.exp74
-rwxr-xr-xlrm/test/testcases/rscmgmt.log_filter13
-rw-r--r--lrm/test/testcases/serialize33
-rw-r--r--lrm/test/testcases/serialize.exp100
-rw-r--r--lrm/test/testcases/stonith2
-rw-r--r--lrm/test/testcases/stonith.exp2
-rwxr-xr-xlrm/test/testcases/xmllint.sh20
-rw-r--r--replace/Makefile.am29
-rw-r--r--replace/NoSuchFunctionName.c31
-rw-r--r--replace/alphasort.c53
-rw-r--r--replace/daemon.c83
-rw-r--r--replace/inet_pton.c245
-rw-r--r--replace/scandir.c236
-rw-r--r--replace/setenv.c50
-rw-r--r--replace/strerror.c37
-rw-r--r--replace/strlcat.c33
-rw-r--r--replace/strlcpy.c32
-rw-r--r--replace/strndup.c38
-rw-r--r--replace/strnlen.c31
-rw-r--r--replace/unsetenv.c51
-rw-r--r--replace/uuid_parse.c519
300 files changed, 82087 insertions, 0 deletions
diff --git a/.hg_archival.txt b/.hg_archival.txt
new file mode 100644
index 0000000..22a0b4f
--- /dev/null
+++ b/.hg_archival.txt
@@ -0,0 +1,2 @@
+repo: e3ffdd7ae81c596b2be7e1e110d2c1255161340e
+node: 0a7add1d9996b6d869d441da6c82fb7b8abcef4f
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..8ce7ca5
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,92 @@
+syntax: glob
+
+# Autofoo entries
+*.o
+*.la
+*.lo
+*.loT
+*.pyc
+.libs
+.deps
+*.cache
+.cvsignore
+compile
+configure
+configure.status
+configure.lineno
+depcomp
+aclocal.m4
+libtool
+ltmain.sh
+ltconfig
+libltdl
+mkinstalldirs
+install-sh
+missing
+py-compile
+autom4te*
+libtool.m4
+ltdl.m4
+libltdl.tar
+autoconf
+autoheader
+automake
+include/glue_config.h
+include/stamp-h1
+include/pils/plugin.h
+include/stamp-h2
+ylwrap
+
+# BEAM Entries
+*.beam
+parser-messages
+MISC_ERRORS
+cscope.files
+cscope.out
+patches
+updates
+logs
+
+# OS and Editor Artifacts
+.DS_Store
+*.diff
+*.patch
+*~
+
+# Project build targets
+lib/clplumbing/base64_md5_test
+lib/clplumbing/ipctest
+lib/clplumbing/ipctransientclient
+lib/clplumbing/ipctransientserver
+logd/ha_logd
+logd/ha_logger
+logd/logtest
+lrm/admin/lrmadmin
+lrm/lrmd/lrmd
+lrm/test/apitest
+lrm/test/callbacktest
+lrm/test/plugintest
+lrm/test/lrmregtest
+lrm/test/lrmregtest-heartbeat
+lrm/test/lrmregtest-lsb
+lrm/test/regression.sh
+lrm/test/LRMBasicSanityCheck
+lrm/test/simple_ops
+
+# Misc
+GPATH
+GRTAGS
+GSYMS
+GTAGS
+HTML
+TAGS
+.gres.*
+*.orig
+.gdb_history
+
+# Entries better done as regexp's to avoid matching too broadly
+syntax: regexp
+^config\.*
+README$
+Makefile$
+Makefile.in$
diff --git a/.hgsigs b/.hgsigs
new file mode 100644
index 0000000..ddb9f3d
--- /dev/null
+++ b/.hgsigs
@@ -0,0 +1,4 @@
+b6dca003bb176978af803eeb33019b6aef3c58b0 0 iEYEABECAAYFAktnGJAACgkQWnQN9wr0w1ywBACghXYwYkv/70Xg5AQMzVjRWKZecIoAnjRUytRoYl+dhhqbhfdXSD+/Bfvw
+6007185b487e3f2dc3b24674a9105761b2cde6ea 0 iEYEABECAAYFAktoWfsACgkQWnQN9wr0w1ySZwCfQILyC2VJrCnVEU2zvTIyI7ustDAAn37hhb9JM8JQVKLfPEbqIloz1m3m
+979c4ffae287976631a30d10258903aea6fb28fa 0 iEYEABECAAYFAktoY38ACgkQWnQN9wr0w1wHxgCeMZyOt8ccxmIsvIHg4/y6KmqtTVAAn2jn7dOmFMjA8m4ju59YaQ1Bznhb
+798645ead29e20b361af883fce695b85caf3392b 0 iEYEABECAAYFAlPJCM4ACgkQWnQN9wr0w1wv+QCeJQOjaYNXNJZA61n7Fu8f63CeVBEAnja4WqiYC+TS4HvmRJz6oNi6p48u
diff --git a/.hgtags b/.hgtags
new file mode 100644
index 0000000..6e84e98
--- /dev/null
+++ b/.hgtags
@@ -0,0 +1,68 @@
+01ecac8670a6c2e47202a9ce2f5e27e9dcdbeff6 STABLE-1.1.3
+19d11d8d62c270c48a3feab5ed66b18897c9cc8d sle11-rc9
+2185d55c12e37c48abc239dd1f8b3b9ef012fd6b obs-2.1.2-1
+235a71009062702c906cc68f23904ddcbe17535f STABLE-2.0.6
+29540582671a9e33ae2122d319c68258346f1a3f STABLE-2.1.1
+2cb36a1c01c76ef3e3a449f16b13730c761efff2 STABLE-2.1.3
+2ece20ad31a4271076e5c43dd3f2ea25caa55635 Series-Root-1.0
+3b8dc33a402daaf7e3754acadd1898c0fe69072f STABLE-2.0.3
+45c377d7a35dba92d46321d2f824bc0d9b17f54e obs-2.1.2-24
+67a443d135f128ca28f15e4e8999d3e0caabed61 sle11-rc3
+68de68ef5f0a7b97a4ff0d9806c598527c8659b8 STABLE-2.0.4
+705b21e4b623f7d2fc5c83d99beffb709905c996 STABLE-0.4.9c
+7190f69e29a08350bcec753509eb37f53593334a beta-2.99.0
+7f90244e5c25372e70178f77f44c76a8564e1665 SLE10-SP1
+7f90244e5c25372e70178f77f44c76a8564e1665 STABLE-2.1.0
+823208439a98179d7c01d6eed1db50dd96802663 sle11-rc7
+86fa06f08a123868eb272a20bf850cec1805a12f STABLE-2.0.8
+97d025dd33648a1e50a3a1bf40573669440dd1b6 obs-2.1.2-4
+9b34f480b8e8966e9ed4276507cc562564763720 beta-2.99.2
+9eb2a4db4ff595d18302426029b03153fad77ef7 beta-2.99.1
+a230062a445096b89cf75bef85e285ee55626a78 obs-2.1.2-2
+af867b71bcc645f3d3c56fe8fdd883b17a851e46 STABLE-2.0.0
+b4a0a0ffd15eb2dd1285bdbd86ea9716a9d0bf36 STABLE-2.0.5
+b906db882c37647abfd21fa1473950445ad7813c STABLE-2.1.2
+ba476a3948ea0cf52098fa050a27a8856a214825 sle11-rc2
+be0d49da51a810e870356b7f2a52013e5c775c0d Beta-0.4.9a
+c77ad4549888539e7fc9a6b56cccdb1403749198 STABLE-0.4.9e
+c7d672b9f3ece79ad26fb8a7df20265bcb596515 sle11-beta6
+cf0265eed1b5b3b3f25f7e56eb807d21ca261d68 SLES10-GA-2.0.7-1.5
+cf0265eed1b5b3b3f25f7e56eb807d21ca261d68 STABLE-2.0.7
+d1899e1eecc09b7a6e66a02609408272bb856c6a STABLE-1.1.5
+dae6b0b3e109afc5df29a7127ab6dd9e1bd0a20a sle11-rc5
+e3691501a2d0631c3796b6a728fadf7d90691203 obs-2.1.2-15
+e3855af19554339204b5b2b2a199a7bc31e22843 STABLE-2.0.1
+e3855af19554339204b5b2b2a199a7bc31e22843 STABLE-2.0.2
+e6637f62c87ada212a83942ec5b2a4bf30b98c3f Series-Root-1.2
+f6c2cd2593f365f984ce051db61466738ac05dcd Beta-0.4.9f
+940fa13f6a0a929d15a01af9a0b62c16e4d2706a glue-1.0
+130b1d7af88912d077d32a7c386c3c94d0b2da16 glue-1.0.2-rc1
+78894a112c0a134dc709d2a8772085180444c40c glue-1.0.2-rc2
+7700902a4de3ee84fa2007a4b4602693c5ac26a8 glue-1.0.2-rc2a
+97fcdf789e174b0a0b23e28dcabe2f7d579d426f glue-1.0.2
+0a64e6f77894da1364b17dc3c73b65561717f4aa glue-1.0.3
+0a64e6f77894da1364b17dc3c73b65561717f4aa glue-1.0.3
+0000000000000000000000000000000000000000 glue-1.0.3
+0000000000000000000000000000000000000000 glue-1.0.3
+979c4ffae287976631a30d10258903aea6fb28fa glue-1.0.3
+979c4ffae287976631a30d10258903aea6fb28fa glue-1.0.3
+0000000000000000000000000000000000000000 glue-1.0.3
+0000000000000000000000000000000000000000 glue-1.0.3
+9bcd134f1ebff7baf80f4b21c3b5f620b0ee976e glue-1.0.3
+9bcd134f1ebff7baf80f4b21c3b5f620b0ee976e glue-1.0.3
+0000000000000000000000000000000000000000 glue-1.0.3
+0000000000000000000000000000000000000000 glue-1.0.3
+2e33ecd820b2673755d1280a259489a026921f63 glue-1.0.3
+761edff8c35ea2cdf3e1bd37d600b06233e61d4f glue-1.0.4-rc1
+3229873980e1028bf05de81f5bafccb3a92b9aa4 glue-1.0.4
+3af80b93d9e5d5e441f3f4c3aad16775ea27d2d9 glue-1.0.5
+1c87a0c58c59fc384b93ec11476cefdbb6ddc1e1 glue-1.0.6
+61200fbe18358e420cdc2037d87e803e150c1eac glue-1.0.7-rc1
+5e06b2ddd24b37ad6c1c25d958d7a9dda7d02f93 glue-1.0.7
+5740338816e1ff07d0e37f36214f442e183984d7 glue-1.0.8-rc1
+c69dc6ace936f501776df92dab3d611c2405f69e glue-1.0.8
+0a08a469fdc8a0db1875369497bc83c0523ceb21 glue-1.0.9
+12055ca2b025ab250a544701edaa1f5aaf63aef1 glue-1.0.10
+02bdcf58f9a098b717784746308e199e12eeb005 glue-1.0.11
+c64d6e96f20ad5ba245f7fb9e1295b14fa179e29 glue-1.0.12-rc1
+d05229decc34d66c4752536dc7c9d812d1e6d5ca glue-1.0.12
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..ec67fd0
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,19 @@
+Alan Robertson <alanr@unix.sh>
+Andreas Mock <andreas.mock@web.de>
+Andrew Beekhof <andrew@beekhof.net>
+Dave Blaschke <debltc@us.ibm.com>
+David Lee <t.d.lee@durham.ac.uk>
+Dejan Muhamedagic <dejan@hello-penguin.com>
+Hannes Eder <heder@google.com>
+Huang Zhen <zhenhltc@cn.ibm.com>
+Junko Ikeda <ikedaj@intellilink.co.jp>
+Lars Marowsky-Bree <lmb@suse.de>
+Martin Bene <martin.bene@icomedias.com>
+Phil Carns <carns@mcs.anl.gov>
+Satomi Taniguchi <taniguchis@intellilink.co.jp>
+Sean Reifschneider <jafo@tummy.com>
+Sebastian Reitenbach <itlistuser@rapideye.de>
+Serge Dubrouski <sergeyfd@gmail.com>
+Simon Horman <horms@verge.net.au>
+Xinwei Hu <hxinwei@gmail.com>
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..602bfc9
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..4366ee3
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,262 @@
+* Fri Jul 18 2014 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- stable release 1.0.12
+- hb_report: add -Q option for quick runs
+- hb_report: dot is not illegal in file names (bnc#884079, deb#715391)
+- build: update spec files for systemd
+- hb_report: update interface to zypper
+- hb_report: support logs with varied timestamps
+- stonith: external/vcenter: add parameter for SSL hostname
+ verification (bnc#851990)
+- hb_report: fix ssh passwords again (bnc#867365)
+- hb_report: Don't use deprecated corosync-fplay (bnc#870913)
+- logd: Add systemd unit file for logd (bnc#863248)
+- hb_report: Add support for xz compression (bnc#854060)
+
+* Thu Sep 26 2013 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- release candidate 1.0.12-rc1
+- clplumbing: increase listen backlog to 128
+- hb_report: add -X option for extra ssh options
+- hb_report: add support for the rfc5424 syslog date format
+- stonith: external/libvirt: fix exit code in reset
+- stonith: external/vcenter: do not list vms in status
+ (bnc#825765)
+- stonith: fix memory leak in external.c
+- hb_report: enable ssh to prompt for passwords (bnc#808373)
+- hb_report: collect RA trace files
+- hb_report: look for nodes in the archived CIB if pacemaker is
+ not running
+- sbd plugin now lives at http://hg.linux-ha.org/sbd/
+- stonith: if debug's not set, do not send debug messages to the
+ logger (bnc#792124)
+- stonith: log status message at the debug level (bnc#792124)
+- stonith: don't always log debug level messages (bnc#792124)
+- stonith: external/vcenter: fix gethosts to produce list of
+ nodes not vms (bnc#792704)
+
+* Mon Oct 15 2012 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- stable release 1.0.11
+- lrmd: set max-children depending on the number of processors
+- lrmd: don't send parameters from ops back to crmd
+- stonith: external/libvirt: support for reboot reset method
+- hb_report: node's type got optional
+- hb_report: make use of bash trace features
+- hb_report: compatibility code for pacemaker v1.1.8
+- build: link libstonith with stonith2 agents
+
+* Mon Jul 16 2012 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- stable release 1.0.10
+- clplumbing: ipc: fix message size checks (bnc#752231)
+- clplumbing: load bz2 compression module by default
+- clplumbing: cl_msg: try compressing message before rejecting it
+ as too big
+- clplumbing: cl_msg: don't use traditional compression by default
+- clplumbing: cl_msg: increase compression threshold
+- clplumbing: fix memleak for Gmain_timeout
+- LRM: lrmd: add basic authentication (lf#2547)
+- LRM: lrmd: use the resource timeout as an override to the
+ default dbus timeout for upstart RA
+- LRM: lrmd: if set, get max-children from the LRMD_MAX_CHILDREN
+ environment var
+- stonith: add CRM stonith resource name to log messages (bnc#728579)
+- stonith: adjust timeouts in the meta-data template (bnc#733337)
+- stonith: external/vcenter: return list of configured hosts on
+ gethosts
+- stonith: external/libvirt: add more search strings for domain
+ start and stop
+- stonith: rhcs: pass the action via stdin too
+- stonith: rhcs: avoid false error if parameter isn't set
+- logd: remove runlevel 4 from the LSB info section in the logd
+ init script (bnc#744120)
+- logd: add try-restart action to the logd init script
+- sbd: Use async IO for disk reads to increase resilience against
+ hung IO (bnc#738295)
+- sbd: Handle IO errors during slot allocation properly (bnc#753559)
+- sbd: Debug mode added (bnc#753559, bnc#738295)
+- hb_report: improve performance
+- hb_report: get corosync blackbox records if available
+- hb_report: add node time information
+
+* Mon Nov 28 2011 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- stable release 1.0.9
+- stonith: external/ipmi: add missing double quote
+- stonith: external/ipmi: add the priv parameter (ipmitool -L)
+- LRM: lrmd: set op status to cancelled for running monitor operations
+- ha_log: increase MAXENTITY size to accommodate long stonith strings
+- hb_report: improve destination directory handling (bnc#727295)
+* Tue Oct 18 2011 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- stable release 1.0.8
+- cl_log: log spamming control
+- LRM: raexecocf: list resource agents properly (bnc#664409)
+- LRM: lrmd: allow storing parameters in local files (lf#2415)
+- LRM: lrmd: limit number of "stayed too long in operation list"
+ log messages (bnc#636576)
+- stonith: external/libvirt: new plugin for libvirt virtualization technologies
+- stonith: external/vcenter: new plugin
+- stonith: external/hetzner: new plugin
+- stonith: sbd: support for multiple devices
+- stonith: sbd: Fix timeout setting on archs where int != long (bnc#635690)
+- stonith: sbd: abort start if watchdog cannot be initialized (bnc#680109)
+- stonith: sbd: Make failing to set the watchdog timeout non-fatal but annoying
+- stonith: sbd: Make the restart interval for servants configurable
+- stonith: sbd: Maximize scheduler and IO priority in the child processes (bnc#702907)
+- stonith: external/sbd: Fix ordering of arguments in reset
+- stonith: external/ipmi: fix unique parameters' attributes
+- stonith: external/rackpdu: split off assignment from local to
+ make it work with non-bash shells
+- stonith: external: avoid false error if parameter isn't set (bnc#646205)
+- hb_report: add .info files with the last byte pos for all logs
+- hb_report: use sudo for remove collectors if connecting with
+ user other than root
+- hb_report: install debuginfo packages on platforms with zypper (bnc#641979)
+- hb_report: improve detecting ssh user
+
+* Tue Nov 30 2010 Dejan Muhamedagic <dejan@suse.de>, Lars Ellenberg <lars.ellenberg@linbit.com>, and many others
+- stable release 1.0.7
+- clplumbing: ipc: adjust socket buffers size when adjusting ipc queue length
+- logd: add a SIGHUP signal handler to timely close/open log files
+- logd: use buffered io with fflush and fsync
+- logd: reopen logfiles on inode change (logrotate)
+- clplumbing: cl_log: keep logfiles open, but default to non-buffered io (lf#2470)
+- clplumbing: cl_log: add new optional common syslog message prefix
+- stonith: use ST_DEVICEID for the short description in meta-data
+- stonith: external: interpret properly exit codes from external stonith
+ plugins (bnc#630357)
+- stonith: external: avoid false out of memory error if a parameter isn't set (bnc#646205)
+- stonith: external: check if PATH already contains GLUE_SHARED_DIR
+ (memory leak, lf#2484)
+- stonith(8): reduce the number of stonith plugin invocations (bnc#630357)
+- stonith(8): use cl_log for logging if invoked by stonithd (pcmk 1.1)
+- stonith: external/sbd: make sbd use realtime priority for IO (works only with CFQ)
+- stonith: cyclades: add the serial_port parameter to the meta-data
+- stonith: external/riloe: add support for http proxies
+- stonith: external/ipmi: provide opt param "passwd_method" to hide
+ the ipmi password from config and logs
+- stonith: external/nut: support for the Network UPS Tools
+- stonith: external/rackpdu: remove displaced local command
+- stonith: rcd_serial: rename dtr|rts parameter to dtr_rts
+- configure: test for POSIX signals (fixes rcd_serial)
+
+* Fri Jul 9 2010 Dejan Muhamedagic <dejan@suse.de>
+- stable release 1.0.6
+- clplumbing: Add identity info of the user on the other side of socket
+- ha_logger: log strings longer than 1024
+- lrmd: remove operation history on client unregister (lf#2161)
+- lrmd: don't allow cancelled operations to get back to the repeating op list (lf#2417)
+- lrmd: exclude stonith resources from child count (bnc#612387)
+- lrmd,clientlib: asynchronous resource delete notification (lf#2439)
+- stonith: add -V (version) to stonith
+- stonith: add -E option to get the configuration from the environment
+- stonith: ha_log: feed the message to stdout and not on command line
+- stonith: external/sbd,xen0: fix wrong reference from ha_log to ha_log.sh (deb#585120)
+- stonith: external/sbd: reduce monitoring
+- stonith: external/rackpdu: check the snmpset and snmpwalk exit codes
+- hb_report: create cib.txt after sanitizing the CIB (lf#2415)
+
+* Mon Apr 15 2010 Dejan Muhamedagic <dejan@suse.de>
+- stable release 1.0.5
+- clplumbing: revert changeset 81ad41d14f72 which breaks the ABI
+
+* Mon Apr 12 2010 Dejan Muhamedagic <dejan@suse.de>
+- stable release 1.0.4
+- clplumbing: fix memory leak in cl_msg/lrmd (lf#1841,2389)
+- clplumbing: Add identity info of the user on the other side of socket
+- clplumbing: Fix erroneous "Stack hogger failed 0xffffffff" warnings
+- lrmd: fix possible null pointer dereference
+- lrmd: raise severity from debug to info for some log messages
+- lrmd: on shutdown exit once all operations finished (lf#2340)
+- lrmd: don't add the cancel option in flush to the running operations (bnc#578644)
+- lrmd: check if tables exist before free_str_table and prevent
+ segfault (bnc#587887)
+- stonith: new external/ippower9258 plugin
+- stonith: external/sbd: fix status operation
+- stonith: external/sbd: add support for heartbeat
+- stonith: external/ibmrsa-telnet: fix ha_log.sh invocation
+- stonith: external/ibmrsa-telnet: fix expect regex
+- stonith: external/ipmi: make reset work when the node is off
+- stonith: external/riloe: log error message on unrecognized power method
+- hb_report: don't create dot files if there are more than 20 PE files
+- hb_report: make dot and png files for PE inputs (if there are
+ not too many)
+- hb_report: do not filter CIB/PE files by default (use -s to
+ force filtering)
+- hb_report: add -Z option to force destination directory cleanup
+- hb_report: allow for default destination
+- hb_report: when creating cts reports get information from the log
+- hb_report: new option -d to keep the directory
+- hb_report: don't give up early when creating backtraces (lf#2350)
+
+* Tue Feb 02 2010 Dejan Muhamedagic <dejan@suse.de>
+- bugfix release 1.0.3
+- lrmd: don't flush operations which don't belong to the requesting client (lf#2161)
+
+* Mon Feb 01 2010 Dejan Muhamedagic <dejan@suse.de> and MANY others
+- stable release 1.0.2
+- clplumbing: fix a potential resource leak in cl_random (bnc#525393)
+- clplumbing: change the default log format to syslog format
+- lrmd: log outcome of monitor once an hour
+- lrmd: lookup clients by name (lf#2161)
+- lrmd: remove operation history on client unregister (lf#2161)
+- lrmd: fix return code on LSB class RA exec failure (lf#2194)
+- lrmd: close the logd fd too when executing agents (lf#2267)
+- lrmd: restore reset scheduler for children (bnc#551971,lf#2296)
+- lrmd: reset scheduler and priority for children (resource operations)
+- lrmadmin: fix -E option
+- lrmadmin moved to the sbindir
+- stonith: support for RHCS fence agents
+- stonith: external/dracmc-telnet: stonith plugin for Dell
+ Drac/MC Blade Enclosure and Cyclades terminal server
+- stonith: sbd plugin
+- stonith: apcmastersnmp plugin (bnc#518689)
+- stonith: bladehpi plugin (bnc#510299)
+- stonith: WTS MPC: new SNMP based plugin
+- stonith: meatclient: add -w option to wait until we can connect
+- stonith: add -m option to stonith(8) to display metadata (lf#2279)
+- stonith: external: log using ha_log.sh (lf#2294,1971)
+- stonith: external: log output of plugins (bnc#548699,bnc#553340)
+- stonith: external: log messages immediately on manage and status calls
+- stonith: external: remove dependency on .ocf-shellfuncs (lf#2249)
+- stonith: external/riloe: make sure that host is turned on after power
+ off/on reset (lf#2282)
+- stonith: external/riloe: fix check for ilo_can_reset
+- stonith: external/riloe: workaround for the iLO double close in RIBCL (bnc#553340)
+- stonith: external/ipmi: add explanation on reset and power off (LF 2071)
+- stonith: external/ibmrsa-telnet: add support for later RSA cards
+- stonith: cyclades: fix for support for newer PM10 firmware (lf#1938)
+- stonith: wti_nps: add support for internet power switch model (bnc#539912)
+- stonith: wti_mpc: support for MIB versions 1 and 3
+- stonith: external/sbd: fix definition of sector_size for s390x (bnc#542827)
+- stonith: external/sbd: make nodename comparison case insensitive (bnc#534445)
+- stonith: external/sbd: describe "dump" command in help (bnc#529575)
+- stonith: external/sbd: Accept -h (bnc#529574)
+- stonith: external/xen0: add run_dump parameter to dump core before resetting a node
+- hb_report: add man page hb_report.8
+- hb_report: add -V (version) option
+- hb_report: add support for corosync
+- hb_report: add -v option (debugging)
+- hb_report: options -C and -D are obsoleted
+- hb_report: combine log/events if there is no loghost
+- hb_report: extract important events from the logs
+- logd: add init script
+- rpm spec: start logd by default
+- doc: new README for wti_mpc
+- doc: move stonith README files to the doc directory
+- doc: convert man pages to xml
+- build: /usr/share/heartbeat replaced by /usr/share/cluster-glue
+- build: enable IPMI and hpi support
+- build: include time.h in ipcsocket.c and proctrack.c (lf#2263)
+- build: output documentation directory from configure (lf#2276)
+
+* Thu Oct 23 2008 Lars Marowsky-Bree <lmb@suse.de> and MANY others
+- beta release 2.99.2
+- stonith: external/kdumpcheck: new plugin
+- stonith: external/drac5: new plugin
+- stonith: drac3: initialize curl properly and workaround xml parsing problem (lf#1730)
+- stonith external/riloe: a new implementation for HP iLO devices
+
+* Tue Sep 23 2008 Lars Marowsky-Bree <lmb@suse.de> and MANY others
+- beta release 2.99.1
+- stonith: bladehpi: fix a mix of a threaded library and not threaded stonithd (bnc#389344)
+- stonith: external/riloe: fix check for ilo_can_reset
+
+* Tue Aug 19 2008 Andrew Beekhof <abeekhof@suse.de> and MANY others
+- beta release 2.99.0
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..a641d9c
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+-include Makefile
+
+PACKAGE ?= cluster-glue
+TARFILE = $(PACKAGE).tar.bz2
+
+RPM_ROOT = $(shell pwd)
+RPM_OPTS = --define "_sourcedir $(RPM_ROOT)" \
+ --define "_specdir $(RPM_ROOT)" \
+ --define "_srcrpmdir $(RPM_ROOT)"
+
+
+getdistro = $(shell test -e /etc/SuSE-release || echo fedora; test -e /etc/SuSE-release && echo suse)
+DISTRO ?= $(call getdistro)
+TAG ?= tip
+
+hgarchive:
+ rm -f $(TARFILE)
+ hg archive -t tbz2 -r $(TAG) $(TARFILE)
+ echo `date`: Rebuilt $(TARFILE)
+
+srpm: hgarchive
+ rm -f *.src.rpm
+ @echo To create custom builds, edit the flags and options in $(PACKAGE)-$(DISTRO).spec first
+ rpmbuild -bs --define "dist .$(DISTRO)" $(RPM_OPTS) $(PACKAGE)-$(DISTRO).spec
+
+rpm: srpm
+ rpmbuild $(RPM_OPTS) --rebuild $(RPM_ROOT)/*.src.rpm
+
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..93dbaf6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+DISTCHECK_CONFIGURE_FLAGS = \
+ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
+
+MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure DRF/config-h.in \
+ DRF/stamp-h.in libtool.m4 ltdl.m4 libltdl.tar
+
+SUBDIRS = include $(LIBLTDL_DIR) replace lib lrm logd \
+ hb_report doc config
+
+install-exec-local:
+ $(INSTALL) -d $(DESTDIR)/$(HA_COREDIR)
+ -$(INSTALL) -d -m 700 -o root $(DESTDIR)/$(HA_COREDIR)/root
+ -$(INSTALL) -d -m 700 -o nobody $(DESTDIR)/$(HA_COREDIR)/nobody
+ $(INSTALL) -d -m 700 $(DESTDIR)/$(HA_COREDIR)/$(GLUE_DAEMON_USER)
+ -chown $(GLUE_DAEMON_USER) $(DESTDIR)/$(HA_COREDIR)/$(GLUE_DAEMON_USER)
+# Use chown because $(GLUE_DAEMON_USER) may not exist yet
+
+dist-clean-local:
+ rm -f autoconf automake autoheader $(TARFILE)
+
+.PHONY:
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+
diff --git a/README b/README
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..7464c9d
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,193 @@
+#!/bin/sh
+#
+# License: GNU General Public License (GPL)
+# Copyright 2001 horms <horms@vergenet.net>
+# (heavily mangled by alanr)
+#
+# bootstrap: set up the project and get it ready to make
+#
+# Basically, we run autoconf, automake and libtool in the
+# right way to get things set up for this environment.
+#
+# We also look and see if those tools are installed, and
+# tell you where to get them if they're not.
+#
+# Our goal is to not require dragging along anything
+# more than we need. If this doesn't work on your system,
+# (i.e., your /bin/sh is broken) send us a patch.
+#
+# This code loosely based on the corresponding named script in
+# enlightenment, and also on the sort-of-standard autoconf
+# bootstrap script.
+
+# Run this to generate all the initial makefiles, etc.
+
+testProgram()
+{
+ cmd=$1
+
+ if [ -z "$cmd" ]; then
+ return 1;
+ fi
+
+ arch=`uname -s`
+
+ # Make sure the which is in an if-block... on some platforms it throws exceptions
+ #
+ # The ERR trap is not executed if the failed command is part
+ # of an until or while loop, part of an if statement, part of a &&
+ # or || list.
+ if
+ which $cmd </dev/null >/dev/null 2>&1
+ then
+ :
+ else
+ return 1
+ fi
+
+ # The GNU standard is --version
+ if
+ $cmd --version </dev/null >/dev/null 2>&1
+ then
+ return 0
+ fi
+
+ # Maybe it suppports -V instead
+ if
+ $cmd -V </dev/null >/dev/null 2>&1
+ then
+ return 0
+ fi
+
+ # Nope, the program seems broken
+ return 1
+}
+
+gnu="ftp://ftp.gnu.org/pub/gnu"
+
+for command in autoconf213 autoconf253 autoconf259 autoconf
+do
+ if
+ testProgram $command == 1
+ then
+ autoconf=$command
+ autoheader=`echo "$autoconf" | sed -e 's/autoconf/autoheader/'`
+ autom4te=`echo "$autoconf" | sed -e 's/autoconf/autmo4te/'`
+ autoreconf=`echo "$autoconf" | sed -e 's/autoconf/autoreconf/'`
+ autoscan=`echo "$autoconf" | sed -e 's/autoconf/autoscan/'`
+ autoupdate=`echo "$autoconf" | sed -e 's/autoconf/autoupdate/'`
+ ifnames=`echo "$autoconf" | sed -e 's/autoconf/ifnames/'`
+ fi
+done
+
+for command in automake14 automake-1.4 automake15 automake-1.5 automake17 automake-1.7 automake19 automake-1.9 automake-1.11 automake
+do
+ if
+ testProgram $command
+ then
+ : OK $pkg is installed
+ automake=$command
+ aclocal=`echo "$automake" | sed -e 's/automake/aclocal/'`
+ fi
+done
+
+for command in libtool14 libtool15 libtool glibtool
+do
+ URL=$gnu/$pkg/
+ if
+ testProgram $command
+ then
+ libtool=$command
+ libtoolize=`echo "$libtool" | sed -e 's/libtool/libtoolize/'`
+ fi
+done
+
+if [ -z $autoconf ]; then
+ echo You must have autoconf installed to compile the cluster-glue package.
+ echo Download the appropriate package for your system,
+ echo or get the source tarball at: $gnu/autoconf/
+ exit 1
+
+elif [ -z $automake ]; then
+ echo You must have automake installed to compile the cluster-glue package.
+ echo Download the appropriate package for your system,
+ echo or get the source tarball at: $gnu/automake/
+ exit 1
+
+elif [ -z $libtool ]; then
+ echo You must have libtool installed to compile the cluster-glue package.
+ echo Download the appropriate package for your system,
+ echo or get the source tarball at: $gnu/libtool/
+ exit 1
+fi
+
+oneline() {
+ read x; echo "$x"
+}
+
+LT_version=`$libtool --version | oneline | sed -e 's%^[^0-9]*%%' -e s'% .*%%'`
+LT_majvers=`echo "$LT_version" | sed -e 's%\..*%%'`
+LT_minvers=`echo "$LT_version" | sed -e 's%^[^.]*\.%%' `
+LT_minnum=`echo "$LT_minvers" | sed -e 's%[^0-9].*%%'`
+
+if
+ [ $LT_majvers -lt 1 ] || [ $LT_majvers = 1 -a $LT_minnum -lt 4 ]
+then
+ echo "Minimum version of libtool is 1.4. You have $LT_version installed."
+ exit 1
+fi
+
+# Create local copies so that the incremental updates will work.
+rm -f ./autoconf ./automake ./autoheader ./libtool
+ln -s `which $libtool` ./libtool
+ln -s `which $autoconf` ./autoconf
+ln -s `which $automake` ./automake
+ln -s `which $autoheader` ./autoheader
+
+printf "$autoconf:\t"
+$autoconf --version | head -n 1
+
+printf "$automake:\t"
+$automake --version | head -n 1
+
+rm -rf libltdl libltdl.tar
+echo $libtoolize --ltdl --force --copy
+# Unset GREP_OPTIONS as any coloring can mess up the AC_CONFIG_AUX_DIR matching patterns
+GREP_OPTIONS= $libtoolize --ltdl --force --copy
+
+arch=`uname -s`
+# Disable the errors on FreeBSD until a fix can be found.
+if [ ! "$arch" = "FreeBSD" ]; then
+set -e
+#
+# All errors are fatal from here on out...
+# The shell will complain and exit on any "uncaught" error code.
+#
+#
+# And the trap will ensure sure some kind of error message comes out.
+#
+trap 'echo ""; echo "$0 exiting due to error (sorry!)." >&2' 0
+fi
+
+# Emulate the old --ltdl-tar option...
+# If the libltdl directory is required we will unpack it later
+tar -cf libltdl.tar libltdl
+rm -rf libltdl
+
+echo $aclocal $ACLOCAL_FLAGS
+$aclocal $ACLOCAL_FLAGS
+
+echo $autoheader
+$autoheader
+
+echo $automake --add-missing --include-deps --copy
+$automake --add-missing --include-deps --copy
+
+echo $autoconf
+$autoconf
+
+test -f libtool.m4 || touch libtool.m4
+test -f ltdl.m4 || touch ltdl.m4
+
+echo Now run ./configure
+trap '' 0
diff --git a/cluster-glue-fedora.spec b/cluster-glue-fedora.spec
new file mode 100644
index 0000000..b480ff5
--- /dev/null
+++ b/cluster-glue-fedora.spec
@@ -0,0 +1,249 @@
+# Keep around for when/if required
+## define alphatag XXX
+
+%define gname haclient
+%define uname hacluster
+%define nogroup nobody
+
+# Directory where we install documentation
+%global glue_docdir %{_defaultdocdir}/%{name}-%{version}
+
+# When downloading directly from Mercurial, it will automatically add this prefix
+# Invoking 'hg archive' wont but you can add one with: hg archive -t tgz -p "Reusable-Cluster-Components-" -r $upstreamversion $upstreamversion.tar.gz
+%global upstreamprefix Reusable-Cluster-Components-
+%global upstreamversion d97b9dea436e
+
+Name: cluster-glue
+Summary: Reusable cluster components
+Version: 1.0.12
+Release: 1%{?dist}
+License: GPLv2+ and LGPLv2+
+Url: http://www.linux-ha.org/wiki/Cluster_Glue
+Group: System Environment/Base
+Source0: cluster-glue.tar.bz2
+Requires: perl-TimeDate
+Requires: cluster-glue-libs = %{version}-%{release}
+
+# Directives to allow upgrade from combined heartbeat packages in Fedora11
+Provides: heartbeat-stonith = 3.0.0-1
+Provides: heartbeat-pils = 3.0.0-1
+Obsoletes: heartbeat-stonith < 3.0.0-1
+Obsoletes: heartbeat-pils < 3.0.0-1
+Obsoletes: heartbeat-common
+
+## Setup/build bits
+
+BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+
+# Build dependencies
+BuildRequires: automake autoconf libtool pkgconfig which
+BuildRequires: bzip2-devel glib2-devel python-devel libxml2-devel libaio-devel
+BuildRequires: OpenIPMI-devel openssl-devel
+BuildRequires: libxslt docbook-dtds docbook-style-xsl
+BuildRequires: help2man
+BuildRequires: asciidoc
+
+%if 0%{?fedora}
+BuildRequires: libcurl-devel libnet-devel
+%endif
+
+%if 0%{?fedora} || 0%{?centos} > 4 || 0%{?rhel} > 4
+BuildRequires: libtool-ltdl-devel openhpi-devel
+BuildRequires: net-snmp-devel >= 5.4
+%else
+BuildRequires: gcc-c++
+%endif
+
+%if 0%{?fedora} > 11 || 0%{?centos} > 5 || 0%{?rhel} > 5
+BuildRequires: libuuid-devel
+%else
+BuildRequires: e2fsprogs-devel
+%endif
+
+%if %{defined systemd_requires}
+BuildRequires: systemd
+%{?systemd_requires}
+%endif
+
+%prep
+%setup -q -n cluster-glue
+
+./autogen.sh
+
+# RHEL <= 5 does not support ./configure --docdir=,
+# hence, use this ugly hack
+%if 0%{?centos} <= 5 || 0%{?rhel} <= 5
+export docdir=%{glue_docdir}
+%configure \
+ --enable-fatal-warnings=yes \
+ --with-daemon-group=%{gname} \
+ --with-daemon-user=%{uname} \
+ --localstatedir=%{_var} \
+ --libdir=%{_libdir}
+%else
+%configure \
+ --enable-fatal-warnings=yes \
+ --with-daemon-group=%{gname} \
+ --with-daemon-user=%{uname} \
+ --localstatedir=%{_var} \
+ --libdir=%{_libdir} \
+%if %{defined _unitdir}
+ --with-systemdsystemunitdir=%{_unitdir} \
+%endif
+ --docdir=%{glue_docdir}
+%endif
+
+%build
+make %{?jobs:-j%jobs} docdir=%{glue_docdir}
+
+%install
+rm -rf %{buildroot}
+make install DESTDIR=%{buildroot} docdir=%{glue_docdir}
+
+
+## tree fix up
+# Dont package static libs
+find %{buildroot} -name '*.a' -exec rm {} \;
+find %{buildroot} -name '*.la' -exec rm {} \;
+
+%clean
+rm -rf %{buildroot}
+
+# cluster-glue
+
+%description
+A collection of common tools that are useful for writing cluster managers
+such as Pacemaker.
+Provides a local resource manager that understands the OCF and LSB
+standards, and an interface to common STONITH devices.
+
+%files
+%defattr(-,root,root)
+%dir %{_datadir}/%{name}
+%if %{defined _unitdir}
+%{_unitdir}/logd.service
+%else
+%{_sysconfdir}/init.d/logd
+%endif
+%{_datadir}/%{name}/ha_cf_support.sh
+%{_datadir}/%{name}/openais_conf_support.sh
+%{_datadir}/%{name}/utillib.sh
+%{_datadir}/%{name}/ha_log.sh
+
+%{_sbindir}/ha_logger
+%{_sbindir}/hb_report
+%{_sbindir}/lrmadmin
+%{_sbindir}/cibsecret
+%{_sbindir}/meatclient
+%{_sbindir}/stonith
+%dir %{_libdir}/heartbeat
+%dir %{_libdir}/heartbeat/plugins
+%dir %{_libdir}/heartbeat/plugins/RAExec
+%dir %{_libdir}/heartbeat/plugins/InterfaceMgr
+%dir %{_libdir}/heartbeat/plugins/compress
+%{_libdir}/heartbeat/lrmd
+%{_libdir}/heartbeat/ha_logd
+%{_libdir}/heartbeat/plugins/RAExec/*.so
+%{_libdir}/heartbeat/plugins/InterfaceMgr/*.so
+%{_libdir}/heartbeat/plugins/compress/*.so
+%dir %{_libdir}/stonith
+%dir %{_libdir}/stonith/plugins
+%dir %{_libdir}/stonith/plugins/stonith2
+%{_libdir}/stonith/plugins/external
+%{_libdir}/stonith/plugins/stonith2/*.so
+%{_libdir}/stonith/plugins/stonith2/*.py*
+%exclude %{_libdir}/stonith/plugins/external/ssh
+%exclude %{_libdir}/stonith/plugins/stonith2/null.so
+%exclude %{_libdir}/stonith/plugins/stonith2/ssh.so
+%{_libdir}/stonith/plugins/xen0-ha-dom0-stonith-helper
+%dir %{_var}/lib/heartbeat
+%dir %{_var}/lib/heartbeat/cores
+%dir %attr (0700, root, root) %{_var}/lib/heartbeat/cores/root
+%dir %attr (0700, nobody, %{nogroup}) %{_var}/lib/heartbeat/cores/nobody
+%dir %attr (0700, %{uname}, %{gname}) %{_var}/lib/heartbeat/cores/%{uname}
+%{_mandir}/man1/*
+%{_mandir}/man8/*
+%doc doc/stonith/README*
+%doc logd/logd.cf
+%doc AUTHORS
+%doc COPYING
+%doc ChangeLog
+
+# cluster-glue-libs
+
+%package -n cluster-glue-libs
+Summary: Reusable cluster libraries
+Group: Development/Libraries
+Obsoletes: libheartbeat2
+
+%description -n cluster-glue-libs
+A collection of libraries that are useful for writing cluster managers
+such as Pacemaker.
+
+%pre
+getent group %{gname} >/dev/null || groupadd -r %{gname}
+getent passwd %{uname} >/dev/null || \
+useradd -r -g %{gname} -d %{_var}/lib/heartbeat/cores/hacluster -s /sbin/nologin \
+-c "cluster user" %{uname}
+exit 0
+
+%if %{defined _unitdir}
+%post
+%systemd_post logd.service
+
+%preun
+%systemd_preun logd.service
+
+%postun
+%systemd_postun_with_restart logd.service
+%endif
+
+%post -n cluster-glue-libs -p /sbin/ldconfig
+
+%postun -n cluster-glue-libs -p /sbin/ldconfig
+
+%files -n cluster-glue-libs
+%defattr(-,root,root)
+%{_libdir}/lib*.so.*
+%doc AUTHORS
+%doc COPYING.LIB
+
+# cluster-glue-libs-devel
+
+%package -n cluster-glue-libs-devel
+Summary: Headers and libraries for writing cluster managers
+Group: Development/Libraries
+Requires: cluster-glue-libs = %{version}-%{release}
+Obsoletes: libheartbeat-devel
+
+%description -n cluster-glue-libs-devel
+Headers and shared libraries for a useful for writing cluster managers
+such as Pacemaker.
+
+%files -n cluster-glue-libs-devel
+%defattr(-,root,root)
+%dir %{_libdir}/heartbeat/plugins
+%dir %{_libdir}/heartbeat/plugins/test
+%dir %{_libdir}/heartbeat
+%dir %{_datadir}/%{name}
+%{_libdir}/lib*.so
+%{_libdir}/heartbeat/ipctest
+%{_libdir}/heartbeat/ipctransientclient
+%{_libdir}/heartbeat/ipctransientserver
+%{_libdir}/heartbeat/transient-test.sh
+%{_libdir}/heartbeat/base64_md5_test
+%{_libdir}/heartbeat/logtest
+%{_includedir}/clplumbing
+%{_includedir}/heartbeat
+%{_includedir}/stonith
+%{_includedir}/pils
+%{_datadir}/%{name}/lrmtest
+%{_libdir}/heartbeat/plugins/test/test.so
+%{_libdir}/stonith/plugins/external/ssh
+%{_libdir}/stonith/plugins/stonith2/null.so
+%{_libdir}/stonith/plugins/stonith2/ssh.so
+%doc AUTHORS
+%doc COPYING
+%doc COPYING.LIB
+
+%changelog
diff --git a/cluster-glue-suse.spec b/cluster-glue-suse.spec
new file mode 100644
index 0000000..e2ca7c7
--- /dev/null
+++ b/cluster-glue-suse.spec
@@ -0,0 +1,307 @@
+#
+# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany.
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+
+# norootforbuild
+
+#
+# Since this spec file supports multiple distributions, ensure we
+# use the correct group for each.
+#
+
+%define uid 90
+%define gname haclient
+%define uname hacluster
+
+# Directory where we install documentation
+%global glue_docdir %{_defaultdocdir}/%{name}
+
+Name: cluster-glue
+Summary: Reusable cluster components
+Version: 1.0.12
+Release: 1%{?dist}
+License: GPL v2 or later; LGPL v2.1 or later
+Url: http://www.linux-ha.org/wiki/Cluster_Glue
+Group: Productivity/Clustering/HA
+Source: cluster-glue.tar.bz2
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+AutoReqProv: on
+BuildRequires: automake autoconf libtool e2fsprogs-devel glib2-devel pkgconfig python-devel libxml2-devel
+BuildRequires: libnet net-snmp-devel OpenIPMI-devel openhpi-devel
+BuildRequires: libxslt docbook_4 docbook-xsl-stylesheets
+BuildRequires: help2man
+BuildRequires: asciidoc
+BuildRequires: libbz2-devel libaio-devel
+
+Obsoletes: heartbeat-common
+Provides: heartbeat-common
+Requires(pre): /usr/sbin/groupadd /usr/bin/getent /usr/sbin/useradd
+
+# SLES10 needs tcpd-devel but doesn't have libcurl
+# in SLES10 docbook has no dependency on sgml-skel
+%if 0%{?suse_version} < 1020
+BuildRequires: tcpd-devel
+BuildRequires: sgml-skel
+%else
+BuildRequires: libcurl-devel
+%endif
+
+%if %{defined systemd_requires}
+BuildRequires: systemd
+%{?systemd_requires}
+%endif
+
+%description
+A collection of common tools derived from the Heartbeat project that are
+useful for writing cluster managers such as Pacemaker.
+Provides a local resource manager that understands the OCF and LSB
+standards, and an interface to common STONITH devices.
+
+%package -n libglue2
+License: GPL v2 only; GPL v2 or later; LGPL v2.1 or later
+Summary: The Pacemaker scalable High-Availability cluster resource manager
+Group: Productivity/Clustering/HA
+Obsoletes: libheartbeat2
+Provides: libheartbeat2
+Requires: %{name} = %{version}-%{release}
+
+%description -n libglue2
+A collection of libraries that are useful for writing cluster managers
+such as Pacemaker.
+
+%package -n libglue-devel
+License: GPL v2 only; GPL v2 or later; LGPL v2.1 or later
+Summary: The Pacemaker scalable High-Availability cluster resource manager
+Group: Development/Libraries/C and C++
+Requires: %{name} = %{version}-%{release}
+Requires: libglue2 = %{version}-%{release}
+Obsoletes: libheartbeat-devel
+Provides: libheartbeat-devel
+
+%description -n libglue-devel
+Headers and shared libraries for a useful for writing cluster managers
+such as Pacemaker.
+
+%prep
+###########################################################
+%setup -n cluster-glue -q
+###########################################################
+
+%build
+CFLAGS="${CFLAGS} ${RPM_OPT_FLAGS}"
+export CFLAGS
+
+./autogen.sh
+# SLES <= 10 does not support ./configure --docdir=,
+# hence, use this ugly hack
+%if 0%{?suse_version} < 1020
+export docdir=%{glue_docdir}
+%configure \
+ --enable-fatal-warnings=yes \
+ --with-package-name=%{name} \
+ --with-daemon-group=%{gname} \
+ --with-daemon-user=%{uname}
+%else
+%configure \
+ --enable-fatal-warnings=yes \
+ --with-package-name=%{name} \
+ --with-daemon-group=%{gname} \
+ --with-daemon-user=%{uname} \
+ --with-rundir=%{_rundir} \
+%if %{defined _unitdir}
+ --with-systemdsystemunitdir=%{_unitdir} \
+%endif
+ --docdir=%{glue_docdir}
+%endif
+
+make %{?_smp_mflags} docdir=%{glue_docdir}
+###########################################################
+
+%install
+###########################################################
+make DESTDIR=$RPM_BUILD_ROOT docdir=%{glue_docdir} install
+# Dont package static libs or compiled python
+find $RPM_BUILD_ROOT -name '*.a' -type f -print0 | xargs -0 rm -f
+find $RPM_BUILD_ROOT -name '*.la' -type f -print0 | xargs -0 rm -f
+find $RPM_BUILD_ROOT -name '*.pyc' -type f -print0 | xargs -0 rm -f
+find $RPM_BUILD_ROOT -name '*.pyo' -type f -print0 | xargs -0 rm -f
+
+%if %{defined _unitdir}
+ln -s /usr/sbin/service %{buildroot}%{_sbindir}/rclogd
+%else
+test -d $RPM_BUILD_ROOT/sbin || mkdir $RPM_BUILD_ROOT/sbin
+(
+ cd $RPM_BUILD_ROOT/sbin
+ ln -s /etc/init.d/logd rclogd
+)
+%endif
+
+###########################################################
+
+%clean
+###########################################################
+if
+ [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ]
+then
+ rm -rf $RPM_BUILD_ROOT
+fi
+rm -rf $RPM_BUILD_DIR/cluster-glue
+###########################################################
+
+%pre
+if
+ getent group %{gname} >/dev/null
+then
+ : OK group haclient already present
+else
+ /usr/sbin/groupadd -o -r -g %{uid} %{gname} 2>/dev/null || :
+fi
+if
+ getent passwd %{uname} >/dev/null
+then
+ : OK hacluster user already present
+else
+ /usr/sbin/useradd -r -g %{gname} -c "heartbeat processes" \
+ -d %{_var}/lib/heartbeat/cores/%{uname} -o -u %{uid} \
+ %{uname} 2>/dev/null || :
+fi
+%if %{defined _unitdir}
+ %service_add_pre logd.service
+%endif
+
+%if %{defined _unitdir}
+%post
+%service_add_post logd.service
+
+%preun
+%service_del_preun logd.service
+
+%postun
+%service_del_postun logd.service
+%else
+%preun
+%stop_on_removal logd
+
+%post
+%{insserv_force_if_yast logd}
+
+%postun
+%insserv_cleanup
+%endif
+
+%post -n libglue2
+/sbin/ldconfig
+
+%postun -n libglue2
+/sbin/ldconfig
+
+%files
+###########################################################
+%defattr(-,root,root)
+
+%dir %{_libdir}/heartbeat
+%dir %{_var}/lib/heartbeat
+%dir %{_var}/lib/heartbeat/cores
+%dir %attr (0700, root, root) %{_var}/lib/heartbeat/cores/root
+%dir %attr (0700, nobody, nobody) %{_var}/lib/heartbeat/cores/nobody
+%dir %attr (0700, %{uname}, %{gname}) %{_var}/lib/heartbeat/cores/%{uname}
+
+%dir %{_libdir}/heartbeat/plugins
+%dir %{_libdir}/heartbeat/plugins/RAExec
+%dir %{_libdir}/heartbeat/plugins/InterfaceMgr
+%dir %{_libdir}/heartbeat/plugins/compress
+
+%dir %{_libdir}/stonith
+%dir %{_libdir}/stonith/plugins
+%dir %{_libdir}/stonith/plugins/stonith2
+
+%dir %{_datadir}/%{name}
+%{_datadir}/%{name}/ha_cf_support.sh
+%{_datadir}/%{name}/openais_conf_support.sh
+%{_datadir}/%{name}/utillib.sh
+%{_datadir}/%{name}/ha_log.sh
+
+%{_sbindir}/ha_logger
+%{_sbindir}/hb_report
+%{_sbindir}/lrmadmin
+%{_sbindir}/cibsecret
+%{_sbindir}/meatclient
+%{_sbindir}/stonith
+
+%if %{defined _unitdir}
+%{_unitdir}/logd.service
+%{_sbindir}/rclogd
+%else
+%{_sysconfdir}/init.d/logd
+/sbin/rclogd
+%endif
+
+%doc %{_mandir}/man1/*
+%doc %{_mandir}/man8/*
+%doc AUTHORS
+%doc COPYING
+%doc ChangeLog
+%doc logd/logd.cf
+%doc doc/stonith/README*
+
+%{_libdir}/heartbeat/lrmd
+%{_libdir}/heartbeat/ha_logd
+
+%{_libdir}/heartbeat/plugins/RAExec/*.so
+%{_libdir}/heartbeat/plugins/InterfaceMgr/*.so
+%{_libdir}/heartbeat/plugins/compress/*.so
+
+%{_libdir}/stonith/plugins/external
+%{_libdir}/stonith/plugins/stonith2/*.so
+%{_libdir}/stonith/plugins/stonith2/*.py
+%{_libdir}/stonith/plugins/xen0-ha-dom0-stonith-helper
+%exclude %{_libdir}/stonith/plugins/external/ssh
+%exclude %{_libdir}/stonith/plugins/stonith2/null.so
+%exclude %{_libdir}/stonith/plugins/stonith2/ssh.so
+
+%files -n libglue2
+%defattr(-,root,root)
+%{_libdir}/lib*.so.*
+%doc AUTHORS
+%doc COPYING.LIB
+
+%files -n libglue-devel
+%defattr(-,root,root)
+
+%dir %{_libdir}/heartbeat
+%dir %{_libdir}/heartbeat/plugins
+%dir %{_libdir}/heartbeat/plugins/test
+%dir %{_datadir}/%{name}
+
+%{_libdir}/lib*.so
+%{_libdir}/heartbeat/ipctest
+%{_libdir}/heartbeat/ipctransientclient
+%{_libdir}/heartbeat/ipctransientserver
+%{_libdir}/heartbeat/transient-test.sh
+%{_libdir}/heartbeat/base64_md5_test
+%{_libdir}/heartbeat/logtest
+%{_includedir}/clplumbing
+%{_includedir}/heartbeat
+%{_includedir}/stonith
+%{_includedir}/pils
+%{_datadir}/%{name}/lrmtest
+%{_libdir}/heartbeat/plugins/test/test.so
+%{_libdir}/stonith/plugins/external/ssh
+%{_libdir}/stonith/plugins/stonith2/null.so
+%{_libdir}/stonith/plugins/stonith2/ssh.so
+%doc AUTHORS
+%doc COPYING
+%doc COPYING.LIB
+
+%changelog
diff --git a/config/Makefile.am b/config/Makefile.am
new file mode 100644
index 0000000..fa41516
--- /dev/null
+++ b/config/Makefile.am
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2005 Guochun Shi (gshi@ncsa.uiuc.edu)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = byteorder_test.c
diff --git a/config/byteorder_test.c b/config/byteorder_test.c
new file mode 100644
index 0000000..0583803
--- /dev/null
+++ b/config/byteorder_test.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+int
+main ()
+{
+ unsigned int a = 0x1234;
+
+ if ( (unsigned int) ( ((unsigned char *)&a)[0]) == 0x34 ) {
+ printf("little-endian\n");
+ return 0;
+ } else {
+ printf("big-endian\n");
+ return 1;
+ }
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..36bcf12
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,1439 @@
+dnl
+dnl autoconf for Pacemaker
+dnl
+dnl License: GNU General Public License (GPL)
+
+dnl ===============================================
+dnl Bootstrap
+dnl ===============================================
+AC_PREREQ(2.53)
+
+dnl Suggested structure:
+dnl information on the package
+dnl checks for programs
+dnl checks for libraries
+dnl checks for header files
+dnl checks for types
+dnl checks for structures
+dnl checks for compiler characteristics
+dnl checks for library functions
+dnl checks for system services
+
+AC_INIT(cluster-glue, 1.0.12, linux-ha-dev@lists.linux-ha.org)
+
+FEATURES=""
+HB_PKG=heartbeat
+
+AC_CONFIG_AUX_DIR(.)
+AC_CANONICAL_HOST
+
+dnl Where #defines go (e.g. `AC_CHECK_HEADERS' below)
+dnl
+dnl Internal header: include/config.h
+dnl - Contains ALL defines
+dnl - include/config.h.in is generated automatically by autoheader
+dnl - NOT to be included in any header files except lha_internal.h
+dnl (which is also not to be included in any other header files)
+dnl
+dnl External header: include/crm_config.h
+dnl - Contains a subset of defines checked here
+dnl - Manually edit include/crm_config.h.in to have configure include
+dnl new defines
+dnl - Should not include HAVE_* defines
+dnl - Safe to include anywhere
+AM_CONFIG_HEADER(include/config.h include/glue_config.h)
+ALL_LINGUAS="en fr"
+
+AC_ARG_WITH(version,
+ [ --with-version=version Override package version (if you're a packager needing to pretend) ],
+ [ PACKAGE_VERSION="$withval" ])
+
+AC_ARG_WITH(pkg-name,
+ [ --with-pkg-name=name Override package name (if you're a packager needing to pretend) ],
+ [ PACKAGE_NAME="$withval" ])
+
+PKG_PROG_PKG_CONFIG
+AC_ARG_WITH([systemdsystemunitdir],
+ [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
+ [with_systemdsystemunitdir=auto])
+AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
+ def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
+
+ AS_IF([test "x$def_systemdsystemunitdir" = "x"],
+ [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
+ [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
+ with_systemdsystemunitdir=no],
+ [with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
+AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
+ [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
+AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
+
+AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)
+AC_DEFINE_UNQUOTED(GLUE_VERSION, "$PACKAGE_VERSION", Current version of the glue library)
+
+CC_IN_CONFIGURE=yes
+export CC_IN_CONFIGURE
+
+LDD=ldd
+
+dnl ========================================================================
+dnl Compiler characteristics
+dnl ========================================================================
+
+AC_PROG_CC dnl Can force other with environment variable "CC".
+AM_PROG_CC_C_O
+AC_PROG_CC_STDC
+
+AC_LIBTOOL_DLOPEN dnl Enable dlopen support...
+AC_LIBLTDL_CONVENIENCE dnl make libltdl a convenience lib
+AC_PROG_LIBTOOL
+
+AC_C_STRINGIZE
+AC_TYPE_SIZE_T
+AC_CHECK_SIZEOF(char)
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(clock_t, [], [#include <sys/times.h>])
+AC_STRUCT_TIMEZONE
+
+dnl ===============================================
+dnl Helpers
+dnl ===============================================
+cc_supports_flag() {
+ local CFLAGS="$@"
+ AC_MSG_CHECKING(whether $CC supports "$@")
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE(int main(){return 0;})] ,[RC=0; AC_MSG_RESULT(yes)],[RC=1; AC_MSG_RESULT(no)])
+ return $RC
+}
+
+dnl ===============================================
+dnl Configure Options
+dnl ===============================================
+
+dnl Some systems, like Solaris require a custom package name
+AC_ARG_WITH(pkgname,
+ [ --with-pkgname=name name for pkg (typically for Solaris) ],
+ [ PKGNAME="$withval" ],
+ [ PKGNAME="LXHAhb" ],
+ )
+AC_SUBST(PKGNAME)
+
+AC_ARG_ENABLE([ansi],
+[ --enable-ansi force GCC to compile to ANSI/ANSI standard for older compilers.
+ [default=yes]])
+
+AC_ARG_ENABLE([fatal-warnings],
+[ --enable-fatal-warnings very pedantic and fatal warnings for gcc
+ [default=yes]])
+
+AC_ARG_ENABLE([pretty],
+[ --enable-pretty
+ Pretty-print compiler output unless there is an error
+ [default=no]])
+
+AC_ARG_ENABLE([quiet],
+[ --enable-quiet
+ Supress make output unless there is an error
+ [default=no]])
+
+AC_ARG_ENABLE([thread-safe],
+[ --enable-thread-safe Enable some client libraries to be thread safe.
+ [default=no]])
+
+AC_ARG_ENABLE([bundled-ltdl],
+[ --enable-bundled-ltdl Configure, build and install the standalone ltdl library bundled with ${PACKAGE} [default=no]])
+LTDL_LIBS=""
+
+AC_ARG_ENABLE([upstart],
+AS_HELP_STRING([--enable-upstart],
+ [Enable Upstart support in lrmd. [default=no]]))
+
+INITDIR=""
+AC_ARG_WITH(initdir,
+ [ --with-initdir=DIR directory for init (rc) scripts [${INITDIR}]],
+ [ INITDIR="$withval" ])
+
+OCF_ROOT_DIR="/usr/lib/ocf"
+AC_ARG_WITH(ocf-root,
+ [ --with-ocf-root=DIR directory for OCF scripts [${OCF_ROOT_DIR}]],
+ [ if test x"$withval" = xprefix; then OCF_ROOT_DIR=${prefix}; else
+ OCF_ROOT_DIR="$withval"; fi ])
+
+AC_ARG_WITH(
+ daemon-group,
+ [ --with-daemon-group=GROUP_NAME
+ Group to run our programs as. [default=haclient] ],
+ [ GLUE_DAEMON_GROUP="$withval" ],
+ [ GLUE_DAEMON_GROUP="haclient" ],
+ )
+
+AC_ARG_WITH(
+ daemon-user,
+ [ --with-daemon-user=USER_NAME
+ User to run privileged non-root things as. [default=hacluster] ],
+ [ GLUE_DAEMON_USER="$withval" ],
+ [ GLUE_DAEMON_USER="hacluster" ],
+ )
+
+
+AC_ARG_WITH(
+ rundir,
+ [ --with-rundir=DIR
+ directory to store state information [default=localstatedir/run] ],
+ [ GLUE_STATE_DIR="$withval" ],
+ [ GLUE_STATE_DIR="${localstatedir}/run" ],
+ )
+
+dnl ===============================================
+dnl General Processing
+dnl ===============================================
+
+AC_SUBST(HB_PKG)
+
+INIT_EXT=""
+echo Our Host OS: $host_os/$host
+
+if test "X$OCF_ROOT_DIR" = X; then
+ OCF_ROOT_DIR="/usr/lib/ocf"
+fi
+
+AC_MSG_NOTICE(Sanitizing prefix: ${prefix})
+case $prefix in
+ NONE) prefix=/usr;;
+esac
+
+AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix})
+case $exec_prefix in
+ dnl For consistency with Heartbeat, map NONE->$prefix
+ NONE) exec_prefix=$prefix;;
+ prefix) exec_prefix=$prefix;;
+esac
+
+AC_MSG_NOTICE(Sanitizing INITDIR: ${INITDIR})
+case $INITDIR in
+ prefix) INITDIR=$prefix;;
+ "")
+ AC_MSG_CHECKING(which init (rc) directory to use)
+ for initdir in /etc/init.d /etc/rc.d/init.d /sbin/init.d \
+ /usr/local/etc/rc.d /etc/rc.d
+ do
+ if
+ test -d $initdir
+ then
+ INITDIR=$initdir
+ break
+ fi
+ done
+ AC_MSG_RESULT($INITDIR);;
+esac
+AC_SUBST(INITDIR)
+
+AC_MSG_NOTICE(Sanitizing libdir: ${libdir})
+case $libdir in
+ dnl For consistency with Heartbeat, map NONE->$prefix
+ *prefix*|NONE)
+ AC_MSG_CHECKING(which lib directory to use)
+ for aDir in lib64 lib
+ do
+ trydir="${exec_prefix}/${aDir}"
+ if
+ test -d ${trydir}
+ then
+ libdir=${trydir}
+ break
+ fi
+ done
+ AC_MSG_RESULT($libdir);
+ ;;
+esac
+
+DLOPEN_FORCE_FLAGS=""
+AC_SUBST(DLOPEN_FORCE_FLAGS)
+
+
+dnl Expand autoconf variables so that we dont end up with '${prefix}'
+dnl in #defines and python scripts
+dnl NOTE: Autoconf deliberately leaves them unexpanded to allow
+dnl make exec_prefix=/foo install
+dnl No longer being able to do this seems like no great loss to me...
+
+eval prefix="`eval echo ${prefix}`"
+eval exec_prefix="`eval echo ${exec_prefix}`"
+eval bindir="`eval echo ${bindir}`"
+eval sbindir="`eval echo ${sbindir}`"
+eval libexecdir="`eval echo ${libexecdir}`"
+eval datadir="`eval echo ${datadir}`"
+eval sysconfdir="`eval echo ${sysconfdir}`"
+eval sharedstatedir="`eval echo ${sharedstatedir}`"
+eval localstatedir="`eval echo ${localstatedir}`"
+eval libdir="`eval echo ${libdir}`"
+eval includedir="`eval echo ${includedir}`"
+eval oldincludedir="`eval echo ${oldincludedir}`"
+eval infodir="`eval echo ${infodir}`"
+eval mandir="`eval echo ${mandir}`"
+
+dnl docdir is a recent addition to autotools
+eval docdir="`eval echo ${docdir}`"
+if test "x$docdir" = "x"; then
+ docdir="`eval echo ${datadir}/doc`"
+fi
+AC_SUBST(docdir)
+
+AC_MSG_CHECKING(for the location of the lock directory)
+for HA_VARLOCKDIR in ${localstatedir}/lock ${localstatedir}/spool/lock ${localstatedir}/spool/locks ${localstatedir}/lock
+do
+ if
+ test -d "$HA_VARLOCKDIR"
+ then
+ AC_MSG_RESULT($HA_VARLOCKDIR)
+ break
+ fi
+done
+
+AC_SUBST(HA_VARLOCKDIR)
+AC_DEFINE_UNQUOTED(HA_VARLOCKDIR,"$HA_VARLOCKDIR", System lock directory)
+
+dnl Home-grown variables
+eval INITDIR="${INITDIR}"
+
+for j in prefix exec_prefix bindir sbindir libexecdir datadir sysconfdir \
+ sharedstatedir localstatedir libdir includedir oldincludedir infodir \
+ mandir INITDIR docdir HA_VARLOCKDIR
+do
+ dirname=`eval echo '${'${j}'}'`
+ if
+ test ! -d "$dirname"
+ then
+ AC_MSG_WARN([$j directory ($dirname) does not exist!])
+ fi
+done
+
+dnl This OS-based decision-making is poor autotools practice;
+dnl feature-based mechanisms are strongly preferred.
+dnl
+dnl So keep this section to a bare minimum; regard as a "necessary evil".
+
+ON_LINUX=0
+REBOOT_OPTIONS="-f"
+POWEROFF_OPTIONS="-f"
+
+case "$host_os" in
+*bsd*) LIBS="-L/usr/local/lib"
+ CPPFLAGS="$CPPFLAGS -I/usr/local/include"
+ INIT_EXT=".sh"
+ ;;
+*solaris*)
+ REBOOT_OPTIONS="-n"
+ POWEROFF_OPTIONS="-n"
+ ;;
+*linux*)
+ ON_LINUX=1
+ REBOOT_OPTIONS="-nf"
+ POWEROFF_OPTIONS="-nf"
+ AC_DEFINE_UNQUOTED(ON_LINUX, $ON_LINUX, Compiling for Linux platform)
+ ;;
+darwin*)
+ AC_DEFINE_UNQUOTED(ON_DARWIN, 1, Compiling for Darwin platform)
+ LIBS="$LIBS -L${prefix}/lib"
+ CFLAGS="$CFLAGS -I${prefix}/include"
+ ;;
+esac
+
+AM_CONDITIONAL(ON_LINUX, test $ON_LINUX = 1)
+
+dnl Eventually remove this
+dnl CFLAGS="$CFLAGS -I${prefix}/include/heartbeat"
+
+AC_SUBST(INIT_EXT)
+AC_DEFINE_UNQUOTED(HA_LOG_FACILITY, LOG_DAEMON, Default logging facility)
+
+AC_MSG_NOTICE(Host CPU: $host_cpu)
+
+case "$host_cpu" in
+ ppc64|powerpc64)
+ case $CFLAGS in
+ *powerpc64*) ;;
+ *) if test "$GCC" = yes; then
+ CFLAGS="$CFLAGS -m64"
+ fi ;;
+ esac
+esac
+
+AC_MSG_CHECKING(which format is needed to print uint64_t)
+case "$host_cpu" in
+ s390x)U64T="%lu";;
+ *64*) U64T="%lu";;
+ *) U64T="%llu";;
+esac
+AC_MSG_RESULT($U64T)
+AC_DEFINE_UNQUOTED(U64T, "$U64T", Correct printf format for logging uint64_t)
+
+dnl Variables needed for substitution
+AC_DEFINE_UNQUOTED(GLUE_DAEMON_USER,"$GLUE_DAEMON_USER", User to run daemons as)
+AC_SUBST(GLUE_DAEMON_USER)
+
+AC_DEFINE_UNQUOTED(GLUE_DAEMON_GROUP,"$GLUE_DAEMON_GROUP", Group to run daemons as)
+AC_SUBST(GLUE_DAEMON_GROUP)
+
+dnl Eventually move out of the heartbeat dir tree and create symlinks when needed
+GLUE_DAEMON_DIR=$libdir/heartbeat
+AC_DEFINE_UNQUOTED(GLUE_DAEMON_DIR,"$GLUE_DAEMON_DIR", Location for daemons)
+AC_SUBST(GLUE_DAEMON_DIR)
+
+GLUE_STATE_DIR=${localstatedir}/run
+AC_DEFINE_UNQUOTED(GLUE_STATE_DIR,"$GLUE_STATE_DIR", Where to keep state files and sockets)
+AC_SUBST(GLUE_STATE_DIR)
+
+GLUE_SHARED_DIR=${datadir}/"$PACKAGE_NAME"
+AC_DEFINE_UNQUOTED(GLUE_SHARED_DIR,"$GLUE_SHARED_DIR", Location for scripts)
+AC_SUBST(GLUE_SHARED_DIR)
+
+AC_DEFINE_UNQUOTED(HA_VARRUNDIR,"$GLUE_STATE_DIR", Where Heartbeat keeps state files and sockets - old name)
+AC_SUBST(HA_VARRUNDIR)
+
+HA_VARLIBHBDIR=${localstatedir}/lib/heartbeat
+AC_DEFINE_UNQUOTED(HA_VARLIBHBDIR,"$HA_VARLIBHBDIR", Whatever this used to mean)
+AC_SUBST(HA_VARLIBHBDIR)
+AC_DEFINE_UNQUOTED(HA_VARLIBDIR,"$HA_VARLIBHBDIR", Whatever this used to mean)
+AC_SUBST(HA_VARLIBDIR)
+
+AC_DEFINE_UNQUOTED(OCF_ROOT_DIR,"$OCF_ROOT_DIR", OCF root directory - specified by the OCF standard)
+AC_SUBST(OCF_ROOT_DIR)
+
+OCF_RA_DIR="${OCF_ROOT_DIR}/resource.d/"
+AC_DEFINE_UNQUOTED(OCF_RA_DIR,"$OCF_RA_DIR", Location for OCF RAs)
+AC_SUBST(OCF_RA_DIR)
+
+HA_LOGDAEMON_IPC="${localstatedir}/lib/heartbeat/log_daemon"
+AC_DEFINE_UNQUOTED(HA_LOGDAEMON_IPC, "$HA_LOGDAEMON_IPC", Logging Daemon IPC socket name)
+AC_SUBST(HA_LOGDAEMON_IPC)
+
+HA_URLBASE="http://linux-ha.org/wiki/"
+AC_DEFINE_UNQUOTED(HA_URLBASE, "$HA_URLBASE", Web site base URL)
+AC_SUBST(HA_URLBASE)
+
+HA_COREDIR="${localstatedir}/lib/heartbeat/cores"
+AC_DEFINE_UNQUOTED(HA_COREDIR,"$HA_COREDIR", top directory of area to drop core files in)
+AC_SUBST(HA_COREDIR)
+
+LRM_VARLIBDIR="${localstatedir}/lib/heartbeat/lrm"
+AC_DEFINE_UNQUOTED(LRM_VARLIBDIR,"$LRM_VARLIBDIR", LRM directory)
+AC_SUBST(LRM_VARLIBDIR)
+
+LRM_CIBSECRETS="${localstatedir}/lib/heartbeat/lrm/secrets"
+AC_DEFINE_UNQUOTED(LRM_CIBSECRETS,"$LRM_CIBSECRETS", CIB secrets location)
+AC_SUBST(LRM_CIBSECRETS)
+
+AC_DEFINE_UNQUOTED(PILS_BASE_PLUGINDIR,"$libdir/heartbeat/plugins", Default plugin search path)
+AC_DEFINE_UNQUOTED(HA_PLUGIN_DIR,"$libdir/heartbeat/plugins", Where to find plugins)
+AC_DEFINE_UNQUOTED(LRM_PLUGIN_DIR,"$libdir/heartbeat/plugins/RAExec", Where to find LRM plugins)
+
+AC_DEFINE_UNQUOTED(LSB_RA_DIR,"$INITDIR", Location for LSB RAs)
+LSB_RA_DIR=$INITDIR
+AC_SUBST(LSB_RA_DIR)
+
+AC_DEFINE_UNQUOTED(HA_SYSCONFDIR, "$sysconfdir", Location of system configuration files)
+
+HA_HBCONF_DIR=${sysconfdir}/ha.d/
+AC_DEFINE_UNQUOTED(HA_HBCONF_DIR,"$HA_HBCONF_DIR", Location for v1 Heartbeat configuration)
+AC_SUBST(HA_HBCONF_DIR)
+
+HB_RA_DIR=${sysconfdir}/ha.d/resource.d/
+AC_DEFINE_UNQUOTED(HB_RA_DIR,"$HB_RA_DIR", Location for v1 Heartbeat RAs)
+AC_SUBST(HB_RA_DIR)
+
+stonith_plugindir="${libdir}/stonith/plugins"
+stonith_ext_plugindir="${stonith_plugindir}/external"
+stonith_rhcs_plugindir="${stonith_plugindir}/rhcs"
+AC_DEFINE_UNQUOTED(ST_TEXTDOMAIN, "stonith", Stonith plugin domain)
+AC_DEFINE_UNQUOTED(STONITH_MODULES, "$stonith_plugindir", Location of stonith plugins)
+AC_DEFINE_UNQUOTED(STONITH_EXT_PLUGINDIR, "$stonith_ext_plugindir", Location of non-plugin stonith scripts)
+AC_DEFINE_UNQUOTED(STONITH_RHCS_PLUGINDIR, "$stonith_rhcs_plugindir", Location of RHCS fence scripts)
+AC_SUBST(stonith_plugindir)
+AC_SUBST(stonith_ext_plugindir)
+AC_SUBST(stonith_rhcs_plugindir)
+
+dnl Old names for new things
+AC_DEFINE_UNQUOTED(HA_CCMUSER, "$GLUE_DAEMON_USER", User to run daemons as)
+AC_DEFINE_UNQUOTED(HA_APIGROUP, "$GLUE_DAEMON_GROUP", Group to run daemons as)
+AC_DEFINE_UNQUOTED(HA_LIBHBDIR, "$GLUE_DAEMON_DIR", Location for daemons)
+
+LRM_DIR=lrm
+AC_SUBST(LRM_DIR)
+
+AC_PATH_PROGS(HG, hg false)
+AC_MSG_CHECKING(build version)
+GLUE_BUILD_VERSION=unknown
+if test -f $srcdir/.hg_archival.txt; then
+ GLUE_BUILD_VERSION=`cat $srcdir/.hg_archival.txt | awk '/node:/ { print $2 }'`
+elif test -x $HG -a -d .hg; then
+ GLUE_BUILD_VERSION=`$HG id -itb`
+ if test $? != 0; then
+ GLUE_BUILD_VERSION=unknown
+ fi
+fi
+
+AC_DEFINE_UNQUOTED(GLUE_BUILD_VERSION, "$GLUE_BUILD_VERSION", Build version)
+AC_MSG_RESULT($GLUE_BUILD_VERSION)
+AC_SUBST(GLUE_BUILD_VERSION)
+
+dnl check byte order
+AC_MSG_CHECKING(for byteorder)
+AC_C_BIGENDIAN(
+[AC_MSG_RESULT(big-endian); AC_DEFINE(CONFIG_BIG_ENDIAN, 1, [big-endian])],
+[AC_MSG_RESULT(little-endian); AC_DEFINE(CONFIG_LITTLE_ENDIAN, 1, [little-endian])],
+)
+
+
+dnl ===============================================
+dnl Program Paths
+dnl ===============================================
+
+PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin"
+export PATH
+
+
+dnl Replacing AC_PROG_LIBTOOL with AC_CHECK_PROG because LIBTOOL
+dnl was NOT being expanded all the time thus causing things to fail.
+AC_CHECK_PROGS(LIBTOOL, glibtool libtool libtool15 libtool13)
+
+AM_PATH_PYTHON
+AC_CHECK_PROGS(MAKE, gmake make)
+AC_PATH_PROGS(HTML2TXT, lynx w3m)
+AC_PATH_PROGS(HELP2MAN, help2man)
+AC_PATH_PROGS(POD2MAN, pod2man, pod2man)
+AC_PATH_PROGS(SSH, ssh, /usr/bin/ssh)
+AC_PATH_PROGS(SCP, scp, /usr/bin/scp)
+AC_PATH_PROGS(HG, hg, /bin/false)
+AC_PATH_PROGS(TAR, tar)
+AC_PATH_PROGS(MD5, md5)
+AC_PATH_PROGS(RPM, rpm)
+AC_PATH_PROGS(TEST, test)
+AC_PATH_PROGS(PING, ping, /bin/ping)
+AC_PATH_PROGS(IFCONFIG, ifconfig, /sbin/ifconfig)
+AC_PATH_PROGS(MAILCMD, mailx mail)
+AC_PATH_PROGS(EGREP, egrep)
+AC_PATH_PROGS(PKGCONFIG, pkg-config)
+AC_PATH_PROGS(XML2CONFIG, xml2-config)
+
+AC_ARG_ENABLE([doc],
+ AS_HELP_STRING([--enable-doc], [build documentation (default is yes)]),
+ [], [enable_doc=yes])
+if test "x$enable_doc" != "xno"; then
+ AC_PATH_PROGS(XSLTPROC, xsltproc)
+ if test "x$XSLTPROC" = "x"; then
+ AC_MSG_WARN([xsltproc not installed, unable to (re-)build manual pages])
+ fi
+ AC_PATH_PROGS(ASCIIDOC, asciidoc)
+ if test "x$ASCIIDOC" = "x"; then
+ AC_MSG_WARN([asciidoc not installed, unable to (re-)build manual pages])
+ fi
+fi
+AM_CONDITIONAL(BUILD_DOC, test "x$XSLTPROC" != "x" )
+
+AC_PATH_PROGS(VALGRIND_BIN, valgrind, /usr/bin/valgrind)
+AC_DEFINE_UNQUOTED(VALGRIND_BIN, "$VALGRIND_BIN", Valgrind command)
+
+AC_SUBST(MAILCMD)
+AC_SUBST(EGREP)
+AC_SUBST(SHELL)
+AC_SUBST(PING)
+AC_SUBST(TEST)
+AC_SUBST(RPM)
+AC_SUBST(XSLTPROC)
+
+AC_MSG_CHECKING(ifconfig option to list interfaces)
+for IFCONFIG_A_OPT in "-A" "-a" ""
+do
+ $IFCONFIG $IFCONFIG_A_OPT > /dev/null 2>&1
+ if
+ test "$?" = 0
+ then
+ AC_DEFINE_UNQUOTED(IFCONFIG_A_OPT, "$IFCONFIG_A_OPT", option for ifconfig command)
+ AC_MSG_RESULT($IFCONFIG_A_OPT)
+ break
+ fi
+done
+
+AC_SUBST(IFCONFIG_A_OPT)
+
+if test x"${LIBTOOL}" = x""; then
+ AC_MSG_ERROR(You need (g)libtool installed in order to build ${PACKAGE})
+fi
+if test x"${MAKE}" = x""; then
+ AC_MSG_ERROR(You need (g)make installed in order to build ${PACKAGE})
+fi
+
+AM_CONDITIONAL(BUILD_HELP, test x"${HELP2MAN}" != x"")
+if test x"${HELP2MAN}" != x""; then
+ FEATURES="$FEATURES manpages"
+fi
+
+dnl ===============================================
+dnl Libraries
+dnl ===============================================
+AC_CHECK_LIB(socket, socket)
+AC_CHECK_LIB(c, dlopen) dnl if dlopen is in libc...
+AC_CHECK_LIB(dl, dlopen) dnl for Linux
+AC_CHECK_LIB(rt, sched_getscheduler) dnl for Tru64
+AC_CHECK_LIB(gnugetopt, getopt_long) dnl if available
+AC_CHECK_LIB(uuid, uuid_parse) dnl e2fsprogs
+AC_CHECK_LIB(uuid, uuid_create) dnl ossp
+AC_CHECK_LIB(posix4, sched_getscheduler)
+
+if test x"${PKGCONFIG}" = x""; then
+ AC_MSG_ERROR(You need pkgconfig installed in order to build ${PACKAGE})
+fi
+
+dnl
+dnl On many systems libcrypto is needed when linking against libsnmp.
+dnl Check to see if it exists, and if so use it.
+dnl
+AC_CHECK_LIB(crypto, CRYPTO_free, CRYPTOLIB="-lcrypto",)
+AC_SUBST(CRYPTOLIB)
+
+if test "x${enable_thread_safe}" = "xyes"; then
+ GPKGNAME="gthread-2.0"
+else
+ GPKGNAME="glib-2.0"
+fi
+
+if
+ $PKGCONFIG --exists $GPKGNAME
+then
+ GLIBCONFIG="$PKGCONFIG $GPKGNAME"
+else
+ set -x
+ echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH
+ $PKGCONFIG --exists $GPKGNAME; echo $?
+ $PKGCONFIG --cflags $GPKGNAME; echo $?
+ $PKGCONFIG $GPKGNAME; echo $?
+ set +x
+
+ AC_MSG_ERROR(You need glib2-devel installed in order to build ${PACKAGE})
+fi
+AC_MSG_RESULT(using $GLIBCONFIG)
+
+#
+# Where is dlopen?
+#
+if test "$ac_cv_lib_c_dlopen" = yes; then
+ LIBADD_DL=""
+elif test "$ac_cv_lib_dl_dlopen" = yes; then
+ LIBADD_DL=-ldl
+else
+ LIBADD_DL=${lt_cv_dlopen_libs}
+fi
+dnl
+dnl Check for location of gettext
+dnl
+dnl On at least Solaris 2.x, where it is in libc, specifying lintl causes
+dnl grief. Ensure minimal result, not the sum of all possibilities.
+dnl And do libc first.
+dnl Known examples:
+dnl c: Linux, Solaris 2.6+
+dnl intl: BSD, AIX
+
+AC_CHECK_LIB(c, gettext)
+if test x$ac_cv_lib_c_gettext != xyes; then
+ AC_CHECK_LIB(intl, gettext)
+fi
+
+if test x$ac_cv_lib_c_gettext != xyes -a x$ac_cv_lib_intl_gettext != xyes; then
+ AC_MSG_ERROR(You need gettext installed in order to build ${PACKAGE})
+fi
+
+if test "X$GLIBCONFIG" != X; then
+ AC_MSG_CHECKING(for special glib includes: )
+ GLIBHEAD=`$GLIBCONFIG --cflags`
+ AC_MSG_RESULT($GLIBHEAD)
+ CPPFLAGS="$CPPFLAGS $GLIBHEAD"
+
+ AC_MSG_CHECKING(for glib library flags)
+ GLIBLIB=`$GLIBCONFIG --libs`
+ AC_MSG_RESULT($GLIBLIB)
+ LIBS="$LIBS $GLIBLIB"
+fi
+
+dnl ========================================================================
+dnl Headers
+dnl ========================================================================
+
+AC_HEADER_STDC
+AC_CHECK_HEADERS(arpa/inet.h)
+AC_CHECK_HEADERS(asm/types.h)
+AC_CHECK_HEADERS(assert.h)
+AC_CHECK_HEADERS(auth-client.h)
+AC_CHECK_HEADERS(ctype.h)
+AC_CHECK_HEADERS(dirent.h)
+AC_CHECK_HEADERS(errno.h)
+AC_CHECK_HEADERS(fcntl.h)
+AC_CHECK_HEADERS(getopt.h)
+AC_CHECK_HEADERS(glib.h)
+AC_CHECK_HEADERS(grp.h)
+AC_CHECK_HEADERS(limits.h)
+AC_CHECK_HEADERS(linux/errqueue.h,,,
+ [#ifdef HAVE_LINUX_TYPES_H
+ # include <linux/types.h>
+ #endif
+ ])
+AC_CHECK_HEADERS(malloc.h)
+AC_CHECK_HEADERS(netdb.h)
+AC_CHECK_HEADERS(netinet/in.h)
+AC_CHECK_HEADERS(netinet/ip.h)
+AC_CHECK_HEADERS(pthread.h)
+AC_CHECK_HEADERS(pwd.h)
+AC_CHECK_HEADERS(sgtty.h)
+AC_CHECK_HEADERS(signal.h)
+AC_CHECK_HEADERS(stdarg.h)
+AC_CHECK_HEADERS(stddef.h)
+AC_CHECK_HEADERS(stdio.h)
+AC_CHECK_HEADERS(stdlib.h)
+AC_CHECK_HEADERS(string.h)
+AC_CHECK_HEADERS(strings.h)
+AC_CHECK_HEADERS(sys/dir.h)
+AC_CHECK_HEADERS(sys/ioctl.h)
+AC_CHECK_HEADERS(sys/param.h)
+AC_CHECK_HEADERS(sys/poll.h)
+AC_CHECK_HEADERS(sys/reboot.h)
+AC_CHECK_HEADERS(sys/resource.h)
+AC_CHECK_HEADERS(sys/select.h)
+AC_CHECK_HEADERS(sys/socket.h)
+AC_CHECK_HEADERS(sys/sockio.h)
+AC_CHECK_HEADERS(sys/stat.h)
+AC_CHECK_HEADERS(sys/time.h)
+AC_CHECK_HEADERS(sys/timeb.h)
+AC_CHECK_HEADERS(sys/types.h)
+AC_CHECK_HEADERS(sys/uio.h)
+AC_CHECK_HEADERS(sys/un.h)
+AC_CHECK_HEADERS(sys/utsname.h)
+AC_CHECK_HEADERS(sys/wait.h)
+AC_CHECK_HEADERS(time.h)
+AC_CHECK_HEADERS(unistd.h)
+AC_CHECK_HEADERS(winsock.h)
+AC_CHECK_HEADERS(sys/termios.h)
+AC_CHECK_HEADERS(termios.h)
+
+dnl These headers need prerequisits before the tests will pass
+dnl AC_CHECK_HEADERS(net/if.h)
+dnl AC_CHECK_HEADERS(netinet/icmp6.h)
+dnl AC_CHECK_HEADERS(netinet/ip6.h)
+dnl AC_CHECK_HEADERS(netinet/ip_icmp.h)
+
+AC_MSG_CHECKING(for special libxml2 includes)
+if test "x$XML2CONFIG" = "x"; then
+ AC_MSG_ERROR(libxml2 config not found)
+else
+ XML2HEAD="`$XML2CONFIG --cflags`"
+ AC_MSG_RESULT($XML2HEAD)
+ AC_CHECK_LIB(xml2, xmlReadMemory)
+fi
+
+CPPFLAGS="$CPPFLAGS $XML2HEAD"
+
+AC_CHECK_HEADERS(libxml/xpath.h)
+if test "$ac_cv_header_libxml_xpath_h" != "yes"; then
+ AC_MSG_ERROR(The libxml developement headers were not found)
+fi
+
+dnl Check syslog.h for 'facilitynames' table
+dnl
+AC_CACHE_CHECK([for facilitynames in syslog.h],ac_cv_HAVE_SYSLOG_FACILITYNAMES,[
+AC_TRY_COMPILE([
+#define SYSLOG_NAMES
+#include <stdlib.h>
+#include <syslog.h>
+],
+[ void *fnames; fnames = facilitynames; ],
+ac_cv_HAVE_SYSLOG_FACILITYNAMES=yes,ac_cv_HAVE_SYSLOG_FACILITYNAMES=no,ac_cv_HAVE_SYSLOG_FACILITYNAMES=cross)])
+if test x"$ac_cv_HAVE_SYSLOG_FACILITYNAMES" = x"yes"; then
+ AC_DEFINE(HAVE_SYSLOG_FACILITYNAMES,1,[ ])
+fi
+
+dnl Check for POSIX signals
+dnl
+AC_CACHE_CHECK([have POSIX signals],ac_cv_HAVE_POSIX_SIGNALS,[
+AC_TRY_COMPILE([
+#include <signal.h>
+],
+[ struct sigaction act, oact; sigaction(0, &act, &oact); return 0;],
+ac_cv_HAVE_POSIX_SIGNALS=yes,ac_cv_HAVE_POSIX_SIGNALS=no,ac_cv_HAVE_POSIX_SIGNALS=cross)])
+if test x"$ac_cv_HAVE_POSIX_SIGNALS" = x"yes"; then
+ AC_DEFINE(HAVE_POSIX_SIGNALS,1,[ ])
+fi
+
+dnl 'reboot()' system call: one argument (e.g. Linux) or two (e.g. Solaris)?
+dnl
+AC_CACHE_CHECK([number of arguments in reboot system call],
+ ac_cv_REBOOT_ARGS,[
+ AC_TRY_COMPILE(
+ [#include <sys/reboot.h>],
+ [(void)reboot(0);],
+ ac_cv_REBOOT_ARGS=1,
+ [AC_TRY_COMPILE(
+ [#include <sys/reboot.h>],
+ [(void)reboot(0,(void *)0);],
+ ac_cv_REBOOT_ARGS=2,
+ ac_cv_REBOOT_ARGS=0
+ )],
+ ac_cv_REBOOT_ARGS=0
+ )
+ ]
+)
+dnl Argument count of 0 suggests no known 'reboot()' call.
+if test "$ac_cv_REBOOT_ARGS" -ge "1"; then
+ AC_DEFINE_UNQUOTED(REBOOT_ARGS,$ac_cv_REBOOT_ARGS,[number of arguments for reboot system call])
+fi
+
+AC_PATH_PROGS(REBOOT, reboot, /sbin/reboot)
+AC_SUBST(REBOOT)
+AC_SUBST(REBOOT_OPTIONS)
+AC_DEFINE_UNQUOTED(REBOOT, "$REBOOT", path to the reboot command)
+AC_DEFINE_UNQUOTED(REBOOT_OPTIONS, "$REBOOT_OPTIONS", reboot options)
+
+AC_PATH_PROGS(POWEROFF_CMD, poweroff, /sbin/poweroff)
+AC_SUBST(POWEROFF_CMD)
+AC_SUBST(POWEROFF_OPTIONS)
+AC_DEFINE_UNQUOTED(POWEROFF_CMD, "$POWEROFF_CMD", path to the poweroff command)
+AC_DEFINE_UNQUOTED(POWEROFF_OPTIONS, "$POWEROFF_OPTIONS", poweroff options)
+
+dnl Sockets are our preferred and supported comms mechanism. But the
+dnl implementation needs to be able to convey credentials: some don't.
+dnl So on a few OSes, credentials-carrying streams might be a better choice.
+dnl
+dnl Solaris releases up to and including "9" fall into this category
+dnl (its sockets don't carry credentials; streams do).
+dnl
+dnl At Solaris 10, "getpeerucred()" is available, for both sockets and
+dnl streams, so it should probably use (preferred) socket mechanism.
+
+AC_CHECK_HEADERS(stropts.h) dnl streams available (fallback option)
+
+AC_CHECK_HEADERS(ucred.h) dnl e.g. Solaris 10 decl. of "getpeerucred()"
+AC_CHECK_FUNCS(getpeerucred)
+
+dnl ************************************************************************
+dnl checks for headers needed by clplumbing On BSD
+AC_CHECK_HEADERS(sys/syslimits.h)
+if test "$ac_cv_header_sys_param_h" = no; then
+ AC_CHECK_HEADERS(sys/ucred.h)
+else
+ AC_CHECK_HEADERS(sys/ucred.h,[],[],[#include <sys/param.h>])
+fi
+
+dnl ************************************************************************
+dnl checks for headers needed by clplumbing On Solaris
+AC_CHECK_HEADERS(sys/cred.h xti.h)
+
+dnl ************************************************************************
+dnl checks for headers needed by clplumbing On FreeBSD/Solaris
+AC_CHECK_HEADERS(sys/filio.h)
+
+dnl ========================================================================
+dnl Structures
+dnl ========================================================================
+
+AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[[#include <time.h>]])
+AC_CHECK_TYPES([nfds_t],,,[[#include <poll.h>]])
+
+AC_MSG_CHECKING(if clock_t is long enough)
+if test $ac_cv_sizeof_clock_t -ge 8; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(CLOCK_T_IS_LONG_ENOUGH, 1, [Set if CLOCK_T is adequate by itself for the "indefinite future" (>= 100 years)])
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl ========================================================================
+dnl Functions
+dnl ========================================================================
+
+AC_CHECK_FUNCS(g_log_set_default_handler)
+AC_CHECK_FUNCS(getopt, AC_DEFINE(HAVE_DECL_GETOPT, 1, [Have getopt function]))
+AC_CHECK_FUNCS(getpeereid)
+
+dnl **********************************************************************
+dnl Check for various argv[] replacing functions on various OSs
+dnl
+dnl Borrowed from Proftpd
+dnl Proftpd is Licenced under the terms of the GNU General Public Licence
+dnl and is available from http://www.proftpd.org/
+dnl
+
+AC_CHECK_FUNCS(setproctitle)
+AC_CHECK_HEADERS(libutil.h)
+AC_CHECK_LIB(util, setproctitle,
+ [AC_DEFINE(HAVE_SETPROCTITLE,1,[ ])
+ ac_cv_func_setproctitle="yes" ; LIBS="$LIBS -lutil"])
+
+if test "$ac_cv_func_setproctitle" = "yes"; then
+ pf_argv_set="PF_ARGV_NONE"
+fi
+
+if test "$pf_argv_set" = ""; then
+ AC_CHECK_HEADERS(sys/pstat.h)
+ if test "$ac_cv_header_pstat_h" = "yes"; then
+ AC_CHECK_FUNCS(pstat)
+
+ if test "$ac_cv_func_pstat" = "yes"; then
+ pf_argv_set="PF_ARGV_PSTAT"
+ else
+ pf_argv_set="PF_ARGV_WRITEABLE"
+ fi
+ fi
+
+ if test "$pf_argv_set" = ""; then
+ AC_EGREP_HEADER([#define.*PS_STRINGS.*],sys/exec.h,
+ have_psstrings="yes",have_psstrings="no")
+ if test "$have_psstrings" = "yes"; then
+ pf_argv_set="PF_ARGV_PSSTRINGS"
+ fi
+ fi
+
+ if test "$pf_argv_set" = ""; then
+ AC_CACHE_CHECK(whether __progname and __progname_full are available,
+ pf_cv_var_progname,
+ AC_TRY_LINK([extern char *__progname, *__progname_full;],
+ [__progname = "foo"; __progname_full = "foo bar";],
+ pf_cv_var_progname="yes", pf_cv_var_progname="no"))
+
+ if test "$pf_cv_var_progname" = "yes"; then
+ AC_DEFINE(HAVE___PROGNAME,1,[ ])
+ fi
+
+ AC_CACHE_CHECK(which argv replacement method to use,
+ pf_cv_argv_type,
+ AC_EGREP_CPP(yes,[
+#if defined(__GNU_HURD__)
+ yes
+#endif
+ ],pf_cv_argv_type="new", pf_cv_argv_type="writeable"))
+
+ if test "$pf_cv_argv_type" = "new"; then
+ pf_argv_set="PF_ARGV_NEW"
+ fi
+
+ if test "$pf_argv_set" = ""; then
+ pf_argv_set="PF_ARGV_WRITEABLE"
+ fi
+ fi
+fi
+AC_DEFINE_UNQUOTED(PF_ARGV_TYPE, $pf_argv_set,
+ mechanism to pretty-print ps output: setproctitle-equivalent)
+
+dnl End of tests borrowed from Proftpd
+
+dnl ========================================================================
+dnl ltdl
+dnl ========================================================================
+
+AC_CHECK_LIB(ltdl, lt_dlopen, [LTDL_foo=1])
+if test "x${enable_bundled_ltdl}" = "xyes"; then
+ if test $ac_cv_lib_ltdl_lt_dlopen = yes; then
+ AC_MSG_NOTICE([Disabling usage of installed ltdl])
+ fi
+ ac_cv_lib_ltdl_lt_dlopen=no
+fi
+
+LIBLTDL_DIR=""
+if test $ac_cv_lib_ltdl_lt_dlopen != yes ; then
+ AC_MSG_NOTICE([Installing local ltdl])
+ LIBLTDL_DIR=libltdl
+ ( cd $srcdir ; $TAR -xvf libltdl.tar )
+ if test "$?" -ne 0; then
+ AC_MSG_ERROR([$TAR of libltdl.tar in $srcdir failed])
+ fi
+ AC_CONFIG_SUBDIRS(libltdl)
+else
+ LIBS="$LIBS -lltdl"
+ AC_MSG_NOTICE([Using installed ltdl])
+ INCLTDL=""
+ LIBLTDL=""
+fi
+
+AC_SUBST(INCLTDL)
+AC_SUBST(LIBLTDL)
+AC_SUBST(LIBLTDL_DIR)
+
+dnl ========================================================================
+dnl libnet
+dnl ========================================================================
+
+AC_ARG_ENABLE([libnet],
+ [ --enable-libnet Use libnet for ARP based funcationality, [default=try]],
+ [], [enable_libnet=try])
+
+libnet=""
+libnet_version="none"
+LIBNETLIBS=""
+LIBNETDEFINES=""
+
+AC_MSG_CHECKING(if libnet is required)
+libnet_fatal=$enable_libnet
+case $enable_libnet in
+ no) ;;
+ yes|libnet10|libnet11|10|11) libnet_fatal=yes;;
+ try)
+ case $host_os in
+ *Linux*|*linux*) libnet_fatal=no;;
+ *) libnet_fatal=yes;; dnl legacy behavior
+ esac
+ ;;
+ *) libnet_fatal=yes; enable_libnet=try;;
+esac
+AC_MSG_RESULT($libnet_fatal)
+
+if test "x$enable_libnet" != "xno"; then
+ AC_PATH_PROGS(LIBNETCONFIG, libnet-config)
+
+ AC_CHECK_LIB(nsl, t_open) dnl -lnsl
+ AC_CHECK_LIB(socket, socket) dnl -lsocket
+ AC_CHECK_LIB(net, libnet_get_hwaddr, LIBNETLIBS=" -lnet", [])
+ fi
+
+AC_MSG_CHECKING(for libnet)
+if test "x$LIBNETLIBS" != "x" -o "x$enable_libnet" = "xlibnet11"; then
+ LIBNETDEFINES=""
+ if test "$ac_cv_lib_nsl_t_open" = yes; then
+ LIBNETLIBS="-lnsl $LIBNETLIBS"
+ fi
+ if test "$ac_cv_lib_socket_socket" = yes; then
+ LIBNETLIBS="-lsocket $LIBNETLIBS"
+ fi
+
+ libnet=net
+ libnet_version="libnet1.1"
+fi
+
+if test "x$enable_libnet" = "xtry" -o "x$enable_libnet" = "xlibnet10"; then
+ if test "x$LIBNETLIBS" = x -a "x${LIBNETCONFIG}" != "x" ; then
+ LIBNETDEFINES="`$LIBNETCONFIG --defines` `$LIBNETCONFIG --cflags`";
+ LIBNETLIBS="`$LIBNETCONFIG --libs`";
+ libnet_version="libnet1.0 (old)"
+ case $LIBNETLIBS in
+ *-l*) libnet=`echo $LIBNETLIBS | sed 's%.*-l%%'`;;
+ *) libnet_version=none;;
+ esac
+
+ CPPFLAGS="$CPPFLAGS $LIBNETDEFINES"
+
+ AC_CHECK_HEADERS(libnet.h)
+ if test "$ac_cv_header_libnet_h" = no; then
+ libnet_version=none
+ fi
+ fi
+fi
+AC_MSG_RESULT(found $libnet_version)
+
+if test "$libnet_version" = none; then
+ LIBNETLIBS=""
+ LIBNETDEFINES=""
+ if test $libnet_fatal = yes; then
+ AC_MSG_ERROR(libnet not found)
+ fi
+
+else
+ AC_CHECK_LIB($libnet,libnet_init,
+ [new_libnet=yes; AC_DEFINE(HAVE_LIBNET_1_1_API, 1, Libnet 1.1 API)],
+ [new_libnet=no; AC_DEFINE(HAVE_LIBNET_1_0_API, 1, Libnet 1.0 API)],$LIBNETLIBS)
+fi
+
+dnl ************************************************************************
+dnl * Check for netinet/icmp6.h to enable the IPv6addr resource agent
+AC_CHECK_HEADERS(netinet/icmp6.h,[],[],[#include <sys/types.h>])
+AM_CONDITIONAL(USE_IPV6ADDR, test "$ac_cv_header_netinet_icmp6_h" = yes -a "$new_libnet" = yes )
+
+
+dnl ========================================================================
+dnl SNMP
+dnl ========================================================================
+
+SNMPLIB=""
+SNMPCONFIG=""
+
+ENABLE_SNMP="yes"
+if test "x${enable_snmp}" = "xno"; then
+ ENABLE_SNMP="no"
+fi
+
+AC_CHECK_HEADERS(ucd-snmp/snmp.h,[],[],[#include <sys/types.h>
+#include <ucd-snmp/asn1.h>])
+AC_CHECK_HEADERS(net-snmp/net-snmp-config.h)
+
+if test "x${ENABLE_SNMP}" = "xno"; then
+ # nothing
+ :
+elif test "x${ac_cv_header_net_snmp_net_snmp_config_h}" = "xyes"; then
+ AC_PATH_PROGS(SNMPCONFIG, net-snmp-config)
+ if test "X${SNMPCONFIG}" = "X"; then
+ AC_MSG_RESULT(You need the net_snmp development package to continue.)
+ ENABLE_SNMP="no"
+ else
+ AC_MSG_CHECKING(for special snmp libraries)
+ SNMPLIB=`${SNMPCONFIG} --libs`
+ AC_MSG_RESULT($SNMPLIB)
+ fi
+elif test "x${ac_cv_header_ucd_snmp_snmp_h}" = "xyes"; then
+ # UCD SNMP
+ # ucd-snmp-config does not seem to exist, so just
+ # rely on people having their LDFLAGS set to the path where
+ AC_CHECK_LIB(snmp, init_snmp, SNMPLIB="-lsnmp")
+ if test "X${SNMPLIB}" = "X"; then
+ AC_CHECK_LIB(ucdsnmp, init_snmp, SNMPLIB="-lucdsnmp")
+ fi
+ if test "X${SNMPLIB}" = "X"; then
+ ENABLE_SNMP="no"
+ AC_MSG_RESULT("Could not find ucdsnmp libary."
+ "Please make sure that libsnmp or libucdsnmp"
+ "are in your library path. Or the path to LDFLAGS")
+ fi
+else
+ ENABLE_SNMP="no"
+fi
+
+AC_SUBST(SNMPLIB)
+
+dnl ========================================================================
+dnl Stonith Devices
+dnl ========================================================================
+
+if test "x${enable_ipmilan}" = "x"; then
+ enable_ipmilan="yes"
+fi
+if test "x${enable_ipmilan}" = "xyes" -o "x${enable_ipmilan}" = "xtry"; then
+ AC_MSG_CHECKING(For libOpenIPMI version 1.4 or greater)
+ AC_TRY_COMPILE([#include <OpenIPMI/ipmiif.h>],
+ [ #if (OPENIPMI_VERSION_MAJOR == 1) && (OPENIPMI_VERSION_MINOR < 4)
+ #error "Too Old"
+ #endif ],
+ AC_MSG_RESULT("yes"); enable_ipmilan="yes",
+ AC_MSG_RESULT("no"); enable_ipmilan="no")
+else
+ enable_ipmilan="no"
+fi
+
+AC_CHECK_HEADERS(curl/curl.h)
+AC_CHECK_HEADERS(openhpi/SaHpi.h)
+AC_CHECK_HEADERS(vacmclient_api.h)
+
+AM_CONDITIONAL(USE_APC_SNMP, test "$ENABLE_SNMP" = "yes")
+AM_CONDITIONAL(USE_VACM, test "$ac_cv_header_vacmclient_api_h" = yes)
+AM_CONDITIONAL(USE_DRAC3, test "$ac_cv_header_curl_curl_h" = yes -a "$ac_cv_header_libxml_xpath_h" = yes)
+AM_CONDITIONAL(USE_OPENHPI, test "$ac_cv_header_openhpi_SaHpi_h" = yes && pkg-config --atleast-version 2.6 openhpi)
+AM_CONDITIONAL(IPMILAN_BUILD, test "X$enable_ipmilan" = "Xyes")
+
+dnl ========================================================================
+dnl ZLIB and BZ2
+dnl ========================================================================
+
+dnl check if header file and lib are there for zlib
+zlib_installed="yes"
+AC_CHECK_HEADERS(zlib.h, , [zlib_installed="no"],)
+AC_CHECK_LIB(z, compress , , [zlib_installed="no"])
+AM_CONDITIONAL(BUILD_ZLIB_COMPRESS_MODULE, test "x${zlib_installed}" = "xyes")
+if test "x${zlib_installed}" = "xno"; then
+ FatalMissingThing "zlib" \
+ "The zlib library is missing"
+fi
+
+bz2_installed="yes"
+AC_CHECK_HEADERS(bzlib.h, , [bz2_installed="no"],)
+AC_CHECK_LIB(bz2, BZ2_bzBuffToBuffCompress , , [bz2_installed="no"])
+AM_CONDITIONAL(BUILD_BZ2_COMPRESS_MODULE, test "x${bz2_installed}" = "xyes")
+
+#if test x$ac_cv_lib_bz2_BZ2_bzBuffToBuffCompress != xyes ; then
+# AC_MSG_ERROR(BZ2 libraries not found)
+#fi
+
+if test x$ac_cv_header_bzlib_h != xyes; then
+ AC_MSG_ERROR(BZ2 Development headers not found)
+fi
+
+dnl ========================================================================
+dnl Upstart via DBus
+dnl ========================================================================
+
+if test x$enable_upstart = xyes; then
+ PKG_CHECK_MODULES(DBUS, [dbus-1, dbus-glib-1])
+ AC_SUBST(DBUS_CFLAGS)
+ AC_SUBST(DBUS_LIBS)
+ AC_PATH_PROGS(DBUS_BINDING_TOOL, dbus-binding-tool)
+fi
+AM_CONDITIONAL(UPSTART, test x$enable_upstart = xyes)
+
+
+dnl ========================================================================
+dnl checks for library functions to replace them
+dnl
+dnl NoSuchFunctionName:
+dnl is a dummy function which no system supplies. It is here to make
+dnl the system compile semi-correctly on OpenBSD which doesn't know
+dnl how to create an empty archive
+dnl
+dnl scandir: Only on BSD.
+dnl System-V systems may have it, but hidden and/or deprecated.
+dnl A replacement function is supplied for it.
+dnl
+dnl setenv: is some bsdish function that should also be avoided (use
+dnl putenv instead)
+dnl On the other hand, putenv doesn't provide the right API for the
+dnl code and has memory leaks designed in (sigh...) Fortunately this
+dnl A replacement function is supplied for it.
+dnl
+dnl strerror: returns a string that corresponds to an errno.
+dnl A replacement function is supplied for it.
+dnl
+dnl unsetenv: is some bsdish function that should also be avoided (No
+dnl replacement)
+dnl A replacement function is supplied for it.
+dnl
+dnl strnlen: is a gnu function similar to strlen, but safer.
+dnl We wrote a tolearably-fast replacement function for it.
+dnl
+dnl strndup: is a gnu function similar to strdup, but safer.
+dnl We wrote a tolearably-fast replacement function for it.
+dnl
+dnl daemon: is a GNU function. The daemon() function is for programs wishing to
+dnl detach themselves from the controlling terminal and run in the
+dnl background as system daemon
+dnl A replacement function is supplied for it.
+
+AC_REPLACE_FUNCS(alphasort inet_pton NoSuchFunctionName scandir setenv strerror unsetenv strnlen strndup daemon strlcpy strlcat)
+
+dnl ========================================================================
+dnl Compiler flags
+dnl ========================================================================
+
+dnl Make sure that CFLAGS is not exported. If the user did
+dnl not have CFLAGS in their environment then this should have
+dnl no effect. However if CFLAGS was exported from the user's
+dnl environment, then the new CFLAGS will also be exported
+dnl to sub processes.
+
+CC_ERRORS=""
+CC_EXTRAS=""
+
+if export | fgrep " CFLAGS=" > /dev/null; then
+ SAVED_CFLAGS="$CFLAGS"
+ unset CFLAGS
+ CFLAGS="$SAVED_CFLAGS"
+ unset SAVED_CFLAGS
+fi
+
+if test "$GCC" != yes; then
+ CFLAGS="$CFLAGS -g"
+ enable_fatal_warnings=no
+else
+ CFLAGS="$CFLAGS -ggdb"
+
+ # We had to eliminate -Wnested-externs because of libtool changes
+ EXTRA_FLAGS="-fgnu89-inline
+ -fstack-protector-all
+ -Wall
+ -Waggregate-return
+ -Wbad-function-cast
+ -Wcast-qual
+ -Wcast-align
+ -Wdeclaration-after-statement
+ -Wendif-labels
+ -Wfloat-equal
+ -Wformat=2
+ -Wformat-security
+ -Wformat-nonliteral
+ -Winline
+ -Wmissing-prototypes
+ -Wmissing-declarations
+ -Wmissing-format-attribute
+ -Wnested-externs
+ -Wno-long-long
+ -Wno-strict-aliasing
+ -Wpointer-arith
+ -Wstrict-prototypes
+ -Wunsigned-char
+ -Wwrite-strings"
+
+# Additional warnings it might be nice to enable one day
+# -Wshadow
+# -Wunreachable-code
+
+ for j in $EXTRA_FLAGS
+ do
+ if
+ cc_supports_flag $j
+ then
+ CC_EXTRAS="$CC_EXTRAS $j"
+ fi
+ done
+
+dnl In lib/ais/Makefile.am there's a gcc option available as of v4.x
+
+ GCC_MAJOR=`gcc -v 2>&1 | awk 'END{print $3}' | sed 's/[.].*//'`
+ AM_CONDITIONAL(GCC_4, test "${GCC_MAJOR}" = 4)
+
+dnl System specific options
+
+ case "$host_os" in
+ *linux*|*bsd*)
+ if test "${enable_fatal_warnings}" = "unknown"; then
+ enable_fatal_warnings=yes
+ fi
+ ;;
+ esac
+
+ if test "x${enable_fatal_warnings}" != xno && cc_supports_flag -Werror ; then
+ enable_fatal_warnings=yes
+ else
+ enable_fatal_warnings=no
+ fi
+
+ if test "x${enable_ansi}" != xno && cc_supports_flag -std=iso9899:199409 ; then
+ AC_MSG_NOTICE(Enabling ANSI Compatibility)
+ CC_EXTRAS="$CC_EXTRAS -ansi -D_GNU_SOURCE -DANSI_ONLY"
+ fi
+
+ AC_MSG_NOTICE(Activated additional gcc flags: ${CC_EXTRAS})
+fi
+
+CFLAGS="$CFLAGS $CC_EXTRAS"
+
+NON_FATAL_CFLAGS="$CFLAGS"
+AC_SUBST(NON_FATAL_CFLAGS)
+
+dnl
+dnl We reset CFLAGS to include our warnings *after* all function
+dnl checking goes on, so that our warning flags don't keep the
+dnl AC_*FUNCS() calls above from working. In particular, -Werror will
+dnl *always* cause us troubles if we set it before here.
+dnl
+dnl
+if test "x${enable_fatal_warnings}" = xyes ; then
+ AC_MSG_NOTICE(Enabling Fatal Warnings)
+ CFLAGS="$CFLAGS -Werror"
+fi
+AC_SUBST(CFLAGS)
+
+dnl This is useful for use in Makefiles that need to remove one specific flag
+CFLAGS_COPY="$CFLAGS"
+AC_SUBST(CFLAGS_COPY)
+
+AC_SUBST(LIBADD_DL) dnl extra flags for dynamic linking libraries
+AC_SUBST(LIBADD_INTL) dnl extra flags for GNU gettext stuff...
+
+AC_SUBST(LOCALE)
+
+dnl Options for cleaning up the compiler output
+PRETTY_CC=""
+QUIET_LIBTOOL_OPTS=""
+QUIET_MAKE_OPTS=""
+if test x"${enable_pretty}" = "xyes"; then
+ enable_quiet="yes"
+ echo "install_sh: ${install_sh}"
+ PRETTY_CC="`pwd`/tools/ccdv"
+ dnl It would be nice if this was rebuilt when needed too...
+ mkdir `pwd`/tools/ 2>/dev/null
+ ${CC} $CFLAGS -o `pwd`/tools/ccdv ${srcdir}/tools/ccdv.c
+ CC="\$(PRETTY_CC) ${CC}"
+fi
+if test "x${enable_quiet}" = "xyes"; then
+ QUIET_LIBTOOL_OPTS="--quiet"
+ QUIET_MAKE_OPTS="--quiet"
+fi
+
+AC_MSG_RESULT(Supress make details: ${enable_quiet})
+AC_MSG_RESULT(Pretty print compiler output: ${enable_pretty})
+
+dnl Put the above variables to use
+LIBTOOL="${LIBTOOL} --tag=CC \$(QUIET_LIBTOOL_OPTS)"
+MAKE="${MAKE} \$(QUIET_MAKE_OPTS)"
+
+AC_SUBST(CC)
+AC_SUBST(MAKE)
+AC_SUBST(LIBTOOL)
+AC_SUBST(PRETTY_CC)
+AC_SUBST(QUIET_MAKE_OPTS)
+AC_SUBST(QUIET_LIBTOOL_OPTS)
+
+dnl The Makefiles and shell scripts we output
+AC_CONFIG_FILES(Makefile \
+config/Makefile \
+include/Makefile \
+ include/pils/Makefile \
+ include/pils/plugin.h \
+ include/clplumbing/Makefile \
+ include/lrm/Makefile \
+ include/stonith/Makefile \
+lib/Makefile \
+ lib/pils/Makefile \
+ lib/clplumbing/Makefile \
+ lib/stonith/Makefile \
+ lib/lrm/Makefile \
+ lib/plugins/Makefile \
+ lib/plugins/InterfaceMgr/Makefile \
+ lib/plugins/compress/Makefile \
+ lib/plugins/lrm/Makefile \
+ lib/plugins/lrm/dbus/Makefile \
+ lib/plugins/stonith/Makefile \
+ lib/plugins/stonith/ribcl.py \
+ lib/plugins/stonith/external/Makefile \
+ lib/plugins/stonith/external/drac5 \
+ lib/plugins/stonith/external/kdumpcheck \
+ lib/plugins/stonith/external/ssh \
+ lib/plugins/stonith/external/ippower9258 \
+ lib/plugins/stonith/external/xen0-ha \
+lrm/Makefile \
+ lrm/lrmd/Makefile \
+ lrm/admin/Makefile \
+ lrm/admin/cibsecret \
+ lrm/test/Makefile \
+ lrm/test/regression.sh \
+ lrm/test/lrmregtest \
+ lrm/test/LRMBasicSanityCheck \
+ lrm/test/testcases/Makefile \
+logd/Makefile \
+logd/logd \
+logd/logd.service \
+replace/Makefile \
+hb_report/Makefile \
+ hb_report/hb_report \
+doc/Makefile \
+ doc/ha_logd.xml \
+ doc/ha_logger.xml \
+ doc/stonith.xml \
+ doc/meatclient.xml \
+ doc/stonith/Makefile
+)
+
+dnl Now process the entire list of files added by previous
+dnl calls to AC_CONFIG_FILES()
+AC_OUTPUT()
+
+dnl *****************
+dnl Configure summary
+dnl *****************
+
+AC_MSG_RESULT([])
+AC_MSG_RESULT([$PACKAGE configuration:])
+AC_MSG_RESULT([ Version = ${VERSION} (Build: $GLUE_BUILD_VERSION)])
+AC_MSG_RESULT([ Features =${FEATURES}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([ Prefix = ${prefix}])
+AC_MSG_RESULT([ Executables = ${sbindir}])
+AC_MSG_RESULT([ Man pages = ${mandir}])
+AC_MSG_RESULT([ Libraries = ${libdir}])
+AC_MSG_RESULT([ Header files = ${includedir}])
+AC_MSG_RESULT([ Arch-independent files = ${datadir}])
+AC_MSG_RESULT([ Documentation = ${docdir}])
+AC_MSG_RESULT([ State information = ${localstatedir}])
+AC_MSG_RESULT([ System configuration = ${sysconfdir}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([ Use system LTDL = ${ac_cv_lib_ltdl_lt_dlopen}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([ HA group name = ${GLUE_DAEMON_GROUP}])
+AC_MSG_RESULT([ HA user name = ${GLUE_DAEMON_USER}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([ CFLAGS = ${CFLAGS}])
+AC_MSG_RESULT([ Libraries = ${LIBS}])
+AC_MSG_RESULT([ Stack Libraries = ${CLUSTERLIBS}])
+
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..c8d67a8
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,53 @@
+#
+# heartbeat: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Michael Moerz
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in hb_report.xml ha_logd.xml ha_logger.xml stonith.xml meatclient.xml
+
+CLEANFILES = $(man_MANS)
+
+SUBDIRS = stonith
+
+hanoarchdir = $(datadir)/heartbeat
+
+man_MANS =
+
+if BUILD_DOC
+man_MANS += hb_report.8 ha_logd.8 ha_logger.1 stonith.8 meatclient.8
+
+EXTRA_DIST = $(man_MANS)
+
+STYLESHEET_PREFIX ?= http://docbook.sourceforge.net/release/xsl/current
+MANPAGES_STYLESHEET ?= $(STYLESHEET_PREFIX)/manpages/docbook.xsl
+HTML_STYLESHEET ?= $(STYLESHEET_PREFIX)/xhtml/docbook.xsl
+FO_STYLESHEET ?= $(STYLESHEET_PREFIX)/fo/docbook.xsl
+
+XSLTPROC_OPTIONS ?= --xinclude
+XSLTPROC_MANPAGES_OPTIONS ?= $(XSLTPROC_OPTIONS)
+XSLTPROC_HTML_OPTIONS ?= $(XSLTPROC_OPTIONS)
+XSLTPROC_FO_OPTIONS ?= $(XSLTPROC_OPTIONS)
+
+%.5 %.8 %.1: %.xml
+ $(XSLTPROC) \
+ $(XSLTPROC_MANPAGES_OPTIONS) \
+ $(MANPAGES_STYLESHEET) $<
+
+hb_report.8: hb_report.8.txt
+ a2x -f manpage $<
+
+endif
diff --git a/doc/ha_logd.xml.in b/doc/ha_logd.xml.in
new file mode 100644
index 0000000..368f06d
--- /dev/null
+++ b/doc/ha_logd.xml.in
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<refentry id="re-ha_logd">
+ <refentryinfo>
+ <date>December 8, 2009</date>
+ <productname>@PACKAGE_NAME@</productname>
+ <productnumber>@VERSION@</productnumber>
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Robertson</surname>
+ <contrib>ha_logd</contrib>
+ <email>alanr@unix.sh</email>
+ </author>
+ <author>
+ <surname>Shi</surname>
+ <firstname>Guochun</firstname>
+ <contrib>ha_logd</contrib>
+ <email>gshi@ncsa.uiuc.edu</email>
+ </author>
+ <author>
+ <surname>Lars</surname>
+ <firstname>Marowsky-Bree</firstname>
+ <contrib>ha_logd</contrib>
+ <email>lmb@suse.de</email>
+ </author>
+ <author>
+ <firstname>Florian</firstname>
+ <surname>Haas</surname>
+ <contrib>man page</contrib>
+ <email>florian.haas@linbit.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>ha_logd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="manual">System administration utilities</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+ <refname>ha_logd</refname>
+ <refpurpose>Logging Daemon for High-Availability Linux</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ha_logd</command>
+ <arg choice="opt"><option>-s</option></arg>
+ <arg choice="opt"><option>-k</option></arg>
+ <arg choice="opt"><option>-d</option></arg>
+ <arg choice="opt"><option>-h</option></arg>
+ <arg choice="opt"><option>-c</option> <replaceable>file</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsection id="rs-ha_logd-description">
+ <title>Description</title>
+ <para><command>ha_logd</command> is a logging daemon for
+ Linux-HA. It receives messages from a local domain socket
+ <filename>@HA_LOGDAEMON_IPC@</filename>, and writes them to
+ appropriate files and syslog if enabled. The reason for utilizing
+ this logging daemon is that occasionally Heartbeat suffers from
+ disk I/O delays. By sending log messages to a logging daemon,
+ heartbeat can avoid such I/O delays.</para>
+ </refsection>
+ <refsection id="rs-ha_logd-options">
+ <title>Options</title>
+ <para>The following options are supported:</para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-s</option>
+ </term>
+ <listitem>
+ <para>Show <command>ha_logd</command> status (either
+ <token>running</token> or <token>stopped</token>)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-k</option>
+ </term>
+ <listitem>
+ <para>Stop (kill) the daemon</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-d</option>
+ </term>
+ <listitem>
+ <para>Daemonize (without this option,
+ <command>ha_logd</command> will run in the
+ foreground)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-h</option>
+ </term>
+ <listitem>
+ <para>Show a brief usage message</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-c</option> <replaceable>file</replaceable>
+ </term>
+ <listitem>
+ <para>Configuration file. You may configure a regular log
+ file, debug log file, log facility, and entity. For details,
+ see the example <filename>ha_logd.cf</filename> file found
+ in the documentation.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsection>
+ <refsection id="rs-ha_logd-files">
+ <title>Files</title>
+ <itemizedlist>
+ <listitem>
+ <para><filename>@GLUE_STATE_DIR@/ha_logd.pid</filename> &ndash; PID file</para>
+ </listitem>
+ <listitem>
+ <para><filename>ha_logd.cf</filename> &ndash; example configuration file</para>
+ </listitem>
+ </itemizedlist>
+ </refsection>
+ <refsection id="rs-ha_logd-seealso">
+ <title>See also</title>
+ <para>
+ <citerefentry><refentrytitle>heartbeat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>ha_logger</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsection>
+</refentry>
diff --git a/doc/ha_logger.xml.in b/doc/ha_logger.xml.in
new file mode 100644
index 0000000..dce7fe2
--- /dev/null
+++ b/doc/ha_logger.xml.in
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<refentry id="re-ha_logger">
+ <refentryinfo>
+ <date>December 8, 2009</date>
+ <productname>@PACKAGE_NAME@</productname>
+ <productnumber>@VERSION@</productnumber>
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Robertson</surname>
+ <contrib>ha_logd</contrib>
+ <email>alanr@unix.sh</email>
+ </author>
+ <author>
+ <surname>Shi</surname>
+ <firstname>Guochun</firstname>
+ <contrib>ha_logd</contrib>
+ <email>gshi@ncsa.uiuc.edu</email>
+ </author>
+ <author>
+ <surname>Lars</surname>
+ <firstname>Marowsky-Bree</firstname>
+ <contrib>ha_logd</contrib>
+ <email>lmb@suse.de</email>
+ </author>
+ <author>
+ <firstname>Florian</firstname>
+ <surname>Haas</surname>
+ <contrib>man page</contrib>
+ <email>florian.haas@linbit.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>ha_logger</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="manual">User commands</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+ <refname>ha_logger</refname>
+ <refpurpose>Log a message to files and/or syslog through the HA
+ Logging Daemon</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ha_logger</command>
+ <arg choice="opt">
+ <option>-D</option>
+ <group choice="plain">
+ <arg>ha-log</arg>
+ <arg>ha-debug</arg>
+ </group>
+ </arg>
+ <arg choice="opt">
+ <option>-t</option>
+ <replaceable>tag</replaceable>
+ </arg>
+ <arg choice="plain" rep="repeat">
+ <replaceable>message</replaceable>
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsection id="rs-ha_logger-description">
+ <title>Description</title>
+ <para><command>ha_logger</command> is used to log a message to
+ files/syslog through the HA Logging Daemon.</para>
+ </refsection>
+ <refsection id="rs-ha_logger-options">
+ <title>Options</title>
+ <para>The following options are supported:</para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-D</option> <token>ha-log</token>|<token>ha-debug</token>
+ </term>
+ <listitem>
+ <para>Log the message to different
+ files. <token>ha-log</token> will log the message to the log
+ file and the debug file, while <token>ha-debug</token> will
+ log the message to the debug file only.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-t</option> <replaceable>tag</replaceable>
+ </term>
+ <listitem>
+ <para>Mark every line in the log with the specified
+ <replaceable>tag</replaceable>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <replaceable>message</replaceable>
+ </term>
+ <listitem>
+ <para>The message that should be logged.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsection>
+ <refsection id="rs-ha_logger-seealso">
+ <title>See also</title>
+ <para>
+ <citerefentry><refentrytitle>heartbeat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>ha_logd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsection>
+</refentry>
diff --git a/doc/hb_report.8.txt b/doc/hb_report.8.txt
new file mode 100644
index 0000000..5efbc32
--- /dev/null
+++ b/doc/hb_report.8.txt
@@ -0,0 +1,478 @@
+:man source: hb_report
+:man version: 1.2
+:man manual: Pacemaker documentation
+
+hb_report(8)
+============
+
+
+NAME
+----
+hb_report - create report for CRM based clusters (Pacemaker)
+
+
+SYNOPSIS
+--------
+*hb_report* -f {time|"cts:"testnum} [-t time] [-u user] [-l file]
+ [-n nodes] [-E files] [-p patt] [-L patt] [-e prog]
+ [-MSDCZAQVsvhd] [dest]
+
+
+DESCRIPTION
+-----------
+The hb_report(1) is a utility to collect all information (logs,
+configuration files, system information, etc) relevant to
+Pacemaker (CRM) over the given period of time.
+
+
+OPTIONS
+-------
+dest::
+ The report name. It can also contain a path where to put the
+ report tarball. If left out, the tarball is created in the
+ current directory named "hb_report-current_date", for instance
+ hb_report-Wed-03-Mar-2010.
+
+*-d*::
+ Don't create the compressed tar, but leave the result in a
+ directory.
+
+*-f* { time | "cts:"testnum }::
+ The start time from which to collect logs. The time is in the
+ format as used by the Date::Parse perl module. For cts tests,
+ specify the "cts:" string followed by the test number. This
+ option is required.
+
+*-t* time::
+ The end time to which to collect logs. Defaults to now.
+
+*-n* nodes::
+ A list of space separated hostnames (cluster members).
+ hb_report may try to find out the set of nodes by itself, but
+ if it runs on the loghost which, as it is usually the case,
+ does not belong to the cluster, that may be difficult. Also,
+ OpenAIS doesn't contain a list of nodes and if Pacemaker is
+ not running, there is no way to find it out automatically.
+ This option is cumulative (i.e. use -n "a b" or -n a -n b).
+
+*-l* file::
+ Log file location. If, for whatever reason, hb_report cannot
+ find the log files, you can specify its absolute path.
+
+*-E* files::
+ Extra log files to collect. This option is cumulative. By
+ default, /var/log/messages are collected along with the
+ cluster logs.
+
+*-M*::
+ Don't collect extra log files, but only the file containing
+ messages from the cluster subsystems.
+
+*-L* patt::
+ A list of regular expressions to match in log files for
+ analysis. This option is additive (default: "CRIT: ERROR:").
+
+*-p* patt::
+ Additional patterns to match parameter name which contain
+ sensitive information. This option is additive (default: "passw.*").
+
+*-Q*::
+ Quick run. Gathering some system information can be expensive.
+ With this option, such operations are skipped and thus
+ information collecting sped up. The operations considered
+ I/O or CPU intensive: verifying installed packages content,
+ sanitizing files for sensitive information, and producing dot
+ files from PE inputs.
+
+*-A*::
+ This is an OpenAIS cluster. hb_report has some heuristics to
+ find the cluster stack, but that is not always reliable.
+ By default, hb_report assumes that it is run on a Heartbeat
+ cluster.
+
+*-u* user::
+ The ssh user. hb_report will try to login to other nodes
+ without specifying a user, then as "root", and finally as
+ "hacluster". If you have another user for administration over
+ ssh, please use this option.
+
+*-X* ssh-options::
+ Extra ssh options. These will be added to every ssh
+ invocation. Alternatively, use `$HOME/.ssh/config` to setup
+ desired ssh connection options.
+
+*-S*::
+ Single node operation. Run hb_report only on this node and
+ don't try to start slave collectors on other members of the
+ cluster. Under normal circumstances this option is not
+ needed. Use if ssh(1) does not work to other nodes.
+
+*-Z*::
+ If the destination directory exist, remove it instead of
+ exiting (this is default for CTS).
+
+*-V*::
+ Print the version including the last repository changeset.
+
+*-v*::
+ Increase verbosity. Normally used to debug unexpected
+ behaviour.
+
+*-h*::
+ Show usage and some examples.
+
+*-D* (obsolete)::
+ Don't invoke editor to fill the description text file.
+
+*-e* prog (obsolete)::
+ Your favourite text editor. Defaults to $EDITOR, vim, vi,
+ emacs, or nano, whichever is found first.
+
+*-C* (obsolete)::
+ Remove the destination directory once the report has been put
+ in a tarball.
+
+EXAMPLES
+--------
+Last night during the backup there were several warnings
+encountered (logserver is the log host):
+
+ logserver# hb_report -f 3:00 -t 4:00 -n "node1 node2" report
+
+collects everything from all nodes from 3am to 4am last night.
+The files are compressed to a tarball report.tar.bz2.
+
+Just found a problem during testing:
+
+ # note the current time
+ node1# date
+ Fri Sep 11 18:51:40 CEST 2009
+ node1# /etc/init.d/heartbeat start
+ node1# nasty-command-that-breaks-things
+ node1# sleep 120 #wait for the cluster to settle
+ node1# hb_report -f 18:51 hb1
+
+ # if hb_report can't figure out that this is corosync
+ node1# hb_report -f 18:51 -A hb1
+
+ # if hb_report can't figure out the cluster members
+ node1# hb_report -f 18:51 -n "node1 node2" hb1
+
+The files are compressed to a tarball hb1.tar.bz2.
+
+INTERPRETING RESULTS
+--------------------
+The compressed tar archive is the final product of hb_report.
+This is one example of its content, for a CTS test case on a
+three node OpenAIS cluster:
+
+ $ ls -RF 001-Restart
+
+ 001-Restart:
+ analysis.txt events.txt logd.cf s390vm13/ s390vm16/
+ description.txt ha-log.txt openais.conf s390vm14/
+
+ 001-Restart/s390vm13:
+ STOPPED crm_verify.txt hb_uuid.txt openais.conf@ sysinfo.txt
+ cib.txt dlm_dump.txt logd.cf@ pengine/ sysstats.txt
+ cib.xml events.txt messages permissions.txt
+
+ 001-Restart/s390vm13/pengine:
+ pe-input-738.bz2 pe-input-740.bz2 pe-warn-450.bz2
+ pe-input-739.bz2 pe-warn-449.bz2 pe-warn-451.bz2
+
+ 001-Restart/s390vm14:
+ STOPPED crm_verify.txt hb_uuid.txt openais.conf@ sysstats.txt
+ cib.txt dlm_dump.txt logd.cf@ permissions.txt
+ cib.xml events.txt messages sysinfo.txt
+
+ 001-Restart/s390vm16:
+ STOPPED crm_verify.txt hb_uuid.txt messages sysinfo.txt
+ cib.txt dlm_dump.txt hostcache openais.conf@ sysstats.txt
+ cib.xml events.txt logd.cf@ permissions.txt
+
+The top directory contains information which pertains to the
+cluster or event as a whole. Files with exactly the same content
+on all nodes will also be at the top, with per-node links created
+(as it is in this example the case with openais.conf and logd.cf).
+
+The cluster log files are named ha-log.txt regardless of the
+actual log file name on the system. If it is found on the
+loghost, then it is placed in the top directory. If not, the top
+directory ha-log.txt contains all nodes logs merged and sorted by
+time. Files named messages are excerpts of /var/log/messages from
+nodes.
+
+Most files are copied verbatim or they contain output of a
+command. For instance, cib.xml is a copy of the CIB found in
+/var/lib/heartbeat/crm/cib.xml. crm_verify.txt is output of the
+crm_verify(8) program.
+
+Some files are result of a more involved processing:
+
+ *analysis.txt*::
+ A set of log messages matching user defined patterns (may be
+ provided with the -L option).
+
+ *events.txt*::
+ A set of log messages matching event patterns. It should
+ provide information about major cluster motions without
+ unnecessary details. These patterns are devised by the
+ cluster experts. Currently, the patterns cover membership
+ and quorum changes, resource starts and stops, fencing
+ (stonith) actions, and cluster starts and stops. events.txt
+ is always generated for each node. In case the central
+ cluster log was found, also combined for all nodes.
+
+ *permissions.txt*::
+ One of the more common problem causes are file and directory
+ permissions. hb_report looks for a set of predefined
+ directories and checks their permissions. Any issues are
+ reported here.
+
+ *backtraces.txt*::
+ gdb generated backtrace information for cores dumped
+ within the specified period.
+
+ *sysinfo.txt*::
+ Various release information about the platform, kernel,
+ operating system, packages, and anything else deemed to be
+ relevant. The static part of the system.
+
+ *sysstats.txt*::
+ Output of various system commands such as ps(1), uptime(1),
+ netstat(8), and ifconfig(8). The dynamic part of the system.
+
+description.txt should contain a user supplied description of the
+problem, but since it is very seldom used, it will be dropped
+from the future releases.
+
+PREREQUISITES
+-------------
+
+ssh::
+ It is not strictly required, but you won't regret having a
+ password-less ssh. It is not too difficult to setup and will save
+ you a lot of time. If you can't have it, for example because your
+ security policy does not allow such a thing, or you just prefer
+ menial work, then you will have to resort to the semi-manual
+ semi-automated report generation. See below for instructions.
+ +
+ If you need to supply a password for your passphrase/login, then
+ always use the `-u` option.
+ +
+ For extra ssh(1) options, if you're too lazy to setup
+ $HOME/.ssh/config, use the `-X` option. Do not forget to put
+ the options in quotes.
+
+sudo::
+ If the ssh user (as specified with the `-u` option) is other
+ than `root`, then `hb_report` uses `sudo` to collect the
+ information which is readable only by the `root` user. In that
+ case it is required to setup the `sudoers` file properly. The
+ user (or group to which the user belongs) should have the
+ following line:
+ +
+ <user> ALL = NOPASSWD: /usr/sbin/hb_report
+ +
+ See the `sudoers(5)` man page for more details.
+
+Times::
+ In order to find files and messages in the given period and to
+ parse the `-f` and `-t` options, `hb_report` uses perl and one of the
+ `Date::Parse` or `Date::Manip` perl modules. Note that you need
+ only one of these. Furthermore, on nodes which have no logs and
+ where you don't run `hb_report` directly, no date parsing is
+ necessary. In other words, if you run this on a loghost then you
+ don't need these perl modules on the cluster nodes.
+ +
+ On rpm based distributions, you can find `Date::Parse` in
+ `perl-TimeDate` and on Debian and its derivatives in
+ `libtimedate-perl`.
+
+Core dumps::
+ To backtrace core dumps gdb is needed and the packages with
+ the debugging info. The debug info packages may be installed
+ at the time the report is created. Let's hope that you will
+ need this really seldom.
+
+TIMES
+-----
+
+Specifying times can at times be a nuisance. That is why we have
+chosen to use one of the perl modules--they do allow certain
+freedom when talking dates. You can either read the instructions
+at the
+http://search.cpan.org/dist/TimeDate/lib/Date/Parse.pm#EXAMPLE_DATES[Date::Parse
+examples page].
+or just rely on common sense and try stuff like:
+
+ 3:00 (today at 3am)
+ 15:00 (today at 3pm)
+ 2007/9/1 2pm (September 1st at 2pm)
+ Tue Sep 15 20:46:27 CEST 2009 (September 15th etc)
+
+`hb_report` will (probably) complain if it can't figure out what do
+you mean.
+
+Try to delimit the event as close as possible in order to reduce
+the size of the report, but still leaving a minute or two around
+for good measure.
+
+`-f` is not optional. And don't forget to quote dates when they
+contain spaces.
+
+
+Should I send all this to the rest of Internet?
+-----------------------------------------------
+
+By default, the sensitive data in CIB and PE files is not mangled
+by `hb_report` because that makes PE input files mostly useless.
+If you still have no other option but to send the report to a
+public mailing list and do not want the sensitive data to be
+included, use the `-s` option. Without this option, `hb_report`
+will issue a warning if it finds information which should not be
+exposed. By default, parameters matching 'passw.*' are considered
+sensitive. Use the `-p` option to specify additional regular
+expressions to match variable names which may contain information
+you don't want to leak. For example:
+
+ # hb_report -f 18:00 -p "user.*" -p "secret.*" /var/tmp/report
+
+Heartbeat's ha.cf is always sanitized. Logs and other files are
+not filtered.
+
+LOGS
+----
+
+It may be tricky to find syslog logs. The scheme used is to log a
+unique message on all nodes and then look it up in the usual
+syslog locations. This procedure is not foolproof, in particular
+if the syslog files are in a non-standard directory. We look in
+/var/log /var/logs /var/syslog /var/adm /var/log/ha
+/var/log/cluster. In case we can't find the logs, please supply
+their location:
+
+ # hb_report -f 5pm -l /var/log/cluster1/ha-log -S /tmp/report_node1
+
+If you have different log locations on different nodes, well,
+perhaps you'd like to make them the same and make life easier for
+everybody.
+
+Files starting with "ha-" are preferred. In case syslog sends
+messages to more than one file, if one of them is named ha-log or
+ha-debug those will be favoured over syslog or messages.
+
+hb_report supports also archived logs in case the period
+specified extends that far in the past. The archives must reside
+in the same directory as the current log and their names must
+be prefixed with the name of the current log (syslog-1.gz or
+messages-20090105.bz2).
+
+If there is no separate log for the cluster, possibly unrelated
+messages from other programs are included. We don't filter logs,
+but just pick a segment for the period you specified.
+
+MANUAL REPORT COLLECTION
+------------------------
+
+So, your ssh doesn't work. In that case, you will have to run
+this procedure on all nodes. Use `-S` so that `hb_report` doesn't
+bother with ssh:
+
+ # hb_report -f 5:20pm -t 5:30pm -S /tmp/report_node1
+
+If you also have a log host which is not in the cluster, then
+you'll have to copy the log to one of the nodes and tell us where
+it is:
+
+ # hb_report -f 5:20pm -t 5:30pm -l /var/tmp/ha-log -S /tmp/report_node1
+
+OPERATION
+---------
+hb_report collects files and other information in a fairly
+straightforward way. The most complex tasks are discovering the
+log file locations (if syslog is used which is the most common
+case) and coordinating the operation on multiple nodes.
+
+The instance of hb_report running on the host where it was
+invoked is the master instance. Instances running on other nodes
+are slave instances. The master instance communicates with slave
+instances by ssh. There are multiple ssh invocations per run, so
+it is essential that the ssh works without password, i.e. with
+the public key authentication and authorized_keys.
+
+The operation consists of three phases. Each phase must finish
+on all nodes before the next one can commence. The first phase
+consists of logging unique messages through syslog on all nodes.
+This is the shortest of all phases.
+
+The second phase is the most involved. During this phase all
+local information is collected, which includes:
+
+- logs (both current and archived if the start time is far in the past)
+- various configuration files (corosync, heartbeat, logd)
+- the CIB (both as xml and as represented by the crm shell)
+- pengine inputs (if this node was the DC at any point in
+ time over the given period)
+- system information and status
+- package information and status
+- dlm lock information
+- backtraces (if there were core dumps)
+
+The third phase is collecting information from all nodes and
+analyzing it. The analyzis consists of the following tasks:
+
+- identify files equal on all nodes which may then be moved to
+ the top directory
+- save log messages matching user defined patterns
+ (defaults to ERRORs and CRITical conditions)
+- report if there were coredumps and by whom
+- report crm_verify(8) results
+- save log messages matching major events to events.txt
+- in case logging is configured without loghost, node logs and
+ events files are combined using a perl utility
+
+
+BUGS
+----
+Finding logs may at times be extremely difficult, depending on
+how weird the syslog configuration. It would be nice to ask
+syslog-ng developers to provide a way to find out the log
+destination based on facility and priority.
+
+If you think you found a bug, please rerun with the -v option and
+attach the output to bugzilla.
+
+hb_report can function in a satisfactory way only if ssh works to
+all nodes using authorized_keys (without password).
+
+There are way too many options.
+
+
+AUTHOR
+------
+Written by Dejan Muhamedagic, <dejan@suse.de>
+
+
+RESOURCES
+---------
+Pacemaker: <http://clusterlabs.org/>
+
+Heartbeat and other Linux HA resources: <http://linux-ha.org/wiki>
+
+OpenAIS: <http://www.openais.org/>
+
+Corosync: <http://www.corosync.org/>
+
+
+SEE ALSO
+--------
+Date::Parse(3)
+
+
+COPYING
+-------
+Copyright \(C) 2007-2009 Dejan Muhamedagic. Free use of this
+software is granted under the terms of the GNU General Public License (GPL).
+
diff --git a/doc/meatclient.xml.in b/doc/meatclient.xml.in
new file mode 100644
index 0000000..778a57c
--- /dev/null
+++ b/doc/meatclient.xml.in
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<refentry id="re-meatclient">
+ <refentryinfo>
+ <date>December 4, 2009</date>
+ <productname>Cluster Glue</productname>
+ <productnumber>@VERSION@</productnumber>
+ <authorgroup>
+ <author>
+ <firstname>Gregor</firstname>
+ <surname>Binder</surname>
+ <contrib>meatclient</contrib>
+ <email>gbinder@sysfive.com</email>
+ </author>
+ <author>
+ <firstname>Michael</firstname>
+ <surname>M&ouml;rz</surname>
+ <contrib>man page</contrib>
+ <email>mimem@debian.org</email>
+ </author>
+ <author>
+ <firstname>Simon</firstname>
+ <surname>Horman</surname>
+ <contrib>man page</contrib>
+ <email>horms@vergenet.net</email>
+ </author>
+ <author>
+ <firstname>Florian</firstname>
+ <surname>Haas</surname>
+ <contrib>man page</contrib>
+ <email>florian.haas@linbit.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>meatclient</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="manual">System administration utilities</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+ <refname>meatclient</refname>
+ <refpurpose>Manually confirm that a node has been removed from the
+ cluster</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <para><command>meatclient</command> <option>-c</option> <replaceable>nodename</replaceable></para>
+ </refsynopsisdiv>
+ <refsection id="rs-meatclient-description">
+ <title>Description</title>
+ <para><command>meatclient</command> confirms that a node has been
+ manually removed from the cluster. It instructs the cluster
+ manager, via the meatware STONITH plugin, that it is safe to
+ continue cluster operations.</para>
+ </refsection>
+ <refsection id="rs-meatclient-options">
+ <title>Options</title>
+ <para>The following options are supported:</para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-c</option> <replaceable>nodename</replaceable>
+ </term>
+ <listitem>
+ <para><replaceable>nodename</replaceable> is the name of the
+ cluster node that has been fenced.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsection>
+ <refsection id="rs-meatclient-seealso">
+ <title>See also</title>
+ <para>
+ <citerefentry><refentrytitle>heartbeat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>stonith</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsection>
+</refentry>
diff --git a/doc/stonith.xml.in b/doc/stonith.xml.in
new file mode 100644
index 0000000..575c339
--- /dev/null
+++ b/doc/stonith.xml.in
@@ -0,0 +1,315 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<refentry id="re-stonith">
+ <refentryinfo>
+ <date>December 7, 2009</date>
+ <productname>@PACKAGE_NAME@</productname>
+ <productnumber>@VERSION@</productnumber>
+ <authorgroup>
+ <author>
+ <firstname>Alan</firstname>
+ <surname>Robertson</surname>
+ <contrib>stonith</contrib>
+ <email>alanr@unix.sh</email>
+ </author>
+ <author>
+ <firstname>Simon</firstname>
+ <surname>Horman</surname>
+ <contrib>man page</contrib>
+ <email>horms@vergenet.net</email>
+ </author>
+ <author>
+ <firstname>Florian</firstname>
+ <surname>Haas</surname>
+ <contrib>man page</contrib>
+ <email>florian.haas@linbit.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>stonith</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="manual">System administration utilities</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+ <refname>stonith</refname>
+ <refpurpose>extensible interface for remotely powering down a node
+ in the cluster</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>stonith</command>
+ <arg choice="plain"><option>-h</option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>stonith</command>
+ <arg choice="opt"><option>-s</option></arg>
+ <arg choice="opt"><option>-h</option></arg>
+ <arg choice="plain"><option>-L</option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>stonith</command>
+ <arg choice="opt"><option>-s</option></arg>
+ <arg choice="opt"><option>-h</option></arg>
+ <arg choice="plain"><option>-t</option> <replaceable>stonith-device-type</replaceable></arg>
+ <arg choice="plain"><option>-n</option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>stonith</command>
+ <arg choice="opt"><option>-s</option></arg>
+ <arg choice="opt"><option>-h</option></arg>
+ <arg choice="plain"><option>-t</option> <replaceable>stonith-device-type</replaceable></arg>
+ <group choice="req" rep="norepeat">
+ <group choice="plain" rep="repeat">
+ <arg choice="plain"><replaceable>name</replaceable>=<replaceable>value</replaceable></arg>
+ </group>
+ <arg choice="plain"><option>-p</option> <replaceable>stonith-device-parameters</replaceable></arg>
+ <arg choice="plain"><option>-F</option> <replaceable>stonith-device-parameters-file</replaceable></arg>
+ </group>
+ <arg choice="opt"><option>-c</option> <replaceable>count</replaceable></arg>
+ <arg choice="opt"><option>-l</option></arg>
+ <arg choice="opt"><option>-S</option></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>stonith</command>
+ <arg choice="opt"><option>-s</option></arg>
+ <arg choice="opt"><option>-h</option></arg>
+ <arg choice="plain"><option>-t</option> <replaceable>stonith-device-type</replaceable></arg>
+ <group choice="req" rep="norepeat">
+ <group choice="plain" rep="repeat">
+ <arg choice="plain"><replaceable>name</replaceable>=<replaceable>value</replaceable></arg>
+ </group>
+ <arg choice="plain"><option>-p</option> <replaceable>stonith-device-parameters</replaceable></arg>
+ <arg choice="plain"><option>-F</option> <replaceable>stonith-device-parameters-file</replaceable></arg>
+ </group>
+ <arg choice="opt"><option>-c</option> <replaceable>count</replaceable></arg>
+ <arg choice="opt"><option>-T</option>
+ <group choice="req">
+ <arg choice="plain">reset</arg>
+ <arg choice="plain">on</arg>
+ <arg choice="plain">off</arg>
+ </group>
+ </arg>
+ <arg><replaceable>nodename</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsection id="rs-stonith-description">
+ <title>Description</title>
+ <para>The STONITH module provides an extensible interface for
+ remotely powering down a node in the cluster (STONITH = Shoot The
+ Other Node In The Head). The idea is quite simple: when the
+ software running on one machine wants to make sure another machine
+ in the cluster is not using a resource, pull the plug on the other
+ machine. It's simple and reliable, albeit admittedly
+ brutal.</para>
+ </refsection>
+ <refsection id="rs-stonith-options">
+ <title>Options</title>
+ <para>The following options are supported:</para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-c</option> <replaceable>count</replaceable>
+ </term>
+ <listitem>
+ <para>Perform any actions identified by the
+ <option>-l</option>, <option>-S</option> and
+ <option>-T</option> options <replaceable>count</replaceable>
+ times.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-F</option> <replaceable>stonith-device-parameters-file</replaceable>
+ </term>
+ <listitem>
+ <para>Path of file specifying parameters for a stonith
+ device. To determine the syntax of the parameters file for a
+ given device type run:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t stonith-device-type -n</userinput></screen>
+ <para>All of the listed parameters need to appear in order
+ on a single line in the parameters file and be delimited by
+ whitespace.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-h</option>
+ </term>
+ <listitem>
+ <para>Display detailed information about a stonith device
+ including description, configuration information, parameters
+ and any other related information. When specified without a
+ stonith-device-type, detailed information on all stonith
+ devices is displayed.</para>
+ <para>If you don't yet own a stonith device and want to know
+ more about the ones we support, this information is likely
+ to be helpful.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-L</option>
+ </term>
+ <listitem>
+ <para>List the valid stonith device types, suitable for
+ passing as an argument to the <option>-t</option>
+ option.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-l</option>
+ </term>
+ <listitem>
+ <para>List the hosts controlled by the stonith device.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-n</option>
+ </term>
+ <listitem>
+ <para>Output the parameter names of the stonith device.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <replaceable>name</replaceable>=<replaceable>value</replaceable>
+ </term>
+ <listitem>
+ <para>Parameter, in the form of a name/value pair, to pass
+ directly to the stonith device. To determine the syntax of
+ the parameters for a given device type run:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t stonith-device-type -n</userinput></screen>
+ <para>All of the listed parameter names need to be passed
+ with their corresponding values.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-p</option> <replaceable>stonith-device-parameters</replaceable>
+ </term>
+ <listitem>
+ <para>Parameters to pass directly to the stonith device. To
+ determine the syntax of the parameters for a given device
+ type run:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t stonith-device-type -n</userinput></screen>
+ <para>All of the listed parameter names need to appear in
+ order and be delimited by whitespace.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-S</option>
+ </term>
+ <listitem>
+ <para>Show the status of the stonith device.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-s</option>
+ </term>
+ <listitem>
+ <para>Silent operation. Suppress logging of error messages to standard error.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-T</option> <replaceable>action</replaceable>
+ </term>
+ <listitem>
+ <para>The stonith action to perform on the node identified
+ by nodename. Chosen from <token>reset</token>,
+ <token>on</token>, and <token>off</token>.</para>
+ <note>
+ <para>If a nodename is specified without the
+ <option>-T</option> option, the stonith action defaults to
+ <token>reset</token>.</para>
+ </note>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-t</option> <replaceable>stonith-device-type</replaceable>
+ </term>
+ <listitem>
+ <para>The type of the stonith device to be used to effect
+ stonith. A list of supported devices for an installation may
+ be obtained using the <option>-L</option> option.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>-v</option>
+ </term>
+ <listitem>
+ <para>Ignored.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsection>
+ <refsection id="rs-stonith-examples">
+ <title>Examples</title>
+ <para>To determine which stonith devices are available on your installation, use the <option>-L</option> option:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -L</userinput></screen>
+ <para>All of the supported devices will be displayed one per line.
+ Choose one from this list that is best for your environment -
+ let's use <code>wti_nps</code> for the rest of this example. To get detailed
+ information about this device, use the <option>-h</option> option:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps -h</userinput></screen>
+ <para>Included in the output is the list of valid parameter names
+ for <code>wti_nps</code>. To get <emphasis>just</emphasis> the
+ list of valid parameter names, use the <option>-n</option> option
+ instead:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps -n</userinput></screen>
+ <para>All of the required parameter names will be displayed one
+ per line. For <code>wti_nps</code> the output is:</para>
+ <screen><computeroutput>ipaddr</computeroutput>
+<computeroutput>password</computeroutput></screen>
+ <para>There are three ways to pass these parameters to the device.
+ The first (and preferred) way is by passing name/value pairs on
+ the <command>stonith</command> command line:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw ...</userinput></screen>
+ <para>The second way, which is maintained only for backward
+ compatibility with legacy clusters, is passing the values
+ <emphasis>in order</emphasis> on the <command>stonith</command>
+ command line with the <option>-p</option> option:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps -p "my-dev-ip my-dev-pw" ...</userinput></screen>
+ <para>The third way, which is also maintained only for backward
+ compatibility with legacy clusters, is placing the values <emphasis>in order</emphasis>
+ on a single line in a config file:</para>
+ <programlisting>my-dev-ip my-dev-pw</programlisting>
+ <para>... and passing the name of the file on the stonith command
+ line with the <option>-F</option> option:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps -F ~/my-wtinps-config ...</userinput></screen>
+ <para>To make sure you have the configuration set up correctly and
+ that the device is available for stonith operations, use the
+ <option>-S</option> option:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw -S</userinput></screen>
+ <para>If all is well at this point, you should see something similar to:</para>
+ <screen><computeroutput>stonith: wti_nps device OK.</computeroutput></screen>
+ <para>If you don't, some debugging may be necessary to determine
+ if the config info is correct, the device is powered on, etc. The
+ <option>-d</option> option can come in handy here - you can add it
+ to any <command>stonith</command> command to cause it to generate
+ debug output.</para>
+ <para>To get the list of hosts controlled by the device, use the
+ <option>-l</option> option:</para>
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw -l</userinput></screen>
+ <para>All of the hosts controlled by the device will be displayed one per line. For <code>wti_nps</code> the output could be:</para>
+ <screen><computeroutput>node1</computeroutput>
+ <computeroutput>node2</computeroutput>
+ <computeroutput>node3</computeroutput></screen>
+ <para>To power off one of these hosts, use the <option>-T</option> option:
+ <screen><computeroutput># </computeroutput><userinput>stonith -t wti_nps ipaddr=my-dev-ip password=my-dev-pw -T off <replaceable>node</replaceable></userinput></screen></para>
+ </refsection>
+ <refsection id="rs-stonith-seealso">
+ <title>See also</title>
+ <para>
+ <citerefentry><refentrytitle>heartbeat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>meatclient</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsection>
+</refentry>
diff --git a/doc/stonith/Makefile.am b/doc/stonith/Makefile.am
new file mode 100644
index 0000000..4c9b76f
--- /dev/null
+++ b/doc/stonith/Makefile.am
@@ -0,0 +1,37 @@
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+stdocdir = $(docdir)/stonith
+
+stdoc_DATA = README.bladehpi \
+ README.cyclades \
+ README.drac3 \
+ README.dracmc \
+ README.external \
+ README.ibmrsa \
+ README.ibmrsa-telnet \
+ README.meatware \
+ README.rackpdu \
+ README.rcd_serial \
+ README.riloe \
+ README.vacm \
+ README.wti_mpc \
+ README_kdumpcheck.txt \
+ README.vcenter
+
+if IPMILAN_BUILD
+stdoc_DATA += README.ipmilan
+endif
diff --git a/doc/stonith/README.bladehpi b/doc/stonith/README.bladehpi
new file mode 100644
index 0000000..3119ef7
--- /dev/null
+++ b/doc/stonith/README.bladehpi
@@ -0,0 +1,101 @@
+
+STONITH module for IBM BladeCenter via OpenHPI
+----------------------------------------------
+
+Requirements:
+ Linux-HA bladehpi STONITH plugin requires OpenHPI 2.6+
+ OpenHPI requires Net-SNMP 5.0+
+ OpenHPI requires BladeCenter Management Module 1.08+
+
+This STONITH module talks to IBM BladeCenters via SNMP through use of
+the OpenHPI BladeCenter plugin (snmp_bc). For more information about
+installing OpenHPI, setting up the BladeCenter SNMP agent, etc. please
+visit http://www.openhpi.org/. Once OpenHPI is installed properly,
+the STONITH plugin will automatically be built the next time Linux-HA
+is built.
+
+Use the OpenHPI configuration file (i.e. /etc/openhpi/openhpi.conf)
+to configure the BladeCenters of interest to STONITH. For example,
+the following excerpt:
+
+ plugin libsnmp_bc
+
+ handler libsnmp_bc {
+ entity_root = "{SYSTEM_CHASSIS,1}" # Required
+ host = "9.254.253.252" # Required
+ community = "community" # Version 1 Required.
+ version = "3" # Required. SNMP protocol version (1|3)
+ security_name = "userid" # Version 3 Required.
+ passphrase = "userpass" # Version 3. Required if security_level is authNoPriv or authPriv.
+ auth_type = "MD5" # Version 3. Passphrase encoding (MD5|SHA)
+ security_level = "authNoPriv" # Version 3. (noAuthNoPriv|authNoPriv|authPriv)
+ }
+
+defines how to access the BladeCenter at 9.254.253.252 using SNMPV3
+with an ID/password of userid/userpass. The entity_root must be
+passed to the STONITH bladehpi plugin as its single required parameter.
+For example, to query the list of blades present in the BladeCenter
+configured above, run:
+
+ stonith -t bladehpi -p "{SYSTEM_CHASSIS,1}" -l
+
+which is the same as:
+
+ stonith -t bladehpi "entity_root={SYSTEM_CHASSIS,1}" -l
+
+Use the BladeCenter Management Module web interface to set the Blade
+Information to match "uname -n" for each blade in the cluster. For
+example, with the BladeCeter configured above use a brower to access
+http://9.254.253.252, login with userid/userpass, and then go to
+Blade Tasks -> Configuration -> Blade Information, enter the proper
+names, and select Save. Be aware that heartbeat must be restarted
+before these changes take effect or, if using the OpenHPI daemon,
+the daemon must be restarted.
+
+More than one BladeCenter can be placed in the OpenHPI configuration
+file by using different numbers with the entity_root. For example,
+
+ plugin libsnmp_bc
+
+ handler libsnmp_bc {
+ entity_root = "{SYSTEM_CHASSIS,1}" # Required
+ host = "9.254.253.252" # Required
+ :
+ }
+ handler libsnmp_bc {
+ entity_root = "{SYSTEM_CHASSIS,2}" # Required
+ host = "9.254.253.251" # Required
+ :
+ }
+
+There is an optional parameter, soft_reset, that is true|1 if bladehpi
+should use soft reset (power cycle) to reset nodes or false|0 if it
+should use hard reset (power off, wait, power on); the default is
+false. As an example, to override the default value the above stonith
+command would become:
+
+ stonith -t bladehpi -p "{SYSTEM_CHASSIS,1} true" -l
+
+which is the same as:
+
+ stonith -t bladehpi "entity_root={SYSTEM_CHASSIS,1} soft_reset=true" -l
+
+The difference between the two is that a soft reset is much quicker
+but may return before the node has been reset because bladehpi relies
+on BladeCenter firmware to cycle the node's power, while a hard reset
+is slower but guaranteed not to return until the node is dead because
+bladehpi powers off the node, waits until it is off, then powers it
+on again.
+
+NOTE: Set the OPENHPI_CONF environment variable to contain the
+fully-qualified path of the OpenHPI configuration file, for example:
+
+ export OPENHPI_CONF=/etc/openhpi/openhpi.conf
+
+NOTE: If OpenHPI is not configured with --disable-daemon before being
+built and installed, make sure that the OpenHPI daemon is running
+before using the bladehpi plugin.
+
+NOTE: If debugging of the environment is needed, configure OpenHPI
+with --enable-debuggable and rebuild/reinstall, export
+OPENHPI_DEBUG=YES, and run stonith commands with the -d option.
diff --git a/doc/stonith/README.cyclades b/doc/stonith/README.cyclades
new file mode 100644
index 0000000..3ccf9db
--- /dev/null
+++ b/doc/stonith/README.cyclades
@@ -0,0 +1,61 @@
+STONITH module for Cyclades AlterPath PM
+----------------------------------------
+
+This STONITH module talks to Cyclades AlterPath PM series of power managers
+via TS, ACS or KVM equipment.
+
+Access to the frontend device (TS, ACS or KVM) is done via root user with
+passwordless ssh.
+
+For that, it is necessary to create a public/private keypar with _empty_
+passphrase on _each_ machine which is part of the cluster.
+
+Small HOWTO follows:
+
+# ssh-keygen -t rsa
+Generating public/private rsa key pair.
+Enter file in which to save the key (/root/.ssh/id_rsa):
+Created directory '/home/root/.ssh'.
+Enter passphrase (empty for no passphrase):
+Enter same passphrase again:
+Your identification has been saved in /root/.ssh/id_rsa.
+Your public key has been saved in /root/.ssh/id_rsa.pub.
+The key fingerprint is:
+dc:e0:71:55:fd:2a:b0:19:d6:3c:48:e5:45:db:b4:be root@hostname.network
+
+Next step is to append the public key (/root/.ssh/id_rsa.pub)
+to the authorized_keys file on the TS/ACS/KVM box. The authorized
+keys file location is set at the SSH daemon configuration file.
+The default location is /etc/ssh/authorized_keys, so:
+
+[root@clusterhost]# scp /root/.ssh/id_rsa.pub root@alterpath:/tmp
+
+login to the TS/ACS/KVM box normally and append the public key.
+
+# ssh root@alterpath
+Password: ....
+
+[root@CAS root]# cat /tmp/id_rsa.pub >> /etc/ssh/authorized_keys
+
+The following entries must be present on /etc/ssh/sshd_config for the
+passwordless scheme to work properly:
+
+RSAAuthentication yes
+PubkeyAuthentication yes
+AuthorizedKeysFile /etc/ssh/authorized_keys
+
+Next step is to test if the configuration has been done successfully:
+
+[root@clusterhost root]# ssh root@alterpath
+[root@CAS root]#
+
+If it logins automatically without asking for a password, then everything
+has been done correctly!
+
+Note that such configuration procedure (including generation of the key pair)
+has to be done for each machine in the cluster which intends to use the
+AlterPath PM as a STONITH device.
+
+------
+Any questions please contact Cyclades support at <support@cyclades.com>
+or <marcelo.tosatti@cyclades.com>
diff --git a/doc/stonith/README.drac3 b/doc/stonith/README.drac3
new file mode 100644
index 0000000..e3c071b
--- /dev/null
+++ b/doc/stonith/README.drac3
@@ -0,0 +1,18 @@
+Stonith module for Dell DRACIII remote access card
+--------------------------------------------------
+
+This module uses the Dell DRACIII PCI card as a stonith device.
+It sends the XML commands over HTTPS to the DRACIII web server.
+
+The card firmware must be version 2.0 at least, with support for SSL based
+service and many bug fixes over 1.x versions.
+
+This module uses libcurl, libxml2 (gnome xml libs) and libssl.
+
+Any hints, bug reports, improvements, etc. will be apreciated.
+
+---
+Roberto Moreda <moreda@alfa21.com> http://www.alfa21.com
+Alfa21 A Coruña (Spain)
+UNIX, Linux & TCP/IP Services - High Availability Solutions
+
diff --git a/doc/stonith/README.dracmc b/doc/stonith/README.dracmc
new file mode 100644
index 0000000..761f5ad
--- /dev/null
+++ b/doc/stonith/README.dracmc
@@ -0,0 +1,87 @@
+dracmc-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+ Connects to Dell Drac/MC Blade Enclosure via a Cyclades
+ terminal server with telnet and switches power of named
+ blade servers appropriatelly.
+
+Description:
+ Dell offers the Drac/MC in their blade enclosures. The
+Drac/MC can be accessed in different ways. One way to interface to it
+is to connect the blade enclosure's Drac/MC serial port to a Cyclades
+terminal server. You can then access the Drac/MC via telnet via the
+Cyclades. Once logged in, you can use 'help' to show all available
+commands. With the 'serveraction' command, you can control both
+hard and soft resets as well as power to a particular blade. The
+blades are named 'Server-X', where 'X' is a number which corresponds
+to the blade number in the enclosure. This plugin allows using the
+Drac/MC with stonith. It uses python's standards 'telnetlib' library
+to log in and issue commands. The code is very similar to the original
+ibmrsa-telnet plugin released by Andreas and was quite easy to
+modify for this application.
+ One complication is that the Cyclades only allows one active
+connection. If someone or something has a connection active, the
+terminal server closes the new attempted connection. Since this
+situation can be common, for example if trying to stonith two blades
+or when the plugin is started by multiple cluster nodes, there is a
+built in retry mechanism for login. On 10 retries, the code gives up
+and throws.
+ When running this resource, it is best to not run it as a clone,
+rather as a normal, single-instance resource. Make sure to create a
+location constraint that excludes the node that is to be fenced.
+
+Required parameters:
+ nodename: The name of the server you want to touch on your network
+ cyclades_ip: The IP address of the cyclades terminal server
+ cyclades_port: The port for telnet to access on the cyclades (i.e. 7032)
+ servername: The DRAC/MC server name of the blade (i.e. Server-7)
+ username: The login user name for the DRAC/MC
+ password: The login password for the DRAC/MC
+
+Example configuration
+
+These are examples: you should adjust parameters, scores and
+timeout values to fit your environment.
+
+crm shell:
+
+ primitive fence_node1 stonith:external/dracmc-telnet \
+ nodename=node1 cyclades_ip=10.0.0.1 cyclades_port=7001 \
+ servername=Server-1 username=USERID password=PASSWORD \
+ op monitor interval="200m" timeout="60s"
+ location loc-fence_node1 fence_node1 -inf: node1
+
+XML:
+
+<?xml version="1.0" ?>
+<cib>
+ <configuration>
+ <resources>
+ <primitive id="r_stonith-node01" class="stonith" type="external/dracmc-telnet" provider="heartbeat" resource_stickiness="0">
+ <operations>
+ <op name="monitor" interval="200m" timeout="60s" prereq="nothing" id="r_stonith-node01-mon"/>
+ <op name="start" timeout="180" id="r_stonith-node01-start"/>
+ <op name="stop" timeout="180" id="r_stonith-node01-stop"/>
+ </operations>
+ <instance_attributes id="r_stonith-node01">
+ <attributes>
+ <nvpair id="r_stonith-node01-nodename" name="nodename" value="node01"/>
+ <nvpair id="r_stonith-node01-cyclades_ip" name="cyclades_ip" value="192.168.0.1"/>
+ <nvpair id="r_stonith-node01-cyclades_port" name="cyclades_port" value="7032"/>
+ <nvpair id="r_stonith-node01-servername" name="servername" value="Server-7"/>
+ <nvpair id="r_stonith-node01-username" name="username" value="USERID"/>
+ <nvpair id="r_stonith-node01-password" name="password" value="PASSWORD"/>
+ <nvpair id="r_stonith-node01-type" name="type" value="dellblade"/>
+ </attributes>
+ </instance_attributes>
+ </primitive>
+ </resources>
+ <constraints>
+ <rsc_location id="r_stonith-node01_prefer_node02" rsc="r_stonith-node01">
+ <rule id="r_stonith-node01_prefer_node02_rule" score="50">
+ <expression attribute="#uname" id="r_stonith-node01_prefer_node02_expr" operation="eq" value="node02"/>
+ </rule>
+ </rsc_location>
+ </constraints>
+
+ </configuration>
+</cib>
+
diff --git a/doc/stonith/README.external b/doc/stonith/README.external
new file mode 100644
index 0000000..a70ccde
--- /dev/null
+++ b/doc/stonith/README.external
@@ -0,0 +1,90 @@
+EXTERNAL module for Linux-HA STONITH
+
+
+This stonith plugin runs an external command written in your favorite
+language to shutdown the given host. The external command should return
+a zero exit status after a successful shutdown, or non-zero exit status
+for a shutdown failure. Failures notifications will be sent to syslog.
+
+To create your own external plugin, write a script that supports the
+following actions:
+
+ reset
+ on (optional)
+ off (optional)
+ gethosts
+ status
+ getconfignames
+ getinfo-devid
+ getinfo-devname
+ getinfo-devdescr
+ getinfo-devurl
+ getinfo-xml
+
+and place it in the /usr/lib/stonith/plugins/external directory - the
+script must be a regular executable file that is NOT writable by group
+or others in order to be recognized as an external plugin. If the
+action requires information to be returned, such as the list of hosts
+or config names or any of the getinfo calls, simply write the
+information to stdout. When complete, return zero to indicate the
+action succeeded or non-zero to indicate the action failed. You can
+use the ssh (sh) and riloe (pyhton) scripts already in that directory
+as working examples.
+
+To make sure that your external plugin is recognized, run "stonith -L"
+and look for its name in the output, something along the lines of:
+
+ external/yourplugin
+
+To configure the plugin on an R1 (legacy) cluster, add a line similar
+to the following to /etc/ha.d/ha.cf:
+
+ stonith external/yourplugin /etc/ha.d/yourplugin.cfg
+
+where /etc/ha.d/yourplugin.cfg contains a single line with all of your
+plugin's parameters:
+
+ parm1-value parm2-value ...
+
+Another way to configure the plugin on a legacy cluster is to add a line
+similiar to the following to /etc/ha.d/ha.cf instead:
+
+ stonith_host * external/yourplugin parm1-value parm2-value ...
+
+where all of your plugin's parameters are placed at the end of the line.
+
+Please note that all parameters come in to the plugin in name/value
+(environment variable) form, but in R1 configurations, they appear as a
+list of parameters. They are ordered in the config file or on the
+stonith_host line according to the ordering specified in the output of
+the getconfignames operation.
+
+To configure the plugin on an R2 cluster, place lines similar to the
+following into the <resources> section of your CIB, which is contained
+in /var/lib/heartbeat/crm/cib.xml:
+
+ <clone id="DoFencing">
+ <instance_attributes>
+ <nvpair name="clone_max" value="2"/>
+ <nvpair name="clone_node_max" value="1"/>
+ </instance_attributes>
+ <primitive id="child_DoFencing" class="stonith" type="external/yourplugin" provider="heartbeat">
+ <operations>
+ <op name="monitor" interval="5s" timeout="20s" requires="nothing"/>
+ <op name="start" timeout="20s" requires="nothing"/>
+ </operations>
+ <instance_attributes>
+ <nvpair name="parm1-name" value="parm1-value"/>
+ <nvpair name="parm2-name" value="parm2-value"/>
+ <!-- ... -->
+ </instance_attributes>
+ </primitive>
+ </clone>
+
+Whatever <nvpair> parameters specified in the <attributes> section of
+the CIB are passed to the script as environment variables. For the
+example above, the parameters are passed as parm1-name=parm1-value,
+parm2-name=parm2-value and so on.
+
+Additional information can be found at
+http://linux-ha.org/wiki/ExternalStonithPlugins.
diff --git a/doc/stonith/README.ibmrsa b/doc/stonith/README.ibmrsa
new file mode 100644
index 0000000..b34031b
--- /dev/null
+++ b/doc/stonith/README.ibmrsa
@@ -0,0 +1,9 @@
+See
+
+ftp://ftp.software.ibm.com/systems/support/system_x_pdf/d3basmst.pdf
+ftp://ftp.software.ibm.com/systems/support/system_x_pdf/88p9248.pdf
+http://www.redbooks.ibm.com/abstracts/sg246495.html
+
+for documentation about IBM management processors and the
+IBMmpcli utility.
+
diff --git a/doc/stonith/README.ibmrsa-telnet b/doc/stonith/README.ibmrsa-telnet
new file mode 100644
index 0000000..109bdd9
--- /dev/null
+++ b/doc/stonith/README.ibmrsa-telnet
@@ -0,0 +1,55 @@
+ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+ Connects to IBM RSA Board via telnet and switches power
+ of server appropriately.
+
+Description:
+
+ IBM offers Remote Supervisor Adapters II for several
+ servers. These RSA boards can be accessed in different ways.
+ One of that is via telnet. Once logged in you can use 'help' to
+ show all available commands. With 'power' you can reset, power on and
+ off the controlled server. This command is used in combination
+ with python's standard library 'telnetlib' to do it automatically.
+
+Code snippet for cib
+
+ It's useful to give a location preference so that the stonith agent
+ is run on the/an other node. This is not necessary as one node can kill
+ itself via RSA Board. But: If this node becomes crazy my experiences
+ showed that the node is not able to shoot itself anymore properly.
+
+ You have to adjust parameters, scores and timeout values to fit your
+ HA environment.
+
+<?xml version="1.0" ?>
+<cib>
+ <configuration>
+ <resources>
+ <primitive id="r_stonith-node01" class="stonith" type="external/ibmrsa" provider="heartbeat" resource_stickiness="0">
+ <operations>
+ <op name="monitor" interval="60" timeout="300" prereq="nothing" id="r_stonith-node01-mon"/>
+ <op name="start" timeout="180" id="r_stonith-node01-start"/>
+ <op name="stop" timeout="180" id="r_stonith-node01-stop"/>
+ </operations>
+ <instance_attributes id="r_stonith-node01">
+ <attributes>
+ <nvpair id="r_stonith-node01-nodename" name="nodename" value="node01"/>
+ <nvpair id="r_stonith-node01-ipaddr" name="ipaddr" value="192.168.0.1"/>
+ <nvpair id="r_stonith-node01-userid" name="userid" value="userid"/>
+ <nvpair id="r_stonith-node01-passwd" name="passwd" value="password"/>
+ <nvpair id="r_stonith-node01-type" name="type" value="ibm"/>
+ </attributes>
+ </instance_attributes>
+ </primitive>
+ </resources>
+ <constraints>
+ <rsc_location id="r_stonith-node01_not_on_node01" rsc="r_stonith-node01">
+ <rule id="r_stonith-node01_not_on_node01_rule" score="-INFINITY">
+ <expression attribute="#uname" id="r_stonith-node01_not_on_node01_expr" operation="eq" value="node01"/>
+ </rule>
+ </rsc_location>
+ </constraints>
+
+ </configuration>
+</cib>
+
diff --git a/doc/stonith/README.ipmilan b/doc/stonith/README.ipmilan
new file mode 100644
index 0000000..eef86cf
--- /dev/null
+++ b/doc/stonith/README.ipmilan
@@ -0,0 +1,131 @@
+ IPMILAN STONITH Module
+ Copyright (c) 2003 Intel Corp.
+ yixiong.zou@intel.com
+
+1. Intro
+
+IPMILAN STONITH module works by sending a node an IPMI message, in particular,
+a 'chassis control' command. Currently the message is sent over the LAN.
+
+2. Hardware Requirement
+
+In order to use this module, the node has to be IPMI v1.5 compliant and
+also supports IPMI over LAN. For example, the Intel Langley platform.
+
+Note: IPMI over LAN is an optional feature defined by IPMI v1.5 spec.
+So even if a system is IPMI compliant/compatible, it might still not
+support IPMI over LAN. If you are sure this is your case and you still
+want to try this plugin, read section 6, IPMI v1.5 without IPMI over
+LAN Support.
+
+3. Software Requirement
+
+This module needs OpenIPMI (http://openipmi.sf.net) to compile.
+Version 1.4.x or 2.0.x is supported.
+
+4. Hardware Configuration
+
+How to configure the node so it accepts IPMI lan packets is beyond the
+scope of this document. Consult your product manual for this.
+
+5. STONITH Configuration
+
+Each node in the cluster has to be configured individually. So normally there
+would be at least two entries, unless you want to use a different STONITH
+device for the other nodes in the cluster. ;)
+
+The configuration file syntax looks like this:
+
+ <node1> <ip> <port> <auth> <priv> <user> <pass> <reset_method>
+ <node2> <ip> <port> <auth> <priv> <user> <pass> <reset_method>
+ ...
+
+ node: the hostname.
+
+ ip: the IP address of the node. If a node has more than one IP addresses,
+ this is the IP address of the interface which accepts IPMI messages. :)
+
+ port: the port number to send the IPMI message to. The default is 623.
+ But it could be different or even configurable.
+
+ auth: the authorization type of the IPMI session. Valid choices are
+ "none", "straight", "md2", and "md5".
+
+ priv: the privilege level of the user. Valid choices are "operator"
+ or "admin". These are the privilege levels required to run the
+ 'chassis control' command.
+
+ user: the username. use "" if it is empty. Cannot exceed 16 characters.
+
+ pass: the password. use "" if it is empty. Cannot exceed 16 characters.
+
+ reset_method: (optional) which IPMI chassis control to send
+ to reset the host. Possible values are power_cycle (default)
+ and hard_reset.
+
+Each line is white-space delimited and lines begins with '#' are ignored.
+
+6. IPMI v1.5 without IPMI over LAN Support
+
+If somehow your computer have a BMC but without LAN support, you might
+still be able to use this module.
+
+ 0) Make sure OpenIPMI is installed. OpenIPMI 1.0.3 should work.
+
+ 1) Create a /etc/ipmi_lan.conf file.
+
+ Here's a sample of how this file should look like
+
+ addr 172.16.1.249 999
+ PEF_alerting on
+ per_msg_auth off
+ priv_limit admin
+ allowed_auths_admin none md2 md5
+ user 20 on "" "" admin 5 md2 md5 none
+
+ If you do not understand what each line means, do a man on ipmilan.
+
+ 2) run ipmilan as root.
+
+ 3) Try send youself an IPMI packet over the network using ipmicmd see
+ if it works.
+
+ ipmicmd -k "0f 00 06 01" lan 172.16.1.249 999 none admin "" ""
+
+ The result should be something like:
+
+ Connection 0 to the BMC is up0f 07 00 01 00 01 80 01 19 01 8f 77 00 00 4b 02
+
+ 4) Configure your system so everytime it boots up, the ipmi device
+ drivers are all loaded and ipmilan is run. This is all OS dependent
+ so I can't tell you what to do.
+
+ The major draw back of this is that you will not be able to power it up
+ once it's power down, which for a real IPMI, you could.
+
+
+7. Bugs
+
+Some IPMI device does not return 0x0, success, to the host who issued the reset
+command. A timeout, 0xc3, could be returned instead. So I am counting that
+also as a "successful reset".
+
+Note: This behavior is not fully IPMI v1.5 compliant. Based on the IPMI v1.5
+spec, the IPMI device should return the appropriate return code. And it is
+even allowed to return the appropriate return code before performing the
+action.
+
+
+8. TODO
+
+1) Right now the timeout on each host is hard coded to be 10 seconds. It will
+ be nice to be able to set this value for individual host.
+
+2) A better way of detecting the success of the reset operation will be good. A
+ lot of times the host which carried out the reset does not return a success.
+
+3) The os_handler should be contributed back to the OpenIPMI project so that
+ we do not need to maintain it here. It does not make sense for every little
+ app like this to write its own os_handler. A generic one like in this
+ program should be sufficient.
+
diff --git a/doc/stonith/README.ippower9258 b/doc/stonith/README.ippower9258
new file mode 100644
index 0000000..6873efd
--- /dev/null
+++ b/doc/stonith/README.ippower9258
@@ -0,0 +1,68 @@
+IP Power 9258 as external stonith device.
+=========================================
+
+Device Information
+==================
+
+ Warning:
+ ========
+
+ Aviosys provides different types and versions of IP Power 9258.
+ The device is currently available with four or eight power outlets.
+ This script was tested with firmware version: V1.55 2009/12/22
+
+ Especially "IP Power 9258 HP" uses a different http command interface.
+ ======================================================================
+
+ Resources for device documentation:
+
+ Manufacturer URL: http://www.aviosys.com/ippower9258.htm
+ Manual URL: http://www.aviosys.com/manual.htm
+ Manual current version URL:
+ http://www.aviosys.com/images/9258_manual_20081104.pdf
+
+The documentation of the http command interface defines three
+supported commands:
+
+ GetPower - useful for testing status of the device and of each port
+ SetPower - used to control status of each power outlet
+ SetSchedule+Power - useless for stonith
+
+Common documented structure of these three commands is
+
+ http://username:password@a.b.c.d/Set.cmd?CMD=command[+param=value...]
+ param is one or more of P60 to P67 and value is 0 or 1
+ expected response for GetPower is of the format
+ <html>P60=1,P61=0,P62=1,P63=1,P64=0,P65=0,P66=0,P67=0</html>
+ SetPower does respond with the same format but restricts the list
+ to the modified ports.
+ P60 to P67 represent the status of the power outlet 1 to 8: 0 <=>
+ power off; 1 <=> power on.
+
+IP Power 9258 allows to assign port names (pw1Name to pw8Name) to each
+port. These names can be used with the web interface (web form with
+post-method).
+
+Script specific notes
+=====================
+
+There is no documented http command to retrieve port names via the
+http command interface. We try to get the hostlist via the web
+interface.
+
+This script assumes a one to one mapping between names of hostlist and
+port attributes of power outlet:
+
+ 1st hostname in hostlist connected to 1st power outlet with port
+ status P60 and port name pw1Name.
+ ...
+ 8th hostname in hostlist connected to 8th power outlet with port
+ status P67 and port name pw8Name.
+
+If the hostlist parameter is not defined, then all assigned outlets
+are inserted into the hostlist. Unused outlets should have empty
+names. The node names obviously have to match the corresponding outlet
+names. A reserved hostname is "*not-defined*". This is a
+sript-internal placeholder for unused outlets. It does not appear in
+the hostlist.
+
diff --git a/doc/stonith/README.meatware b/doc/stonith/README.meatware
new file mode 100644
index 0000000..0b9b15d
--- /dev/null
+++ b/doc/stonith/README.meatware
@@ -0,0 +1,26 @@
+
+MEATWARE Module for Linux-HA STONITH
+
+ABOUT:
+
+ This is a port of the "meatware" stomith method found in the GFS
+ distribution (see http://globalfilesystem.org/) to the Linux-HA
+ project. It notifies operators if a node needs to be reset and
+ waits for confirmation.
+
+USAGE:
+
+ The module can be used like any other stonith module. It will
+ syslog a message at CRIT level if it needs an operator to power-cycle
+ a node on its behalf.
+ To confirm that a manual reset has been done, execute
+
+ "meatclient -c <host>".
+
+ If you abort the confirmation, the module will report that the reset
+ has failed.
+
+AUTHOR:
+
+ Gregor Binder <gbinder@sysfive.com>
+
diff --git a/doc/stonith/README.rackpdu b/doc/stonith/README.rackpdu
new file mode 100644
index 0000000..69a0f44
--- /dev/null
+++ b/doc/stonith/README.rackpdu
@@ -0,0 +1,21 @@
+APC Rack PDU
+
+The product information pages:
+
+http://www.apcc.com/products/family/index.cfm?id=70
+
+The User's Guide:
+
+http://www.apcmedia.com/salestools/ASTE-6Z6KAV_R1_EN.pdf
+
+Apparently, an existing http or telnet session will make the
+plugin fail.
+
+In case your nodes are equipped with multiple power supplies, the
+PDU supports synchronous operation on multiple outlets on up to
+four Switched Rack PDUs. See the User's Guide for more
+information on how to setup outlet groups.
+
+NB: There has been a report by one user that in case a link
+between two PDUs in the chain is broken, the PDU returns success
+even though it failed. This needs to be verified.
diff --git a/doc/stonith/README.rcd_serial b/doc/stonith/README.rcd_serial
new file mode 100644
index 0000000..8b4abb4
--- /dev/null
+++ b/doc/stonith/README.rcd_serial
@@ -0,0 +1,186 @@
+rcd_serial - RC Delayed Serial
+------------------------------
+
+This stonith plugin uses one (or both) of the control lines of a serial
+device (on the stonith host) to reboot another host (the stonith'ed host)
+by closing its reset switch. A simple idea with one major problem - any
+glitch which occurs on the serial line of the stonith host can potentially
+cause a reset of the stonith'ed host. Such "glitches" can occur when the
+stonith host is powered up or reset, during BIOS detection of the serial
+ports, when the kernel loads up the serial port driver, etc.
+
+To fix this, you need to introduce a delay between the assertion of the
+control signal on the serial port and the closing of the reset switch.
+Then any glitches will be dissipated. When you really want to do the
+business, you hold the control signal high for a "long time" rather than
+just tickling it "glitch-fashion" by, e.g., using the rcd_serial plugin.
+
+As the name of the plugin suggests, one way to achieve the required delay is
+to use a simple RC circuit and an npn transistor:
+
+
+ . .
+ RTS . . ----------- +5V
+ or ---------- . |
+ DTR . | . Rl reset
+ . | T1 . | |\logic
+ . Rt | ------RWL--------| ------->
+ . | b| /c . |/
+ . |---Rb---|/ .
+ . | |\ . (m/b wiring typical
+ . C | \e . only - YMMV!)
+ . | | .
+ . | | .
+ SG ---------------------------RWG----------- 0V
+ . .
+ . . stonith'ed host
+ stonith host --->.<----- RC circuit ----->.<---- RWL = reset wire live
+ (serial port) . . RWG = reset wire ground
+
+
+The characteristic delay (in seconds) is given by the product of Rt (in ohms)
+and C (in Farads). Suitable values for the 4 components of the RC circuit
+above are:
+
+Rt = 20k
+C = 47uF
+Rb = 360k
+T1 = BC108
+
+which gives a delay of 20 x 10e3 x 47 x 10e-6 = 0.94s. In practice the
+actual delay achieved will depend on the pull-up load resistor Rl if Rl is
+small: for Rl greater than 3k there is no significant dependence but lower
+than this and the delay will increase - to about 1.4s at 1k and 1.9s at 0.5k.
+
+This circuit will work but it is a bit dangerous for the following reasons:
+
+1) If by mistake you open the serial port with minicom (or virtually any
+other piece of software) you will cause a stonith reset ;-(. This is
+because opening the port will by default cause the assertion of both DTR
+and RTS, and a program like minicom will hold them high thenceforth (unless
+and until a receive buffer overflow pulls RTS down).
+
+2) Some motherboards have the property that when held in the reset state,
+all serial outputs are driven high. Thus, if you have the circuit above
+attached to a serial port on such a motherboard, if you were to press the
+(manual) reset switch and hold it in for more than a second or so, you will
+cause a stonith reset of the attached system ;-(.
+
+This problem can be solved by adding a second npn transistor to act as a
+shorting switch across the capacitor, driven by the other serial output:
+
+
+ . .
+ . . ----------- +5V
+ RTS ----------------- . |
+ . | . Rl reset
+ . | T1 . | |\logic
+ . Rt | ------RWL--------| ------->
+ . | b| /c . |/
+ . T2 --|---Rb---|/ .
+ . | / | |\ . (m/b wiring typical
+ . b| /c | | \e . only - YMMV!)
+ DTR ------Rb--|/ C | .
+ . |\ | | .
+ . | \e | | .
+ . | | | .
+ SG ----------------------------------RWG------------- 0V
+ . .
+ . . stonith'ed host
+stonith->.<--------- RC circuit ------->.<---- RWL = reset wire live
+ host . . RWG = reset wire ground
+
+
+Now when RTS goes high it can only charge up C and cause a reset if DTR is
+simultaneously kept low - if DTR goes high, T2 will switch on and discharge
+the capacitor. Only a very unusual piece of software e.g. the rcd_serial
+plugin, is going to achieve this (rather bizarre) combination of signals
+(the "meaning" of which is something along the lines of "you are clear to
+send but I'm not ready"!). T2 can be another BC108 and with Rb the same.
+
+RS232 signal levels are typically +-8V to +-12V so a 16V rating or greater
+for the capacitor is sufficient BUT NOTE that a _polarised_ electrolytic should
+not be used because the voltage switches around as the capacitor charges.
+Nitai make a range of non-polar aluminium electrolytic capacitors. A 16V 47uF
+radial capacitor measures 6mm diameter by 11mm long and along with the 3
+resistors (1/8W are fine) and the transistors, the whole circuit can be built
+in the back of a DB9 serial "plug" so that all that emerges from the plug are
+the 2 reset wires to go to the stonith'ed host's m/b reset pins.
+
+NOTE that with these circuits the reset wires are now POLARISED and hence
+they are labelled RWG and RWL above. You cannot connect to the reset pins
+either way round as you can when connecting a manual reset switch! You'll
+soon enough know if you've got it the wrong way round because your machine
+will be in permanent reset state ;-(
+
+
+How to find out if your motherboard can be reset by these circuits
+------------------------------------------------------------------
+
+You can either build it first and then suck it and see, or, you need a
+multimeter. The 0V rail of your system is available in either
+of the 2 black wires in the middle of a spare power connector (one of
+those horrible 4-way plugs which you push with difficulty into the back
+of hard disks, etc. Curse IBM for ever specifying such a monstrosity!).
+Likewise, the +5V rail is the red wire. (The yellow one is +12V, ignore
+this.)
+
+First, with the system powered down and the meter set to read ohms:
+
+ check that one of the reset pins is connected to 0V - this then
+ is the RWG pin;
+
+ check that the other pin (RWL) has a high resistance wrt 0V
+ (probably > 2M) and has a small resistance wrt to +5V - between
+ 0.5k and 10k (or higher, doesn't really matter) will be fine.
+
+Second, with the system powered up and the meter set to read Volts:
+
+ check that RWG is indeed that i.e. there should be 0V between it
+ and the 0V rail;
+
+ check that RWL is around +5V wrt the 0V rail.
+
+If all this checks out, you are _probably_ OK. However, I've got one
+system which checks out fine but actually won't work. The reason is that
+when you short the reset pins, the actual current drain is much higher than
+one would expect. Why, I don't know, but there is a final test you can do
+to detect this kind of system.
+
+With the system powered up and the meter set to read milliamps:
+
+ short the reset pins with the meter i.e. reset the system, and
+ note how much current is actually drained when the system is in
+ the reset state.
+
+Mostly you will find that the reset current is 1mA or less and this is
+fine. On the system I mention above, it is 80mA! If the current is
+greater than 20mA or so, you have probably had it with the simple circuits
+above, although reducing the base bias resistor will get you a bit further.
+Otherwise, you have to use an analog switch (like the 4066 - I had to use 4
+of these in parallel to reset my 80mA system) which is tedious because then
+you need a +5V supply rail to the circuit so you can no longer just build it
+in the back of a serial plug. Mail me if you want the details.
+
+With the circuit built and the rcd_serial plugin compiled, you can use:
+
+stonith -t rcd_serial -p "testhost /dev/ttyS0 rts XXX" testhost
+
+to test it. XXX is the duration in millisecs so just keep increasing this
+until you get a reset - but wait a few secs between each attempt because
+the capacitor takes time to discharge. Once you've found the minimum value
+required to cause a reset, add say 200ms for safety and use this value
+henceforth.
+
+Finally, of course, all the usual disclaimers apply. If you follow my
+advice and destroy your system, sorry. But it's highly unlikely: serial
+port outputs are internally protected against short circuits, and reset pins
+are designed to be short circuited! The only circumstance in which I can
+see a possibility of damaging something by incorrect wiring would be if the
+2 systems concerned were not at the same earth potential. Provided both
+systems are plugged into the same mains system (i.e. are not miles apart
+and connected only by a very long reset wire ;-) this shouldn't arise.
+
+John Sutton
+john@scl.co.uk
+October 2002
diff --git a/doc/stonith/README.riloe b/doc/stonith/README.riloe
new file mode 100644
index 0000000..4befe95
--- /dev/null
+++ b/doc/stonith/README.riloe
@@ -0,0 +1,36 @@
+Note for iLO 3 users
+
+This plugin doesn't support the iLO version 3. Please use ipmilan
+or external/ipmi, iLO3 should support IPMI.
+
+Alain St-Denis wrote the riloe plugin. Here is short usage:
+
+primitive st0 stonith:external/riloe \
+ hostlist=target-node \
+ ilo_hostname=ilo-ip-address \
+ ilo_user=admin ilo_password=secret ilo_protocol=2.0
+
+The following additional parameters are available:
+
+ilo_can_reset:
+ Set to "1" if the ilo is capable of rebooting the host.
+ Defaults to '0'.
+
+ilo_protocol:
+ Defaults to 1.2. Set to the protocol version ilo supports.
+
+ilo_powerdown_method:
+ "button" or "power", the former simulates pressing the
+ button, the latter pulling the power plug. Defaults to
+ "power". The "button" method is easier on the host, but
+ requires ACPI. "power" should be more reliable, but not to
+ be used excessively for testing.
+
+ilo_proxyhost (string): Proxy hostname
+ proxy hostname if required to access ILO board
+
+ilo_proxyport (string, [3128]): Proxy port
+ proxy port if required to access ILO board
+ parameter will be ignored if proxy hostname is not set
+
+
diff --git a/doc/stonith/README.vacm b/doc/stonith/README.vacm
new file mode 100644
index 0000000..c9083ee
--- /dev/null
+++ b/doc/stonith/README.vacm
@@ -0,0 +1,40 @@
+20 December 2000
+
+I (rather poorly) integrated this contributed stonith driver into the
+linux-ha-stonith release. There is a problem that needs to be
+resolved by autoconf in that the driver will not compile unless
+libvacmclient is installed on the system.
+
+For now, what I've done is included a line in stonith/Makefile that you can
+uncomment if you want to compile the vacm stonith module. Look in the
+Makefile in this directory for the following lines and do like it says
+
+
+# If you want the VA Linux Cluster stonith module installed,
+# uncomment the following line. You must have the vacmclient library
+#VACM_STONITH = vacm_stonith.so
+
+Please direct questions about the operation of the stonith module to
+Mike Tilstra (see the announcement to the linux-ha-dev mailing list
+attached below.)
+
+
+-Eric.
+eric.ayers@compgen.com
+
+------------------------------------------------------------------------------
+
+From: Mike Tilstra <conrad@sistina.com>
+Sender: linux-ha-dev-admin@lists.tummy.com
+To: linux-ha-dev@lists.tummy.com
+Subject: [Linux-ha-dev] stonith module for VACM
+Date: Tue, 19 Dec 2000 16:41:38 -0600
+
+This was in need for some testing I'm doing, so I hacked this up quick. It
+works for me, but I'm willing to bet there's atleast one bug in it.
+
+Figured others might like it.
+
+...
+--
+Mike Tilstra conrad@sistina.com \ No newline at end of file
diff --git a/doc/stonith/README.vcenter b/doc/stonith/README.vcenter
new file mode 100644
index 0000000..e6cc9a5
--- /dev/null
+++ b/doc/stonith/README.vcenter
@@ -0,0 +1,90 @@
+VMware vCenter/ESX STONITH Module
+=================================
+
+1. Intro
+--------
+
+VMware vCenter/ESX STONITH Module is intended to provide STONITH support to
+clusters in VMware Virtual Infrastructures. It is able to deal with virtual
+machines running on physically different HostSystems (e.g. ESX/ESXi) by using
+VMware vSphere Web Services SDK http://www.vmware.com/support/developer/vc-sdk/
+and connecting directly on each HostSystem or through a VMware vCenter: in this
+last case the module locates the specified virtual machine in the Virtual
+Infrastructure and performs actions required by cluster policies.
+
+2. Software requirements
+------------------------
+
+VMware vSphere CLI, which includes both CLI tools and Perl SDK
+http://www.vmware.com/support/developer/vcli/ . The plugin has been tested with
+version 4.1 http://www.vmware.com/download/download.do?downloadGroup=VCLI41
+
+
+3. vCenter/ESX authentication settings
+--------------------------------------
+
+Create the credentials file with credstore_admin.pl:
+
+/usr/lib/vmware-vcli/apps/general/credstore_admin.pl \
+ -s 10.1.1.1 -u myuser -p mypass
+
+This should create $HOME/.vmware/credstore/vicredentials.xml
+Copy it to a system folder, e.g. /etc
+
+cp -p $HOME/.vmware/credstore/vicredentials.xml /etc
+
+
+4. Testing
+----------
+
+The plugin can be invoked directly to perform a very first connection test
+(replace all the provided sample values):
+
+VI_SERVER=10.1.1.1 \
+ VI_CREDSTORE=/etc/vicredentials.xml \
+ HOSTLIST="hostname1=vmname1;hostname2=vmname2" \
+ RESETPOWERON=0 \
+ /usr/lib/stonith/plugins/external/vcenter gethosts
+
+If everything works correctly you should get:
+
+hostname1
+hostname2
+
+When invoked in this way, the plugin connects to VI_SERVER, authenticates with
+credentials stored in VI_CREDSTORE and tries to retrieve the list of virtual
+machines (case insensitive) matching vmname1 and vmname2 (and any other listed).
+When finished, it reports the list back by mapping virtual machine names to
+hostnames as provided in HOSTLIST. If you see the full list of hostnames as a
+result, then everything is going well. If otherwise you are having a partial or
+empty list, you have to check parameters.
+
+You can even test "reset", "off" and "on" commands, to test (carefully!) the
+full chain. E.g.
+
+VI_SERVER=10.1.1.1 \
+ VI_CREDSTORE=/etc/vicredentials.xml \
+ HOSTLIST="hostname1=vmname1;hostname2=vmname2" \
+ RESETPOWERON=0 \
+ /usr/lib/stonith/plugins/external/vcenter reset hostname2
+
+In the above examples the referring infrastructure is a vCenter with several
+ESXi nodes. Server IP and credentials are referred to vCenter.
+
+5. CRM configuration
+--------------------
+
+The following is a sample procedure to setup STONITH for an HA 2-node cluster
+(replace all the provided sample values):
+
+crm configure primitive vfencing stonith::external/vcenter params \
+ VI_SERVER="10.1.1.1" VI_CREDSTORE="/etc/vicredentials.xml" \
+ HOSTLIST="hostname1=vmname1;hostname2=vmname2" RESETPOWERON="0" \
+ op monitor interval="60s"
+
+crm configure clone Fencing vfencing
+
+crm configure property stonith-enabled="true"
+
+
+
diff --git a/doc/stonith/README.wti_mpc b/doc/stonith/README.wti_mpc
new file mode 100644
index 0000000..050953d
--- /dev/null
+++ b/doc/stonith/README.wti_mpc
@@ -0,0 +1,85 @@
+STONITH module for WTI MPC
+--------------------------
+
+
+****Introduction.
+
+wti_mpc module uses snmp for controlling the MPC power distribution unit. It has
+been tested with MPC-8H and MPC-18H and should be compatible with the whole
+MPC series:
+ * MPC-20*
+ * MPC-16*
+ * MPC-18*
+ * MPC-8*
+
+****Unit configuration.
+
+wti_mpc STONITH modules uses SNMP v1, therefore it should be configured on the
+device side. To do so, you should login to device, go to "Network
+configuration" (/N), select "SNMP access" (25) and turn it on (enable/1). At the
+SNMP access screen set "Version" (2) to "V1/V2 Only", set "Read only" (3) to
+"No and set any "Community" (10) you want. You may also set other options as
+you need. You may check your setup by issuing the following command:
+
+ snmpwalk -v1 -c <community> <host> .1.3.6.1.2.1.1.1.0
+
+and result should be something like this:
+
+ SNMPv2-MIB::sysDescr.0 = STRING: Linux 85.195.135.236 2.4.18_mvl30-cllf #1991 Sun Mar 16 14:39:29 PST 2008 ppc
+
+
+****Plugin configuration.
+
+ Plugin declares the following configuration variables:
+
+ *ipaddr - ip address or hostname of a MPC unit.
+ *port - ip port, should be 161, as MPC listens for incoming SNMP
+ packets on that port. It is made for future use actually.
+ *community - Community that you've specified on previous step.
+ *mib_version - Should be 3 for MPC devices with firmware version 1.62
+ and later. 1 is for firmware version 1.44 and below.
+ 2 is unused right now, if you have device, with mib V2
+ feel free to contact me and I'll add it.
+
+****MIB version issue
+
+ WTI guys have several time changed OIDs, used by MPC devices. I own two
+types of the devices:
+ *With firmware v 1.44 which is compatible with MIB version 1
+ *With firmware v 1.62 which is compatible with MIB version 3
+
+I suppose there are exist MIB v2, but i cannot find it and I'd not able
+to test it.
+Anyway, this plugin supports both V1 and V3 versions, and the correct version
+is selected by the "mib-version" configuration parameter. Default value is "1",
+so if you do not specify this parameter or assign a unsupported value to it,
+it will fall back to mib version 1.
+
+****Outlets and groups
+
+ MPC devices forces unique names of the outlets. This is a big problem
+for STONITH plugin, cause it uses nodes unames as outlet names, so in case
+you have a node with several power plugs, you should have set the node uname
+as name of all the plugs. The MPC device simply doesn't allows this.
+ So, this plugin works with a GROUPS instead of a PLUGS. You may give
+any unique names for your physical outlets on the MPC, but you MUST create
+a plug group, name it using node's uname and include plugs, corresponding to
+that particular node to this group. It should be done even for node with
+single power supply. Some example:
+
+ Let's pretend you have a node "atest", with two power cords, connected
+to plugs A1 and B1. You have to create a group ("Plug grouping parameters" (/G)
+-> Add Plug Group to directory (2)), name it "atest" ("Plug Group Name (1)) and
+assign plugs A1 and B1 to that group ("Plug access" (2)). Now save your
+configuration and try to retrieve host list:
+
+ stonith -t wti_mpc ipaddr=<host> port=161 community=<community> mib-version=<version> -l
+
+result should be:
+
+ atest
+
+
+------------------
+(C) Denis Chapligin <chollya@satgate.net>, SatGate, 2009
+
diff --git a/doc/stonith/README_kdumpcheck.txt b/doc/stonith/README_kdumpcheck.txt
new file mode 100644
index 0000000..cc8787c
--- /dev/null
+++ b/doc/stonith/README_kdumpcheck.txt
@@ -0,0 +1,151 @@
+ Kdump check STONITH plugin "kdumpcheck"
+1. Introduction
+ This plugin's purpose is to avoid STONITH for a node which is doing kdump.
+ It confirms whether the node is doing kdump or not when STONITH reset or
+ off operation is executed.
+ If the target node is doing kdump, this plugin considers that STONITH
+ succeeded. If not, it considers that STONITH failed.
+
+ NOTE: This plugin has no ability to shutdown or startup a node.
+ So it has to be used with other STONITH plugin.
+ Then, when this plugin failed, the next plugin which can kill a node
+ is executed.
+ NOTE: This plugin works only on Linux.
+
+2. The way to check
+ When STONITH reset or off is executed, kdumpcheck connects to the target
+ node, and checks the size of /proc/vmcore.
+ It judges that the target node is _not_ doing kdump when the size of
+ /proc/vmcore on the node is zero, or the file doesn't exist.
+ Then kdumpcheck returns "STONITH failed" to stonithd, and the next plugin
+ is executed.
+
+3. Expanding mkdumprd
+ This plugin requires non-root user and ssh connection even on 2nd kernel.
+ So, you need to apply mkdumprd_for_kdumpcheck.patch to /sbin/mkdumprd.
+ This patch is tested with mkdumprd version 5.0.39.
+ The patch adds the following functions:
+ i) Start udevd with specified .rules files.
+ ii) Bring the specified network interface up.
+ iii) Start sshd.
+ iv) Add the specified user to the 2nd kernel.
+ The user is to check whether the node is doing kdump or not.
+ v) Execute sync command after dumping.
+
+ NOTE: i) to iv) expandings are only for the case that filesystem partition
+ is specified as the location where the vmcore should be dumped.
+
+4. Parameters
+ kdumpcheck's parameters are the following.
+ hostlist : The list of hosts that the STONITH device controls.
+ delimiter is "," or " ".
+ indispensable setting. (default:none)
+ identity_file: a full-path of the private key file for the user
+ who checks doing kdump.
+ (default: $HOME/.ssh/id_rsa, $HOME/.ssh/id_dsa and
+ $HOME/.ssh/identity)
+
+ NOTE: To execute this plugin first, set the highest priority to this plugin
+ in all STONITH resources.
+
+5. How to Use
+ To use this tool, do the following steps at all nodes in the cluster.
+ 1) Add an user to check doing kdump.
+ ex.)
+ # useradd kdumpchecker
+ # passwd kdumpchecker
+ 2) Allow passwordless login from the node which will do STONITH to all
+ target nodes for the user added at step 1).
+ ex.)
+ $ cd
+ $ mkdir .ssh
+ $ chmod 700 .ssh
+ $ cd .ssh
+ $ ssh-keygen (generate authentication keys with empty passphrase)
+ $ scp id_rsa.pub kdumpchecker@target_node:"~/.ssh/."
+ $ ssh kdumpchecker@target_node
+ $ cd ~/.ssh
+ $ cat id_rsa.pub >> authorized_keys
+ $ chmod 600 autorized_keys
+ $ rm id_rsa.pub
+ 3) Limit the command that the user can execute.
+ Describe the following commands in a line at the head of the user's
+ public key in target node's authorized_keys file.
+ [command="test -s /proc/vmcore"]
+ And describe some options (like no-pty, no-port-forwarding and so on)
+ according to your security policy.
+ ex.)
+ $ vi ~/.ssh/authorized_keys
+ command="test -s /proc/vmcore",no-port-forwarding,no-X11-forwarding,
+ no-agent-forwarding,no-pty ssh-rsa AAA..snip..== kdumpchecker@node1
+ 4) Add settings in /etc/kdump.conf.
+ network_device : network interface name to check doing kdump.
+ indispensable setting. (default: none)
+ kdump_check_user : user name to check doing kdump.
+ specify non-root user.
+ (default: "kdumpchecker")
+ udev_rules : .rules files' names.
+ specify if you use udev for mapping devices.
+ specified files have to be in /etc/udev/rules.d/.
+ you can specify two or more files.
+ delimiter is "," or " ". (default: none)
+ ex.)
+ # vi /etc/kdump.conf
+ ext3 /dev/sda1
+ network_device eth0
+ kdump_check_user kdumpchecker
+ udev_rules 10-if.rules
+ 5) Apply the patch to /sbin/mkdumprd.
+ # cd /sbin
+ # patch -p 1 < mkdumprd_for_kdumpcheck.patch
+ 6) Restart kdump service.
+ # service kdump restart
+ 7) Describe cib.xml to set STONITH plugin.
+ (See "2. Parameters" and "6. Appendix")
+
+6. Appendix
+ A sample cib.xml.
+ <clone id="clnStonith">
+ <instance_attributes id="instance_attributes.id238245a">
+ <nvpair id="clone0_clone_max" name="clone_max" value="2"/>
+ <nvpair id="clone0_clone_node_max" name="clone_node_max" value="1"/>
+ </instance_attributes>
+ <group id="grpStonith">
+ <instance_attributes id="instance_attributes.id2382455"/>
+ <primitive id="grpStonith-kdumpcheck" class="stonith" type="external/kd
+ umpcheck">
+ <instance_attributes id="instance_attributes.id238240a">
+ <nvpair id="nvpair.id238240b" name="hostlist" value="node1,node2"/>
+ <nvpair id="nvpair.id238240c" name="priority" value="1"/>
+ <nvpair id="nvpair.id2382408b" name="stonith-timeout" value="30s"/>
+ </instance_attributes>
+ <operations>
+ <op id="grpStonith-kdumpcheck-start" name="start" interval="0" tim
+ eout="300" on-fail="restart"/>
+ <op id="grpStonith-kdumpcheck-monitor" name="monitor" interval="10"
+ timeout="60" on-fail="restart"/>
+ <op id="grpStonith-kdumpcheck-stop" name="stop" interval="0" timeou
+ t="300" on-fail="block"/>
+ </operations>
+ <meta_attributes id="primitive-grpStonith-kdump-check.meta"/>
+ </primitive>
+ <primitive id="grpStonith-ssh" class="stonith" type="external/ssh">
+ <instance_attributes id="instance_attributes.id2382402a">
+ <nvpair id="nvpair.id2382408a" name="hostlist" value="node1,node2"/
+ >
+ <nvpair id="nvpair.id238066b" name="priority" value="2"/>
+ <nvpair id="nvpair.id2382408c" name="stonith-timeout" value="60s"/>
+ </instance_attributes>
+ <operations>
+ <op id="grpStonith-ssh-start" name="start" interval="0" timeout="30
+ 0" on-fail="restart"/>
+ <op id="grpStonith-ssh-monitor" name="monitor" interval="10" timeou
+ t="60" on-fail="restart"/>
+ <op id="grpStonith-ssh-stop" name="stop" interval="0" timeout="300"
+ on-fail="block"/>
+ </operations>
+ <meta_attributes id="primitive-grpStonith-ssh.meta"/>
+ </primitive>
+ </group>
+ </clone>
+
diff --git a/hb_report/Makefile.am b/hb_report/Makefile.am
new file mode 100644
index 0000000..cd4ad65
--- /dev/null
+++ b/hb_report/Makefile.am
@@ -0,0 +1,26 @@
+#
+# heartbeat: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Michael Moerz
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+hanoarchdir = $(datadir)/$(PACKAGE_NAME)
+
+hanoarch_DATA = utillib.sh ha_cf_support.sh openais_conf_support.sh
+sbin_SCRIPTS = hb_report
+
diff --git a/hb_report/ha_cf_support.sh b/hb_report/ha_cf_support.sh
new file mode 100644
index 0000000..7b35c98
--- /dev/null
+++ b/hb_report/ha_cf_support.sh
@@ -0,0 +1,83 @@
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic@suse.de>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+
+#
+# Stack specific part (heartbeat)
+# ha.cf/logd.cf parsing
+#
+getcfvar() {
+ [ -f "$CONF" ] || return
+ sed 's/#.*//' < $CONF |
+ grep -w "^$1" |
+ sed 's/^[^[:space:]]*[[:space:]]*//'
+}
+iscfvarset() {
+ test "`getcfvar $1`"
+}
+iscfvartrue() {
+ getcfvar "$1" |
+ egrep -qsi "^(true|y|yes|on|1)"
+}
+uselogd() {
+ iscfvartrue use_logd &&
+ return 0 # if use_logd true
+ iscfvarset logfacility ||
+ iscfvarset logfile ||
+ iscfvarset debugfile ||
+ return 0 # or none of the log options set
+ false
+}
+get_hb_logvars() {
+ # unless logfacility is set to none, heartbeat/ha_logd are
+ # going to log through syslog
+ HA_LOGFACILITY=`getcfvar logfacility`
+ [ "" = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=$DEFAULT_HA_LOGFACILITY
+ [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""
+ HA_LOGFILE=`getcfvar logfile`
+ HA_DEBUGFILE=`getcfvar debugfile`
+}
+getlogvars() {
+ HA_LOGFACILITY=${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY}
+ HA_LOGLEVEL="info"
+ cfdebug=`getcfvar debug` # prefer debug level if set
+ isnumber $cfdebug || cfdebug=""
+ [ "$cfdebug" ] && [ $cfdebug -gt 0 ] &&
+ HA_LOGLEVEL="debug"
+ if uselogd; then
+ [ -f "$LOGD_CF" ] || {
+ debug "logd used but logd.cf not found: using defaults"
+ return # no configuration: use defaults
+ }
+ debug "reading log settings from $LOGD_CF"
+ get_logd_logvars
+ else
+ debug "reading log settings from $CONF"
+ get_hb_logvars
+ fi
+}
+cluster_info() {
+ echo "heartbeat version: `$HA_BIN/heartbeat -V`"
+}
+essential_files() {
+ cat<<EOF
+d $HA_VARLIB 0755 root root
+d $HA_VARLIB/ccm 0750 hacluster haclient
+d $PCMK_LIB 0750 hacluster haclient
+d $PE_STATE_DIR 0750 hacluster haclient
+d $CIB_DIR 0750 hacluster haclient
+EOF
+}
diff --git a/hb_report/hb_report.in b/hb_report/hb_report.in
new file mode 100755
index 0000000..a3f9c66
--- /dev/null
+++ b/hb_report/hb_report.in
@@ -0,0 +1,1445 @@
+#!/bin/sh
+
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic@suse.de>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+
+. @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs
+
+HA_NOARCHBIN=@datadir@/@PACKAGE_NAME@
+
+. $HA_NOARCHBIN/utillib.sh
+
+unset LANG
+export LC_ALL=POSIX
+
+PROG=`basename $0`
+
+# the default syslog facility is not (yet) exported by heartbeat
+# to shell scripts
+#
+DEFAULT_HA_LOGFACILITY="daemon"
+export DEFAULT_HA_LOGFACILITY
+LOGD_CF=`findlogdcf @sysconfdir@ $HA_DIR`
+export LOGD_CF
+
+SSH_PASSWORD_NODES=""
+: ${SSH_OPTS="-o StrictHostKeyChecking=no -o EscapeChar=none"}
+LOG_PATTERNS="CRIT: ERROR:"
+# PEINPUTS_PATT="peng.*PEngine Input stored"
+
+# Important events
+#
+# Patterns format:
+# title extended_regexp
+# NB: don't use spaces in titles or regular expressions!
+EVENT_PATTERNS="
+membership crmd.*ccm_event.*(NEW|LOST)|pcmk_peer_update.*(lost|memb):
+quorum crmd.*crm_update_quorum:.Updating.quorum.status|crmd.*ais.disp.*quorum.(lost|ac?quir)
+pause Process.pause.detected
+resources lrmd.*rsc:(start|stop)
+stonith crmd.*te_fence_node.*Exec|stonith-ng.*log_oper.*reboot|stonithd.*(requests|(Succeeded|Failed).to.STONITH|result=)
+start_stop Configuration.validated..Starting.heartbeat|Corosync.Cluster.Engine|Executive.Service.RELEASE|crm_shutdown:.Requesting.shutdown|pcmk_shutdown:.Shutdown.complete
+"
+
+init_tmpfiles
+
+#
+# the instance where user runs hb_report is the master
+# the others are slaves
+#
+if [ x"$1" = x__slave ]; then
+ SLAVE=1
+fi
+
+usage() {
+ cat<<EOF
+usage: hb_report -f {time|"cts:"testnum} [-t time]
+ [-u user] [-X ssh-options] [-l file] [-n nodes] [-E files]
+ [-p patt] [-L patt] [-e prog] [-MSDZAQVsvhd] [dest]
+
+ -f time: time to start from or a CTS test number
+ -t time: time to finish at (dflt: now)
+ -d : don't compress, but leave result in a directory
+ -n nodes: node names for this cluster; this option is additive
+ (use either -n "a b" or -n a -n b)
+ if you run $PROG on the loghost or use autojoin,
+ it is highly recommended to set this option
+ -u user: ssh user to access other nodes (dflt: empty, root, hacluster)
+ -X ssh-options: extra ssh(1) options
+ -l file: log file
+ -E file: extra logs to collect; this option is additive
+ (dflt: /var/log/messages)
+ -s : sanitize the PE and CIB files
+ -p patt: regular expression to match variables containing sensitive data;
+ this option is additive (dflt: "passw.*")
+ -L patt: regular expression to match in log files for analysis;
+ this option is additive (dflt: $LOG_PATTERNS)
+ -e prog: your favourite editor
+ -Q : don't run resource intensive operations (speed up)
+ -M : don't collect extra logs (/var/log/messages)
+ -D : don't invoke editor to write description
+ -Z : if destination directories exist, remove them instead of exiting
+ (this is default for CTS)
+ -A : this is an OpenAIS cluster
+ -S : single node operation; don't try to start report
+ collectors on other nodes
+ -v : increase verbosity
+ -V : print version
+ dest : report name (may include path where to store the report)
+EOF
+
+[ "$1" != short ] &&
+ cat<<EOF
+
+ . the multifile output is stored in a tarball {dest}.tar.bz2
+ . the time specification is as in either Date::Parse or
+ Date::Manip, whatever you have installed; Date::Parse is
+ preferred
+ . we try to figure where is the logfile; if we can't, please
+ clue us in ('-l')
+ . we collect only one logfile and /var/log/messages; if you
+ have more than one logfile, then use '-E' option to supply
+ as many as you want ('-M' empties the list)
+
+ Examples
+
+ hb_report -f 2pm report_1
+ hb_report -f "2007/9/5 12:30" -t "2007/9/5 14:00" report_2
+ hb_report -f 1:00 -t 3:00 -l /var/log/cluster/ha-debug report_3
+ hb_report -f "09sep07 2:00" -u hbadmin report_4
+ hb_report -f 18:00 -p "usern.*" -p "admin.*" report_5
+ hb_report -f cts:133 ctstest_133
+
+ . WARNING . WARNING . WARNING . WARNING . WARNING . WARNING .
+
+ We won't sanitize the CIB and the peinputs files, because
+ that would make them useless when trying to reproduce the
+ PE behaviour. You may still choose to obliterate sensitive
+ information if you use the -s and -p options, but in that
+ case the support may be lacking as well. The logs and the
+ crm_mon, ccm_tool, and crm_verify output are *not* sanitized.
+
+ Additional system logs (/var/log/messages) are collected in
+ order to have a more complete report. If you don't want that
+ specify -M.
+
+ IT IS YOUR RESPONSIBILITY TO PROTECT THE DATA FROM EXPOSURE!
+EOF
+ exit
+}
+version() {
+ echo "@PACKAGE_NAME@: @PACKAGE_VERSION@ (@GLUE_BUILD_VERSION@)"
+ exit
+}
+#
+# these are "global" variables
+#
+setvarsanddefaults() {
+ local now=`perl -e 'print time()'`
+ # used by all
+ DEST=""
+ FROM_TIME=""
+ CTS=""
+ TO_TIME=0
+ HA_LOG=""
+ UNIQUE_MSG="Mark:HB_REPORT:$now"
+ SANITIZE="passw.*"
+ DO_SANITIZE=""
+ FORCE_REMOVE_DEST=""
+ COMPRESS="1"
+ # logs to collect in addition
+ # NB: they all have to be in syslog format
+ #
+ EXTRA_LOGS="/var/log/messages"
+ # used only by the master
+ NO_SSH=""
+ SSH_USER=""
+ TRY_SSH="root hacluster"
+ SLAVEPIDS=""
+ NO_DESCRIPTION="1"
+ SKIP_LVL=0
+ VERBOSITY=0
+}
+#
+# caller may skip collecting information if the skip level
+# exceeds the given value
+#
+skip_lvl() {
+ [ $SKIP_LVL -ge $1 ]
+}
+chkname() {
+ [ "$1" ] || usage short
+ echo $1 | grep -qs '[^a-zA-Z0-9@_+=:.-]' &&
+ fatal "$1 contains illegal characters"
+}
+set_dest() {
+ # default DEST has already been set earlier (if the
+ # argument is missing)
+ if [ $# -eq 1 ]; then
+ DEST=`basename $1`
+ DESTDIR=`dirname $1`
+ fi
+ chkname $DEST
+ if [ -z "$COMPRESS" -a -e "$DESTDIR/$DEST" ]; then
+ if [ "$FORCE_REMOVE_DEST" -o "$CTS" ]; then
+ rm -rf $DESTDIR/$DEST
+ else
+ fatal "destination directory $DESTDIR/$DEST exists, please cleanup or use -Z"
+ fi
+ fi
+}
+chktime() {
+ [ "$1" ] || fatal "bad time specification: $2 (try 'YYYY-M-D H:M:S') "
+}
+no_dir() {
+ fatal "could not create the working directory $WORKDIR"
+}
+time2str() {
+ perl -e "use POSIX; print strftime('%x %X',localtime($1));"
+}
+# try to figure out where pacemaker ... etc
+get_pe_state_dir() {
+ PE_STATE_DIR=`strings $CRM_DAEMON_DIR/pengine |
+ awk 'NF==1&&/var\/lib\/.*pengine$/'`
+ test -d "$PE_STATE_DIR"
+}
+get_cib_dir() {
+ CIB_DIR=`strings $CRM_DAEMON_DIR/crmd |
+ awk 'NF==1&&/var\/lib\/.*(cib|crm)$/'`
+ test -d "$CIB_DIR"
+}
+get_pe_state_dir2() {
+ # PE_STATE_DIR
+ local localstatedir lastf
+ localstatedir=`dirname $HA_VARLIB`
+ lastf=$(2>/dev/null ls -rt `2>/dev/null find /var/lib -name pengine -type d |
+ sed 's,$,/*.last,'` | tail -1)
+ if [ -f "$lastf" ]; then
+ PE_STATE_DIR=`dirname $lastf`
+ else
+ for p in pacemaker/pengine pengine heartbeat/pengine; do
+ if [ -d $localstatedir/$p ]; then
+ debug "setting PE_STATE_DIR to $localstatedir/$p"
+ PE_STATE_DIR=$localstatedir/$p
+ break
+ fi
+ done
+ fi
+}
+get_cib_dir2() {
+ # CIB
+ # HA_VARLIB is normally set to {localstatedir}/heartbeat
+ local localstatedir
+ localstatedir=`dirname $HA_VARLIB`
+ for p in pacemaker/cib heartbeat/crm; do
+ if [ -f $localstatedir/$p/cib.xml ]; then
+ debug "setting CIB_DIR to $localstatedir/$p"
+ CIB_DIR=$localstatedir/$p
+ break
+ fi
+ done
+}
+get_crm_daemon_dir() {
+ # CRM_DAEMON_DIR
+ local libdir p
+ libdir=`dirname $HA_BIN`
+ for p in pacemaker heartbeat; do
+ if [ -x $libdir/$p/crmd ]; then
+ debug "setting CRM_DAEMON_DIR to $libdir/$p"
+ CRM_DAEMON_DIR=$libdir/$p
+ return 0
+ fi
+ done
+ return 1
+}
+get_crm_daemon_dir2() {
+ # CRM_DAEMON_DIR again (brute force)
+ local p d d2
+ for p in /usr /usr/local /opt; do
+ for d in libexec lib64 lib; do
+ for d2 in pacemaker heartbeat; do
+ if [ -x $p/$d/$d2/crmd ]; then
+ debug "setting CRM_DAEMON_DIR to $p/$d/$d2"
+ CRM_DAEMON_DIR=$p/$d/$d2
+ break
+ fi
+ done
+ done
+ done
+}
+compatibility_pcmk() {
+ get_crm_daemon_dir || get_crm_daemon_dir2
+ if [ ! -d "$CRM_DAEMON_DIR" ]; then
+ fatal "cannot find pacemaker daemon directory!"
+ fi
+ get_pe_state_dir || get_pe_state_dir2
+ get_cib_dir || get_cib_dir2
+ debug "setting PCMK_LIB to `dirname $CIB_DIR`"
+ PCMK_LIB=`dirname $CIB_DIR`
+ # PTEST
+ PTEST=`echo_ptest_tool`
+ export PE_STATE_DIR CIB_DIR CRM_DAEMON_DIR PCMK_LIB PTEST
+}
+
+#
+# find log files
+#
+logmark() {
+ logger -p $*
+ debug "run: logger -p $*"
+}
+#
+# first try syslog files, if none found then use the
+# logfile/debugfile settings
+#
+findlog() {
+ local logf=""
+ if [ "$HA_LOGFACILITY" ]; then
+ logf=`findmsg $UNIQUE_MSG | awk '{print $1}'`
+ fi
+ if [ -f "$logf" ]; then
+ echo $logf
+ else
+ echo ${HA_DEBUGFILE:-$HA_LOGFILE}
+ [ "${HA_DEBUGFILE:-$HA_LOGFILE}" ] &&
+ debug "will try with ${HA_DEBUGFILE:-$HA_LOGFILE}"
+ fi
+}
+
+#
+# find log slices
+#
+
+find_decompressor() {
+ if echo $1 | grep -qs 'xz$'; then
+ echo "xz -dc"
+ elif echo $1 | grep -qs 'bz2$'; then
+ echo "bzip2 -dc"
+ elif echo $1 | grep -qs 'gz$'; then
+ echo "gzip -dc"
+ else
+ echo "cat"
+ fi
+}
+#
+# check if the log contains a piece of our segment
+#
+is_our_log() {
+ local logf=$1
+ local from_time=$2
+ local to_time=$3
+
+ local cat=`find_decompressor $logf`
+ local first_time="`$cat $logf | head -10 | find_first_ts`"
+ local last_time="`$cat $logf | tail -10 | tac | find_first_ts`"
+ if [ x = "x$first_time" -o x = "x$last_time" ]; then
+ return 0 # skip (empty log?)
+ fi
+ if [ $from_time -gt $last_time ]; then
+ # we shouldn't get here anyway if the logs are in order
+ return 2 # we're past good logs; exit
+ fi
+ if [ $from_time -ge $first_time ]; then
+ return 3 # this is the last good log
+ fi
+ # have to go further back
+ if [ $to_time -eq 0 -o $to_time -ge $first_time ]; then
+ return 1 # include this log
+ else
+ return 0 # don't include this log
+ fi
+}
+#
+# go through archived logs (timewise backwards) and see if there
+# are lines belonging to us
+# (we rely on untouched log files, i.e. that modify time
+# hasn't been changed)
+#
+arch_logs() {
+ local next_log
+ local logf=$1
+ local from_time=$2
+ local to_time=$3
+
+ # look for files such as: ha-log-20090308 or
+ # ha-log-20090308.gz (.bz2) or ha-log.0, etc
+ ls -t $logf $logf*[0-9z] 2>/dev/null |
+ while read next_log; do
+ is_our_log $next_log $from_time $to_time
+ case $? in
+ 0) ;; # noop, continue
+ 1) echo $next_log # include log and continue
+ debug "found log $next_log"
+ ;;
+ 2) break;; # don't go through older logs!
+ 3) echo $next_log # include log and continue
+ debug "found log $next_log"
+ break
+ ;; # don't go through older logs!
+ esac
+ done
+}
+#
+# print part of the log
+#
+print_log() {
+ local cat=`find_decompressor $1`
+ $cat $1
+}
+print_logseg() {
+ if test -x $HA_NOARCHBIN/print_logseg; then
+ $HA_NOARCHBIN/print_logseg "$1" "$2" "$3"
+ return
+ fi
+
+ local logf=$1
+ local from_time=$2
+ local to_time=$3
+ local tmp sourcef
+
+ # uncompress to a temp file (if necessary)
+ local cat=`find_decompressor $logf`
+ if [ "$cat" != "cat" ]; then
+ tmp=`mktemp` ||
+ fatal "disk full"
+ add_tmpfiles $tmp
+ $cat $logf > $tmp ||
+ fatal "disk full"
+ sourcef=$tmp
+ else
+ sourcef=$logf
+ tmp=""
+ fi
+
+ if [ "$from_time" = 0 ]; then
+ FROM_LINE=1
+ else
+ FROM_LINE=`findln_by_time $sourcef $from_time`
+ fi
+ if [ -z "$FROM_LINE" ]; then
+ warning "couldn't find line for time $from_time; corrupt log file?"
+ return
+ fi
+
+ TO_LINE=""
+ if [ "$to_time" != 0 ]; then
+ TO_LINE=`findln_by_time $sourcef $to_time`
+ if [ -z "$TO_LINE" ]; then
+ warning "couldn't find line for time $to_time; corrupt log file?"
+ return
+ fi
+ fi
+ dumplog $sourcef $FROM_LINE $TO_LINE
+ debug "including segment [$FROM_LINE-$TO_LINE] from $logf"
+}
+#
+# print some log info (important for crm history)
+#
+loginfo() {
+ local logf=$1
+ local fake=$2
+ local nextpos=`python -c "f=open('$logf');f.seek(0,2);print f.tell()+1"`
+ if [ "$fake" ]; then
+ echo "synthetic:$logf $nextpos"
+ else
+ echo "$logf $nextpos"
+ fi
+}
+#
+# find log/set of logs which are interesting for us
+#
+dumplogset() {
+ local logf=$1
+ local from_time=$2
+ local to_time=$3
+
+ local logf_set=`arch_logs $logf $from_time $to_time`
+ if [ x = "x$logf_set" ]; then
+ return
+ fi
+
+ local num_logs=`echo "$logf_set" | wc -l`
+ local oldest=`echo $logf_set | awk '{print $NF}'`
+ local newest=`echo $logf_set | awk '{print $1}'`
+ local mid_logfiles=`echo $logf_set | awk '{for(i=NF-1; i>1; i--) print $i}'`
+
+ # the first logfile: from $from_time to $to_time (or end)
+ # logfiles in the middle: all
+ # the last logfile: from beginning to $to_time (or end)
+ case $num_logs in
+ 1) print_logseg $newest $from_time $to_time;;
+ *)
+ print_logseg $oldest $from_time 0
+ for f in $mid_logfiles; do
+ print_log $f
+ debug "including complete $f logfile"
+ done
+ print_logseg $newest 0 $to_time
+ ;;
+ esac
+}
+
+#
+# cts_findlogseg finds lines for the CTS test run (FROM_LINE and
+# TO_LINE) and then extracts the timestamps to get FROM_TIME and
+# TO_TIME
+#
+cts_findlogseg() {
+ local testnum=$1
+ local logf=$2
+ if [ "x$logf" = "x" ]; then
+ logf=`findmsg "Running test.*\[ *$testnum\]" | awk '{print $1}'`
+ fi
+ getstampproc=`find_getstampproc < $logf`
+ export getstampproc # used by linetime
+
+ FROM_LINE=`grep -n "Running test.*\[ *$testnum\]" $logf | tail -1 | sed 's/:.*//'`
+ if [ -z "$FROM_LINE" ]; then
+ warning "couldn't find line for CTS test $testnum; corrupt log file?"
+ exit 1
+ else
+ FROM_TIME=`linetime $logf $FROM_LINE`
+ fi
+ TO_LINE=`grep -n "Running test.*\[ *$(($testnum+1))\]" $logf | tail -1 | sed 's/:.*//'`
+ [ "$TO_LINE" -a $FROM_LINE -lt $TO_LINE ] ||
+ TO_LINE=`wc -l < $logf`
+ TO_TIME=`linetime $logf $TO_LINE`
+ debug "including segment [$FROM_LINE-$TO_LINE] from $logf"
+ dumplog $logf $FROM_LINE $TO_LINE
+}
+
+#
+# this is how we pass environment to other hosts
+#
+dumpenv() {
+ cat<<EOF
+DEST=$DEST
+FROM_TIME=$FROM_TIME
+TO_TIME=$TO_TIME
+USER_NODES="$USER_NODES"
+NODES="$NODES"
+MASTER_NODE="$MASTER_NODE"
+HA_LOG=$HA_LOG
+MASTER_IS_HOSTLOG=$MASTER_IS_HOSTLOG
+UNIQUE_MSG=$UNIQUE_MSG
+SANITIZE="$SANITIZE"
+DO_SANITIZE="$DO_SANITIZE"
+SKIP_LVL="$SKIP_LVL"
+EXTRA_LOGS="$EXTRA_LOGS"
+USER_CLUSTER_TYPE="$USER_CLUSTER_TYPE"
+CONF="$CONF"
+B_CONF="$B_CONF"
+PACKAGES="$PACKAGES"
+CORES_DIRS="$CORES_DIRS"
+VERBOSITY="$VERBOSITY"
+EOF
+}
+is_collector() {
+ test "$SLAVE"
+}
+is_node() {
+ test "$THIS_IS_NODE"
+}
+is_master() {
+ ! is_collector && test "$WE" = "$MASTER_NODE"
+}
+start_slave_collector() {
+ local node=$1
+
+ dumpenv |
+ if [ "$node" = "$WE" ]; then
+ debug "running: $LOCAL_SUDO hb_report __slave"
+ $LOCAL_SUDO hb_report __slave
+ else
+ debug "running: ssh $SSH_OPTS $node \"$SUDO hb_report __slave"
+ ssh $SSH_OPTS $node \
+ "$SUDO hb_report __slave"
+ fi | (cd $WORKDIR && tar xf -)
+}
+
+#
+# does ssh work?
+# and how
+# test the provided ssh user
+# or try to find a ssh user which works without password
+# if ssh works without password, we can run the collector in the
+# background and save some time
+#
+testsshconn() {
+ ssh $SSH_OPTS -T -o Batchmode=yes $1 true 2>/dev/null
+}
+findsshuser() {
+ local n u rc
+ local ssh_s ssh_user="__undef" try_user_list
+ if [ -z "$SSH_USER" ]; then
+ try_user_list="__default $TRY_SSH"
+ else
+ try_user_list="$SSH_USER"
+ fi
+ for n in $NODES; do
+ rc=1
+ [ "$n" = "$WE" ] && continue
+ for u in $try_user_list; do
+ if [ "$u" != '__default' ]; then
+ ssh_s=$u@$n
+ else
+ ssh_s=$n
+ fi
+ if testsshconn $ssh_s; then
+ debug "ssh $ssh_s OK"
+ ssh_user="$u"
+ try_user_list="$u" # we support just one user
+ rc=0
+ break
+ else
+ debug "ssh $ssh_s failed"
+ fi
+ done
+ [ $rc = 1 ] &&
+ SSH_PASSWORD_NODES="$SSH_PASSWORD_NODES $n"
+ done
+ if [ -n "$SSH_PASSWORD_NODES" ]; then
+ warning "passwordless ssh to node(s) $SSH_PASSWORD_NODES does not work"
+ fi
+
+ if [ "$ssh_user" = "__undef" ]; then
+ return 1
+ fi
+ if [ "$ssh_user" != "__default" ]; then
+ SSH_USER=$ssh_user
+ fi
+ return 0
+}
+node_needs_pwd() {
+ local n
+ for n in $SSH_PASSWORD_NODES; do
+ [ "$n" = "$1" ] && return 0
+ done
+ return 1
+}
+say_ssh_user() {
+ if [ -n "$SSH_USER" ]; then
+ echo $SSH_USER
+ else
+ echo your user
+ fi
+}
+
+#
+# the usual stuff
+#
+getbacktraces() {
+ local f bf flist
+ flist=$(
+ for f in `find_files "$CORES_DIRS" $1 $2`; do
+ bf=`basename $f`
+ test `expr match $bf core` -gt 0 &&
+ echo $f
+ done)
+ [ "$flist" ] && {
+ getbt $flist > $3
+ debug "found backtraces: $flist"
+ }
+}
+pe2dot() {
+ local pef=`basename $1`
+ local dotf=`basename $pef .bz2`.dot
+ test -z "$PTEST" && return
+ (
+ cd `dirname $1`
+ $PTEST -D $dotf -x $pef >/dev/null 2>&1
+ )
+}
+getpeinputs() {
+ local pe_dir flist
+ local f
+ pe_dir=$PE_STATE_DIR
+ debug "looking for PE files in $pe_dir"
+ flist=$(
+ find_files $pe_dir $1 $2 | grep -v '[.]last$'
+ )
+ [ "$flist" ] && {
+ mkdir $3/`basename $pe_dir`
+ (
+ cd $3/`basename $pe_dir`
+ for f in $flist; do
+ ln -s $f
+ done
+ )
+ debug "found `echo $flist | wc -w` pengine input files in $pe_dir"
+ }
+ if [ `echo $flist | wc -w` -le 20 ]; then
+ for f in $flist; do
+ skip_lvl 1 || pe2dot $3/`basename $pe_dir`/`basename $f`
+ done
+ else
+ debug "too many PE inputs to create dot files"
+ fi
+}
+getratraces() {
+ local trace_dir flist
+ local f
+ trace_dir=$HA_VARLIB/trace_ra
+ test -d "$trace_dir" || return 0
+ debug "looking for RA trace files in $trace_dir"
+ flist=$(find_files $trace_dir $1 $2 | sed "s,`dirname $trace_dir`/,,g")
+ [ "$flist" ] && {
+ tar -cf - -C `dirname $trace_dir` $flist | tar -xf - -C $3
+ debug "found `echo $flist | wc -w` RA trace files in $trace_dir"
+ }
+}
+touch_DC_if_dc() {
+ local dc
+ dc=`crmadmin -D 2>/dev/null | awk '{print $NF}'`
+ if [ "$WE" = "$dc" ]; then
+ touch $1/DC
+ fi
+}
+corosync_blackbox() {
+ local from_time=$1
+ local to_time=$2
+ local outf=$3
+ local inpf
+ inpf=`find_files /var/lib/corosync $from_time $to_time | grep -w fdata`
+ if [ -f "$inpf" ]; then
+ corosync-blackbox > $outf
+ touch -r $inpf $outf
+ fi
+}
+getconfigurations() {
+ local conf
+ local dest=$1
+ for conf in $CONFIGURATIONS; do
+ if [ -f $conf ]; then
+ cp -p $conf $dest
+ elif [ -d $conf ]; then
+ (
+ cd `dirname $conf` &&
+ tar cf - `basename $conf` | (cd $dest && tar xf -)
+ )
+ fi
+ done
+}
+
+
+#
+# some basic system info and stats
+#
+sys_info() {
+ cluster_info
+ hb_report -V # our info
+ echo "resource-agents: `grep 'Build version:' @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs`"
+ crm_info
+ pkg_versions $PACKAGES
+ skip_lvl 1 || verify_packages $PACKAGES
+ echo "Platform: `uname`"
+ echo "Kernel release: `uname -r`"
+ echo "Architecture: `uname -m`"
+ [ `uname` = Linux ] &&
+ echo "Distribution: `distro`"
+}
+sys_stats() {
+ set -x
+ uname -n
+ uptime
+ ps axf
+ ps auxw
+ top -b -n 1
+ ifconfig -a
+ ip addr list
+ netstat -i
+ arp -an
+ test -d /proc && {
+ cat /proc/cpuinfo
+ }
+ lsscsi
+ lspci
+ mount
+ # df can block, run in background, allow for 5 seconds (!)
+ local maxcnt=5
+ df &
+ while kill -0 $! >/dev/null 2>&1; do
+ sleep 1
+ if [ $maxcnt -le 0 ]; then
+ warning "df appears to be hanging, continuing without it"
+ break
+ fi
+ maxcnt=$((maxcnt-1))
+ done
+ set +x
+}
+time_status() {
+ date
+ ntpdc -pn
+}
+dlm_dump() {
+ if which dlm_tool >/dev/null 2>&1 ; then
+ echo NOTICE - Lockspace overview:
+ dlm_tool ls
+ dlm_tool ls | grep name |
+ while read X N ; do
+ echo NOTICE - Lockspace $N:
+ dlm_tool lockdump $N
+ done
+ echo NOTICE - Lockspace history:
+ dlm_tool dump
+ fi
+}
+
+
+#
+# replace sensitive info with '****'
+#
+sanitize() {
+ local f rc
+ for f in $1/$B_CONF; do
+ [ -f "$f" ] && sanitize_one $f
+ done
+ rc=0
+ for f in $1/$CIB_F $1/pengine/*; do
+ if [ -f "$f" ]; then
+ if [ "$DO_SANITIZE" ]; then
+ sanitize_one $f
+ else
+ test_sensitive_one $f && rc=1
+ fi
+ fi
+ done
+ [ $rc -ne 0 ] && {
+ warning "some PE or CIB files contain possibly sensitive data"
+ warning "you may not want to send this report to a public mailing list"
+ }
+}
+
+#
+# remove duplicates if files are same, make links instead
+#
+consolidate() {
+ for n in $NODES; do
+ if [ -f $1/$2 ]; then
+ rm $1/$n/$2
+ else
+ mv $1/$n/$2 $1
+ fi
+ ln -s ../$2 $1/$n
+ done
+}
+
+#
+# some basic analysis of the report
+#
+checkcrmvfy() {
+ for n in $NODES; do
+ if [ -s $1/$n/$CRM_VERIFY_F ]; then
+ echo "WARN: crm_verify reported warnings at $n:"
+ cat $1/$n/$CRM_VERIFY_F
+ fi
+ done
+}
+checkbacktraces() {
+ for n in $NODES; do
+ [ -s $1/$n/$BT_F ] && {
+ echo "WARN: coredumps found at $n:"
+ egrep 'Core was generated|Program terminated' \
+ $1/$n/$BT_F |
+ sed 's/^/ /'
+ }
+ done
+}
+checkpermissions() {
+ for n in $NODES; do
+ if [ -s $1/$n/$PERMISSIONS_F ]; then
+ echo "WARN: problem with permissions/ownership at $n:"
+ cat $1/$n/$PERMISSIONS_F
+ fi
+ done
+}
+checklogs() {
+ local logs pattfile l n
+ logs=$(find $1 -name $HALOG_F;
+ for l in $EXTRA_LOGS; do find $1/* -name `basename $l`; done)
+ [ "$logs" ] || return
+ pattfile=`mktemp` ||
+ fatal "cannot create temporary files"
+ add_tmpfiles $pattfile
+ for p in $LOG_PATTERNS; do
+ echo "$p"
+ done > $pattfile
+ echo ""
+ echo "Log patterns:"
+ for n in $NODES; do
+ cat $logs | grep -f $pattfile
+ done
+}
+
+#
+# check if files have same content in the cluster
+#
+cibdiff() {
+ local d1 d2
+ d1=`dirname $1`
+ d2=`dirname $2`
+ if [ -f $d1/RUNNING -a -f $d2/RUNNING ] ||
+ [ -f $d1/STOPPED -a -f $d2/STOPPED ]; then
+ if which crm_diff > /dev/null 2>&1; then
+ crm_diff -c -n $1 -o $2
+ else
+ info "crm_diff(8) not found, cannot diff CIBs"
+ fi
+ else
+ echo "can't compare cibs from running and stopped systems"
+ fi
+}
+txtdiff() {
+ diff -bBu $1 $2
+}
+diffcheck() {
+ [ -f "$1" ] || {
+ echo "$1 does not exist"
+ return 1
+ }
+ [ -f "$2" ] || {
+ echo "$2 does not exist"
+ return 1
+ }
+ case `basename $1` in
+ $CIB_F)
+ cibdiff $1 $2;;
+ $B_CONF)
+ txtdiff $1 $2;; # confdiff?
+ *)
+ txtdiff $1 $2;;
+ esac
+}
+analyze_one() {
+ local rc node0 n
+ rc=0
+ node0=""
+ for n in $NODES; do
+ if [ "$node0" ]; then
+ diffcheck $1/$node0/$2 $1/$n/$2
+ rc=$(($rc+$?))
+ else
+ node0=$n
+ fi
+ done
+ return $rc
+}
+analyze() {
+ local f flist
+ flist="$HOSTCACHE $MEMBERSHIP_F $CIB_F $CRM_MON_F $B_CONF logd.cf $SYSINFO_F"
+ for f in $flist; do
+ printf "Diff $f... "
+ ls $1/*/$f >/dev/null 2>&1 || {
+ echo "no $1/*/$f :/"
+ continue
+ }
+ if analyze_one $1 $f; then
+ echo "OK"
+ [ "$f" != $CIB_F ] &&
+ consolidate $1 $f
+ else
+ echo ""
+ fi
+ done
+ checkcrmvfy $1
+ checkbacktraces $1
+ checkpermissions $1
+ checklogs $1
+}
+events_all() {
+ local Epatt title p
+ Epatt=`echo "$EVENT_PATTERNS" |
+ while read title p; do [ -n "$p" ] && echo -n "|$p"; done |
+ sed 's/.//'
+ `
+ grep -E "$Epatt" $1
+}
+events() {
+ local destdir n
+ destdir=$1
+ if [ -f $destdir/$HALOG_F ]; then
+ events_all $destdir/$HALOG_F > $destdir/events.txt
+ for n in $NODES; do
+ awk "\$4==\"$n\"" $destdir/events.txt > $destdir/$n/events.txt
+ done
+ else
+ for n in $NODES; do
+ [ -f $destdir/$n/$HALOG_F ] ||
+ continue
+ events_all $destdir/$n/$HALOG_F > $destdir/$n/events.txt
+ done
+ fi
+}
+
+#
+# description template, editing, and other notes
+#
+mktemplate() {
+ cat<<EOF
+Please edit this template and describe the issue/problem you
+encountered. Then, post to
+ Linux-HA@lists.linux-ha.org
+or file a bug at
+ http://developerbugs.linux-foundation.org/
+
+See http://linux-ha.org/wiki/ReportingProblems for detailed
+description on how to report problems.
+
+Thank you.
+
+Date: `date`
+By: $PROG $userargs
+Subject: [short problem description]
+Severity: [choose one] enhancement minor normal major critical blocking
+Component: [choose one] CRM LRM CCM RA fencing $CLUSTER_TYPE comm GUI tools other
+
+Detailed description:
+---
+[...]
+---
+
+EOF
+
+ if [ -f $WORKDIR/$SYSINFO_F ]; then
+ echo "Common system info found:"
+ cat $WORKDIR/$SYSINFO_F
+ else
+ for n in $NODES; do
+ if [ -f $WORKDIR/$n/$SYSINFO_F ]; then
+ echo "System info $n:"
+ sed 's/^/ /' $WORKDIR/$n/$SYSINFO_F
+ fi
+ done
+ fi
+}
+edittemplate() {
+ local ec
+ if ec=`pickfirst $EDITOR vim vi emacs nano`; then
+ $ec $1
+ else
+ warning "could not find a text editor"
+ fi
+}
+pickcompress() {
+ if COMPRESS_PROG=`pickfirst bzip2 gzip xz`; then
+ if [ "$COMPRESS_PROG" = xz ]; then
+ COMPRESS_EXT=.xz
+ elif [ "$COMPRESS_PROG" = bzip2 ]; then
+ COMPRESS_EXT=.bz2
+ else
+ COMPRESS_EXT=.gz
+ fi
+ else
+ warning "could not find a compression program; the resulting tarball may be huge"
+ COMPRESS_PROG=cat
+ COMPRESS_EXT=
+ fi
+}
+# get the right part of the log
+getlog() {
+ local cnt
+ local outf
+ outf=$WORKDIR/$HALOG_F
+
+ if [ "$HA_LOG" ]; then # log provided by the user?
+ [ -f "$HA_LOG" ] || { # not present
+ is_collector || # warning if not on slave
+ warning "$HA_LOG not found; we will try to find log ourselves"
+ HA_LOG=""
+ }
+ fi
+ if [ "$HA_LOG" = "" ]; then
+ HA_LOG=`findlog`
+ [ "$HA_LOG" ] &&
+ cnt=`fgrep -c $UNIQUE_MSG < $HA_LOG`
+ fi
+ if [ "$HA_LOG" = "" -o ! -f "$HA_LOG" ]; then
+ if [ "$CTS" ]; then
+ cts_findlogseg $CTS > $outf
+ else
+ warning "no log at $WE"
+ fi
+ return
+ fi
+ if [ "$cnt" ] && [ $cnt -gt 1 -a $cnt -eq $NODECNT ]; then
+ MASTER_IS_HOSTLOG=1
+ info "found the central log!"
+ fi
+
+ if [ "$NO_str2time" ]; then
+ warning "a log found; but we cannot slice it"
+ warning "please install the perl Date::Parse module"
+ elif [ "$CTS" ]; then
+ cts_findlogseg $CTS $HA_LOG > $outf
+ else
+ getstampproc=`find_getstampproc < $HA_LOG`
+ if [ "$getstampproc" ]; then
+ export getstampproc # used by linetime
+ dumplogset $HA_LOG $FROM_TIME $TO_TIME > $outf &&
+ loginfo $HA_LOG > $outf.info ||
+ fatal "disk full"
+ else
+ warning "could not figure out the log format of $HA_LOG"
+ fi
+ fi
+}
+#
+# get all other info (config, stats, etc)
+#
+collect_info() {
+ local l
+ sys_info > $WORKDIR/$SYSINFO_F 2>&1 &
+ sys_stats > $WORKDIR/$SYSSTATS_F 2>&1 &
+ getconfig $WORKDIR
+ getpeinputs $FROM_TIME $TO_TIME $WORKDIR &
+ crmconfig $WORKDIR &
+ skip_lvl 1 || touch_DC_if_dc $WORKDIR &
+ getbacktraces $FROM_TIME $TO_TIME $WORKDIR/$BT_F
+ getconfigurations $WORKDIR
+ check_perms > $WORKDIR/$PERMISSIONS_F 2>&1
+ dlm_dump > $WORKDIR/$DLM_DUMP_F 2>&1
+ time_status > $WORKDIR/$TIME_F 2>&1
+ corosync_blackbox $FROM_TIME $TO_TIME $WORKDIR/$COROSYNC_RECORDER_F
+ getratraces $FROM_TIME $TO_TIME $WORKDIR
+ wait
+ skip_lvl 1 || sanitize $WORKDIR
+
+ for l in $EXTRA_LOGS; do
+ [ "$NO_str2time" ] && break
+ [ ! -f "$l" ] && continue
+ if [ "$l" = "$HA_LOG" -a "$l" != "$HALOG_F" ]; then
+ ln -s $HALOG_F $WORKDIR/`basename $l`
+ continue
+ fi
+ getstampproc=`find_getstampproc < $l`
+ if [ "$getstampproc" ]; then
+ export getstampproc # used by linetime
+ dumplogset $l $FROM_TIME $TO_TIME > $WORKDIR/`basename $l` &&
+ loginfo $l > $WORKDIR/`basename $l`.info ||
+ fatal "disk full"
+ else
+ warning "could not figure out the log format of $l"
+ fi
+ done
+}
+finalword() {
+ if [ "$COMPRESS" = "1" ]; then
+ echo "The report is saved in $DESTDIR/$DEST.tar$COMPRESS_EXT"
+ else
+ echo "The report is saved in $DESTDIR/$DEST"
+ fi
+ echo " "
+ echo "Thank you for taking time to create this report."
+}
+
+[ $# -eq 0 ] && usage
+
+# check for the major prereq for a) parameter parsing and b)
+# parsing logs
+#
+NO_str2time=""
+t=`str2time "12:00"`
+if [ "$t" = "" ]; then
+ NO_str2time=1
+ is_collector ||
+ fatal "please install the perl Date::Parse module"
+fi
+
+WE=`uname -n` # who am i?
+tmpdir=`mktemp -t -d .hb_report.workdir.XXXXXX` ||
+ fatal "disk full"
+add_tmpfiles $tmpdir
+WORKDIR=$tmpdir
+
+#
+# part 1: get and check options; and the destination
+#
+if ! is_collector; then
+ setvarsanddefaults
+ userargs="$@"
+ DESTDIR=.
+ DEST="hb_report-"`date +"%a-%d-%b-%Y"`
+ while getopts f:t:l:u:X:p:L:e:E:n:MSDCZAVsvhdQ o; do
+ case "$o" in
+ h) usage;;
+ V) version;;
+ f)
+ if echo "$OPTARG" | grep -qs '^cts:'; then
+ FROM_TIME=0 # to be calculated later
+ CTS=`echo "$OPTARG" | sed 's/cts://'`
+ DEST="cts-$CTS-"`date +"%a-%d-%b-%Y"`
+ else
+ FROM_TIME=`str2time "$OPTARG"`
+ chktime "$FROM_TIME" "$OPTARG"
+ fi
+ ;;
+ t) TO_TIME=`str2time "$OPTARG"`
+ chktime "$TO_TIME" "$OPTARG"
+ ;;
+ n) NODES_SOURCE=user
+ USER_NODES="$USER_NODES $OPTARG"
+ ;;
+ u) SSH_USER="$OPTARG";;
+ X) SSH_OPTS="$SSH_OPTS $OPTARG";;
+ l) HA_LOG="$OPTARG";;
+ e) EDITOR="$OPTARG";;
+ p) SANITIZE="$SANITIZE $OPTARG";;
+ s) DO_SANITIZE="1";;
+ Q) SKIP_LVL=$((SKIP_LVL + 1));;
+ L) LOG_PATTERNS="$LOG_PATTERNS $OPTARG";;
+ S) NO_SSH=1;;
+ D) NO_DESCRIPTION=1;;
+ C) : ;;
+ Z) FORCE_REMOVE_DEST=1;;
+ M) EXTRA_LOGS="";;
+ E) EXTRA_LOGS="$EXTRA_LOGS $OPTARG";;
+ A) USER_CLUSTER_TYPE="openais";;
+ v) VERBOSITY=$((VERBOSITY + 1));;
+ d) COMPRESS="";;
+ [?]) usage short;;
+ esac
+ done
+ shift $(($OPTIND-1))
+ [ $# -gt 1 ] && usage short
+ set_dest $*
+ [ "$FROM_TIME" ] || usage short
+ WORKDIR=$tmpdir/$DEST
+else
+ WORKDIR=$tmpdir/$DEST/$WE
+fi
+
+mkdir -p $WORKDIR
+[ -d $WORKDIR ] || no_dir
+
+if is_collector; then
+ cat > $WORKDIR/.env
+ . $WORKDIR/.env
+fi
+
+[ $VERBOSITY -gt 1 ] && {
+ is_collector || {
+ info "high debug level, please read debug.out"
+ }
+ PS4='+ `date +"%T"`: ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: '
+ if echo "$SHELL" | grep bash > /dev/null &&
+ [ ${BASH_VERSINFO[0]} = "4" ]; then
+ exec 3>>$WORKDIR/debug.out
+ BASH_XTRACEFD=3
+ else
+ exec 2>>$WORKDIR/debug.out
+ fi
+ set -x
+}
+
+compatibility_pcmk
+
+# allow user to enforce the cluster type
+# if not, then it is found out on _all_ nodes
+if [ -z "$USER_CLUSTER_TYPE" ]; then
+ CLUSTER_TYPE=`get_cluster_type`
+else
+ CLUSTER_TYPE=$USER_CLUSTER_TYPE
+fi
+
+# the very first thing we must figure out is which cluster
+# stack is used
+CORES_DIRS="`2>/dev/null ls -d $HA_VARLIB/cores $PCMK_LIB/cores | uniq`"
+PACKAGES="pacemaker libpacemaker3
+pacemaker-pygui pacemaker-pymgmt pymgmt-client
+openais libopenais2 libopenais3 corosync libcorosync4
+resource-agents cluster-glue libglue2 ldirectord
+heartbeat heartbeat-common heartbeat-resources libheartbeat2
+ocfs2-tools ocfs2-tools-o2cb ocfs2console
+ocfs2-kmp-default ocfs2-kmp-pae ocfs2-kmp-xen ocfs2-kmp-debug ocfs2-kmp-trace
+drbd drbd-kmp-xen drbd-kmp-pae drbd-kmp-default drbd-kmp-debug drbd-kmp-trace
+drbd-heartbeat drbd-pacemaker drbd-utils drbd-bash-completion drbd-xen
+lvm2 lvm2-clvm cmirrord
+libdlm libdlm2 libdlm3
+hawk ruby lighttpd
+kernel-default kernel-pae kernel-xen
+glibc
+"
+case "$CLUSTER_TYPE" in
+openais)
+ CONF=/etc/corosync/corosync.conf # corosync?
+ if test -f $CONF; then
+ CORES_DIRS="$CORES_DIRS /var/lib/corosync"
+ else
+ CONF=/etc/ais/openais.conf
+ CORES_DIRS="$CORES_DIRS /var/lib/openais"
+ fi
+ CF_SUPPORT=$HA_NOARCHBIN/openais_conf_support.sh
+ MEMBERSHIP_TOOL_OPTS=""
+ unset HOSTCACHE HB_UUID_F
+ ;;
+heartbeat)
+ CONF=$HA_CF
+ CF_SUPPORT=$HA_NOARCHBIN/ha_cf_support.sh
+ MEMBERSHIP_TOOL_OPTS="-H"
+ ;;
+esac
+B_CONF=`basename $CONF`
+
+if test -f "$CF_SUPPORT"; then
+ . $CF_SUPPORT
+else
+ fatal "no stack specific support: $CF_SUPPORT"
+fi
+
+if [ "x$CTS" = "x" ] || is_collector; then
+ getlogvars
+ debug "log settings: facility=$HA_LOGFACILITY logfile=$HA_LOGFILE debugfile=$HA_DEBUGFILE"
+elif ! is_collector; then
+ ctslog=`findmsg "CTS: Stack:" | awk '{print $1}'`
+ debug "Using CTS control file: $ctslog"
+ USER_NODES=`grep CTS: $ctslog | grep -v debug: | grep " \* " | sed s:.*\\\*::g | sort -u | tr '\\n' ' '`
+ HA_LOGFACILITY=`findmsg "CTS:.*Environment.SyslogFacility" | awk '{print $NF}'`
+ NODES_SOURCE=user
+fi
+
+# the goods
+ANALYSIS_F=analysis.txt
+DESCRIPTION_F=description.txt
+HALOG_F=ha-log.txt
+BT_F=backtraces.txt
+SYSINFO_F=sysinfo.txt
+SYSSTATS_F=sysstats.txt
+DLM_DUMP_F=dlm_dump.txt
+TIME_F=time.txt
+export ANALYSIS_F DESCRIPTION_F HALOG_F BT_F SYSINFO_F SYSSTATS_F DLM_DUMP_F TIME_F
+CRM_MON_F=crm_mon.txt
+MEMBERSHIP_F=members.txt
+HB_UUID_F=hb_uuid.txt
+HOSTCACHE=hostcache
+CRM_VERIFY_F=crm_verify.txt
+PERMISSIONS_F=permissions.txt
+CIB_F=cib.xml
+CIB_TXT_F=cib.txt
+COROSYNC_RECORDER_F=fdata.txt
+export CRM_MON_F MEMBERSHIP_F CRM_VERIFY_F CIB_F CIB_TXT_F HB_UUID_F PERMISSIONS_F
+export COROSYNC_RECORDER_F
+CONFIGURATIONS="/etc/drbd.conf /etc/drbd.d /etc/booth/booth.conf"
+export CONFIGURATIONS
+
+THIS_IS_NODE=""
+if ! is_collector; then
+ MASTER_NODE=$WE
+ NODES=`getnodes`
+ debug "nodes: `echo $NODES`"
+fi
+NODECNT=`echo $NODES | wc -w`
+if [ "$NODECNT" = 0 ]; then
+ fatal "could not figure out a list of nodes; is this a cluster node?"
+fi
+if echo $NODES | grep -wqs $WE; then # are we a node?
+ THIS_IS_NODE=1
+fi
+
+# this only on master
+if ! is_collector; then
+
+ # if this is not a node, then some things afterwards might
+ # make no sense (not work)
+ if ! is_node && [ "$NODES_SOURCE" != user ]; then
+ warning "this is not a node and you didn't specify a list of nodes using -n"
+ fi
+#
+# part 2: ssh business
+#
+ # find out if ssh works
+ if [ -z "$NO_SSH" ]; then
+ # if the ssh user was supplied, consider that it
+ # works; helps reduce the number of ssh invocations
+ findsshuser
+ if [ -n "$SSH_USER" ]; then
+ SSH_OPTS="$SSH_OPTS -o User=$SSH_USER"
+ fi
+ fi
+ # assume that only root can collect data
+ SUDO=""
+ if [ -z "$SSH_USER" -a `id -u` != 0 ] ||
+ [ -n "$SSH_USER" -a "$SSH_USER" != root ]; then
+ debug "ssh user other than root, use sudo"
+ SUDO="sudo -u root"
+ fi
+ LOCAL_SUDO=""
+ if [ `id -u` != 0 ]; then
+ debug "local user other than root, use sudo"
+ LOCAL_SUDO="sudo -u root"
+ fi
+fi
+
+if is_collector && [ "$HA_LOGFACILITY" ]; then
+ logmark $HA_LOGFACILITY.$HA_LOGLEVEL $UNIQUE_MSG
+ # allow for the log message to get (hopefully) written to the
+ # log file
+ sleep 1
+fi
+
+#
+# part 4: find the logs and cut out the segment for the period
+#
+
+# if the master is also a node, getlog is going to be invoked
+# from the collector
+(is_master && is_node) ||
+ getlog
+
+if ! is_collector; then
+ for node in $NODES; do
+ if node_needs_pwd $node; then
+ info "Please provide password for `say_ssh_user` at $node"
+ info "Note that collecting data will take a while."
+ start_slave_collector $node
+ else
+ start_slave_collector $node &
+ SLAVEPIDS="$SLAVEPIDS $!"
+ fi
+ done
+fi
+
+#
+# part 5: endgame:
+# slaves tar their results to stdout, the master waits
+# for them, analyses results, asks the user to edit the
+# problem description template, and prints final notes
+#
+if is_collector; then
+ collect_info
+ (cd $WORKDIR/.. && tar -h -cf - $WE)
+else
+ if [ -n "$SLAVEPIDS" ]; then
+ wait $SLAVEPIDS
+ fi
+ analyze $WORKDIR > $WORKDIR/$ANALYSIS_F &
+ events $WORKDIR &
+ mktemplate > $WORKDIR/$DESCRIPTION_F
+ [ "$NO_DESCRIPTION" ] || {
+ echo press enter to edit the problem description...
+ read junk
+ edittemplate $WORKDIR/$DESCRIPTION_F
+ }
+ wait
+ if [ "$COMPRESS" = "1" ]; then
+ pickcompress
+ (cd $WORKDIR/.. && tar cf - $DEST) | $COMPRESS_PROG > $DESTDIR/$DEST.tar$COMPRESS_EXT
+ else
+ mv $WORKDIR $DESTDIR
+ fi
+ finalword
+fi
diff --git a/hb_report/openais_conf_support.sh b/hb_report/openais_conf_support.sh
new file mode 100644
index 0000000..b96d1aa
--- /dev/null
+++ b/hb_report/openais_conf_support.sh
@@ -0,0 +1,97 @@
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic@suse.de>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+
+#
+# Stack specific part (openais)
+# openais.conf/logd.cf parsing
+#
+# cut out a stanza
+getstanza() {
+ awk -v name="$1" '
+ !in_stanza && NF==2 && /^[a-z][a-z]*[[:space:]]*{/ { # stanza start
+ if ($1 == name)
+ in_stanza = 1
+ }
+ in_stanza { print }
+ in_stanza && NF==1 && $1 == "}" { exit }
+ '
+}
+# supply stanza in $1 and variable name in $2
+# (stanza is optional)
+getcfvar() {
+ [ -f "$CONF" ] || return
+ sed 's/#.*//' < $CONF |
+ if [ $# -eq 2 ]; then
+ getstanza "$1"
+ shift 1
+ else
+ cat
+ fi |
+ awk -v varname="$1" '
+ NF==2 && match($1,varname":$")==1 { print $2; exit; }
+ '
+}
+iscfvarset() {
+ test "`getcfvar $1`"
+}
+iscfvartrue() {
+ getcfvar $1 $2 |
+ egrep -qsi "^(true|y|yes|on|1)"
+}
+uselogd() {
+ iscfvartrue use_logd
+}
+get_ais_logvars() {
+ if iscfvartrue to_file; then
+ HA_LOGFILE=`getcfvar logfile`
+ HA_LOGFILE=${HA_LOGFILE:-"syslog"}
+ HA_DEBUGFILE=$HA_LOGFILE
+ elif iscfvartrue to_syslog; then
+ HA_LOGFACILITY=`getcfvar syslog_facility`
+ HA_LOGFACILITY=${HA_LOGFACILITY:-"daemon"}
+ fi
+}
+getlogvars() {
+ HA_LOGFACILITY=${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY}
+ HA_LOGLEVEL="info"
+ iscfvartrue debug && # prefer debug level if set
+ HA_LOGLEVEL="debug"
+ if uselogd; then
+ [ -f "$LOGD_CF" ] || {
+ debug "logd used but logd.cf not found: using defaults"
+ return # no configuration: use defaults
+ }
+ debug "reading log settings from $LOGD_CF"
+ get_logd_logvars
+ else
+ debug "reading log settings from $CONF"
+ get_ais_logvars
+ fi
+}
+cluster_info() {
+ : echo "openais version: how?"
+ if [ "$CONF" = /etc/corosync/corosync.conf ]; then
+ /usr/sbin/corosync -v
+ fi
+}
+essential_files() {
+ cat<<EOF
+d $PCMK_LIB 0750 hacluster haclient
+d $PE_STATE_DIR 0750 hacluster haclient
+d $CIB_DIR 0750 hacluster haclient
+EOF
+}
diff --git a/hb_report/utillib.sh b/hb_report/utillib.sh
new file mode 100644
index 0000000..0fcab80
--- /dev/null
+++ b/hb_report/utillib.sh
@@ -0,0 +1,752 @@
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic@suse.de>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+
+#
+# figure out the cluster type, depending on the process list
+# and existence of configuration files
+#
+get_cluster_type() {
+ if ps -ef | egrep -qs '[a]isexec|[c]orosync' ||
+ [ -f /etc/ais/openais.conf -a ! -f "$HA_CF" ] ||
+ [ -f /etc/corosync/corosync.conf -a ! -f "$HA_CF" ]
+ then
+ debug "this is OpenAIS cluster stack"
+ echo "openais"
+ else
+ debug "this is Heartbeat cluster stack"
+ echo "heartbeat"
+ fi
+}
+#
+# find out which membership tool is installed
+#
+echo_membership_tool() {
+ local f membership_tools
+ membership_tools="ccm_tool crm_node"
+ for f in $membership_tools; do
+ which $f 2>/dev/null && break
+ done
+}
+# find out if ptest or crm_simulate
+#
+echo_ptest_tool() {
+ local f ptest_progs
+ ptest_progs="crm_simulate ptest"
+ for f in $ptest_progs; do
+ which $f 2>/dev/null && break
+ done
+}
+#
+# find nodes for this cluster
+#
+getnodes() {
+ # 1. set by user?
+ if [ "$USER_NODES" ]; then
+ echo $USER_NODES
+ # 2. running crm
+ elif iscrmrunning; then
+ debug "querying CRM for nodes"
+ get_crm_nodes
+ # 3. hostcache
+ elif [ -f $HA_VARLIB/hostcache ]; then
+ debug "reading nodes from $HA_VARLIB/hostcache"
+ awk '{print $1}' $HA_VARLIB/hostcache
+ # 4. ha.cf
+ elif [ "$CLUSTER_TYPE" = heartbeat ]; then
+ debug "reading nodes from ha.cf"
+ getcfvar node
+ # 5. if the cluster's stopped, try the CIB
+ elif [ -f $CIB_DIR/$CIB_F ]; then
+ debug "reading nodes from the archived $CIB_DIR/$CIB_F"
+ (CIB_file=$CIB_DIR/$CIB_F get_crm_nodes)
+ fi
+}
+
+logd_getcfvar() {
+ sed 's/#.*//' < $LOGD_CF |
+ grep -w "^$1" |
+ sed 's/^[^[:space:]]*[[:space:]]*//'
+}
+get_logd_logvars() {
+ # unless logfacility is set to none, heartbeat/ha_logd are
+ # going to log through syslog
+ HA_LOGFACILITY=`logd_getcfvar logfacility`
+ [ "" = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=$DEFAULT_HA_LOGFACILITY
+ [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""
+ HA_LOGFILE=`logd_getcfvar logfile`
+ HA_DEBUGFILE=`logd_getcfvar debugfile`
+}
+findlogdcf() {
+ local f
+ for f in \
+ `test -x $HA_BIN/ha_logd &&
+ which strings > /dev/null 2>&1 &&
+ strings $HA_BIN/ha_logd | grep 'logd\.cf'` \
+ `for d; do echo $d/logd.cf $d/ha_logd.cf; done`
+ do
+ if [ -f "$f" ]; then
+ echo $f
+ debug "found logd.cf at $f"
+ return 0
+ fi
+ done
+ debug "no logd.cf"
+ return 1
+}
+#
+# logging
+#
+syslogmsg() {
+ local severity logtag
+ severity=$1
+ shift 1
+ logtag=""
+ [ "$HA_LOGTAG" ] && logtag="-t $HA_LOGTAG"
+ logger -p ${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY}.$severity $logtag $*
+}
+
+#
+# find log destination
+#
+findmsg() {
+ local d syslogdirs favourites mark log
+ # this is tricky, we try a few directories
+ syslogdirs="/var/log /var/logs /var/syslog /var/adm
+ /var/log/ha /var/log/cluster /var/log/pacemaker
+ /var/log/heartbeat /var/log/crm /var/log/corosync /var/log/openais"
+ favourites="ha-*"
+ mark=$1
+ log=""
+ for d in $syslogdirs; do
+ [ -d $d ] || continue
+ log=`grep -l -e "$mark" $d/$favourites` && break
+ test "$log" && break
+ log=`grep -l -e "$mark" $d/*` && break
+ test "$log" && break
+ done 2>/dev/null
+ [ "$log" ] &&
+ ls -t $log | tr '\n' ' '
+ [ "$log" ] &&
+ debug "found HA log at `ls -t $log | tr '\n' ' '`" ||
+ debug "no HA log found in $syslogdirs"
+}
+
+#
+# print a segment of a log file
+#
+str2time() {
+ perl -e "\$time='$*';" -e '
+ $unix_tm = 0;
+ eval "use Date::Parse";
+ if (!$@) {
+ $unix_tm = str2time($time);
+ } else {
+ eval "use Date::Manip";
+ if (!$@) {
+ $unit_tm = UnixDate(ParseDateString($time), "%s");
+ }
+ }
+ if ($unix_tm != "") {
+ $unix_tm = int($unix_tm);
+ }
+ print $unix_tm;
+ '
+}
+getstamp_syslog() {
+ awk '{print $1,$2,$3}'
+}
+getstamp_legacy() {
+ awk '{print $2}' | sed 's/_/ /'
+}
+getstamp_rfc5424() {
+ awk '{print $1}'
+}
+get_ts() {
+ local l="$1" ts
+ ts=$(str2time `echo "$l" | $getstampproc`)
+ if [ -z "$ts" ]; then
+ local fmt
+ for fmt in rfc5424 syslog legacy; do
+ [ "getstamp_$fmt" = "$getstampproc" ] && continue
+ ts=$(str2time `echo "$l" | getstamp_$fmt`)
+ [ -n "$ts" ] && break
+ done
+ fi
+ echo $ts
+}
+linetime() {
+ get_ts "`tail -n +$2 $1 | head -1`"
+}
+find_getstampproc() {
+ local t l func trycnt
+ t=0 l="" func=""
+ trycnt=10
+ while [ $trycnt -gt 0 ] && read l; do
+ t=$(str2time `echo $l | getstamp_syslog`)
+ if [ "$t" ]; then
+ func="getstamp_syslog"
+ debug "the log file is in the syslog format"
+ break
+ fi
+ t=$(str2time `echo $l | getstamp_rfc5424`)
+ if [ "$t" ]; then
+ func="getstamp_rfc5424"
+ debug "the log file is in the rfc5424 format"
+ break
+ fi
+ t=$(str2time `echo $l | getstamp_legacy`)
+ if [ "$t" ]; then
+ func="getstamp_legacy"
+ debug "the log file is in the legacy format (please consider switching to syslog format)"
+ break
+ fi
+ trycnt=$(($trycnt-1))
+ done
+ echo $func
+}
+find_first_ts() {
+ local l ts
+ while read l; do
+ ts=`get_ts "$l"`
+ [ "$ts" ] && break
+ warning "cannot extract time: |$l|; will try the next one"
+ done
+ echo $ts
+}
+findln_by_time() {
+ local logf=$1
+ local tm=$2
+ local first=1
+ local last=`wc -l < $logf`
+ local tmid mid trycnt
+ while [ $first -le $last ]; do
+ mid=$((($last+$first)/2))
+ trycnt=10
+ while [ $trycnt -gt 0 ]; do
+ tmid=`linetime $logf $mid`
+ [ "$tmid" ] && break
+ warning "cannot extract time: $logf:$mid; will try the next one"
+ trycnt=$(($trycnt-1))
+ # shift the whole first-last segment
+ first=$(($first-1))
+ last=$(($last-1))
+ mid=$((($last+$first)/2))
+ done
+ if [ -z "$tmid" ]; then
+ warning "giving up on log..."
+ return
+ fi
+ if [ $tmid -gt $tm ]; then
+ last=$(($mid-1))
+ elif [ $tmid -lt $tm ]; then
+ first=$(($mid+1))
+ else
+ break
+ fi
+ done
+ echo $mid
+}
+
+dumplog() {
+ local logf=$1
+ local from_line=$2
+ local to_line=$3
+ [ "$from_line" ] ||
+ return
+ tail -n +$from_line $logf |
+ if [ "$to_line" ]; then
+ head -$(($to_line-$from_line+1))
+ else
+ cat
+ fi
+}
+
+#
+# find files newer than a and older than b
+#
+isnumber() {
+ echo "$*" | grep -qs '^[0-9][0-9]*$'
+}
+touchfile() {
+ local t
+ t=`mktemp` &&
+ perl -e "\$file=\"$t\"; \$tm=$1;" -e 'utime $tm, $tm, $file;' &&
+ echo $t
+}
+find_files() {
+ local dirs from_time to_time
+ local from_stamp to_stamp findexp
+ dirs=$1
+ from_time=$2
+ to_time=$3
+ isnumber "$from_time" && [ "$from_time" -gt 0 ] || {
+ warning "sorry, can't find files based on time if you don't supply time"
+ return
+ }
+ if ! from_stamp=`touchfile $from_time`; then
+ warning "can't create temporary files"
+ return
+ fi
+ add_tmpfiles $from_stamp
+ findexp="-newer $from_stamp"
+ if isnumber "$to_time" && [ "$to_time" -gt 0 ]; then
+ if ! to_stamp=`touchfile $to_time`; then
+ warning "can't create temporary files"
+ return
+ fi
+ add_tmpfiles $to_stamp
+ findexp="$findexp ! -newer $to_stamp"
+ fi
+ find $dirs -type f $findexp
+}
+
+#
+# check permissions of files/dirs
+#
+pl_checkperms() {
+perl -e '
+# check permissions and ownership
+# uid and gid are numeric
+# everything must match exactly
+# no error checking! (file should exist, etc)
+($filename, $perms, $in_uid, $in_gid) = @ARGV;
+($mode,$uid,$gid) = (stat($filename))[2,4,5];
+$p=sprintf("%04o", $mode & 07777);
+$p ne $perms and exit(1);
+$uid ne $in_uid and exit(1);
+$gid ne $in_gid and exit(1);
+' $*
+}
+num_id() {
+ getent $1 $2 | awk -F: '{print $3}'
+}
+chk_id() {
+ [ "$2" ] && return 0
+ echo "$1: id not found"
+ return 1
+}
+check_perms() {
+ local f p uid gid n_uid n_gid
+ essential_files |
+ while read type f p uid gid; do
+ [ -$type $f ] || {
+ echo "$f wrong type or doesn't exist"
+ continue
+ }
+ n_uid=`num_id passwd $uid`
+ chk_id "$uid" "$n_uid" || continue
+ n_gid=`num_id group $gid`
+ chk_id "$gid" "$n_gid" || continue
+ pl_checkperms $f $p $n_uid $n_gid || {
+ echo "wrong permissions or ownership for $f:"
+ ls -ld $f
+ }
+ done
+}
+
+#
+# coredumps
+#
+pkg_mgr_list() {
+# list of:
+# regex pkg_mgr
+# no spaces allowed in regex
+ cat<<EOF
+zypper.install zypper
+EOF
+}
+listpkg_zypper() {
+ local bins
+ local binary=$1 core=$2
+ gdb $binary $core </dev/null 2>&1 |
+ awk '
+ # this zypper version dumps all packages on a single line
+ /Missing separate debuginfos.*zypper.install/ {
+ sub(".*zypper.install ",""); print
+ exit}
+ n>0 && /^Try: zypper install/ {gsub("\"",""); print $NF}
+ n>0 {n=0}
+ /Missing separate debuginfo/ {n=1}
+ ' | sort -u
+}
+fetchpkg_zypper() {
+ local pkg
+ debug "get debuginfo packages using zypper: $@"
+ zypper -qn ref > /dev/null
+ for pkg in $@; do
+ zypper -qn install -C $pkg >/dev/null
+ done
+}
+find_pkgmgr() {
+ local binary=$1 core=$2
+ local regex pkg_mgr
+ pkg_mgr_list |
+ while read regex pkg_mgr; do
+ if gdb $binary $core </dev/null 2>&1 |
+ grep "$regex" > /dev/null; then
+ echo $pkg_mgr
+ break
+ fi
+ done
+}
+get_debuginfo() {
+ local binary=$1 core=$2
+ local pkg_mgr pkgs
+ gdb $binary $core </dev/null 2>/dev/null |
+ egrep 'Missing.*debuginfo|no debugging symbols found' > /dev/null ||
+ return # no missing debuginfo
+ pkg_mgr=`find_pkgmgr $binary $core`
+ if [ -z "$pkg_mgr" ]; then
+ warning "found core for $binary but there is no debuginfo and we don't know how to get it on this platform"
+ return
+ fi
+ pkgs=`listpkg_$pkg_mgr $binary $core`
+ [ -n "$pkgs" ] &&
+ fetchpkg_$pkg_mgr $pkgs
+}
+findbinary() {
+ local random_binary binary fullpath
+ random_binary=`which cat 2>/dev/null` # suppose we are lucky
+ binary=`gdb $random_binary $1 < /dev/null 2>/dev/null |
+ grep 'Core was generated' | awk '{print $5}' |
+ sed "s/^.//;s/[.':]*$//"`
+ if [ x = x"$binary" ]; then
+ debug "could not detect the program name for core $1 from the gdb output; will try with file(1)"
+ binary=$(file $1 | awk '/from/{
+ for( i=1; i<=NF; i++ )
+ if( $i == "from" ) {
+ print $(i+1)
+ break
+ }
+ }')
+ binary=`echo $binary | tr -d "'"`
+ binary=$(echo $binary | tr -d '`')
+ if [ "$binary" ]; then
+ binary=`which $binary 2>/dev/null`
+ fi
+ fi
+ if [ x = x"$binary" ]; then
+ warning "could not find the program path for core $1"
+ return
+ fi
+ fullpath=`which $binary 2>/dev/null`
+ if [ x = x"$fullpath" ]; then
+ for d in $HA_BIN $CRM_DAEMON_DIR; do
+ if [ -x $d/$binary ]; then
+ echo $d/$binary
+ debug "found the program at $d/$binary for core $1"
+ else
+ warning "could not find the program path for core $1"
+ fi
+ done
+ else
+ echo $fullpath
+ debug "found the program at $fullpath for core $1"
+ fi
+}
+getbt() {
+ local corefile absbinpath
+ which gdb > /dev/null 2>&1 || {
+ warning "please install gdb to get backtraces"
+ return
+ }
+ for corefile; do
+ absbinpath=`findbinary $corefile`
+ [ x = x"$absbinpath" ] && continue
+ get_debuginfo $absbinpath $corefile
+ echo "====================== start backtrace ======================"
+ ls -l $corefile
+ gdb -batch -n -quiet -ex ${BT_OPTS:-"thread apply all bt full"} -ex quit \
+ $absbinpath $corefile 2>/dev/null
+ echo "======================= end backtrace ======================="
+ done
+}
+
+#
+# heartbeat configuration/status
+#
+iscrmrunning() {
+ local pid maxwait
+ ps -ef | grep -qs [c]rmd || return 1
+ crmadmin -D >/dev/null 2>&1 &
+ pid=$!
+ maxwait=100
+ while kill -0 $pid 2>/dev/null && [ $maxwait -gt 0 ]; do
+ sleep 0.1
+ maxwait=$(($maxwait-1))
+ done
+ if kill -0 $pid 2>/dev/null; then
+ kill $pid
+ false
+ else
+ wait $pid
+ fi
+}
+dumpstate() {
+ crm_mon -1 | grep -v '^Last upd' > $1/$CRM_MON_F
+ cibadmin -Ql > $1/$CIB_F
+ `echo_membership_tool` $MEMBERSHIP_TOOL_OPTS -p > $1/$MEMBERSHIP_F 2>&1
+}
+getconfig() {
+ [ -f "$CONF" ] &&
+ cp -p $CONF $1/
+ [ -f "$LOGD_CF" ] &&
+ cp -p $LOGD_CF $1/
+ if iscrmrunning; then
+ dumpstate $1
+ touch $1/RUNNING
+ else
+ cp -p $CIB_DIR/$CIB_F $1/ 2>/dev/null
+ touch $1/STOPPED
+ fi
+ [ "$HOSTCACHE" ] &&
+ cp -p $HA_VARLIB/hostcache $1/$HOSTCACHE 2>/dev/null
+ [ "$HB_UUID_F" ] &&
+ crm_uuid -r > $1/$HB_UUID_F 2>&1
+ [ -f "$1/$CIB_F" ] &&
+ crm_verify -V -x $1/$CIB_F >$1/$CRM_VERIFY_F 2>&1
+}
+crmconfig() {
+ [ -f "$1/$CIB_F" ] && which crm >/dev/null 2>&1 &&
+ CIB_file=$1/$CIB_F crm configure show >$1/$CIB_TXT_F 2>&1
+}
+get_crm_nodes() {
+ cibadmin -Ql -o nodes |
+ awk '
+ /<node / {
+ for( i=1; i<=NF; i++ )
+ if( $i~/^uname=/ ) {
+ sub("uname=.","",$i);
+ sub("\".*","",$i);
+ print $i;
+ next;
+ }
+ }
+ '
+}
+get_live_nodes() {
+ if [ `id -u` = 0 ] && which fping >/dev/null 2>&1; then
+ fping -a $@ 2>/dev/null
+ else
+ local h
+ for h; do ping -c 2 -q $h >/dev/null 2>&1 && echo $h; done
+ fi
+}
+
+#
+# remove values of sensitive attributes
+#
+# this is not proper xml parsing, but it will work under the
+# circumstances
+is_sensitive_xml() {
+ local patt epatt
+ epatt=""
+ for patt in $SANITIZE; do
+ epatt="$epatt|$patt"
+ done
+ epatt="`echo $epatt|sed 's/.//'`"
+ egrep -qs "name=\"$epatt\""
+}
+test_sensitive_one() {
+ local file compress decompress
+ file=$1
+ compress=""
+ echo $file | grep -qs 'gz$' && compress=gzip
+ echo $file | grep -qs 'bz2$' && compress=bzip2
+ if [ "$compress" ]; then
+ decompress="$compress -dc"
+ else
+ compress=cat
+ decompress=cat
+ fi
+ $decompress < $file | is_sensitive_xml
+}
+sanitize_xml_attrs() {
+ local patt
+ sed $(
+ for patt in $SANITIZE; do
+ echo "-e /name=\"$patt\"/s/value=\"[^\"]*\"/value=\"****\"/"
+ done
+ )
+}
+sanitize_hacf() {
+ awk '
+ $1=="stonith_host"{ for( i=5; i<=NF; i++ ) $i="****"; }
+ {print}
+ '
+}
+sanitize_one() {
+ local file compress decompress tmp ref
+ file=$1
+ compress=""
+ echo $file | grep -qs 'gz$' && compress=gzip
+ echo $file | grep -qs 'bz2$' && compress=bzip2
+ if [ "$compress" ]; then
+ decompress="$compress -dc"
+ else
+ compress=cat
+ decompress=cat
+ fi
+ tmp=`mktemp`
+ ref=`mktemp`
+ add_tmpfiles $tmp $ref
+ if [ -z "$tmp" -o -z "$ref" ]; then
+ fatal "cannot create temporary files"
+ fi
+ touch -r $file $ref # save the mtime
+ if [ "`basename $file`" = ha.cf ]; then
+ sanitize_hacf
+ else
+ $decompress | sanitize_xml_attrs | $compress
+ fi < $file > $tmp
+ mv $tmp $file
+ touch -r $ref $file
+}
+
+#
+# keep the user posted
+#
+fatal() {
+ echo "`uname -n`: ERROR: $*" >&2
+ exit 1
+}
+warning() {
+ echo "`uname -n`: WARN: $*" >&2
+}
+info() {
+ echo "`uname -n`: INFO: $*" >&2
+}
+debug() {
+ [ "$VERBOSITY" ] && [ $VERBOSITY -gt 0 ] &&
+ echo "`uname -n`: DEBUG: $*" >&2
+ return 0
+}
+pickfirst() {
+ for x; do
+ which $x >/dev/null 2>&1 && {
+ echo $x
+ return 0
+ }
+ done
+ return 1
+}
+
+# tmp files business
+drop_tmpfiles() {
+ trap 'rm -rf `cat $__TMPFLIST`; rm $__TMPFLIST' EXIT
+}
+init_tmpfiles() {
+ if __TMPFLIST=`mktemp`; then
+ drop_tmpfiles
+ else
+ # this is really bad, let's just leave
+ fatal "eek, mktemp cannot create temporary files"
+ fi
+}
+add_tmpfiles() {
+ test -f "$__TMPFLIST" || return
+ echo $* >> $__TMPFLIST
+}
+
+#
+# get some system info
+#
+distro() {
+ local relf f
+ which lsb_release >/dev/null 2>&1 && {
+ lsb_release -d
+ debug "using lsb_release for distribution info"
+ return
+ }
+ relf=`ls /etc/debian_version 2>/dev/null` ||
+ relf=`ls /etc/slackware-version 2>/dev/null` ||
+ relf=`ls -d /etc/*-release 2>/dev/null` && {
+ for f in $relf; do
+ test -f $f && {
+ echo "`ls $f` `cat $f`"
+ debug "found $relf distribution release file"
+ return
+ }
+ done
+ }
+ warning "no lsb_release, no /etc/*-release, no /etc/debian_version: no distro information"
+}
+
+pkg_ver_deb() {
+ dpkg-query -f '${Name} ${Version}' -W $* 2>/dev/null
+}
+pkg_ver_rpm() {
+ rpm -q --qf '%{name} %{version}-%{release} - %{distribution} %{arch}\n' $* 2>&1 |
+ grep -v 'not installed'
+}
+pkg_ver_pkg_info() {
+ for pkg; do
+ pkg_info | grep $pkg
+ done
+}
+pkg_ver_pkginfo() {
+ for pkg; do
+ pkginfo $pkg | awk '{print $3}' # format?
+ done
+}
+verify_deb() {
+ debsums -s $* 2>/dev/null
+}
+verify_rpm() {
+ rpm --verify $* 2>&1 | grep -v 'not installed'
+}
+verify_pkg_info() {
+ :
+}
+verify_pkginfo() {
+ :
+}
+
+get_pkg_mgr() {
+ local pkg_mgr
+ if which dpkg >/dev/null 2>&1 ; then
+ pkg_mgr="deb"
+ elif which rpm >/dev/null 2>&1 ; then
+ pkg_mgr="rpm"
+ elif which pkg_info >/dev/null 2>&1 ; then
+ pkg_mgr="pkg_info"
+ elif which pkginfo >/dev/null 2>&1 ; then
+ pkg_mgr="pkginfo"
+ else
+ warning "Unknown package manager!"
+ return
+ fi
+ echo $pkg_mgr
+}
+
+pkg_versions() {
+ local pkg_mgr=`get_pkg_mgr`
+ [ -z "$pkg_mgr" ] &&
+ return
+ debug "the package manager is $pkg_mgr"
+ pkg_ver_$pkg_mgr $*
+}
+verify_packages() {
+ local pkg_mgr=`get_pkg_mgr`
+ [ -z "$pkg_mgr" ] &&
+ return
+ verify_$pkg_mgr $*
+}
+
+crm_info() {
+ $CRM_DAEMON_DIR/crmd version 2>&1
+}
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..2e07275
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+SUBDIRS = clplumbing pils stonith lrm
+
+idir=$(includedir)/heartbeat
+i_HEADERS = compress.h glue_config.h ha_msg.h
+
+noinst_HEADERS = config.h lha_internal.h replace_uuid.h
diff --git a/include/clplumbing/GSource.h b/include/clplumbing/GSource.h
new file mode 100644
index 0000000..2acc9eb
--- /dev/null
+++ b/include/clplumbing/GSource.h
@@ -0,0 +1,236 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CLPLUMBING_GSOURCE_H
+# define _CLPLUMBING_GSOURCE_H
+# include <clplumbing/ipc.h>
+
+typedef struct GFDSource_s GFDSource;
+typedef struct GCHSource_s GCHSource;
+typedef struct GWCSource_s GWCSource;
+typedef struct GSIGSource_s GSIGSource;
+typedef struct GTRIGSource_s GTRIGSource;
+
+
+void G_main_setmaxdispatchdelay(GSource* s, unsigned long delayms);
+void G_main_setmaxdispatchtime(GSource* s, unsigned long dispatchms);
+void G_main_setdescription(GSource* s, const char * description);
+
+void G_main_setmaxdispatchdelay_id(guint id, unsigned long delayms);
+void G_main_setmaxdispatchtime_id(guint id, unsigned long dispatchms);
+void G_main_setdescription_id(guint id, const char * description);
+void G_main_setall_id(guint id, const char * description, unsigned long delayms, unsigned long dispatchms);
+
+
+/***********************************************************************
+ * Functions for interfacing input to the mainloop
+ ***********************************************************************/
+
+GSource*
+G_main_add_input(int priority,
+ gboolean can_recurse,
+ GSourceFuncs* funcs);
+
+/***********************************************************************
+ * Functions for interfacing "raw" file descriptors to the mainloop
+ ***********************************************************************/
+/*
+* Add a file descriptor to the gmainloop world...
+ */
+GFDSource* G_main_add_fd(int priority, int fd, gboolean can_recurse
+, gboolean (*dispatch)(int fd, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify);
+
+/*
+ * Delete a file descriptor from the gmainloop world...
+ * Note: destroys the GFDSource object.
+ */
+gboolean G_main_del_fd(GFDSource* fdp);
+
+/*
+ * Notify us that a file descriptor is blocked on output.
+ * (i.e., we should poll for output events)
+ */
+void g_main_output_is_blocked(GFDSource* fdp);
+
+
+/**************************************************************
+ * Functions for interfacing IPC_Channels to the mainloop
+ **************************************************************/
+/*
+ * Add an IPC_channel to the gmainloop world...
+ */
+GCHSource* G_main_add_IPC_Channel(int priority, IPC_Channel* ch
+, gboolean can_recurse
+, gboolean (*dispatch)(IPC_Channel* source_data
+, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify);
+
+/*
+ * the events in this source is paused/resumed
+ */
+
+void G_main_IPC_Channel_pause(GCHSource* chp);
+void G_main_IPC_Channel_resume(GCHSource* chp);
+
+
+/*
+ * Delete an IPC_channel from the gmainloop world...
+ * Note: destroys the GCHSource object, and the IPC_Channel
+ * object automatically.
+ */
+gboolean G_main_del_IPC_Channel(GCHSource* chp);
+
+
+/*
+ * Set the destroy notify function
+ *
+ */
+void set_IPC_Channel_dnotify(GCHSource* chp,
+ GDestroyNotify notify);
+
+
+/*********************************************************************
+ * Functions for interfacing IPC_WaitConnections to the mainloop
+ ********************************************************************/
+/*
+ * Add an IPC_WaitConnection to the gmainloop world...
+ * Note that the dispatch function is called *after* the
+ * connection is accepted.
+ */
+GWCSource* G_main_add_IPC_WaitConnection(int priority, IPC_WaitConnection* ch
+, IPC_Auth* auth_info
+, gboolean can_recurse
+, gboolean (*dispatch)(IPC_Channel* source_data
+, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify);
+
+/*
+ * Delete an IPC_WaitConnection from the gmainloop world...
+ * Note: destroys the GWCSource object, and the IPC_WaitConnection
+ * object automatically.
+ */
+gboolean G_main_del_IPC_WaitConnection(GWCSource* wcp);
+
+
+/**************************************************************
+ * Functions for interfacing Signals to the mainloop
+ **************************************************************/
+/*
+ * Add an Signal to the gmainloop world...
+ */
+GSIGSource* G_main_add_SignalHandler(
+ int priority, int signal,
+ gboolean (*dispatch)(int nsig, gpointer user_data),
+ gpointer userdata, GDestroyNotify notify);
+
+/*
+ * Delete an signal from the gmainloop world...
+ * Note: destroys the GSIGSource object, and the removes the
+ * Signal Handler automatically.
+ */
+gboolean G_main_del_SignalHandler(GSIGSource* chp);
+
+
+/*
+ * Set the destroy notify function
+ *
+ */
+void set_SignalHandler_dnotify(GSIGSource* chp, GDestroyNotify notify);
+
+
+/* manage child process death using sig source*/
+#define DEFAULT_MAXDISPATCHTIME 30 /* in ms */
+void set_sigchld_proctrack(int priority, unsigned long maxdisptime);
+
+
+
+/**************************************************************
+ * Functions for interfacing Manual triggers to the mainloop
+ **************************************************************/
+/*
+ * Add an Trigger to the gmainloop world...
+ */
+GTRIGSource* G_main_add_TriggerHandler(
+ int priority, gboolean (*dispatch)(gpointer user_data),
+ gpointer userdata, GDestroyNotify notify);
+
+/*
+ * Delete an signal from the gmainloop world...
+ * Note: destroys the GTRIGSource object, and the removes the
+ * Trigger Handler automatically.
+ */
+gboolean G_main_del_TriggerHandler(GTRIGSource* chp);
+
+
+/*
+ * Set the destroy notify function
+ *
+ */
+void set_TriggerHandler_dnotify(GTRIGSource* chp, GDestroyNotify notify);
+
+
+void G_main_set_trigger(GTRIGSource* man_src);
+
+/*
+ * Create a trigger for triggering an action in a short-lived (temporary)
+ * child process.
+ *
+ * The name isn't wonderful, but we couldn't think of a better one.
+ */
+GTRIGSource* G_main_add_tempproc_trigger(int priority
+, int (*fun)(gpointer p) /* What to do? */
+ /* Called in child process */
+, const char * procname /* What do we call this process? */
+, gpointer userdata /* Passed to 'triggerfun' */
+, void (*prefork)(gpointer p) /* Called before fork */
+, void (*postfork)(gpointer p) /* Called by parent process
+ * after fork(2) call.
+ * Each has 'userdata'
+ * passed to it.
+ */
+, void (*complete)(gpointer p, int status, int signo, int exitcode)); /* called after the child process completes */
+
+/*
+ * Special notes:
+ * - No more than one child process will be active at a time per trigger
+ * object.
+ *
+ * - If you trigger the action while this object has a child active,
+ * then it will be re-triggered after the currently running child
+ * completes. There is no necessary correlation between the
+ * number of times a the action is triggered and how many
+ * times it is executed. What is guaranteed is that after you
+ * trigger the action, it will happen (at least) once - as soon
+ * as the scheduler gets around to it at the priority you've
+ * assigned it. But if several are triggered while a child
+ * process is running, only one process will be instantiated to
+ * take the action requested by all the trigger calls.
+ *
+ * - Child processes are forked off at the priority of the trigger,
+ * not the priority of the SIGCHLD handler.
+ *
+ * - This is useful for writing out updates to a file for example.
+ * While we're writing one copy out, subsequent updates are
+ * held off until this one completes. When it completes, then
+ * the file is written again - but not "n" times - just the
+ * latest version available at the time the trigger is
+ * activated (run).
+ */
+#endif
diff --git a/include/clplumbing/GSource_internal.h b/include/clplumbing/GSource_internal.h
new file mode 100644
index 0000000..c20a9c9
--- /dev/null
+++ b/include/clplumbing/GSource_internal.h
@@ -0,0 +1,111 @@
+/*
+ * Author: Alan Robertson <alanr@unix.sh>
+ * Copyright (C) 2005 International Business Machines Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <clplumbing/longclock.h>
+#include <clplumbing/GSource.h>
+
+#define MAG_GFDSOURCE 0xfeed0001U
+#define MAG_GCHSOURCE 0xfeed0002U
+#define MAG_GWCSOURCE 0xfeed0003U
+#define MAG_GSIGSOURCE 0xfeed0004U
+#define MAG_GTRIGSOURCE 0xfeed0005U
+#define MAG_GTIMEOUTSRC 0xfeed0006U
+
+#define IS_FDSOURCE(p) (p && (p)->magno == MAG_GFDSOURCE)
+#define IS_CHSOURCE(p) (p && (p)->magno == MAG_GCHSOURCE)
+#define IS_WCSOURCE(p) (p && (p)->magno == MAG_GWCSOURCE)
+#define IS_SIGSOURCE(p) (p && (p)->magno == MAG_GSIGSOURCE)
+#define IS_TRIGSOURCE(p) (p && (p)->magno == MAG_GTRIGSOURCE)
+#define IS_TIMEOUTSRC(p) (p && (p)->magno == MAG_GTIMEOUTSRC)
+
+#define IS_ONEOFOURS(p) (IS_CHSOURCE(p)|IS_FDSOURCE(p)|IS_WCSOURCE(p)|| \
+ IS_SIGSOURCE(p)|IS_TRIGSOURCE(p)||IS_TIMEOUTSRC(p))
+
+
+#define DEFAULT_MAXDISPATCH 0
+#define DEFAULT_MAXDELAY 0
+#define OTHER_MAXDELAY 100
+
+#define COMMON_STRUCTSTART \
+GSource source; /* Common glib struct - must be 1st */ \
+unsigned magno; /* Magic number */ \
+long maxdispatchms; /* Time limit for dispatch function */ \
+long maxdispatchdelayms; /* Max delay before processing */ \
+char detecttime[sizeof(longclock_t)]; \
+ /* Time last input detected */ \
+void* udata; /* User-defined data */ \
+guint gsourceid; /* Source id of this source */ \
+const char * description; /* Description of this source */ \
+GDestroyNotify dnotify
+
+struct GFDSource_s {
+ COMMON_STRUCTSTART;
+ gboolean (*dispatch)(int fd, gpointer user_data);
+ GPollFD gpfd;
+};
+
+
+typedef gboolean (*GCHdispatch)(IPC_Channel* ch, gpointer user_data);
+
+struct GCHSource_s {
+ COMMON_STRUCTSTART;
+ IPC_Channel* ch;
+ gboolean fd_fdx;
+ GPollFD infd;
+ GPollFD outfd;
+ gboolean dontread; /* TRUE when we don't want to read
+ * more input for a while - we're
+ * flow controlling the writer off
+ */
+ gboolean (*dispatch)(IPC_Channel* ch, gpointer user_data);
+};
+
+struct GWCSource_s {
+ COMMON_STRUCTSTART;
+ GPollFD gpfd;
+ IPC_WaitConnection* wch;
+ IPC_Auth* auth_info;
+ gboolean (*dispatch)(IPC_Channel* accept_ch, gpointer udata);
+};
+
+struct GSIGSource_s {
+ COMMON_STRUCTSTART;
+ clock_t sh_detecttime;
+ int signal;
+ gboolean signal_triggered;
+ gboolean (*dispatch)(int signal, gpointer user_data);
+};
+
+struct GTRIGSource_s {
+ COMMON_STRUCTSTART;
+ gboolean manual_trigger;
+ gboolean (*dispatch)(gpointer user_data);
+};
+
+/************************************************************
+ * Functions for IPC_Channels
+ ***********************************************************/
+gboolean G_CH_prepare_int(GSource* source, gint* timeout);
+gboolean G_CH_check_int(GSource* source);
+gboolean G_CH_dispatch_int(GSource* source, GSourceFunc callback,
+ gpointer user_data);
+void G_CH_destroy_int(GSource* source);
+GCHSource*
+G_main_IPC_Channel_constructor(GSource* source, IPC_Channel* ch
+, gpointer userdata, GDestroyNotify notify);
diff --git a/include/clplumbing/Gmain_timeout.h b/include/clplumbing/Gmain_timeout.h
new file mode 100644
index 0000000..c696a9d
--- /dev/null
+++ b/include/clplumbing/Gmain_timeout.h
@@ -0,0 +1,44 @@
+#ifndef _CLPLUMBING_GMAIN_TIMEOUT_H
+#define _CLPLUMBING_GMAIN_TIMEOUT_H
+#include <glib.h>
+/*
+ * Copyright (C) 2002 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+/*
+ * These functions must work correctly even if someone resets the
+ * time-of-day clock. The g_main_timeout_add() function does not have
+ * this property, since it relies on gettimeofday().
+ *
+ * Our functions have the same semantics - except they always work ;-)
+ *
+ * This is because we use longclock_t for our time values.
+ */
+guint Gmain_timeout_add(guint interval
+, GSourceFunc function
+, gpointer data);
+
+guint Gmain_timeout_add_full(gint priority
+, guint interval
+, GSourceFunc function
+, gpointer data
+, GDestroyNotify notify);
+
+void Gmain_timeout_remove(guint tag);
+#endif
diff --git a/include/clplumbing/Makefile.am b/include/clplumbing/Makefile.am
new file mode 100644
index 0000000..599b24c
--- /dev/null
+++ b/include/clplumbing/Makefile.am
@@ -0,0 +1,59 @@
+#
+# linux-ha: Linux-HA heartbeat code
+#
+# Copyright (C) 2002 International Business Machines.
+# Author: Alan Robertson <alanr@unix.sh>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+idir=$(includedir)/clplumbing
+
+i_HEADERS = \
+ Gmain_timeout.h \
+ GSource.h \
+ GSource_internal.h \
+ apphb_cs.h \
+ base64.h \
+ cl_log.h \
+ cl_poll.h \
+ cl_signal.h \
+ cl_pidfile.h \
+ cl_random.h \
+ cl_reboot.h \
+ cl_syslog.h \
+ cl_uuid.h \
+ coredumps.h \
+ cpulimits.h \
+ ipc.h \
+ lsb_exitcodes.h \
+ loggingdaemon.h \
+ longclock.h \
+ mkstemp_mode.h \
+ netstring.h \
+ proctrack.h \
+ realtime.h \
+ replytrack.h \
+ setproctitle.h \
+ timers.h \
+ uids.h \
+ cl_misc.h \
+ md5.h \
+ cl_plugin.h \
+ cl_tiebreaker.h \
+ cl_quorum.h \
+ cl_quorumd.h
diff --git a/include/clplumbing/apphb_cs.h b/include/clplumbing/apphb_cs.h
new file mode 100644
index 0000000..9506db6
--- /dev/null
+++ b/include/clplumbing/apphb_cs.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2002 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+#ifndef _CLPLUMBING_APPHB_CS_H
+#define _CLPLUMBING_APPHB_CS_H
+
+/* Internal client-server messages for APP heartbeat service */
+
+#ifndef HA_VARRUNDIR
+#define HA_VARRUNDIR "/var/run"
+#endif
+#define APPHBSOCKPATH HA_VARRUNDIR "/heartbeat/apphb.comm"
+
+#define APPHB_TLEN 8
+#define APPHB_OLEN 256
+
+#define REGISTER "reg"
+#define UNREGISTER "unreg"
+#define HEARTBEAT "hb"
+#define SETINTERVAL "setint"
+#define SETWARNTIME "setwarn"
+#define SETREBOOT "setboot"
+
+/*
+ * These messages are really primitive.
+ * They don't have any form of version control, they're in host byte order,
+ * and they're all in binary...
+ *
+ * Fortunately, this is a very simple local service ;-)
+ */
+
+/* Generic (no parameter) App heartbeat message */
+struct apphb_msg {
+ char msgtype [APPHB_TLEN];
+};
+
+/* App heartbeat Registration message */
+struct apphb_signupmsg {
+ char msgtype [APPHB_TLEN];
+ char appname [APPHB_OLEN];
+ char appinstance [APPHB_OLEN];
+ char curdir [APPHB_OLEN];
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+};
+
+/* App heartbeat setinterval / setwarn message */
+struct apphb_msmsg {
+ char msgtype [APPHB_TLEN];
+ unsigned long ms;
+};
+
+/* App heartbeat server return code (errno) */
+struct apphb_rc {
+ int rc;
+};
+#endif
diff --git a/include/clplumbing/base64.h b/include/clplumbing/base64.h
new file mode 100644
index 0000000..4ea6810
--- /dev/null
+++ b/include/clplumbing/base64.h
@@ -0,0 +1,50 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CLPLUMBING_BASE64_H
+# define _CLPLUMBING_BASE64_H
+/*
+ *
+ * Base64 conversion functions.
+ * They convert from a binary array into a single string
+ * in base 64. This is almost (but not quite) like section 5.2 of RFC 1341
+ * The only difference is that we don't care about line lengths.
+ * We do use their encoding algorithm.
+ *
+ */
+
+#define B64inunit 3
+#define B64outunit 4
+
+/* How long will the base64 string be for a particular binary object size? */
+/* This is like strlen() and doesn't include the '\0' byte at the end */
+#define B64_stringlen(bytes) \
+ ((((bytes)+(B64inunit-1))/B64inunit)*B64outunit)
+
+/* How many bytes to you need to malloc to store a base64 string? */
+/* (includes space for the '\0' terminator byte) */
+#define B64_stringspace(bytes) (B64_stringlen(bytes)+1)
+
+/* How many bytes will a base64 string take up back in binary? */
+/* Note: This may be as much as two 2 bytes more than strictly needed */
+#define B64_maxbytelen(slen) (((slen) / B64outunit)*B64inunit)
+
+/* Returns strlen() of base64 string returned in "output" */
+int binary_to_base64(const void * data, int nbytes, char * output, int outlen);
+
+/* Returns the size of the binary object we returned in "output" */
+int base64_to_binary(const char * input, int inlen, void * output, int outlen);
+#endif
diff --git a/include/clplumbing/cl_log.h b/include/clplumbing/cl_log.h
new file mode 100644
index 0000000..aa30fcd
--- /dev/null
+++ b/include/clplumbing/cl_log.h
@@ -0,0 +1,99 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CLPLUMBING_CL_LOG_H
+# define _CLPLUMBING_CL_LOG_H
+# include <glib.h>
+# include <syslog.h>
+
+#define TIME_T unsigned long
+#define HA_FAIL 0
+#define HA_OK 1
+#define MAXLINE (512*10)
+
+/* this is defined by the caller */
+struct logspam {
+ const char *id; /* identifier */
+ int max; /* maximum number of messages ... */
+ time_t window; /* ... within this timeframe */
+ time_t reset_time; /* log new messages after this time */
+ const char *advice; /* what to log in case messages get suppressed */
+};
+
+/* this is internal (oblique to the caller) */
+struct msg_ctrl {
+ struct logspam *lspam; /* */
+ time_t *msg_slots; /* msg slot root (space for lspam->max) */
+ int last; /* last used msg slot [0..lspam->max-1]; -1 on init */
+ int cnt; /* current msg count [0..lspam->max] */
+ time_t suppress_t; /* messages blocked since this time */
+};
+
+struct IPC_CHANNEL;
+
+extern int debug_level;
+#define ANYDEBUG (debug_level)
+#define DEBUGDETAILS (debug_level >= 2)
+#define DEBUGAUTH (debug_level >=3)
+#define DEBUGMODULE (debug_level >=3)
+#define DEBUGPKT (debug_level >= 4)
+#define DEBUGPKTCONT (debug_level >= 5)
+
+void cl_direct_log(int priority, const char* buf, gboolean, const char*, int, TIME_T);
+void cl_log(int priority, const char * fmt, ...) G_GNUC_PRINTF(2,3);
+void cl_limit_log(struct msg_ctrl *ml, int priority, const char * fmt, ...) G_GNUC_PRINTF(3,4);
+struct msg_ctrl *cl_limit_log_new(struct logspam *lspam);
+void cl_limit_log_destroy(struct msg_ctrl *ml);
+void cl_limit_log_reset(struct msg_ctrl *ml);
+void cl_perror(const char * fmt, ...) G_GNUC_PRINTF(1,2);
+void cl_log_enable_stderr(int truefalse);
+void cl_log_enable_stdout(int truefalse);
+gboolean cl_log_test_logd(void);
+void cl_log_set_uselogd(int truefalse);
+void cl_log_enable_syslog_filefmt(int truefalse);
+void cl_log_use_buffered_io(int truefalse);
+gboolean cl_log_get_uselogd(void);
+void cl_log_set_facility(int facility);
+void cl_log_set_entity(const char * entity);
+void cl_log_set_syslogprefix(const char *prefix);
+void cl_log_set_logfile(const char * path);
+void cl_log_set_debugfile(const char * path);
+void cl_inherit_logging_environment(int maxqlen);
+int cl_log_set_logd_channel_source( void (*create_callback)(struct IPC_CHANNEL* chan),
+ GDestroyNotify destroy_callback);
+int cl_log_get_logdtime(void);
+void cl_log_set_logdtime(int logdintval);
+
+char * ha_timestamp(TIME_T t);
+void cl_glib_msg_handler(const gchar *log_domain
+, GLogLevelFlags log_level, const gchar *message
+, gpointer user_data);
+
+void cl_flush_logs(void);
+void cl_log_args(int argc, char **argv);
+int cl_log_is_logd_fd(int fd);
+const char * prio2str(int priority);
+
+/* cl_log_use_buffered_io and cl_log_do_fflush as optimization for logd,
+ * so it may buffer a few message lines, then fflush them out in one write.
+ * Set do_fsync != 0, if you even want it to fsync. */
+void cl_log_do_fflush(int do_fsync);
+void cl_log_use_buffered_io(int truefalse);
+/* We now keep the file handles open for a potentially very long time.
+ * Sometimes we may need to close them explicitly. */
+void cl_log_close_log_files(void);
+
+#endif
diff --git a/include/clplumbing/cl_misc.h b/include/clplumbing/cl_misc.h
new file mode 100644
index 0000000..6f698b5
--- /dev/null
+++ b/include/clplumbing/cl_misc.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CLPLUMBING_CL_MISC_H
+#define _CLPLUMBING_CL_MISC_H
+int cl_str_to_boolean(const char*, int*);
+
+int cl_file_exists(const char* filename);
+
+char* cl_get_env(const char* env_name);
+
+int cl_binary_to_int(const char* data, int len);
+
+long cl_get_msec(const char * input); /* string to msec */
+
+#endif
diff --git a/include/clplumbing/cl_pidfile.h b/include/clplumbing/cl_pidfile.h
new file mode 100644
index 0000000..3dba50f
--- /dev/null
+++ b/include/clplumbing/cl_pidfile.h
@@ -0,0 +1,25 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _LOCKFILE_H_
+#define _LOCKFILE_H_
+
+int cl_read_pidfile(const char *filename);
+int cl_read_pidfile_no_checking(const char *filename);
+int cl_lock_pidfile(const char *filename);
+int cl_unlock_pidfile(const char *filename);
+
+#endif
diff --git a/include/clplumbing/cl_plugin.h b/include/clplumbing/cl_plugin.h
new file mode 100644
index 0000000..e2431bf
--- /dev/null
+++ b/include/clplumbing/cl_plugin.h
@@ -0,0 +1,29 @@
+
+
+/*
+ * cl_manage_plugin.c: This file handle plugin loading and deleting
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __CL_PLUGIN__
+#define __CL_PLUGIN__
+
+int cl_remove_plugin(const char* type, const char* pluginname);
+void* cl_load_plugin(const char* type, const char* pluginname);
+
+#endif
diff --git a/include/clplumbing/cl_poll.h b/include/clplumbing/cl_poll.h
new file mode 100644
index 0000000..1b1908f
--- /dev/null
+++ b/include/clplumbing/cl_poll.h
@@ -0,0 +1,46 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef CLPLUMBING_CL_POLL_H
+# define CLPLUMBING_CL_POLL_H
+
+#include <glib.h>
+#include <sys/poll.h>
+
+/*
+ * Poll the file descriptors described by the NFDS structures starting at
+ * FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+ * an event to occur; if TIMEOUT is -1, block until an event occurs.
+ * Returns the number of file descriptors with events, zero if timed out,
+ * or -1 for errors.
+ *
+ * When available, this function uses POSIX signals, and Linux F_SETSIG()
+ * calls to provide this capability. When it is not available it
+ * uses the real poll() call.
+ *
+ */
+int cl_poll(struct pollfd *fds, unsigned int nfds, int timeout_ms);
+
+/*
+ * Call cl_poll_ignore() when you close a file descriptor you monitored
+ * via cl_poll() before, or if you don't want it monitored any more.
+ */
+int cl_poll_ignore(int fd);
+
+/* Select the signal you want us to use (must be a RT signal) */
+int cl_poll_setsig(int nsig);
+
+int cl_glibpoll(GPollFD* ufds, guint nfsd, gint timeout);
+#endif
diff --git a/include/clplumbing/cl_quorum.h b/include/clplumbing/cl_quorum.h
new file mode 100644
index 0000000..b7798ba
--- /dev/null
+++ b/include/clplumbing/cl_quorum.h
@@ -0,0 +1,44 @@
+/*
+ * quorum.h: head file for quorum module
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUORUM_H_
+#define _QUORUM_H_
+
+#define HB_QUORUM_TYPE quorum
+#define HB_QUORUM_TYPE_S "quorum"
+
+#define QUORUM_YES 0
+#define QUORUM_NO 1
+#define QUORUM_TIE 2
+typedef void(*callback_t)(void);
+/*
+ * List of functions provided by implementations of the quorum interface.
+ */
+struct hb_quorum_fns {
+
+ int (*getquorum) (const char* cluster
+ , int member_count, int member_quorum_votes
+ , int total_node_count, int total_quorum_votes);
+ int (*init) (callback_t notify, const char* cluster, const char* quorum_server);
+ void (*stop) (void);
+};
+
+
+#endif
diff --git a/include/clplumbing/cl_quorumd.h b/include/clplumbing/cl_quorumd.h
new file mode 100644
index 0000000..6d282b3
--- /dev/null
+++ b/include/clplumbing/cl_quorumd.h
@@ -0,0 +1,48 @@
+/*
+ * quorum.h: head file for quorum module
+ *
+ * Author: Huang Zhen <zhenhltc@cn.ibm.com>
+ * Copyright (C) 2006 International Business Machines
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUORUMD_H_
+#define _QUORUMD_H_
+
+#define HB_QUORUMD_TYPE quorumd
+#define HB_QUORUMD_TYPE_S "quorumd"
+
+#define CONFIGFILE HA_HBCONF_DIR"/quorumd.conf"
+#define MAX_DN_LEN 256
+#define quorum_log(priority, fmt...); \
+ cl_log(priority, fmt); \
+
+#define quorum_debug(priority, fmt...); \
+ if ( debug_level > 0 ) { \
+ cl_log(priority, fmt); \
+ }
+
+/* List of functions provided by implementations of the quorumd interface. */
+struct hb_quorumd_fns {
+ int (*test) (void);
+ int (*init) (void);
+ int (*load_config_file) (void);
+ int (*dump_data) (int priority);
+ int (*on_connect) (int sock, gnutls_session session, const char* CN);
+};
+
+
+#endif
diff --git a/include/clplumbing/cl_random.h b/include/clplumbing/cl_random.h
new file mode 100644
index 0000000..d1e37ce
--- /dev/null
+++ b/include/clplumbing/cl_random.h
@@ -0,0 +1,81 @@
+
+/*
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ * Copyright (C) 2005 International Business Machines Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+
+/* Intended usage is srand(cl_randseed()).
+ * This returns on "as good as it gets" random number usually taken from
+ * /dev/urandom to have a nice seed for future random numbers generated by
+ * rand(). */
+unsigned int cl_randseed(void);
+
+/* get_next_random() currently rand() based.
+ *
+ * You probably want to use cl_rand_from_interval instead.
+ *
+ * You don't need to srand(), it will seed once with cl_randseed internally.
+ *
+ * It is called that way, because it was exposed in the header file for a long
+ * time, and used to be coded in an attempt to pregenerate a queue of random
+ * numbers from the mainloop, and it would shift the next random number from
+ * that queue and trigger generation of new random numbers "at idle time" to
+ * refill that queue.
+ * Only that functionality never actually worked, is not interessting anymore
+ * anyways (rand() is cheap enough), and is now ripped out.
+ *
+ * So it now does srand(cl_randseed()) once internally,
+ * and from there on is equivalent to calling rand() directly,
+ * and could be called cl_rand().
+ *
+ * If you want your own specific rand seed to re-generate a particular
+ * sequence, call it once, throw away the return code, then call
+ * srand(yourseed). Or don't use it anywhere in your code. */
+int get_next_random(void);
+
+/* generate some random number in the range [a;b];
+ * typically used to randomly delay messages. */
+#define HAVE_CL_RAND_FROM_INTERVAL 1
+static inline
+int cl_rand_from_interval(const int a, const int b)
+{
+ /*
+ * Be careful here, you don't know RAND_MAX at coding time,
+ * only at compile time. If you think
+ * (int)(a + (rand()*(b-a)+(RAND_MAX/2))/RAND_MAX);
+ * was correct, think again with RAND_MAX = INT_MAX,
+ * which is the case for many rand() implementations nowadays.
+ *
+ * Don't do modulo, either, as that will skew the distribution, and
+ * still has possible wraparounds, or an insufficient input set for too
+ * small RAND_MAX.
+ *
+ * Rather do the whole calculation in 64 bit, which should be correct
+ * as long as r, a, b, and RAND_MAX are all int.
+ * Of course, if you prefer, you can do it with floating point as well.
+ */
+#if 1 /* use long long */
+ long long r = get_next_random();
+ r = a + (r * (b-a) + RAND_MAX/2)/RAND_MAX;
+#else /* use floating point */
+ int r = get_next_random();
+ r = a + (int)(1.0 / RAND_MAX * r * (b-a) + 0.5);
+#endif
+ return r;
+}
diff --git a/include/clplumbing/cl_reboot.h b/include/clplumbing/cl_reboot.h
new file mode 100644
index 0000000..1c759c8
--- /dev/null
+++ b/include/clplumbing/cl_reboot.h
@@ -0,0 +1,6 @@
+#ifndef CLPLUMBING_CL_REBOOT_H
+#define CLPLUMBING_CL_REBOOT_H 1
+#include <glib.h>
+void cl_enable_coredump_before_reboot(gboolean yesno); /* not implemented in all OSes */
+void cl_reboot(int msdelaybeforereboot, const char * reason);
+#endif
diff --git a/include/clplumbing/cl_signal.h b/include/clplumbing/cl_signal.h
new file mode 100644
index 0000000..1a13a6b
--- /dev/null
+++ b/include/clplumbing/cl_signal.h
@@ -0,0 +1,91 @@
+/*
+ * cl_signal.h: signal handling routines to be used by Linux-HA programmes
+ *
+ * Copyright (C) 2002 Horms <horms@verge.net.au>
+ *
+ * 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
+ *
+ */
+#ifndef _CL_SIGNAL_H
+#define _CL_SIGNAL_H
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/signal.h>
+
+typedef struct {
+ int sig;
+ void (*handler)(int);
+ int interrupt;
+} cl_signal_mode_t;
+
+#define CL_SIGNAL(_sig, _handler) \
+ cl_signal_set_simple_handler((_sig), (_handler), NULL)
+#if HAVE_SIGIGNORE
+#define CL_IGNORE_SIG(_sig) sigignore((_sig))
+#else
+#define CL_IGNORE_SIG(_sig) CL_SIGNAL((_sig), SIG_IGN)
+#endif
+#define CL_DEFAULT_SIG(_sig) CL_SIGNAL((_sig), SIG_DFL)
+
+#define CL_SIGINTERRUPT(_sig, _flag) siginterrupt((_sig), (_flag))
+
+#define CL_SIGACTION(_signum, _act, _oldact) \
+ sigaction((_signum), (_act), (_oldact))
+#define CL_SIGPROCMASK(_how, _set, _oldset) \
+ cl_signal_block_set((_how), (_set), (_oldset))
+#define CL_SIGPENDING(_set) sigpending(_set)
+#define CL_SIGSUSPEND(_mask) sigsuspend(_mask)
+
+#define CL_SIGEMPTYSET(_set) sigemptyset(_set)
+#define CL_SIGFILLSET(_set) sigfillset(_set)
+#define CL_SIGADDSET(_set, _signum) sigaddset((_set), (_signum))
+#define CL_SIGDELSET(_set, _signum) sigdelset((_set), (_signum))
+#define CL_SIGISMEMBER(_set, _signum) sigmember((_set), (_signum))
+
+#define CL_KILL(_pid, _sig) kill((_pid), (_sig))
+
+#define CL_PID_EXISTS(_pid) ( CL_KILL((_pid), 0) >= 0 || errno != ESRCH )
+
+int
+cl_signal_set_handler(int sig, void (*handler)(int), sigset_t *mask
+, int flags, struct sigaction *oldact);
+
+int
+cl_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact);
+
+int
+cl_signal_set_action(int sig, void (*action)(int, siginfo_t *, void *)
+, sigset_t *mask, int flags, struct sigaction *oldact);
+
+int
+cl_signal_set_simple_action(int sig, void (*action)(int, siginfo_t *, void *)
+, struct sigaction *oldact);
+
+int
+cl_signal_set_interrupt(int sig, int flag);
+
+int
+cl_signal_block(int how, int signal, sigset_t *oldset);
+
+int
+cl_signal_block_set(int how, const sigset_t *set, sigset_t *oldset);
+
+int
+cl_signal_set_handler_mode(const cl_signal_mode_t *mode, sigset_t *set);
+
+
+#endif /* _CL_SIGNAL_H */
diff --git a/include/clplumbing/cl_syslog.h b/include/clplumbing/cl_syslog.h
new file mode 100644
index 0000000..a7c1bfa
--- /dev/null
+++ b/include/clplumbing/cl_syslog.h
@@ -0,0 +1,32 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Functions to support syslog.
+ * David Lee (c) 2005
+ */
+
+#ifndef _CLPLUMBING_CL_SYSLOG_H
+#define _CLPLUMBING_CL_SYSLOG_H
+
+/* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
+int cl_syslogfac_str2int(const char *);
+
+/* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
+/* Returns static string; caller must NOT free. */
+const char *cl_syslogfac_int2str(int);
+
+#endif /* _CLPLUMBING_CL_SYSLOG_H */
diff --git a/include/clplumbing/cl_tiebreaker.h b/include/clplumbing/cl_tiebreaker.h
new file mode 100644
index 0000000..11c10c4
--- /dev/null
+++ b/include/clplumbing/cl_tiebreaker.h
@@ -0,0 +1,35 @@
+/*
+ * cl_tiebreaker.h: head file for tiebreaker module
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CL_TIEBREAKER_H_
+#define _CL_TIEBREAKER_H_
+
+#define HB_TIEBREAKER_TYPE tiebreaker
+#define HB_TIEBREAKER_TYPE_S "tiebreaker"
+
+/*
+ * List of functions provided by implementations of tiebreaker interface.
+ */
+struct hb_tiebreaker_fns {
+ gboolean (*break_tie) (int, int);
+};
+
+
+#endif
diff --git a/include/clplumbing/cl_uuid.h b/include/clplumbing/cl_uuid.h
new file mode 100644
index 0000000..12542cd
--- /dev/null
+++ b/include/clplumbing/cl_uuid.h
@@ -0,0 +1,40 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CL_UUID_H_
+#define _CL_UUID_H_
+#include <glib.h>
+
+typedef struct cl_uuid_s{
+ unsigned char uuid[16];
+}cl_uuid_t;
+
+void cl_uuid_copy(cl_uuid_t* dst, cl_uuid_t* src);
+void cl_uuid_clear(cl_uuid_t* uu);
+int cl_uuid_compare(const cl_uuid_t* uu1, const cl_uuid_t* uu2);
+void cl_uuid_generate(cl_uuid_t* out);
+int cl_uuid_is_null(cl_uuid_t* uu);
+int cl_uuid_parse( char *in, cl_uuid_t* uu);
+#define UU_UNPARSE_SIZEOF 37 /* Including NULL byte */
+void cl_uuid_unparse(const cl_uuid_t* uu, char *out);
+
+/* Suitable for ues as a GHashFunc from glib */
+guint cl_uuid_g_hash(gconstpointer uuid_ptr);
+/* Suitable for ues as a GEqualFunc from glib */
+gboolean cl_uuid_g_equal(gconstpointer uuid_ptr_a, gconstpointer uuid_ptr_b);
+
+
+#endif
diff --git a/include/clplumbing/coredumps.h b/include/clplumbing/coredumps.h
new file mode 100644
index 0000000..4d5ce79
--- /dev/null
+++ b/include/clplumbing/coredumps.h
@@ -0,0 +1,36 @@
+/*
+ * Basic Core dump control functions.
+ *
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+#ifndef _CLPLUMBING_COREFILES_H
+# define _CLPLUMBING_COREFILES_H 1
+ /* Set the root directory of our core directory hierarchy */
+int cl_set_corerootdir(const char * dir);
+ /* Change directory to the directory our core file needs to go in */
+ /* Call after you establish the userid you're running under */
+int cl_cdtocoredir(void);
+ /* Enable/disable core dumps for ourselves and our child processes */
+int cl_enable_coredumps(int truefalse);
+void cl_untaint_coredumps(void);
+void cl_set_coredump_signal_handler(int nsig);
+void cl_set_all_coredump_signal_handlers(void);
+
+#endif
diff --git a/include/clplumbing/cpulimits.h b/include/clplumbing/cpulimits.h
new file mode 100644
index 0000000..f7dd875
--- /dev/null
+++ b/include/clplumbing/cpulimits.h
@@ -0,0 +1,66 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Functions to put limits on CPU consumption.
+ * This allows us to better catch runaway realtime processes that
+ * might otherwise hang the whole system.
+ *
+ * The process is basically this:
+ * - Set the CPU percentage limit with cl_cpu_limit_setpercent()
+ * according to what you expect the CPU percentage to top out at
+ * measured over an interval at >= 10 seconds
+ * - Call cl_cpu_limit_ms_interval() to figure out how often to update
+ * the CPU limit (it returns milliseconds)
+ * - At least as often as indicated above, call cl_cpu_limit_update()
+ * to update our current CPU limit.
+ *
+ * These limits are approximate, so be a little conservative.
+ * If you've gone into an infinite loop, it'll likely get caught ;-)
+ *
+ * Note that exceeding the soft CPU limits we set here will cause a
+ * SIGXCPU signal to be sent.
+ *
+ * The default action for this signal is to cause a core dump.
+ * This is a good choice ;-)
+ *
+ * As of this writing, this code will never set the soft CPU limit less
+ * than two seconds, or greater than 10 seconds.
+ *
+ * It will currrently return a limit update interval between 10000 and
+ * 400000 milliseconds.
+ *
+ */
+
+/*
+ * Set expected CPU percentage upper bound
+ */
+int cl_cpu_limit_setpercent(int ipercent);
+
+/*
+ * Update the current CPU limit
+ */
+int cl_cpu_limit_update(void);
+
+/*
+ * How often should we call cl_cpu_limit_update()?
+ *
+ * Note: return result is in milliseconds
+ */
+int cl_cpu_limit_ms_interval(void);
+
+/* Disable further CPU limits... */
+int cl_cpu_limit_disable(void);
diff --git a/include/clplumbing/ipc.h b/include/clplumbing/ipc.h
new file mode 100644
index 0000000..4a5e151
--- /dev/null
+++ b/include/clplumbing/ipc.h
@@ -0,0 +1,788 @@
+/*
+ * ipc.h IPC abstraction data structures.
+ *
+ * author Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>,
+ * Alan Robertson <alanr@unix.sh>
+ *
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>
+ *
+ * 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
+ *
+ */
+
+#ifndef _IPC_H_
+#define _IPC_H_
+#include <glib.h>
+#undef MIN
+#undef MAX
+#include <sys/types.h>
+#include <sys/poll.h>
+
+#ifdef IPC_TIME_DEBUG
+#include <clplumbing/longclock.h>
+#define MAXIPCTIME 3000
+
+#endif
+
+/* constants */
+#define DEFAULT_MAX_QLEN 64
+#define MAX_MSGPAD 128
+/* channel and connection status */
+#define IPC_CONNECT 1 /* Connected: can read, write */
+#define IPC_WAIT 2 /* Waiting for connection */
+#define IPC_DISCONNECT 3 /* Disconnected, can't read or write*/
+#define IPC_DISC_PENDING 4 /* Disconnected, can't write but */
+ /* may be more data to read */
+
+#define MAXFAILREASON 128
+
+#define IPC_SERVER 1
+#define IPC_CLIENT 2
+#define IPC_PEER 3
+
+#define IPC_ISRCONN(ch) ((ch)->ch_status == IPC_CONNECT \
+ || (ch)->ch_status == IPC_DISC_PENDING)
+
+#define IPC_ISWCONN(ch) ((ch)->ch_status == IPC_CONNECT)
+
+/* general return values */
+#define IPC_OK 0
+#define IPC_FAIL 1
+#define IPC_BROKEN 2
+#define IPC_INTR 3
+#define IPC_TIMEOUT 4
+
+/*
+ * IPC: Sockets-like Interprocess Communication Abstraction
+ *
+ * We have two fundamental abstractions which we maintain.
+ * Everything else is in support of these two abstractions.
+ *
+ * These two main abstractions are:
+ *
+ * IPC_WaitConnection:
+ * A server-side abstraction for waiting for someone to connect.
+ *
+ * IPC_Channel:
+ * An abstraction for an active communications channel.
+ *
+ * All the operations on these two abstractions are carried out
+ * via function tables (channel->ops). Below we refer to the
+ * function pointers in these tables as member functions.
+ *
+ * On the server side, everything starts up with a call to
+ * ipc_wait_conn_constructor(), which returns an IPC_WaitConnection.
+ *
+ * Once the server has the IPC_WaitConnection object in hand,
+ * it can give the result of the get_select_fd() member function
+ * to poll or select to inform you when someone tries to connect.
+ *
+ * Once select tells you someone is trying to connect, you then
+ * use the accept_connection() member function to accept
+ * the connection. accept_connection() returns an IPC_Channel.
+ *
+ * With that, the server can talk to the client, and away they
+ * go ;-)
+ *
+ * On the client side, everything starts up with a call to
+ * ipc_channel_constructor() which we use to talk to the server.
+ * The client is much easier ;-)
+ */
+
+
+typedef struct IPC_WAIT_CONNECTION IPC_WaitConnection;
+typedef struct IPC_CHANNEL IPC_Channel;
+
+typedef struct IPC_MESSAGE IPC_Message;
+typedef struct IPC_QUEUE IPC_Queue;
+typedef struct IPC_AUTH IPC_Auth;
+
+typedef struct IPC_OPS IPC_Ops;
+typedef struct IPC_WAIT_OPS IPC_WaitOps;
+
+
+
+/* wait connection structure. */
+struct IPC_WAIT_CONNECTION{
+ int ch_status; /* wait conn. status.*/
+ void * ch_private; /* wait conn. private data. */
+ IPC_WaitOps *ops; /* wait conn. function table .*/
+};
+
+
+typedef void(*flow_callback_t)(IPC_Channel*, void*);
+
+/* channel structure.*/
+struct IPC_CHANNEL{
+ int ch_status; /* identify the status of channel.*/
+ int refcount; /* reference count */
+ pid_t farside_pid; /* far side pid */
+ void* ch_private; /* channel private data. */
+ /* (may contain conn. info.) */
+ IPC_Ops* ops; /* IPC_Channel function table.*/
+
+ /* number of bytes needed
+ * at the begginging of <ipcmessage>->msg_body
+ * it's for msg head needed to tranmit in wire
+ */
+ unsigned int msgpad;
+
+ /* the number of bytes remainng to send for the first message in send queue
+ 0 means nothing has been sent thus all bytes needs to be send
+ n != 0 means there are still n bytes needs to be sent
+ */
+ unsigned int bytes_remaining;
+
+
+ /* is the send blocking or nonblocking*/
+ gboolean should_send_block;
+
+ /* if send would block, should an error be returned or not */
+ gboolean should_block_fail;
+
+/* There are two queues in channel. One is for sending and the other
+ * is for receiving.
+ * Those two queues are channel's internal queues. They should not be
+ * accessed directly.
+ */
+ /* private: */
+ IPC_Queue* send_queue;
+ IPC_Queue* recv_queue;
+
+ /* buffer pool for receive in this channel*/
+ struct ipc_bufpool* pool;
+
+ /* the follwing is for send flow control*/
+ int high_flow_mark;
+ int low_flow_mark;
+ void* high_flow_userdata;
+ void* low_flow_userdata;
+ flow_callback_t high_flow_callback;
+ flow_callback_t low_flow_callback;
+
+ int conntype;
+
+ char failreason[MAXFAILREASON];
+
+ /* New members to support Multi-level ACLs for the CIB,
+ * available since libplumb.so.2.1.0, added at the
+ * end of the struct to maintain backwards ABI compatibility.
+ *
+ * If you don't like to care for library versions,
+ * create your IPC channels with
+ * c = ipc_wait_conn_constructor(IPC_UDS_CRED, ...),
+ * and these members will be available.
+ */
+ uid_t farside_uid; /* far side uid */
+ gid_t farside_gid; /* far side gid */
+};
+
+struct IPC_QUEUE{
+ size_t current_qlen; /* Current qlen */
+ size_t max_qlen; /* Max allowed qlen */
+ GList* queue; /* List of messages */
+ /* keep the time of the last max queue warning */
+ time_t last_maxqlen_warn;
+ /* and the number of messages lost */
+ unsigned maxqlen_cnt;
+};
+
+/* authentication information : set of gids and uids */
+struct IPC_AUTH {
+ GHashTable * uid; /* hash table for user id */
+ GHashTable * gid; /* hash table for group id */
+};
+
+
+/* Message structure. */
+struct IPC_MESSAGE{
+ size_t msg_len;
+ void* msg_buf;
+ void* msg_body;
+/*
+ * IPC_MESSAGE::msg_done
+ * the callback function pointer which can be called after this
+ * message is sent, received or otherwise processed.
+ *
+ * Parameter:
+ * msg: the back pointer to the message which contains this
+ * function pointer.
+ *
+ */
+ void (* msg_done)(IPC_Message * msg);
+ void* msg_private; /* the message private data. */
+ /* Belongs to message creator */
+ /* May be used by callback function. */
+ IPC_Channel * msg_ch; /* Channel the */
+ /* message is from/in */
+
+};
+
+struct IPC_WAIT_OPS{
+/*
+ * IPC_WAIT_OPS::destroy
+ * destroy the wait connection and free the memory space used by
+ * this wait connection.
+ *
+ * Parameters:
+ * wait_conn (IN): the pointer to the wait connection.
+ *
+ */
+ void (* destroy)(IPC_WaitConnection *wait_conn);
+/*
+ * IPC_WAIT_OPS::get_select_fd
+ * provide a fd which user can listen on for a new coming connection.
+ *
+ * Parameters:
+ * wait_conn (IN) : the pointer to the wait connection which
+ * we're supposed to return the file descriptor for
+ * (the file descriptor can be used with poll too ;-))
+ *
+ * Return values:
+ * integer >= 0 : the select_fd.
+ * -1 : can't get the select fd.
+ *
+ */
+ int (* get_select_fd)(IPC_WaitConnection *wait_conn);
+/*
+ * IPC_WAIT_OPS::accept_connection
+ * accept and create a new connection and verify the authentication.
+ *
+ * Parameters:
+ * wait_conn (IN) : the waiting connection which will accept
+ * create the new connection.
+ * auth_info (IN) : the authentication information which will be
+ * verified for the new connection.
+ *
+ * Return values:
+ * the pointer to the new IPC channel; NULL if the creation or
+ * authentication fails.
+ *
+ */
+ IPC_Channel * (* accept_connection)
+ (IPC_WaitConnection * wait_conn, IPC_Auth *auth_info);
+};
+
+/* Standard IPC channel operations */
+
+struct IPC_OPS{
+/*
+ * IPC_OPS::destroy
+ * brief destroy the channel object.
+ *
+ * Parameters:
+ * ch (IN) : the pointer to the channel which will be destroyed.
+ *
+ */
+ void (*destroy) (IPC_Channel * ch);
+/*
+ * IPC_OPS::initiate_connection
+ * used by service user side to set up a connection.
+ *
+ * Parameters:
+ * ch (IN) : the pointer to channel used to initiate the connection.
+ *
+ * Return values:
+ * IPC_OK : the channel set up the connection successfully.
+ * IPC_FAIL : the connection initiation fails.
+ *
+ */
+ int (* initiate_connection) (IPC_Channel * ch);
+/*
+ * IPC_OPS::verify_auth
+ * used by either side to verify the identity of peer on connection.
+ *
+ * Parameters
+ * ch (IN) : the pointer to the channel.
+ *
+ * Return values:
+ * IPC_OK : the peer is trust.
+ * IPC_FAIL : verifying authentication fails.
+ */
+ int (* verify_auth) (IPC_Channel * ch, IPC_Auth* info);
+/*
+ * IPC_OPS::assert_auth
+ * service user asserts to be certain qualified service user.
+ *
+ * Parameters:
+ * ch (IN): the active channel.
+ * auth (IN): the hash table which contains the asserting information.
+ *
+ * Return values:
+ * IPC_OK : assert the authentication successfully.
+ * IPC_FAIL : assertion fails.
+ *
+ * NOTE: This operation is a bit obscure. It isn't needed with
+ * UNIX domain sockets at all. The intent is that some kinds
+ * of IPC (like FIFOs), do not have an intrinsic method to
+ * authenticate themselves except through file permissions.
+ * The idea is that you must tell it how to chown/grp your
+ * FIFO so that the other side and see that if you can write
+ * this, you can ONLY be the user/group they expect you to be.
+ * But, I think the parameters may be wrong for this ;-)
+ */
+ int (* assert_auth) (IPC_Channel * ch, GHashTable * auth);
+/*
+ * IPC_OPS::send
+ * send the message through the sending connection.
+ *
+ * Parameters:
+ * ch (IN) : the channel which contains the connection.
+ * msg (IN) : pointer to the sending message. User must
+ * allocate the message space.
+ *
+ * Return values:
+ * IPC_OK : the message was either sent out successfully or
+ * appended to the send_queue.
+ * IPC_FAIL : the send operation failed.
+ * IPC_BROKEN : the channel is broken.
+ *
+*/
+ int (* send) (IPC_Channel * ch, IPC_Message* msg);
+
+/*
+ * IPC_OPS::recv
+ * receive the message through receving queue.
+ *
+ * Parameters:
+ * ch (IN) : the channel which contains the connection.
+ * msg (OUT): the IPC_MESSAGE** pointer which contains the pointer
+ * to the received message or NULL if there is no
+ * message available.
+ *
+ * Return values:
+ * IPC_OK : receive operation is completed successfully.
+ * IPC_FAIL : operation failed.
+ * IPC_BROKEN : the channel is broken (disconnected)
+ *
+ * Note:
+ * return value IPC_OK doesn't mean the message is already
+ * sent out to (or received by) the peer. It may be pending
+ * in the send_queue. In order to make sure the message is no
+ * longer needed, please specify the msg_done function in the
+ * message structure and once this function is called, the
+ * message is no longer needed.
+ *
+ * is_sending_blocked() is another way to check if there is a message
+ * pending in the send_queue.
+ *
+ */
+ int (* recv) (IPC_Channel * ch, IPC_Message** msg);
+
+/*
+ * IPC_OPS::waitin
+ * Wait for input to become available
+ *
+ * Parameters:
+ * ch (IN) : the channel which contains the connection.
+ *
+ * Side effects:
+ * If output becomes unblocked while waiting, it will automatically
+ * be resumed without comment.
+ *
+ * Return values:
+ * IPC_OK : a message is pending or output has become unblocked.
+ * IPC_FAIL : operation failed.
+ * IPC_BROKEN : the channel is broken (disconnected)
+ * IPC_INTR : waiting was interrupted by a signal
+ */
+ int (* waitin) (IPC_Channel * ch);
+/*
+ * IPC_OPS::waitout
+ * Wait for output to finish
+ *
+ * Parameters:
+ * ch (IN) : the channel which contains the connection.
+ *
+ * Side effects:
+ * If input becomes available while waiting, it will automatically
+ * be read into the channel queue without comment.
+ *
+ * Return values:
+ * IPC_OK : output no longer blocked
+ * IPC_FAIL : operation failed.
+ * IPC_BROKEN : the channel is broken (disconnected)
+ * IPC_INTR : waiting was interrupted by a signal
+ */
+ int (* waitout) (IPC_Channel * ch);
+
+/*
+ * IPC_OPS::is_message_pending
+ * check to see if there is any messages ready to read, or hangup has
+ * occurred.
+ *
+ * Parameters:
+ * ch (IN) : the pointer to the channel.
+ *
+ * Return values:
+ * TRUE : there are messages ready to read, or hangup.
+ * FALSE: there are no messages ready to be read.
+ */
+ gboolean (* is_message_pending) (IPC_Channel * ch);
+
+/*
+ * IPC_OPS::is_sending_blocked
+ * check the send_queue to see if there are any messages blocked.
+ *
+ * Parameters:
+ * ch (IN) : the pointer to the channel.
+ *
+ * Return values:
+ * TRUE : there are messages blocked (waiting) in the send_queue.
+ * FALSE: there are no message blocked (waiting) in the send_queue.
+ *
+ * See also:
+ * get_send_select_fd()
+ */
+ gboolean (* is_sending_blocked) (IPC_Channel *ch);
+
+/*
+ * IPC_OPS::resume_io
+ * Resume all possible IO operations through the IPC transport
+ *
+ * Parameters:
+ * the pointer to the channel.
+ *
+ * Return values:
+ * IPC_OK : resume all the possible I/O operation successfully.
+ * IPC_FAIL : the operation fails.
+ * IPC_BROKEN : the channel is broken.
+ *
+ */
+ int (* resume_io) (IPC_Channel *ch);
+/*
+ * IPC_OPS::get_send_select_fd()
+ * return a file descriptor which can be given to select/poll. This fd
+ * is used by the IPC code for sending. It is intended that this be
+ * ONLY used with select, poll, or similar mechanisms, not for direct I/O.
+ * Note that due to select(2) and poll(2) semantics, you must check
+ * is_sending_blocked() to see whether you should include this FD in
+ * your poll for writability, or you will loop very fast in your
+ * select/poll loop ;-)
+ *
+ * Parameters:
+ * ch (IN) : the pointer to the channel.
+ *
+ * Return values:
+ * integer >= 0 : the send fd for selection.
+ * -1 : there is no send fd.
+ *
+ * See also:
+ * is_sending_blocked()
+ */
+ int (* get_send_select_fd) (IPC_Channel * ch);
+/*
+ * IPC_OPS::get_recv_select_fd
+ * return a file descriptor which can be given to select. This fd
+ * is for receiving. It is intended that this be ONLY used with select,
+ * poll, or similar mechanisms, NOT for direct I/O.
+ *
+ * Parameters:
+ * ch (IN) : the pointer to the channel.
+ *
+ * Return values:
+ * integer >= 0 : the recv fd for selection.
+ * -1 : there is no recv fd.
+ *
+ * NOTE: This file descriptor is often the same as the send
+ * file descriptor.
+ */
+ int (* get_recv_select_fd) (IPC_Channel * ch);
+/*
+ * IPC_OPS::set_send_qlen
+ * allow user to set the maximum send_queue length.
+ *
+ * Parameters
+ * ch (IN) : the pointer to the channel.
+ * q_len (IN) : the max length for the send_queue.
+ *
+ * Return values:
+ * IPC_OK : set the send queue length successfully.
+ * IPC_FAIL : there is no send queue. (This isn't supposed
+ * to happen).
+ * It means something bad happened.
+ *
+ */
+ int (* set_send_qlen) (IPC_Channel * ch, int q_len);
+/*
+ * IPC_OPS::set_recv_qlen
+ * allow user to set the maximum recv_queue length.
+ *
+ * Parameters:
+ * ch (IN) : the pointer to the channel.
+ * q_len (IN) : the max length for the recv_queue.
+ *
+ * Return values:
+ * IPC_OK : set the recv queue length successfully.
+ * IPC_FAIL : there is no recv queue.
+ *
+ */
+ int (* set_recv_qlen) (IPC_Channel * ch, int q_len);
+
+
+/*
+ * IPC_OPS: set callback for high/low flow mark
+ * ch (IN) : the pointer to the channel
+ * callback (IN) : the callback function
+ * user_data(IN) : a pointer to user_data
+ * callback will be called with channel and
+ * this user_data as parameters
+ *
+ * Return values:
+ * void
+ *
+ */
+
+
+ void (* set_high_flow_callback) (IPC_Channel* ch ,
+ flow_callback_t callback,
+ void* user_data);
+
+ void (* set_low_flow_callback) (IPC_Channel* ch ,
+ flow_callback_t callback,
+ void* user_data);
+
+/*
+ * IPC_OPS::new_ipcmsg
+ * ch (IN) : the pointer to the channel
+ * data (IN) : data to be copied to the message body
+ * len (IN) : data len
+ * private (IN): the pointer value to set as in the message
+ *
+ * Return values:
+ * the pointer to a new created message will be
+ * returned if success or NULL if failure
+ *
+ */
+
+ IPC_Message* (*new_ipcmsg)(IPC_Channel* ch, const void* data,
+ int len, void* private);
+
+
+/*
+ * IPC_OPS::nget_chan_status
+ * ch (IN) : the pointer to the channel
+ *
+ * Return value:
+ * channel status.
+ *
+ */
+ int (*get_chan_status)(IPC_Channel* ch);
+
+
+/*
+ * These two functions returns true if the corresponding queue
+ * is full, otherwise it returns false
+ */
+
+ gboolean (*is_sendq_full)(struct IPC_CHANNEL * ch);
+ gboolean (*is_recvq_full)(struct IPC_CHANNEL * ch);
+
+
+ /* Get the connection type for the channel
+ * it can be IPC_SERVER, IPC_CLIENT, IPC_PEER
+ */
+
+ int (*get_conntype)(struct IPC_CHANNEL* ch);
+
+ int (*disconnect)(struct IPC_CHANNEL* ch);
+
+};
+
+
+/*
+ * ipc_wait_conn_constructor:
+ * the common constructor for ipc waiting connection.
+ * Use ch_type to identify the connection type. Usually it's only
+ * needed by server side.
+ *
+ * Parameters:
+ * ch_type (IN) : the type of the waiting connection to create.
+ * ch_attrs (IN) : the hash table which contains the attributes
+ * needed by this waiting connection in name/value
+ * pair format.
+ *
+ * For example, the only attribute needed by UNIX
+ * domain sockets is path name.
+ *
+ * Return values:
+ * the pointer to a new waiting connection or NULL if the connection
+ * can't be created.
+ * Note:
+ * current implementation supports
+ * IPC_ANYTYPE: This is what program code should typically use.
+ * Internally it is an alias to IPC_UDS_CRED.
+ * IPC_UDS_CRED: Unix Domain Sockets,
+ * farside uid + gid credentials is available.
+ * Available since libplumb.so.2.1.0.
+ * IPC_DOMAIN_SOCKET: An other alias to Unix Domain Sockets;
+ * internally it is equivalent to both above.
+ * Using this explicitly, your code will work
+ * even with libplumb.so.2.0.0.
+ * Which also means that you MUST NOT use the
+ * farside_uid/gid functionality then.
+ */
+extern IPC_WaitConnection * ipc_wait_conn_constructor(const char * ch_type
+, GHashTable* ch_attrs);
+
+/*
+ * ipc_channel_constructor:
+ * brief the common constructor for ipc channel.
+ * Use ch_type to identify the channel type.
+ * Usually this function is only called by client side.
+ *
+ * Parameters:
+ * ch_type (IN): the type of the channel you want to create.
+ * ch_attrs (IN): the hash table which contains the attributes needed
+ * by this channel.
+ * For example, the only attribute needed by UNIX domain
+ * socket is path name.
+ *
+ * Return values:
+ * the pointer to the new channel whose status is IPC_DISCONNECT
+ * or NULL if the channel can't be created.
+ *
+ * Note:
+ * See comments for ipc_wait_conn_constructor above
+ * for currently implemented ch_type channel types.
+ */
+extern IPC_Channel * ipc_channel_constructor(const char * ch_type
+, GHashTable* ch_attrs);
+
+/*
+ * ipc_channel_pair:
+ * Construct a pair of connected IPC channels in a fashion analogous
+ * to pipe(2) or socketpair(2).
+ *
+ * Parameters:
+ * channels: an array of two IPC_Channel pointers for return result
+ */
+int ipc_channel_pair(IPC_Channel* channels[2]);
+
+/*
+ * ipc_set_auth:
+ * A helper function used to convert array of uid and gid into
+ * an authentication structure (IPC_Auth)
+ *
+ * Parameters:
+ * a_uid (IN): the array of a set of user ids.
+ * a_gid (IN): the array of a set of group ids.
+ * num_uid (IN): the number of user ids.
+ * num_gid (IN): the number of group ids.
+ *
+ * Return values:
+ * the pointer to the authentication structure which contains the
+ * set of uid and the set of gid. Or NULL if this structure can't
+ * be created.
+ *
+ */
+
+
+IPC_Auth* ipc_str_to_auth(const char * uidlist, int, const char * gidlist, int);
+
+extern IPC_Auth * ipc_set_auth(uid_t * a_uid, gid_t * a_gid
+, int num_uid, int num_gid);
+
+/* Destroys an object constructed by ipc_set_auth or ipc_str_to_auth() */
+extern void ipc_destroy_auth(IPC_Auth * auth);
+
+extern void ipc_set_pollfunc(int (*)(struct pollfd*, unsigned int, int));
+extern void ipc_bufpool_dump_stats(void);
+
+#ifdef IPC_TIME_DEBUG
+
+enum MSGPOS_IN_IPC{
+ MSGPOS_ENQUEUE,
+ MSGPOS_SEND,
+ MSGPOS_RECV,
+ MSGPOS_DEQUEUE
+};
+
+#endif
+
+
+struct SOCKET_MSG_HEAD{
+ int msg_len;
+ unsigned int magic;
+#ifdef IPC_TIME_DEBUG
+ longclock_t enqueue_time;
+ longclock_t send_time;
+ longclock_t recv_time;
+ longclock_t dequeue_time;
+#endif
+
+};
+
+
+/* MAXMSG is the maximum final message size on the wire. */
+#define MAXMSG (256*1024)
+/* MAXUNCOMPRESSED is the maximum, raw data size prior to compression. */
+/* 1:8 compression ratio is to be expected on data such as xml */
+#define MAXUNCOMPRESSED (2048*1024)
+#define HEADMAGIC 0xabcd
+#define POOL_SIZE (4*1024)
+struct ipc_bufpool{
+
+ int refcount;
+ char* currpos;
+ char* consumepos;
+ char* startpos;
+ char* endpos;
+ int size;
+};
+
+struct ipc_bufpool* ipc_bufpool_new(int);
+
+void ipc_bufpool_del(struct ipc_bufpool* pool);
+
+int ipc_bufpool_spaceleft(struct ipc_bufpool* pool);
+
+int ipc_bufpool_update(struct ipc_bufpool* pool,
+ struct IPC_CHANNEL * ch,
+ int msg_len,
+ IPC_Queue* rqueue);
+
+gboolean ipc_bufpool_full(struct ipc_bufpool* pool,
+ struct IPC_CHANNEL* ch,
+ int*);
+int ipc_bufpool_partial_copy(struct ipc_bufpool* dstpool,
+ struct ipc_bufpool* srcpool);
+
+void ipc_bufpool_ref(struct ipc_bufpool* pool);
+
+void ipc_bufpool_unref(struct ipc_bufpool* pool);
+
+void set_ipc_time_debug_flag(gboolean flag);
+
+/* pathname attribute */
+#define IPC_PATH_ATTR "path"
+/* socket mode attribute */
+#define IPC_MODE_ATTR "sockmode"
+/* Unix domain socket, used by old code.
+ * See also the comment block above ipc_wait_conn_constructor() */
+#define IPC_DOMAIN_SOCKET "uds"
+/* Unix domain socket with farside uid + gid credentials.
+ * Available since libplumb.so.2.1.0 */
+#define IPC_UDS_CRED "uds_c"
+
+#ifdef IPC_UDS_CRED
+# define IPC_ANYTYPE IPC_UDS_CRED
+#else
+# error "No IPC types defined(!)"
+#endif
+
+#endif
diff --git a/include/clplumbing/loggingdaemon.h b/include/clplumbing/loggingdaemon.h
new file mode 100644
index 0000000..ba986f5
--- /dev/null
+++ b/include/clplumbing/loggingdaemon.h
@@ -0,0 +1,32 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Messages sent to the logging daemon */
+#define LD_LOGIT 2
+#define MAXENTITY 64
+
+/* Message contains following header, followed by the text (char[]) itself */
+struct LogDaemonMsgHdr_s {
+ int msgtype;
+ int facility;
+ int priority;
+ int msglen;
+ gboolean use_pri_str;
+ int entity_pid;
+ char entity[MAXENTITY];
+ TIME_T timestamp;
+};
+typedef struct LogDaemonMsgHdr_s LogDaemonMsgHdr;
diff --git a/include/clplumbing/longclock.h b/include/clplumbing/longclock.h
new file mode 100644
index 0000000..ae95b28
--- /dev/null
+++ b/include/clplumbing/longclock.h
@@ -0,0 +1,143 @@
+/*
+ * Longclock operations
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#ifndef _LONGCLOCK_H
+# define _LONGCLOCK_H
+/*
+ * A longclock_t object is a lot like a clock_t object, except that it
+ * won't wrap in the lifetime of the earth. It is guaranteed to be at
+ * least 64 bits. This means it should go for around 2 billion years.
+ *
+ * It is also supposed to be proof against changes in the local time on
+ * the computer. This is easy if you have a properly-working times(2)
+ * for us to use.
+ *
+ * longclock_t's are definitely not comparable between computers, and in
+ * some implementations, not even between processes on the same computer.
+ *
+ *
+ * The functions provided here are:
+ *
+ * unsigned long cl_times(void);
+ * A rational wrapper for the times(2) call
+ * for those cases where only the return value
+ * is wanted.
+ * longclock_t time_longclock(void);
+ * Returns current time as a longclock_t.
+ *
+ * longclock_t msto_longclock(unsigned long);
+ * Converts quantity in milliseconds to longclock_t
+ *
+ * unsigned long longclockto_ms(longclock_t);
+ * Converts quantity in longclock_t to milliseconds
+ * NOTE: Can overflow!
+ *
+ * unsigned long longclockto_long(longclock_t);
+ * Converts quantity in longclock_t to clock_t
+ * NOTE: Can overflow!
+ *
+ * longclock_t secsto_longclock(unsigned long);
+ * Converts quantity in seconds to longclock_t
+ *
+ * longclock_t add_longclock(longclock_t l, longclock_t r);
+ * Adds two longclock_t values
+ *
+ * int cmp_longclock(longclock_t l, longclock_t r);
+ * Returns negative, zero or positive value
+ *
+ * longclock_t sub_longclock(longclock_t l, longclock_t r);
+ * Subtracts two longclock_t values
+ * NOTE: Undefined if l is < r
+ *
+ * longclock_t dsecsto_longclock(double);
+ * Converts quantity in seconds (as a double)
+ * to a longclock_t
+ *
+ * unsigned hz_longclock(void);
+ * Returns frequency of longclock_t clock.
+ *
+ * We provide this constant:
+ *
+ * extern const longclock_t zero_longclock;
+ */
+extern unsigned long cl_times(void);
+
+#ifdef CLOCK_T_IS_LONG_ENOUGH
+# ifndef HAVE_LONGCLOCK_ARITHMETIC
+# define HAVE_LONGCLOCK_ARITHMETIC
+# endif
+
+# include <sys/times.h>
+
+ typedef clock_t longclock_t;
+
+#else /* clock_t isn't at least 64 bits */
+ typedef unsigned long long longclock_t;
+#endif
+
+longclock_t time_longclock(void);
+
+extern const longclock_t zero_longclock;
+
+unsigned hz_longclock(void);
+longclock_t secsto_longclock(unsigned long);
+longclock_t dsecsto_longclock(double);
+longclock_t msto_longclock(unsigned long);
+unsigned long longclockto_ms(longclock_t); /* Can overflow! */
+long longclockto_long(longclock_t); /* May overflow! */
+
+
+#ifndef HAVE_LONGCLOCK_ARITHMETIC
+
+longclock_t add_longclock(longclock_t l, longclock_t r);
+
+ /* Undefined if l is < r according to cmp_longclock() */
+longclock_t sub_longclock(longclock_t l, longclock_t r);
+
+int cmp_longclock(longclock_t l, longclock_t r);
+
+
+#else /* We HAVE_LONGCLOCK_ARITHMETIC */
+
+# define longclockto_long(lc) ((long)(lc))
+
+# define add_longclock(l,r) \
+ ((longclock_t)(l) + (longclock_t)(r))
+
+# define sub_longclock(l,r) \
+ ((longclock_t)(l) - (longclock_t)(r))
+
+# define cmp_longclock(l,r) \
+ (((longclock_t)(l) < (longclock_t)(r)) \
+ ? -1 \
+ : (((longclock_t)(l) > (longclock_t)(r)) \
+ ? +1 : 0))
+#endif
+
+
+/* N.B: Possibly not the best place for this, but it will do for now */
+/* This is consistent with OpenBSD, and is a good choice anyway */
+#define TIME_T unsigned long
+#define TIME_F "%lu"
+#define TIME_X "%lx"
+
+#endif
diff --git a/include/clplumbing/lsb_exitcodes.h b/include/clplumbing/lsb_exitcodes.h
new file mode 100644
index 0000000..e46b5be
--- /dev/null
+++ b/include/clplumbing/lsb_exitcodes.h
@@ -0,0 +1,92 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* LSB status exit codes.
+ *
+ * All of these and the supporting text are taken from the LSB.
+ *
+ * If the status command is given, the init script will return
+ * the following exit status codes.
+ *
+ * 0 program is running or service is OK
+ * 1 program is dead and /var/run pid file exists
+ * 2 program is dead and /var/lock lock file exists
+ * 3 program is stopped
+ * 4 program or service status is unknown
+ * 5-99 reserved for future LSB use
+ * 100-149 reserved for distribution use
+ * 150-199 reserved for application use
+ * 200-254 reserved
+ */
+
+#define LSB_STATUS_OK 0
+#define LSB_STATUS_VAR_PID 1
+#define LSB_STATUS_VAR_LOCK 2
+#define LSB_STATUS_STOPPED 3
+#define LSB_STATUS_UNKNOWN 4
+#define LSB_STATUS_LSBRESERVED 5
+#define LSB_STATUS_DISTRESERVED 100
+#define LSB_STATUS_APPRESERVED 150
+#define LSB_STATUS_RESERVED 200
+/*
+ *
+ * In the case of init script commands other than "status"
+ * (i.e., "start", "stop", "restart", "reload", and "force-reload"),
+ * the init script must return an exit status of zero if the action
+ * described by the argument has been successful. Otherwise, the
+ * exit status shall be non-zero, as defined below. In addition
+ * to straightforward success, the following situations are also
+ * to be considered successful:
+ *
+ * restarting a service (instead of reloading it) with the
+ * "force-reload" argument
+ * running "start" on a service already running
+ * running "stop" on a service already stopped or not running
+ * running "restart" on a service already stopped or not running
+ * In case of an error, while processing any init script action
+ * except for "status", the init script must print an error
+ * message and return one of the following non-zero exit
+ * status codes.
+ *
+ * 1 generic or unspecified error (current practice)
+ * 2 invalid or excess argument(s)
+ * 3 unimplemented feature (for example, "reload")
+ * 4 user had insufficient privilege
+ * 5 program is not installed
+ * 6 program is not configured
+ * 7 program is not running
+ * 8-99 reserved for future LSB use
+ * 100-149 reserved for distribution use
+ * 150-199 reserved for application use
+ * 200-254 reserved
+ *
+ * All error messages must be printed on standard error.
+ * All status messages must be printed on standard output.
+ * (This does not prevent scripts from calling the logging
+ * functions such as log_failure_msg).
+ */
+#define LSB_EXIT_OK 0
+#define LSB_EXIT_GENERIC 1
+#define LSB_EXIT_EINVAL 2
+#define LSB_EXIT_ENOTSUPPORTED 3
+#define LSB_EXIT_EPERM 4
+#define LSB_EXIT_NOTINSTALLED 5
+#define LSB_EXIT_NOTCONFIGED 6
+#define LSB_EXIT_NOTRUNNING 7
+#define LSB_EXIT_LSBRESERVED 8
+#define LSB_EXIT_DISTRESERVED 100
+#define LSB_EXIT_APPRESERVED 150
+#define LSB_EXIT_RESERVED 200
diff --git a/include/clplumbing/md5.h b/include/clplumbing/md5.h
new file mode 100644
index 0000000..95b2c33
--- /dev/null
+++ b/include/clplumbing/md5.h
@@ -0,0 +1,49 @@
+/*
+ * md5.h: MD5 and keyed-MD5 algorithms
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2005 International Business Machines
+ *
+ * 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
+ *
+ */
+
+#ifndef _MD5_H_
+#define _MD5_H__
+
+/*
+ * MD5: The MD5 Message-Digest Algorithm ( RFC 1321 )
+ * return value: 0 - success
+ * <0 - fail
+ * Note: The digest buffer should be not less than 16.
+ *
+ */
+int MD5( const unsigned char *data
+ , unsigned long data_len
+ , unsigned char * digest);
+
+/*
+ * HMAC: Keyed-Hashing for Message Authentication
+ * return value: 0 - success
+ * <0 - fail
+ * Note: The digest buffer should be not less than 16.
+ */
+int HMAC( const unsigned char * key
+ , unsigned int key_len
+ , const unsigned char * data
+ , unsigned long data_len
+ , unsigned char * digest);
+
+#endif
diff --git a/include/clplumbing/mkstemp_mode.h b/include/clplumbing/mkstemp_mode.h
new file mode 100644
index 0000000..ff5f893
--- /dev/null
+++ b/include/clplumbing/mkstemp_mode.h
@@ -0,0 +1,34 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * A slightly safer version of mkstemp(3)
+ *
+ * In this version, the file is initially created mode 0, (using umask) and
+ * then chmod-ed to the requested permissions after calling mkstemp(3).
+ * This guarantees that the file is not even momentarily open beyond the
+ * requested permissions.
+ *
+ * Return values:
+ *
+ * Like mkstemp, it returns the file descriptor of the open file, or -1
+ * on error.
+ *
+ * In addition to the errno values documented for mkstemp(3), this functio
+ * can also fail with any of the errno values documented for chmod(2).
+ *
+ */
+int mkstemp_mode(char* template, mode_t requested_filemode);
diff --git a/include/clplumbing/netstring.h b/include/clplumbing/netstring.h
new file mode 100644
index 0000000..ef24e8f
--- /dev/null
+++ b/include/clplumbing/netstring.h
@@ -0,0 +1,48 @@
+/*
+ * Intracluster message object (struct ha_msg)
+ *
+ * Copyright (C) 1999, 2000 Guochun Shi<gshi@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+#ifndef NET_STRING_H
+#define NET_STRING_H
+#include <stdlib.h>
+#include <stdio.h>
+#include <ha_msg.h>
+
+extern gboolean cl_msg_quiet_fmterr;
+
+/* Convert a message to netstring data */
+char* msg2netstring(const struct ha_msg*, size_t*);
+char * msg2netstring_noauth(const struct ha_msg *m, size_t * slen);
+
+/* Convert netstring data to a message */
+struct ha_msg * netstring2msg(const char*, size_t, int);
+
+/* Is this netstring authentic? */
+int is_auth_netstring(const char* datap, size_t datalen,
+ const char* authstring, size_t authlen);
+
+void cl_set_authentication_computation_method(int (*method)(int authmethod
+, const void * data
+, size_t datalen
+, char * authstr
+, size_t authlen));
+
+#endif
diff --git a/include/clplumbing/proctrack.h b/include/clplumbing/proctrack.h
new file mode 100644
index 0000000..975ff1b
--- /dev/null
+++ b/include/clplumbing/proctrack.h
@@ -0,0 +1,120 @@
+/*
+ * Process tracking object.
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#ifndef _PROCTRACK_H
+# define _PROCTRACK_H
+#include <sys/types.h>
+#include <sys/times.h>
+#include <clplumbing/longclock.h>
+
+/*
+ * We track processes, mainly so we can do something appropriate
+ * when they die, and find processes should we need to kill them...
+ */
+
+typedef struct _ProcTrack ProcTrack;
+typedef struct _ProcTrack_ops ProcTrack_ops;
+typedef struct _ProcTrackKillInfo ProcTrackKillInfo;
+
+/*
+ * The levels of logging possible for our process
+ */
+enum _ProcTrackLogType {
+ PT_LOGNONE = 2, /* Exits never automatically logged */
+ PT_LOGNORMAL, /* Automatically log abnormal exits */
+ PT_LOGVERBOSE /* Automatically log every exit */
+};
+typedef enum _ProcTrackLogType ProcTrackLogType;
+
+#define proctrack_pid(p) (p)->pid
+#define proctrack_data(p) (p)->privatedata
+#define reset_proctrack_data(p) (p)->privatedata = NULL
+#define proctrack_timedout(p) ((p)->timeoutseq > 0)
+
+struct _ProcTrack {
+ pid_t pid;
+ int isapgrp;
+ ProcTrackLogType loglevel;
+ void * privatedata;
+ ProcTrack_ops* ops;
+
+ longclock_t startticks;
+ TIME_T starttime;
+ unsigned timerid;
+ int timeoutseq;
+ ProcTrackKillInfo* killinfo;
+};
+
+/*
+ * The set of operations to perform on our tracked processes.
+ */
+struct _ProcTrack_ops {
+
+ /* Called when a process dies */
+ void (*procdied)
+ (ProcTrack* p, int status, int signo, int exitcode
+ , int waslogged);
+
+ /* Called when a process registers */
+ void (*procregistered)
+ (ProcTrack*p);
+
+ /* Returns a "name" for a process (for messages) */
+ /* (may have to be copied, because it may be a static value) */
+ const char *
+ (*proctype)
+ (ProcTrack* p);
+};
+
+struct _ProcTrackKillInfo {
+ long mstimeout; /* Timeout in milliseconds */
+ int signalno; /* Signal number to issue @ timeout */
+};
+
+/* A function for calling by the process table iterator */
+typedef void (*ProcTrackFun) (ProcTrack* p, void * data);
+
+/* Call this function to activate the procdied member function */
+/* Returns TRUE if 'pid' was registered */
+int ReportProcHasDied(int pid, int status);
+
+/* Create/Log a new tracked process */
+void NewTrackedProc(pid_t pid, int isapgrp, ProcTrackLogType loglevel
+, void * privatedata , ProcTrack_ops* ops);
+
+/* "info" is 0-terminated (terminated by a 0 signal) */
+int SetTrackedProcTimeouts(pid_t pid, ProcTrackKillInfo* info);
+void RemoveTrackedProcTimeouts(pid_t pid);
+
+/* Return information associated with the given PID (or NULL) */
+ProcTrack* GetProcInfo(pid_t pid);
+
+/*
+ * Iterate over the set of tracked processes.
+ * If proctype is NULL, then walk through them all, otherwise only those
+ * of the given type ("f")
+ */
+void ForEachProc(ProcTrack_ops* proctype, ProcTrackFun f, void * data);
+
+void DisableProcLogging(void); /* Useful for shutdowns */
+void EnableProcLogging(void);
+#endif
diff --git a/include/clplumbing/realtime.h b/include/clplumbing/realtime.h
new file mode 100644
index 0000000..45eb76c
--- /dev/null
+++ b/include/clplumbing/realtime.h
@@ -0,0 +1,65 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CLPLUMBING_REALTIME_H
+# define _CLPLUMBING_REALTIME_H
+# include <sched.h>
+
+#if defined(SCHED_RR) && defined(_POSIX_PRIORITY_SCHEDULING) && !defined(ON_DARWIN)
+# define DEFAULT_REALTIME_POLICY SCHED_RR
+#endif
+
+/*
+ *
+ * make_realtime() will make the current process a soft realtime process
+ * and lock it into memory after growing the heap by heapgrowK*1024 bytes
+ *
+ * If you set spolicy or priority to <= 0, then defaults will be used.
+ * Otherwise you need to use a value for spolicy from <sched.h>
+ * and use an appropriate priority for the given spolicy.
+ *
+ * WARNING: badly behaved programs which use the make_realtime() function
+ * can easily hang the machine.
+ */
+
+void cl_make_realtime
+( int spolicy, /* SCHED_RR or SCHED_FIFO (or SCHED_OTHER) */
+ int priority, /* typically 1-99 */
+ int stackgrowK, /* Amount to grow stack by */
+ int heapgrowK /* Amount to grow heap by */
+);
+
+void cl_make_normaltime(void);
+
+/* Cause calls to make_realtime() to be ignored */
+void cl_disable_realtime(void);
+
+/* Cause calls to make_realtime() to be accepted.
+ * This is the default behaviour */
+void cl_enable_realtime(void);
+
+/* Sleep a really short (the shortest) time */
+int cl_shortsleep(void);
+
+/* Print messages if we've done (more) non-realtime mallocs */
+void cl_realtime_malloc_check(void);
+
+/* Number of times we "go to the well" for memory after becoming realtime */
+int cl_nonrealtime_malloc_count(void);
+/* Number of bytes we "got from the well" for memory after becoming realtime */
+unsigned long cl_nonrealtime_malloc_size(void);
+
+#endif
diff --git a/include/clplumbing/replytrack.h b/include/clplumbing/replytrack.h
new file mode 100644
index 0000000..f98fe48
--- /dev/null
+++ b/include/clplumbing/replytrack.h
@@ -0,0 +1,208 @@
+/*
+ * Process tracking object.
+ *
+ * Copyright (c) 2007 Alan Robertson
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#ifndef _REPLYTRACK_H
+# define _REPLYTRACK_H
+#include <sys/types.h>
+#include <sys/times.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/cl_uuid.h>
+
+/*
+ * We track replies - so we can tell when all expected replies were received.
+ *
+ * There is a problem in clusters where a message is sent to each node, and a
+ * reply is expected from each node of knowing when all the replies have been
+ * received.
+ *
+ * If all nodes are up, it's easy to see when all replies are received.
+ * But, if some nodes are down, we really don't want to wait for a timeout
+ * before we decide that we've gotten all the replies we're going to get,
+ * since nodes can be down for potentially very long periods of time, and
+ * waiting for a long timeout can delay things a great deal again and
+ * again - causing significant delays and user frustration.
+ *
+ * That's where these functions come in!
+ * Instead, inform these functions what nodes are up and what ones are down,
+ * and when you receive a reply, and it will tell you when you've gotten
+ * them all - managing all that tedious bookwork for you.
+ */
+
+typedef enum _replytrack_completion_type replytrack_completion_type_t;
+typedef enum _nodetrack_change nodetrack_change_t;
+typedef struct _replytrack replytrack_t;
+typedef struct _nodetrack nodetrack_t;
+typedef struct _nodetrack_intersection nodetrack_intersection_t;
+
+/*
+ * The levels of logging possible for our process
+ */
+enum _replytrack_completion_type {
+ REPLYT_ALLRCVD = 2, /* All replies received */
+ REPLYT_TIMEOUT, /* Timeout occurred with replies missing */
+};
+
+
+typedef void (*replytrack_callback_t)
+( replytrack_t * rl
+, gpointer user_data
+, replytrack_completion_type_t reason);
+
+typedef void (*replytrack_iterator_t)
+( replytrack_t* rl
+, gpointer user_data
+, const char* node
+, cl_uuid_t uuid);
+
+typedef void (*nodetrack_iterator_t)
+( nodetrack_t* rl
+, gpointer user_data
+, const char* node
+, cl_uuid_t uuid);
+
+
+/*
+ * Note:
+ * If you use the timeout feature of this code, it relies on you using glib mainloop
+ * for your scheduling. timeout_ms should be zero for no timeout.
+ */
+replytrack_t* replytrack_new(nodetrack_t* membership
+, replytrack_callback_t callback
+, unsigned long timeout_ms
+, gpointer user_data);
+
+void replytrack_del(replytrack_t *rl);
+gboolean replytrack_gotreply(replytrack_t *rl
+, const char * node
+, cl_uuid_t uuid);
+ /* Returns TRUE if this was the final expected reply */
+/*
+ * Iterate over the set of outstanding replies:
+ * return count of how many items in the iteration
+ */
+int replytrack_outstanding_iterate(replytrack_t* rl
+, replytrack_iterator_t i, gpointer user_data);
+int replytrack_outstanding_count(replytrack_t* rl);
+
+/*
+ * The functions above operate using a view of membership which is established
+ * through the functions below.
+ *
+ * This can either be through the heartbeat low-level membership API, or any
+ * other view of membership you wish. Mentioning a node as either up or down
+ * will automatically add that node to our view of potential membership.
+ *
+ * These functions only support one view of membership per process.
+ *
+ * The general idea of how to use these functions:
+ * Initially:
+ * 1) iterate through init membership and call nodetrack_node(up|down) for
+ * each node to start things off.
+ *
+ * On an ongoing basis:
+ * 2) call nodetrack_node_up whenever a node comes up
+ * We expect a reply from nodes that are up.
+ * 3) call nodetrack_node_down whenever a node goes down
+ * We don't expect a reply from nodes that are down.
+ *
+ * For each set of replies you want tracked:
+ * 4) Create a replytrack_t for a set of expected replies
+ * 5) call replytrack_gotreply() each time you get an expected reply
+ * 6) replist_gotreply() returns TRUE when the final message was received.
+ * (it does this by comparing against the membership as defined below)
+ * 7) you will get a callback when timeout occurs or final message is received
+ * n. b.:
+ * No callback function => manage timeouts yourself
+ * 8) call replytrack_del() when you're done with the reply list
+ * n. b.:
+ * If you have replies outstanding, and you have a timeout and
+ * a callback function set, you will get a warning for destroying
+ * a replytrack_t object 'prematurely'.
+ * You will also log a warning if you call replytrack_gotreply() after
+ * all replies were received or a timeout occurred.
+ *
+ */
+
+/*
+ * The levels of logging possible for our process
+ */
+enum _nodetrack_change {
+ NODET_UP = 2, /* This node came up */
+ NODET_DOWN, /* This node went down */
+};
+
+typedef void (*nodetrack_callback_t)
+( nodetrack_t * mbr
+, const char * node
+, cl_uuid_t u
+, nodetrack_change_t reason
+, gpointer user_data);
+
+nodetrack_t* nodetrack_new(nodetrack_callback_t callback
+, gpointer user_data);
+void nodetrack_del(nodetrack_t*);
+gboolean nodetrack_nodeup(nodetrack_t* mbr, const char * node
+, cl_uuid_t u);
+gboolean nodetrack_nodedown(nodetrack_t* mbr, const char * node
+, cl_uuid_t u);
+gboolean nodetrack_ismember(nodetrack_t* mbr, const char * node
+, cl_uuid_t u);
+int nodetrack_iterate(nodetrack_t* mbr
+, nodetrack_iterator_t i, gpointer user_data);
+
+/* An intesection nodetrack table
+ * A node is put into the "intersection" nodetrack_t table when it is in all
+ * the underlying constituent nodetrack_t tables, and removed when it is
+ * removed from any of them.
+ * Note that you can set a callback to be informed when these "intersection"
+ * membership changes occur.
+ */
+nodetrack_intersection_t*
+ nodetrack_intersection_new(nodetrack_t** tables, int ntables
+, nodetrack_callback_t callback, gpointer user_data);
+void nodetrack_intersection_del(nodetrack_intersection_t*);
+nodetrack_t* nodetrack_intersection_table(nodetrack_intersection_t*);
+
+#if 0
+/*
+ * I don't know if this should be in this library, or just in
+ * the CCM. Probably only the CCM _should_ be using it (when I write it)
+ */
+/*
+ * Use of the nodetrack_hb_* functions implies you're using the heartbeat
+ * peer-connectivity information as your source of information. This is
+ * really only suitable if you're using heartbeat's low-level group membership
+ * for your source of who to expect replies from.
+ * If you're using nodetrack_hb_init, this replaces step (1) above.
+ */
+void nodetrack_hb_init(void)
+/*
+ * If you're using nodetrack_hb_statusmsg, just pass it all status messages
+ * and all peer-connectivity status messages or even all heartbeat messages
+ * (non-status messages will be ignored).
+ * This replaces steps (2) and (3) above _if_ you're using heartbeat low
+ * level membership for your source of who to expect replies from.
+ */
+void nodetrack_hb_statusmsg(struct ha_msg* statusmsg);
+#endif /*0*/
+
+#endif
diff --git a/include/clplumbing/setproctitle.h b/include/clplumbing/setproctitle.h
new file mode 100644
index 0000000..5caeef0
--- /dev/null
+++ b/include/clplumbing/setproctitle.h
@@ -0,0 +1,64 @@
+/*
+ * setproctitle.h
+ *
+ * The code in this file, setproctitle.h is heavily based on code from
+ * proftpd, please see the licening information below.
+ *
+ * This file added to the heartbeat tree by Horms <horms@vergenet.net>
+ *
+ * Code to portably change the title of a programme as displayed
+ * by ps(1).
+ *
+ * heartbeat: Linux-HA heartbeat code
+ *
+ * Copyright (C) 1999,2000,2001 Alan Robertson <alanr@unix.sh>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * ProFTPD - FTP server daemon
+ * Copyright (c) 1997, 1998 Public Flood Software
+ * Copyright (C) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
+ * and other respective copyright holders give permission to link this program
+ * with OpenSSL, and distribute the resulting executable, without including
+ * the source code for OpenSSL in the source distribution.
+ */
+
+#ifndef _HA_SETPROCTITLE_H
+#define _HA_SETPROCTITLE_H
+
+#include <glib.h>
+int init_set_proc_title(int argc, char *argv[], char *envp[]);
+
+void set_proc_title(const char *fmt,...) G_GNUC_PRINTF(1,2);
+
+#endif /* _HA_SETPROCTITLE_H */
diff --git a/include/clplumbing/timers.h b/include/clplumbing/timers.h
new file mode 100644
index 0000000..669ac21
--- /dev/null
+++ b/include/clplumbing/timers.h
@@ -0,0 +1,23 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CLPLUMBING_TIMERS_H
+# define _CLPLUMBING_TIMERS_H
+int setmsrepeattimer(long ms);
+int setmsalarm(long ms);
+int cancelmstimer(void);
+long mssleep(long ms);
+#endif
diff --git a/include/clplumbing/uids.h b/include/clplumbing/uids.h
new file mode 100644
index 0000000..89ba303
--- /dev/null
+++ b/include/clplumbing/uids.h
@@ -0,0 +1,32 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef CLPLUMBING_UIDS_H
+# define CLPLUMBING_UIDS_H
+#include <sys/types.h>
+
+/* Tell us who you want to be - or zero for nobody */
+int drop_privs(uid_t uid, gid_t gid);
+
+/* Return to original privileged state */
+int return_to_orig_privs(void);
+
+/* Drop down to (probably nobody) privileges again */
+int return_to_dropped_privs(void);
+
+/* Return TRUE if we have full privileges at the moment */
+int cl_have_full_privs(void);
+#endif
diff --git a/include/compress.h b/include/compress.h
new file mode 100644
index 0000000..9cd733c
--- /dev/null
+++ b/include/compress.h
@@ -0,0 +1,50 @@
+/*
+ * compress.h: Compression functions for Linux-HA
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _COMPRESS_H_
+#define _COMPRESS_H_
+
+#define HB_COMPRESS_TYPE compress
+#define HB_COMPRESS_TYPE_S "compress"
+
+/*
+ * List of functions provided by implementations of the heartbeat
+ * compress interface.
+ */
+struct hb_compress_fns {
+ int (*compress) (char*, size_t*, const char*, size_t);
+ int (*decompress) (char*, size_t* , const char*, size_t);
+ const char* (*getname) (void);
+};
+
+struct ha_msg;
+
+/* set the compression method*/
+int cl_compress_remove_plugin(const char* pluginname);
+int cl_compress_load_plugin(const char* pluginname);
+struct hb_compress_fns* cl_get_compress_fns(void);
+int cl_set_compress_fns(const char*);
+char* cl_compressmsg(struct ha_msg*m, size_t* len);
+struct ha_msg* cl_decompressmsg(struct ha_msg* m);
+gboolean is_compressed_msg(struct ha_msg* m);
+int cl_compress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen);
+int cl_decompress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen);
+
+#endif
diff --git a/include/glue_config.h.in b/include/glue_config.h.in
new file mode 100644
index 0000000..0850a63
--- /dev/null
+++ b/include/glue_config.h.in
@@ -0,0 +1,104 @@
+/* include/config.h.in. Generated from configure.in by autoheader. */
+
+/* Location for daemons */
+#undef GLUE_DAEMON_DIR
+
+/* Group to run daemons as */
+#undef GLUE_DAEMON_GROUP
+
+/* User to run daemons as */
+#undef GLUE_DAEMON_USER
+
+/* Where to keep state files and sockets */
+#undef GLUE_STATE_DIR
+
+/* Location of shared data */
+#undef GLUE_SHARED_DIR
+
+/* User to run daemons as */
+#undef HA_CCMUSER
+
+/* Group to run daemons as */
+#undef HA_APIGROUP
+
+/* Location for daemons */
+#undef HA_LIBHBDIR
+
+/* top directory of area to drop core files in */
+#undef HA_COREDIR
+
+/* top directory for LRM related files */
+#undef LRM_VARLIBDIR
+
+/* CIB secrets */
+#undef LRM_CIBSECRETS
+
+/* Logging Daemon IPC socket name */
+#undef HA_LOGDAEMON_IPC
+
+/* Default logging facility */
+#undef HA_LOG_FACILITY
+
+/* Default plugin search path */
+#undef PILS_BASE_PLUGINDIR
+
+/* Where to find plugins */
+#undef HA_PLUGIN_DIR
+
+/* Location of system configuration files */
+#undef HA_SYSCONFDIR
+
+/* Web site base URL */
+#undef HA_URLBASE
+
+/* Whatever this used to mean */
+#undef HA_VARLIBHBDIR
+
+#undef HA_VARLIBDIR
+
+/* System lock directory */
+#undef HA_VARLOCKDIR
+
+/* Where Heartbeat keeps state files and sockets - old name */
+#undef HA_VARRUNDIR
+
+/* Location for v1 Heartbeat RAs */
+#undef HB_RA_DIR
+
+/* Where to find LRM plugins */
+#undef LRM_PLUGIN_DIR
+
+/* Location for LSB RAs */
+#undef LSB_RA_DIR
+
+/* Location for OCF RAs */
+#undef OCF_RA_DIR
+
+/* OCF root directory - specified by the OCF standard */
+#undef OCF_ROOT_DIR
+
+/* Compiling for Darwin platform */
+#undef ON_DARWIN
+
+/* Compiling for Linux platform */
+#undef ON_LINUX
+
+/* Current glue version */
+#undef GLUE_VERSION
+
+/* Build version */
+#undef GLUE_BUILD_VERSION
+
+/* Location of non-pluing stonith scripts */
+#undef STONITH_EXT_PLUGINDIR
+
+/* Location of RHCS stonith scripts */
+#undef STONITH_RHCS_PLUGINDIR
+
+/* Location of stonith plugins */
+#undef STONITH_MODULES
+
+/* Stonith plugin domain */
+#undef ST_TEXTDOMAIN
+
+#undef HA_HBCONF_DIR
diff --git a/include/ha_msg.h b/include/ha_msg.h
new file mode 100644
index 0000000..fcb6cf6
--- /dev/null
+++ b/include/ha_msg.h
@@ -0,0 +1,464 @@
+/*
+ * Intracluster message object (struct ha_msg)
+ *
+ * Copyright (C) 1999, 2000 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+#ifndef _HA_MSG_H
+# define _HA_MSG_H 1
+#include <stdio.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/cl_uuid.h>
+#include <compress.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+enum cl_netstring_type{
+ FT_STRING = 0,
+ FT_BINARY,
+ FT_STRUCT,
+ FT_LIST,
+ FT_COMPRESS,
+ FT_UNCOMPRESS
+};
+
+enum cl_msgfmt{
+ MSGFMT_NVPAIR,
+ MSGFMT_NETSTRING
+};
+
+
+#define NEEDHEAD 1
+#define NOHEAD 0
+#define HA_MSG_ASSERT(X) do{ if(!(X)){ \
+ cl_log(LOG_ERR, "Assertion failed on line %d in file \"%s\"" \
+ , __LINE__, __FILE__); \
+ abort(); \
+ } \
+ }while(0)
+
+typedef struct hb_msg_stats_s {
+ unsigned long totalmsgs; /* Total # of messages */
+ /* ever handled */
+ unsigned long allocmsgs; /* # Msgs currently allocated */
+ longclock_t lastmsg;
+}hb_msg_stats_t;
+
+struct ha_msg {
+ int nfields;
+ int nalloc;
+ char ** names;
+ size_t* nlens;
+ void ** values;
+ size_t* vlens;
+ int * types;
+};
+
+typedef struct ha_msg HA_Message;
+
+struct fieldtypefuncs_s{
+
+ /* memfree frees the memory involved*/
+ void (*memfree)(void*);
+
+ /* dup makes a complete copy of the field*/
+ void* (*dup)(const void*, size_t);
+
+ /* display printout the field*/
+ void (*display)(int, int, char* , void*, int);
+
+ /* add the field into a message*/
+ int (*addfield) (struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth);
+
+ /* return the string length required to add this field*/
+ int (*stringlen) (size_t namlen, size_t vallen, const void* value);
+
+ /* return the netstring length required to add this field*/
+ int (*netstringlen) (size_t namlen, size_t vallen, const void* value);
+
+ /* print the field into the provided buffer, convert it first */
+ /* if ncecessary*/
+ int (*tostring)(char*, char*, void* ,size_t,int);
+
+ /* print the field into the provided buffer*/
+ int (*tonetstring)(char*, char*, char*, size_t,
+ void*, size_t, int, size_t*);
+
+ /* convert the given string to a field
+ note: this functions involves allocate memory for
+ for the field
+ */
+ int (*stringtofield)(void*, size_t, int depth, void**, size_t* );
+
+ /* convert the given netstring to a field
+ note: this functions involves allocate memory for
+ for the field
+ */
+ int (*netstringtofield)(const void*, size_t, void**, size_t*);
+
+ /* action before packing*/
+ int (*prepackaction)(struct ha_msg* m, int index);
+
+ /* action before a user get the value of a field*/
+ int (*pregetaction)(struct ha_msg* m, int index);
+
+};
+
+#define NUM_MSG_TYPES 6
+extern struct fieldtypefuncs_s fieldtypefuncs[NUM_MSG_TYPES];
+
+#define MSG_NEEDAUTH 0x01
+#define MSG_ALLOWINTR 0X02
+#define MSG_NEEDCOMPRESS 0x04
+#define MSG_NOSIZECHECK 0x08
+
+#define IFACE "!^!\n"
+#define MSG_START ">>>\n"
+#define MSG_END "<<<\n"
+#define MSG_START_NETSTRING "###\n"
+#define MSG_END_NETSTRING "%%%\n"
+#define EQUAL "="
+
+#define MAXDEPTH 16 /* Maximum recursive message depth */
+#define MAXLENGTH 1024
+
+ /* Common field names for our messages */
+#define F_TYPE "t" /* Message type */
+#define F_SUBTYPE "subt" /* Message type */
+#define F_ORIG "src" /* Real Originator */
+#define F_ORIGUUID "srcuuid" /* Real Originator uuid*/
+#define F_NODE "node" /* Node being described */
+#define F_NODELIST "nodelist" /* Node list being described */
+#define F_DELNODELIST "delnodelist" /* Del node list being described */
+#define F_TO "dest" /* Destination (optional) */
+#define F_TOUUID "destuuid" /* Destination uuid(optional) */
+#define F_STATUS "st" /* New status (type = status) */
+#define F_WEIGHT "weight" /* weight of node */
+#define F_SITE "site" /* site of node */
+#define F_PROTOCOL "protocol" /* Protocol number for communication*/
+#define F_CLIENTNAME "cn" /* Client name */
+#define F_CLIENTSTATUS "cs" /* Client status */
+#define F_TIME "ts" /* Timestamp */
+#define F_SEQ "seq" /* Sequence number */
+#define F_LOAD "ld" /* Load average */
+#define F_COMMENT "info" /* Comment */
+#define F_TTL "ttl" /* Time To Live */
+#define F_AUTH "auth" /* Authentication string */
+#define F_HBGENERATION "hg" /* Heartbeat generation number */
+#define F_CLIENT_GENERATION "client_gen" /* client generation number*/
+#define F_FIRSTSEQ "firstseq" /* Lowest seq # to retransmit */
+#define F_LASTSEQ "lastseq" /* Highest seq # to retransmit */
+#define F_RESOURCES "rsc_hold" /* What resources do we hold? */
+#define F_FROMID "from_id" /* from Client id */
+#define F_TOID "to_id" /* To client id */
+#define F_PID "pid" /* PID of client */
+#define F_UID "uid" /* uid of client */
+#define F_GID "gid" /* gid of client */
+#define F_ISSTABLE "isstable" /* true/false for RESOURCES */
+#define F_APIREQ "reqtype" /* API request type for "hbapi" */
+#define F_APIRESULT "result" /* API request result code */
+#define F_IFNAME "ifname" /* Interface name */
+#define F_PNAME "pname" /* Parameter name */
+#define F_PVALUE "pvalue" /* Parameter name */
+#define F_DEADTIME "deadtime" /* Dead time interval in ms. */
+#define F_KEEPALIVE "keepalive" /* Keep alive time interval in ms. */
+#define F_LOGFACILITY "logfacility" /* Suggested cluster syslog facility */
+#define F_NODETYPE "nodetype" /* Type of node */
+#define F_NUMNODES "numnodes" /* num of total nodes(excluding ping nodes*/
+#define F_RTYPE "rtype" /* Resource type */
+#define F_ORDERSEQ "oseq" /* Order Sequence number */
+#define F_DT "dt" /* Dead time field for heartbeat*/
+#define F_ACKSEQ "ackseq" /* The seq number this msg is acking*/
+#define F_CRM_DATA "crm_xml"
+#define F_XML_TAGNAME "__name__"
+#define F_STATE "state" /*used in ccm for state info*/
+
+
+ /* Message types */
+#define T_STATUS "status" /* Status (heartbeat) */
+#define T_IFSTATUS "ifstat" /* Interface status */
+#define T_ASKRESOURCES "ask_resources" /* Let other node ask my resources */
+#define T_ASKRELEASE "ip-request" /* Please give up these resources... */
+#define T_ACKRELEASE "ip-request-resp"/* Resources given up... */
+#define T_QCSTATUS "query-cstatus" /* Query client status */
+#define T_RCSTATUS "respond-cstatus"/* Respond client status */
+#define T_STONITH "stonith" /* Stonith return code */
+#define T_SHUTDONE "shutdone" /* External Shutdown complete */
+#define T_CRM "crmd" /* Cluster resource manager message */
+#define T_ATTRD "attrd" /* Cluster resource manager message */
+#define T_ADDNODE "addnode" /* Add node message*/
+#define T_DELNODE "delnode" /* Delete node message*/
+#define T_SETWEIGHT "setweight" /* Set node weight*/
+#define T_SETSITE "setsite" /* Set node site*/
+#define T_REQNODES "reqnodes" /* Request node list */
+#define T_REPNODES "repnodes" /* reply node list rquest*/
+
+#define T_APIREQ "hbapi-req" /* Heartbeat API request */
+#define T_APIRESP "hbapi-resp" /* Heartbeat API response */
+#define T_APICLISTAT "hbapi-clstat" /* Client status notification" */
+
+#define NOSEQ_PREFIX "NS_" /* PREFIX: Give no sequence number */
+ /* Used for messages which can't be retransmitted */
+ /* Either they're protocol messages or from dumb (ping) endpoints */
+#define T_REXMIT NOSEQ_PREFIX "rexmit" /* Rexmit request */
+#define T_NAKREXMIT NOSEQ_PREFIX "nak_rexmit" /* NAK Rexmit request */
+#define T_NS_STATUS NOSEQ_PREFIX "st" /* ping status */
+#define T_ACKMSG NOSEQ_PREFIX "ackmsg" /* ACK message*/
+
+/* Messages associated with nice_failback */
+#define T_STARTING "starting" /* Starting Heartbeat */
+ /* (requesting resource report) */
+#define T_RESOURCES "resource" /* Resources report */
+
+/* Messages associated with stonith completion results */
+#define T_STONITH_OK "OK" /* stonith completed successfully */
+#define T_STONITH_BADHOST "badhost" /* stonith failed */
+#define T_STONITH_BAD "bad" /* stonith failed */
+#define T_STONITH_NOTCONFGD "n_stnth" /* no stonith device configured */
+#define T_STONITH_UNNEEDED "unneeded" /* STONITH not required */
+
+/* Set up message statistics area */
+
+int netstring_extra(int);
+int cl_msg_stats_add(longclock_t time, int size);
+
+void cl_msg_setstats(volatile hb_msg_stats_t* stats);
+void cl_dump_msgstats(void);
+void cl_set_compression_threshold(size_t threadhold);
+void cl_set_traditional_compression(gboolean value);
+
+/* Allocate new (empty) message */
+struct ha_msg * ha_msg_new(int nfields);
+
+/* Free message */
+void ha_msg_del(struct ha_msg *msg);
+
+/* Copy message */
+struct ha_msg* ha_msg_copy(const struct ha_msg *msg);
+
+int ha_msg_expand(struct ha_msg* msg );
+
+/*Add a null-terminated name and binary value to a message*/
+int ha_msg_addbin(struct ha_msg * msg, const char * name,
+ const void * value, size_t vallen);
+
+int ha_msg_adduuid(struct ha_msg * msg, const char * name,
+ const cl_uuid_t* uuid);
+
+/* Add null-terminated name and a value to the message */
+int ha_msg_add(struct ha_msg * msg
+ , const char* name, const char* value);
+
+int cl_msg_remove(struct ha_msg* msg, const char* name);
+int cl_msg_remove_value(struct ha_msg* msg, const void* value);
+int cl_msg_remove_offset(struct ha_msg* msg, int offset);
+
+/* Modify null-terminated name and a value to the message */
+int cl_msg_modstring(struct ha_msg * msg,
+ const char* name,
+ const char* value);
+int cl_msg_modbin(struct ha_msg * msg,
+ const char* name,
+ const void* value,
+ size_t vlen);
+
+int cl_msg_moduuid(struct ha_msg * msg, const char * name,
+ const cl_uuid_t* uuid);
+
+int cl_msg_modstruct(struct ha_msg * msg,
+ const char* name,
+ const struct ha_msg* value);
+#define ha_msg_mod(msg, name, value) cl_msg_modstring(msg, name, value)
+int cl_msg_replace(struct ha_msg* msg, int index,
+ const void* value, size_t vlen, int type);
+int cl_msg_replace_value(struct ha_msg* msg, const void *old_value,
+ const void* value, size_t vlen, int type);
+
+/* Add name, value (with known lengths) to the message */
+int ha_msg_nadd(struct ha_msg * msg, const char * name, int namelen
+ , const char * value, int vallen);
+
+/* Add a name/value/type to a message (with sizes for name and value) */
+int ha_msg_nadd_type(struct ha_msg * msg, const char * name, int namelen
+ , const char * value, int vallen, int type);
+
+/* Add name=value string to a message */
+int ha_msg_add_nv(struct ha_msg* msg, const char * nvline, const char * bufmax);
+
+
+/* Return value associated with particular name */
+#define ha_msg_value(m,name) cl_get_string(m, name)
+
+/* Call wait(in|out) but only for a finite time */
+int cl_ipc_wait_timeout(
+ IPC_Channel * chan, int (*waitfun)(IPC_Channel * chan), unsigned int timeout);
+
+/* Reads an IPC stream -- converts it into a message */
+struct ha_msg * msgfromIPC_timeout(IPC_Channel *ch, int flag, unsigned int timeout, int *rc_out);
+struct ha_msg * msgfromIPC(IPC_Channel * f, int flag);
+
+IPC_Message * ipcmsgfromIPC(IPC_Channel * ch);
+
+/* Reads a stream -- converts it into a message */
+struct ha_msg * msgfromstream(FILE * f);
+
+/* Reads a stream with string format--converts it into a message */
+struct ha_msg * msgfromstream_string(FILE * f);
+
+/* Reads a stream with netstring format--converts it into a message */
+struct ha_msg * msgfromstream_netstring(FILE * f);
+
+/* Same as above plus copying the iface name to "iface" */
+struct ha_msg * if_msgfromstream(FILE * f, char *iface);
+
+/* Writes a message into a stream */
+int msg2stream(struct ha_msg* m, FILE * f);
+
+/* Converts a message into a string and adds the iface name on start */
+char * msg2if_string(const struct ha_msg *m, const char * iface);
+
+/* Converts a string gotten via UDP into a message */
+struct ha_msg * string2msg(const char * s, size_t length);
+
+/* Converts a message into a string */
+char * msg2string(const struct ha_msg *m);
+
+/* Converts a message into a string in the provided buffer with certain
+depth and with or without start/end */
+int msg2string_buf(const struct ha_msg *m, char* buf,
+ size_t len, int depth, int needhead);
+
+/* Converts a message into wire format */
+char* msg2wirefmt(struct ha_msg *m, size_t* );
+char* msg2wirefmt_noac(struct ha_msg*m, size_t* len);
+
+/* Converts wire format data into a message */
+struct ha_msg* wirefmt2msg(const char* s, size_t length, int flag);
+
+/* Convets wire format data into an IPC message */
+IPC_Message* wirefmt2ipcmsg(void* p, size_t len, IPC_Channel* ch);
+
+/* Converts an ha_msg into an IPC message */
+IPC_Message* hamsg2ipcmsg(struct ha_msg* m, IPC_Channel* ch);
+
+/* Converts an IPC message into an ha_msg */
+struct ha_msg* ipcmsg2hamsg(IPC_Message*m);
+
+/* Outputs a message to an IPC channel */
+int msg2ipcchan(struct ha_msg*m, IPC_Channel*ch);
+
+/* Outpus a message to an IPC channel without authencating
+the message */
+struct ha_msg* msgfromIPC_noauth(IPC_Channel * ch);
+
+/* Reads from control fifo, and creates a new message from it */
+/* This adds the default sequence#, load avg, etc. to the message */
+struct ha_msg * controlfifo2msg(FILE * f);
+
+/* Check if the message is authenticated */
+gboolean isauthentic(const struct ha_msg * msg);
+
+/* Get the required string length for the given message */
+int get_stringlen(const struct ha_msg *m);
+
+/* Get the requried netstring length for the given message*/
+int get_netstringlen(const struct ha_msg *m);
+
+/* Add a child message to a message as a field */
+int ha_msg_addstruct(struct ha_msg * msg, const char * name, const void* ptr);
+
+int ha_msg_addstruct_compress(struct ha_msg*, const char*, const void*);
+
+/* Get binary data from a message */
+const void * cl_get_binary(const struct ha_msg *msg, const char * name, size_t * vallen);
+
+/* Get uuid data from a message */
+int cl_get_uuid(const struct ha_msg *msg, const char * name, cl_uuid_t* retval);
+
+/* Get string data from a message */
+const char * cl_get_string(const struct ha_msg *msg, const char *name);
+
+/* Get the type for a field from a message */
+int cl_get_type(const struct ha_msg *msg, const char *name);
+
+/* Get a child message from a message*/
+struct ha_msg *cl_get_struct(struct ha_msg *msg, const char* name);
+
+/* Log the contents of a message */
+void cl_log_message (int log_level, const struct ha_msg *m);
+
+/* Supply messaging system with old style authentication/authorization method */
+void cl_set_oldmsgauthfunc(gboolean (*authfunc)(const struct ha_msg*));
+
+/* Set default messaging format */
+void cl_set_msg_format(enum cl_msgfmt mfmt);
+
+/* Add a string to a list*/
+int cl_msg_list_add_string(struct ha_msg* msg, const char* name, const char* value);
+
+/* Return length of a list*/
+int cl_msg_list_length(struct ha_msg* msg, const char* name);
+
+/* Return nth element of a list*/
+void* cl_msg_list_nth_data(struct ha_msg* msg, const char* name, int n);
+
+/* Functions to add/mod/get an integer */
+int ha_msg_add_int(struct ha_msg * msg, const char * name, int value);
+int ha_msg_mod_int(struct ha_msg * msg, const char * name, int value);
+int ha_msg_value_int(const struct ha_msg * msg, const char * name, int* value);
+
+/* Functions to add/mod/get an unsigned long */
+int ha_msg_add_ul(struct ha_msg * msg, const char * name, unsigned long value);
+int ha_msg_mod_ul(struct ha_msg * msg, const char * name, unsigned long value);
+int ha_msg_value_ul(const struct ha_msg * msg, const char * name, unsigned long* value);
+
+/* Functions to add/get a string list*/
+GList* ha_msg_value_str_list(struct ha_msg * msg, const char * name);
+
+int cl_msg_add_list_int(struct ha_msg* msg, const char* name,
+ int* buf, size_t n);
+int cl_msg_get_list_int(struct ha_msg* msg, const char* name,
+ int* buf, size_t* n);
+GList* cl_msg_get_list(struct ha_msg* msg, const char* name);
+int cl_msg_add_list(struct ha_msg* msg, const char* name, GList* list);
+int cl_msg_add_list_str(struct ha_msg* msg, const char* name,
+ char** buf, size_t n);
+
+/* Function to add/get a string hash table*/
+GHashTable* ha_msg_value_str_table(struct ha_msg * msg, const char * name);
+int ha_msg_add_str_table(struct ha_msg * msg, const char * name,
+ GHashTable* hash_table);
+int ha_msg_mod_str_table(struct ha_msg * msg, const char * name,
+ GHashTable* hash_table);
+
+/*internal use for list type*/
+size_t string_list_pack_length(const GList* list);
+int string_list_pack(GList* list, char* buf, char* maxp);
+GList* string_list_unpack(const char* packed_str_list, size_t length);
+void list_cleanup(GList* list);
+
+gboolean must_use_netstring(const struct ha_msg*);
+
+
+#endif /* __HA_MSG_H */
diff --git a/include/lha_internal.h b/include/lha_internal.h
new file mode 100644
index 0000000..bae10a0
--- /dev/null
+++ b/include/lha_internal.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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 *
+ */
+
+#ifndef LHA_INTERNAL_H
+# define LHA_INTERNAL_H
+
+#define EOS '\0'
+#define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) )
+#define STRLEN_CONST(conststr) ((size_t)((sizeof(conststr)/sizeof(char))-1))
+#define STRNCMP_CONST(varstr, conststr) strncmp((varstr), conststr, STRLEN_CONST(conststr)+1)
+#define STRLEN(c) STRLEN_CONST(c)
+#define MALLOCT(t) ((t *) malloc(sizeof(t)))
+
+#define HADEBUGVAL "HA_DEBUG" /* current debug value (if nonzero) */
+#define HALOGD "HA_LOGD" /* whether we use logging daemon or not */
+
+/* Needs to be defined before any other includes, otherwise some system
+ * headers do not behave as expected! Major black magic... */
+#undef _GNU_SOURCE /* in case it was defined on the command line */
+#define _GNU_SOURCE
+
+/* Please leave this as the first #include - Solaris needs it there */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/param.h>
+#ifdef BSD
+# define SCANSEL_CAST (void *)
+#else
+# define SCANSEL_CAST /* Nothing */
+#endif
+
+#if defined(ANSI_ONLY) && !defined(inline)
+# define inline /* nothing */
+# undef NETSNMP_ENABLE_INLINE
+# define NETSNMP_NO_INLINE 1
+#endif
+
+#ifndef HAVE_DAEMON
+ /* We supply a replacement function, but need a prototype */
+int daemon(int nochdir, int noclose);
+#endif /* HAVE_DAEMON */
+
+#ifndef HAVE_SETENV
+ /* We supply a replacement function, but need a prototype */
+int setenv(const char *name, const char * value, int why);
+#endif /* HAVE_SETENV */
+
+#ifndef HAVE_UNSETENV
+ /* We supply a replacement function, but need a prototype */
+int unsetenv(const char *name);
+#endif /* HAVE_UNSETENV */
+
+#ifndef HAVE_STRERROR
+ /* We supply a replacement function, but need a prototype */
+char * strerror(int errnum);
+#endif /* HAVE_STRERROR */
+
+#ifndef HAVE_SCANDIR
+ /* We supply a replacement function, but need a prototype */
+# include <dirent.h>
+int
+scandir (const char *directory_name,
+ struct dirent ***array_pointer,
+ int (*select_function) (const struct dirent *),
+#ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
+ /* This is what the Linux man page says */
+ int (*compare_function) (const struct dirent**, const struct dirent**)
+#else
+ /* This is what the Linux header file says ... */
+ int (*compare_function) (const void *, const void *)
+#endif
+ );
+#endif /* HAVE_SCANDIR */
+
+#ifndef HAVE_ALPHASORT
+# include <dirent.h>
+int
+alphasort(const void *dirent1, const void *dirent2);
+#endif /* HAVE_ALPHASORT */
+
+#ifndef HAVE_INET_PTON
+ /* We supply a replacement function, but need a prototype */
+int
+inet_pton(int af, const char *src, void *dst);
+
+#endif /* HAVE_INET_PTON */
+
+#ifndef HAVE_STRNLEN
+ size_t strnlen(const char *s, size_t maxlen);
+#else
+# define USE_GNU
+#endif
+
+#ifndef HAVE_STRNDUP
+ char *strndup(const char *str, size_t len);
+#else
+# define USE_GNU
+#endif
+#ifndef HAVE_STRLCPY
+ size_t strlcpy(char * dest, const char *source, size_t len);
+#endif
+#ifndef HAVE_STRLCAT
+ size_t strlcat(char * dest, const char *source, size_t len);
+#endif
+
+#ifndef HAVE_NFDS_T
+ typedef unsigned int nfds_t;
+#endif
+
+#ifdef HAVE_STRUCT_UCRED_DARWIN
+# include <sys/utsname.h>
+# ifndef SYS_NMLN
+# define SYS_NMLN _SYS_NAMELEN
+# endif /* SYS_NMLN */
+#endif
+
+#define POINTER_TO_SIZE_T(p) ((size_t)(p)) /*pointer cast as size_t*/
+#define POINTER_TO_SSIZE_T(p) ((ssize_t)(p)) /*pointer cast as ssize_t*/
+#define POINTER_TO_ULONG(p) ((unsigned long)(p)) /*pointer cast as unsigned long*/
+ /* Sometimes we get a const g_something *, but need to pass it internally
+ * to other functions taking a non-const g_something *, which results
+ * with gcc and -Wcast-qual in a compile time warning, and with -Werror
+ * even to a compile time error.
+ * Workarounds have been to e.g. memcpy(&list, _list); or similar,
+ * the reason of which is non-obvious to the casual reader.
+ * This macro achieves the same, and annotates why it is done.
+ */
+#define UNCONST_CAST_POINTER(t, p) ((t)(unsigned long)(p))
+
+#define HAURL(url) HA_URLBASE url
+
+/*
+ * Some compilers may not have defined __FUNCTION__.
+ */
+#ifndef __FUNCTION__
+
+/* Sun studio compiler */
+# ifdef __SUNPRO_C
+# define __FUNCTION__ __func__
+# endif
+
+/* Similarly add your compiler here ... */
+
+#endif
+
+/* You may need to change this for your compiler */
+#ifdef HAVE_STRINGIZE
+# define ASSERT(X) {if(!(X)) ha_assert(#X, __LINE__, __FILE__);}
+#else
+# define ASSERT(X) {if(!(X)) ha_assert("X", __LINE__, __FILE__);}
+#endif
+
+/* shamelessly stolen from linux kernel */
+/* Force a compilation error if condition is true */
+#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
+/* Force a compilation error if condition is true, but also produce a
+ * result (of value 0 and type size_t), so the expression can be used
+ * e.g. in a structure initializer (or where-ever else comma expressions
+ * aren't permitted). */
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
+
+#endif /* LHA_INTERNAL_H */
diff --git a/include/lrm/Makefile.am b/include/lrm/Makefile.am
new file mode 100644
index 0000000..ec4f5a5
--- /dev/null
+++ b/include/lrm/Makefile.am
@@ -0,0 +1,22 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+idir=$(includedir)/heartbeat/lrm
+i_HEADERS = lrm_api.h lrm_msg.h racommon.h raexec.h
diff --git a/include/lrm/lrm_api.h b/include/lrm/lrm_api.h
new file mode 100644
index 0000000..cebff1b
--- /dev/null
+++ b/include/lrm/lrm_api.h
@@ -0,0 +1,455 @@
+/*
+ * Client-side Local Resource Manager API.
+ *
+ * Author: Huang Zhen <zhenh@cn.ibm.com>
+ * Copyright (C) 2004 International Business Machines
+ *
+ * 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
+ *
+ */
+
+/*
+ *
+ * By Huang Zhen <zhenhltc@cn.ibm.com> 2004/2/23
+ *
+ * It is based on the works of Alan Robertson, Lars Marowsky Bree,
+ * Andrew Beekhof.
+ *
+ * The Local Resource Manager needs to provide the following functionalities:
+ * 1. Provide the information of the resources holding by the node to its
+ * clients, including listing the resources and their status.
+ * 2. Its clients can add new resources to lrm or remove from it.
+ * 3. Its clients can ask lrm to operate the resources, including start,
+ * restart, stop and so on.
+ * 4. Provide the information of the lrm itself, including what types of
+ * resource are supporting by lrm.
+ *
+ * The typical clients of lrm are crm and lrmadmin.
+ */
+
+ /*
+ * Notice:
+ * "status" indicates the exit status code of "status" operation
+ * its value is defined in LSB, OCF...
+ *
+ * "state" indicates the state of resource, maybe LRM_RSC_BUSY, LRM_RSC_IDLE
+ *
+ * "op_status" indicates how the op exit. like LRM_OP_DONE,LRM_OP_CANCELLED,
+ * LRM_OP_TIMEOUT,LRM_OP_NOTSUPPORTED.
+ *
+ * "rc" is the return code of an opertioan. it's value is in following enum
+ * which is defined in "raexec.h"
+ * enum UNIFORM_RET_EXECRA {
+ * EXECRA_EXEC_UNKNOWN_ERROR = -2,
+ * EXECRA_NO_RA = -1,
+ * EXECRA_OK = 0,
+ * EXECRA_UNKNOWN_ERROR = 1,
+ * EXECRA_INVALID_PARAM = 2,
+ * EXECRA_UNIMPLEMENT_FEATURE = 3,
+ * EXECRA_INSUFFICIENT_PRIV = 4,
+ * EXECRA_NOT_INSTALLED = 5,
+ * EXECRA_NOT_CONFIGURED = 6,
+ * EXECRA_NOT_RUNNING = 7,
+ *
+ * EXECRA_RA_DEAMON_DEAD1 = 11,
+ * EXECRA_RA_DEAMON_DEAD2 = 12,
+ * EXECRA_RA_DEAMON_STOPPED = 13,
+ * EXECRA_STATUS_UNKNOWN = 14
+ * };
+ */
+
+#ifndef __LRM_API_H
+#define __LRM_API_H 1
+
+#include <glib.h>
+#include <lrm/raexec.h>
+#include <clplumbing/GSource.h>
+
+#define LRM_PROTOCOL_MAJOR 0
+#define LRM_PROTOCOL_MINOR 1
+#define LRM_PROTOCOL_VERSION ((LRM_PROTCOL_MAJOR << 16) | LRM_PROTOCOL_MINOR)
+
+#define RID_LEN 128
+
+/*lrm's client uses this structure to access the resource*/
+typedef struct
+{
+ char* id;
+ char* type;
+ char* class;
+ char* provider;
+ GHashTable* params;
+ struct rsc_ops* ops;
+}lrm_rsc_t;
+
+
+/*used in struct lrm_op_t to show how an operation exits*/
+typedef enum {
+ LRM_OP_PENDING = -1,
+ LRM_OP_DONE,
+ LRM_OP_CANCELLED,
+ LRM_OP_TIMEOUT,
+ LRM_OP_NOTSUPPORTED,
+ LRM_OP_ERROR
+}op_status_t;
+
+/*for all timeouts: in milliseconds. 0 for no timeout*/
+
+/*this structure is the information of the operation.*/
+
+#define EVERYTIME -1
+#define CHANGED -2
+
+/* Notice the interval and target_rc
+ *
+ * when interval==0, the operation will be executed only once
+ * when interval>0, the operation will be executed repeatly with the interval
+ *
+ * when target_rc==EVERYTIME, the client will be notified every time the
+ * operation executed.
+ * when target_rc==CHANGED, the client will be notified when the return code
+ * is different with the return code of last execute of the operation
+ * when target_rc is other value, only when the return code is the same of
+ * target_rc, the client will be notified.
+ */
+
+typedef struct{
+ /*input fields*/
+ char* op_type;
+ GHashTable* params;
+ int timeout;
+ char* user_data;
+ int user_data_len;
+ int interval;
+ int start_delay;
+ int copyparams; /* copy parameters to the rsc */
+ int target_rc;
+
+ /*output fields*/
+ op_status_t op_status;
+ int rc;
+ int call_id;
+ char* output;
+ char* rsc_id;
+ char* app_name;
+ char* fail_reason;
+ unsigned long t_run; /* when did the op run (as age) */
+ unsigned long t_rcchange; /* last rc change (as age) */
+ unsigned long exec_time; /* time it took the op to run */
+ unsigned long queue_time; /* time spent in queue */
+ int rsc_deleted; /* resource just deleted? */
+}lrm_op_t;
+
+extern const lrm_op_t lrm_zero_op; /* an all-zeroes lrm_op_t value */
+
+lrm_op_t* lrm_op_new(void);
+void lrm_free_op(lrm_op_t* op);
+void lrm_free_rsc(lrm_rsc_t* rsc);
+void lrm_free_str_list(GList* list);
+void lrm_free_op_list(GList* list);
+void lrm_free_str_table(GHashTable* table);
+
+
+/*this enum is used in get_cur_state*/
+typedef enum {
+ LRM_RSC_IDLE,
+ LRM_RSC_BUSY
+}state_flag_t;
+
+/* defaults for the asynchronous resource failures */
+enum { DEFAULT_FAIL_RC = EXECRA_UNKNOWN_ERROR };
+#define DEFAULT_FAIL_REASON "asynchronous monitor error"
+#define ASYNC_OP_NAME "asyncmon"
+
+/* in addition to HA_OK and HA_FAIL */
+#define HA_RSCBUSY 2
+
+struct rsc_ops
+{
+/*
+ *perform_op: Performs the operation on the resource.
+ *Notice: op is the operation which need to pass to RA and done asyn
+ *
+ *op: the structure of the operation. Caller can create the op by
+ * lrm_op_new() and release the op using lrm_free_op()
+ *
+ *return: All operations will be asynchronous.
+ * The call will return the call id or failed code immediately.
+ * The call id will be passed to the callback function
+ * when the operation finished later.
+ */
+ int (*perform_op) (lrm_rsc_t*, lrm_op_t* op);
+
+
+/*
+ *cancel_op: cancel the operation on the resource.
+ *
+ *callid: the call id returned by perform_op()
+ *
+ *return: HA_OK for success, HA_FAIL for failure op not found
+ * or other failure
+ * NB: the client always gets a notification over callback
+ * even for operations which were idle (of course, if
+ * the request has been accepted for processing)
+ */
+ int (*cancel_op) (lrm_rsc_t*, int call_id);
+
+/*
+ *flush_ops: throw away all operations queued for this resource,
+ * and return them as cancelled.
+ *
+ *return: HA_OK for success, HA_FAIL for failure
+ * NB: op is not flushed unless it is idle;
+ * in that case this call will block
+ */
+ int (*flush_ops) (lrm_rsc_t*);
+
+/*
+ *get_cur_state:
+ * return the current state of the resource
+ *
+ *cur_state: current state of the resource
+ *
+ *return: cur_state should be in LRM_RSC_IDLE or LRM_RSC_BUSY.
+ * and the function returns a list of ops.
+ * the list includes:
+ * 1. last ops for each type (start/stop/etc) from current client
+ * 2. current pending ops
+ * 3. all recurring ops waiting to execute
+ * the list is sorted by the call_id of ops.
+ * client can release the list using lrm_free_op_list()
+ */
+ GList* (*get_cur_state) (lrm_rsc_t*, state_flag_t* cur_state);
+
+/*
+ *get_last_result:
+ * return the last op of given type from current client
+ *
+ *op_type: the given type
+ *
+ *return: the last op. if there is no such op, return NULL.
+ * client can release the op using lrm_free_op()
+ */
+ lrm_op_t* (*get_last_result)(lrm_rsc_t*, const char *op_type);
+};
+
+
+/*
+ *lrm_op_done_callback_t:
+ * this type of callback functions are called when some
+ * asynchronous operation is done.
+ * client can release op by lrm_free_op()
+ */
+typedef void (*lrm_op_done_callback_t) (lrm_op_t* op);
+
+
+typedef struct ll_lrm
+{
+ struct lrm_ops* lrm_ops;
+}ll_lrm_t;
+
+struct lrm_ops
+{
+ int (*signon) (ll_lrm_t*, const char * app_name);
+
+ int (*signoff) (ll_lrm_t*);
+
+ int (*delete) (ll_lrm_t*);
+
+ int (*set_lrm_callback) (ll_lrm_t*,
+ lrm_op_done_callback_t op_done_callback_func);
+
+/*
+ *set_lrmd_param: set lrmd parameter
+ *get_lrmd_param: get lrmd parameter
+ *
+ *return: HA_OK for success, HA_FAIL for failure
+ * NB: currently used only for max_child_count
+ */
+ int (*set_lrmd_param)(ll_lrm_t*, const char *name, const char *value);
+ char* (*get_lrmd_param)(ll_lrm_t*, const char *name);
+
+/*
+ int (*set_parameters)(ll_lrm_t*, const GHashTable* option);
+
+ GHashTable* (*get_all_parameters)(ll_lrm_t*);
+
+ char * (*get_parameter)(ll_lrm_t *, const char * paramname);
+
+ char * (*get_parameter_description)(ll_lrm_t*);
+*/
+
+/*
+ *get_rsc_class_supported:
+ * Returns the resource classes supported.
+ * e.g. ocf, heartbeat,lsb...
+ *
+ *return: a list of the names of supported resource classes.
+ * caller can release the list by lrm_free_str_list().
+ */
+ GList* (*get_rsc_class_supported)(ll_lrm_t*);
+
+/*
+ *get_rsc_type_supported:
+ * Returns the resource types supported of class rsc_class.
+ * e.g. drdb, apache,IPaddr...
+ *
+ *return: a list of the names of supported resource types.
+ * caller can release the list by lrm_free_str_list().
+ */
+ GList* (*get_rsc_type_supported)(ll_lrm_t*, const char* rsc_class);
+
+/*
+ *get_rsc_provider_supported:
+ * Returns the provider list of the given resource types
+ * e.g. heartbeat, failsafe...
+ *
+ *rsc_provider: if it is null, the default one will used.
+ *
+ *return: a list of the names of supported resource provider.
+ * caller can release the list by lrm_free_str_list().
+ */
+ GList* (*get_rsc_provider_supported)(ll_lrm_t*,
+ const char* rsc_class, const char* rsc_type);
+
+/*
+ *get_rsc_type_metadata:
+ * Returns the metadata of the resource type
+ *
+ *rsc_provider: if it is null, the default one will used.
+ *
+ *return: the metadata. use g_free() to free.
+ *
+ */
+ char* (*get_rsc_type_metadata)(ll_lrm_t*, const char* rsc_class,
+ const char* rsc_type, const char* rsc_provider);
+
+/*
+ *get_all_type_metadatas:
+ * Returns all the metadata of the resource type of the class
+ *
+ *return: A GHashtable, the key is the RA type,
+ * the value is the metadata.
+ * Now only default RA's metadata will be returned.
+ * please use lrm_free_str_table() to free the return value.
+ */
+ GHashTable* (*get_all_type_metadata)(ll_lrm_t*, const char* rsc_class);
+
+/*
+ *get_all_rscs:
+ * Returns all resources.
+ *
+ *return: a list of id of resources.
+ * caller can release the list by lrm_free_str_list().
+ */
+ GList* (*get_all_rscs)(ll_lrm_t*);
+
+
+/*
+ *get_rsc: Gets one resource pointer by the id
+ *
+ *return: the lrm_rsc_t type pointer, NULL for failure
+ * caller can release the pointer by lrm_free_rsc().
+ */
+ lrm_rsc_t* (*get_rsc)(ll_lrm_t*, const char* rsc_id);
+
+/*
+ *add_rsc: Adds a new resource to lrm.
+ * lrmd holds nothing when it starts.
+ * crm or lrmadmin should add resources to lrm using
+ * this function.
+ *
+ *rsc_id: An id which sould be generated by client,
+ * 128byte(include '\0') UTF8 string
+ *
+ *class: the class of the resource
+ *
+ *type: the type of the resource.
+ *
+ *rsc_provider: if it is null, the default provider will used.
+ *
+ *params: the parameters for the resource.
+ *
+ *return: HA_OK for success, HA_FAIL for failure
+ */
+ int (*add_rsc)(ll_lrm_t*, const char* rsc_id, const char* class,
+ const char* type, const char* provider, GHashTable* params);
+
+/*
+ *delete_rsc: delete the resource by the rsc_id
+ *
+ *return: HA_OK for success, HA_FAIL for failure
+ * NB: resource removal is delayed until all operations are
+ * removed; the client, however, gets the reply immediately
+ */
+ int (*delete_rsc)(ll_lrm_t*, const char* rsc_id);
+
+/*
+ *fail_rsc: fail a resource
+ * Allow asynchronous monitor failures. Notifies all clients
+ * which have operations defined for the resource.
+ * The fail_rc parameter should be set to one of the OCF
+ * return codes (if non-positive it defaults to
+ * OCF_ERR_GENERIC). The fail_reason parameter should
+ * contain the description of the failure (i.e. "daemon
+ * panicked" or similar). If NULL is passed or empty string,
+ * it defaults to "asynchronous monitor failure".
+ *
+ *return: HA_OK for success, HA_FAIL for failure
+ */
+ int (*fail_rsc)(ll_lrm_t* lrm, const char* rsc_id,
+ const int fail_rc, const char* fail_reason);
+/*
+ *ipcchan: Return the IPC channel which can be used for determining
+ * when messages are ready to be read.
+ *return: the IPC Channel
+ */
+
+ IPC_Channel* (*ipcchan)(ll_lrm_t*);
+
+/*
+ *msgready: Returns TRUE (1) when a message is ready to be read.
+ */
+ gboolean (*msgready)(ll_lrm_t*);
+
+/*
+ *rcvmsg: Cause the next message to be read - activating callbacks for
+ * processing the message. If no callback processes the message
+ * it will be ignored. The message is automatically disposed of.
+ *
+ *return: the count of message was received.
+ */
+ int (*rcvmsg)(ll_lrm_t*, int blocking);
+
+};
+
+/*
+ *ll_lrm_new:
+ * initializes the lrm client library.
+ *
+ *llctype: "lrm"
+ *
+ */
+ll_lrm_t* ll_lrm_new(const char * llctype);
+
+/*
+ *execra_code2string:
+ * Translate the return code of the operation to string
+ *
+ *code: the rc field in lrm_op_t structure
+ */
+const char *execra_code2string(uniform_ret_execra_t code);
+#endif /* __LRM_API_H */
+
diff --git a/include/lrm/lrm_msg.h b/include/lrm/lrm_msg.h
new file mode 100644
index 0000000..6f671e1
--- /dev/null
+++ b/include/lrm/lrm_msg.h
@@ -0,0 +1,160 @@
+/*
+ * Message Define For Local Resource Manager
+ *
+ * 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
+ *
+ */
+
+/*
+ * By Huang Zhen <zhenh@cn.ibm.com> 2004/2/23
+ *
+ */
+/*
+ * Notice:
+ *"status" indicates the exit status code of "status" operation
+ * its value is defined in LSB
+ *"state" indicates the state of resource, maybe LRM_RSC_BUSY, LRM_RSC_IDLE
+ *"opstate" indicates how the op exit.like LRM_OP_DONE,LRM_OP_CANCELLED,
+ * LRM_OP_TIMEOUT,LRM_OP_NOTSUPPORTED.
+ */
+#ifndef __LRM_MSG_H
+#define __LRM_MSG_H 1
+
+#include <lrm/lrm_api.h>
+
+#define LRM_CMDPATH HA_VARRUNDIR"/heartbeat/lrm_cmd_sock"
+#define LRM_CALLBACKPATH HA_VARRUNDIR"/heartbeat/lrm_callback_sock"
+
+/*define the field type used by lrm*/
+#define F_LRM_TYPE "lrm_t"
+#define F_LRM_APP "lrm_app"
+#define F_LRM_PID "lrm_pid"
+#define F_LRM_UID "lrm_uid"
+#define F_LRM_GID "lrm_gid"
+#define F_LRM_RID "lrm_rid"
+#define F_LRM_RTYPE "lrm_rtype"
+#define F_LRM_RTYPES "lrm_rtypes"
+#define F_LRM_RCLASS "lrm_rclass"
+#define F_LRM_RPROVIDER "lrm_rprovider"
+#define F_LRM_RPROVIDERS "lrm_rproviders"
+#define F_LRM_PARAM "lrm_param"
+#define F_LRM_COPYPARAMS "lrm_copyparams"
+#define F_LRM_TIMEOUT "lrm_timeout"
+#define F_LRM_OP "lrm_op"
+#define F_LRM_OPCNT "lrm_opcount"
+#define F_LRM_OPSTATUS "lrm_opstatus"
+#define F_LRM_RC "lrm_rc"
+#define F_LRM_RET "lrm_ret"
+#define F_LRM_CALLID "lrm_callid"
+#define F_LRM_RCOUNT "lrm_rcount"
+#define F_LRM_RIDS "lrm_rids"
+#define F_LRM_DATALEN "lrm_datalen"
+#define F_LRM_DATA "lrm_data"
+#define F_LRM_STATE "lrm_state"
+#define F_LRM_INTERVAL "lrm_interval"
+#define F_LRM_TARGETRC "lrm_targetrc"
+#define F_LRM_LASTRC "lrm_lastrc"
+#define F_LRM_STATUS "lrm_status"
+#define F_LRM_RSCDELETED "lrm_rscdeleted"
+#define F_LRM_METADATA "lrm_metadata"
+#define F_LRM_USERDATA "lrm_userdata"
+#define F_LRM_DELAY "lrm_delay"
+#define F_LRM_T_RUN "lrm_t_run"
+#define F_LRM_T_RCCHANGE "lrm_t_rcchange"
+#define F_LRM_EXEC_TIME "lrm_exec_time"
+#define F_LRM_QUEUE_TIME "lrm_queue_time"
+#define F_LRM_FAIL_REASON "lrm_fail_reason"
+#define F_LRM_ASYNCMON_RC "lrm_asyncmon_rc"
+#define F_LRM_LRMD_PARAM_NAME "lrm_lrmd_param_name"
+#define F_LRM_LRMD_PARAM_VAL "lrm_lrmd_param_val"
+
+#define PRINT printf("file:%s,line:%d\n",__FILE__,__LINE__);
+
+
+/*define the message typs between lrmd and client lib*/
+#define REGISTER "reg"
+#define GETRSCCLASSES "rclasses"
+#define GETRSCTYPES "rtypes"
+#define GETPROVIDERS "rproviders"
+#define GETRSCMETA "rmetadata"
+#define GETALLRCSES "getall"
+#define GETRSC "getrsc"
+#define GETLASTOP "getlastop"
+#define GETRSCSTATE "getstate"
+#define SETMONITOR "setmon"
+#define GETMONITORS "getmons"
+#define FLUSHRSC "flush"
+#define ADDRSC "addrsc"
+#define DELRSC "delrsc"
+#define FAILRSC "failrsc"
+#define PERFORMOP "op"
+#define ISOPSUPPORT "opspt"
+#define OPDONE "opdone"
+#define MONITOR "monitor"
+#define RETURN "return"
+#define FLUSHOPS "flushops"
+#define CANCELOP "cancelop"
+#define SETLRMDPARAM "setparam"
+#define GETLRMDPARAM "getparam"
+
+#define MAX_INT_LEN 64
+#define MAX_NAME_LEN 255
+#define MAX_VALUE_LEN 255
+#define MAX_PARAM_LEN 1024
+
+
+GHashTable* copy_str_table(GHashTable* hash_table);
+GHashTable* merge_str_tables(GHashTable* old, GHashTable* new);
+void free_str_table(GHashTable* hash_table);
+
+ /*
+ * message for no parameter, like unreg,types,getall
+ * they do not include any paramters
+ */
+struct ha_msg* create_lrm_msg(const char* msg);
+
+/*
+ * message for only one parameter - resource id,
+ * like getrsc,delrsc,flush,getstate,getmons
+ */
+struct ha_msg* create_lrm_rsc_msg(const char* rid, const char* msg);
+
+/* register client message */
+struct ha_msg* create_lrm_reg_msg(const char* app_name);
+
+/*
+ * add new resource
+ * according to the opinion of Lars, it is awkward that we combine all
+ * parameters in to one string. I think so too. So this call may changed soon
+ */
+struct ha_msg* create_lrm_addrsc_msg(const char* rid, const char* class,
+ const char* type, const char* provider, GHashTable* parameter);
+
+/*
+ *
+ *the return message from lrmd for reg,unreg,addrsc,delrsc,isopsupport.
+ *these return messages only include return code.
+ *
+ */
+struct ha_msg* create_lrm_ret(int rc, int fields);
+
+
+/*
+ * the return message for a status change monitoring.
+ */
+
+struct ha_msg* create_rsc_perform_op_msg (const char* rid, lrm_op_t* op);
+
+#endif /* __LRM_MSG_H */
diff --git a/include/lrm/racommon.h b/include/lrm/racommon.h
new file mode 100644
index 0000000..b16aa24
--- /dev/null
+++ b/include/lrm/racommon.h
@@ -0,0 +1,29 @@
+/*
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RACOMMON_H
+#define RACOMMON_H
+
+void get_ra_pathname(const char* class_path, const char* type, const char* provider, char pathname[]);
+gboolean filtered(char * file_name);
+int get_runnable_list(const char* class_path, GList ** rsc_info);
+int get_failed_exec_rc(void);
+void closefiles(void);
+
+#endif /* RACOMMON_H */
diff --git a/include/lrm/raexec.h b/include/lrm/raexec.h
new file mode 100644
index 0000000..0b69831
--- /dev/null
+++ b/include/lrm/raexec.h
@@ -0,0 +1,157 @@
+/*
+ * raexec.h: The universal interface of RA Execution Plugin
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RAEXEC_H
+#define RAEXEC_H
+#include <glib.h>
+#include <lrm/racommon.h>
+
+/* Uniform return value of executing RA */
+enum UNIFORM_RET_EXECRA {
+ EXECRA_EXEC_UNKNOWN_ERROR = -2,
+ EXECRA_NO_RA = -1,
+ EXECRA_OK = 0,
+ EXECRA_UNKNOWN_ERROR = 1,
+ EXECRA_INVALID_PARAM = 2,
+ EXECRA_UNIMPLEMENT_FEATURE = 3,
+ EXECRA_INSUFFICIENT_PRIV = 4,
+ EXECRA_NOT_INSTALLED = 5,
+ EXECRA_NOT_CONFIGURED = 6,
+ EXECRA_NOT_RUNNING = 7,
+ EXECRA_RUNNING_MASTER = 8,
+ EXECRA_FAILED_MASTER = 9,
+
+ /* For status command only */
+ EXECRA_RA_DEAMON_DEAD1 = 11,
+ EXECRA_RA_DEAMON_DEAD2 = 12,
+ EXECRA_RA_DEAMON_STOPPED = 13,
+ EXECRA_STATUS_UNKNOWN = 14
+};
+typedef enum UNIFORM_RET_EXECRA uniform_ret_execra_t;
+
+#define RA_MAX_NAME_LENGTH 240
+#define RA_MAX_DIRNAME_LENGTH 200
+#define RA_MAX_BASENAME_LENGTH 40
+
+/*
+ * RA Execution Interfaces
+ * The plugin usage is divided into two step. First to send out a command to
+ * execute a resource agent via calling function execra. Execra is a unblock
+ * function, always return at once. Then to call function post_query_result to
+ * get the RA exection result.
+*/
+struct RAExecOps {
+ /*
+ * Description:
+ * Launch a exection of a resource agent -- normally is a script
+ *
+ * Parameters:
+ * rsc_id: The resource instance id.
+ * rsc_type: The basename of a RA.
+ * op_type: The operation that hope RA to do, such as "start",
+ * "stop" and so on.
+ * cmd_params: The command line parameters need to be passed to
+ * the RA for a execution.
+ * env_params: The enviroment parameters need to be set for
+ * affecting the action of a RA execution. As for
+ * OCF style RA, it's the only way to pass
+ * parameter to the RA.
+ *
+ * Return Value:
+ * 0: RA execution is ok, while the exec_key is a valid value.
+ * -1: The RA don't exist.
+ * -2: There are invalid command line parameters.
+ * -3: Other unkown error when launching the execution.
+ */
+ int (*execra)(
+ const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+ /*
+ * Description:
+ * Map the specific ret value to a uniform value.
+ *
+ * Parameters:
+ * ret_execra: the RA type specific ret value.
+ * op_type: the operation type
+ * std_output: the output which the RA write to stdout.
+ *
+ * Return Value:
+ * A uniform value without regarding RA type.
+ */
+ uniform_ret_execra_t (*map_ra_retvalue)(
+ int ret_execra
+ , const char * op_type
+ , const char * std_output);
+
+ /*
+ * Description:
+ * List all resource info of this class
+ *
+ * Parameters:
+ * rsc_info: a GList which item data type is rsc_info_t as
+ * defined above, containing all resource info of
+ * this class in the local machine.
+ *
+ * Return Value:
+ * >=0 : succeed. the RA type number of this RA class
+ * -1: failed due to invalid RA directory such as not existing.
+ * -2: failed due to other factors
+ */
+ int (*get_resource_list)(GList ** rsc_info);
+
+ /*
+ * Description:
+ * List all providers of this type
+ *
+ * Parameters:
+ * providers: a GList which item data type is string.
+ * the name of providers of the resource agent
+ *
+ * Return Value:
+ * >=0 : succeed. the provider number of this RA
+ * -1: failed due to invalid RA directory such as not existing.
+ * -2: failed due to other factors
+ */
+ int (*get_provider_list)(const char* ra_type, GList ** providers);
+
+ /*
+ * Description:
+ * List the metadata of the resource agent this class
+ *
+ * Parameters:
+ * rsc_type: the type of the ra
+ *
+ * Return Value:
+ * !NULL : succeed. the RA metadata.
+ * NULL: failed
+ */
+ char* (*get_resource_meta)(const char* rsc_type, const char* provider);
+};
+
+#define RA_EXEC_TYPE RAExec
+#define RA_EXEC_TYPE_S "RAExec"
+
+#endif /* RAEXEC_H */
diff --git a/include/pils/Makefile.am b/include/pils/Makefile.am
new file mode 100644
index 0000000..4b6c6a0
--- /dev/null
+++ b/include/pils/Makefile.am
@@ -0,0 +1,25 @@
+#
+# linux-ha: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Michael Moerz
+# This instance created by Horms
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+idir=$(includedir)/pils
+
+i_HEADERS = generic.h interface.h plugin.h
diff --git a/include/pils/generic.h b/include/pils/generic.h
new file mode 100644
index 0000000..83bf3e3
--- /dev/null
+++ b/include/pils/generic.h
@@ -0,0 +1,118 @@
+#ifndef PILS_GENERIC_H
+#define PILS_GENERIC_H
+/*
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ *
+ * Generic interface (implementation) manager
+ *
+ * This manager will manage any number of types of interfaces.
+ *
+ * This means that when any implementations of our client interfaces register
+ * or unregister, it is us that makes their interfaces show up in the outside
+ * world.
+ *
+ * And, of course, we have to do this in a very generic way, since we have
+ * no idea about the client programs or interface types, or anything else.
+ *
+ * We do that by getting a parameter passed to us which tell us the names
+ * of the interface types we want to manage, and the address of a GHashTable
+ * for each type that we put the implementation in when they register
+ * themselves.
+ *
+ * So, each type of interface that we manage gets its own private
+ * GHashTable of the implementations of that type that are currently
+ * registered.
+ *
+ * For example, if we manage communication modules, their exported
+ * interfaces will be registered in a hash table. If we manage
+ * authentication modules, they'll have their (separate) hash table that
+ * their exported interfaces are registered in.
+ *
+ */
+#include <pils/interface.h>
+
+/*
+ * Header defintions for using the generic interface/implementation
+ * manager plugin.
+ */
+
+/*
+ * Notification types for the callback function.
+ */
+typedef enum {
+ PIL_REGISTER, /* Someone has registered an implementation */
+ PIL_UNREGISTER /* Someone has unregistered an implementation */
+}GenericPILCallbackType;
+
+/* A user callback for the generic interface manager */
+typedef int (*GenericPILCallback)
+( GenericPILCallbackType type /* Event type */
+, PILPluginUniv* univ /* pointer to plugin universe */
+, const char * iftype /* Interface type */
+, const char * ifname /* Implementation (interface) name */
+, void * userptr /* Whatever you want it to be ;-) */
+);
+
+/*
+ * Structures to declare the set of interface types we're managing.
+ */
+typedef struct {
+ const char * iftype; /* What type of interface is this? */
+ GHashTable** ifmap; /* Table with implementation info */
+ void* importfuns; /* Functions for interface to import */
+ GenericPILCallback callback; /* Function2call when events occur */
+ void* userptr; /* Passed to Callback function */
+}PILGenericIfMgmtRqst;
+/*
+ * What does this look like in practice?
+ *
+ * GHashTable* authmodules = NULL;
+ * GHashTable* commmodules = NULL;
+ * PILGenericIfMgmtRqst RegisterRequests[] =
+ * {
+ * {"auth", &authmodules, &authimports, NULL, NULL},
+ * {"comm", &commmodules, &commimports, NULL, NULL},
+ * {NULL, NULL, NULL, NULL, NULL}
+ // NULL entry must be here
+ * };
+ *
+ * PILPlugin* PluginUniverse;
+ *
+ * PluginUniverse = NewPILPlugin("/usr/lib/whatever/plugins");
+ *
+ * PILLoadPlugin(PluginUniverse, "InterfaceMgr", "generic", &RegisterRequests);
+ * // N. B.: Passing RegisterRequests as an argument is essential
+ *
+ * Then, when you load an auth module, its exported interface gets added
+ * to "authmodules". When you unload an auth module, it gets removed
+ * from authmodules.
+ *
+ * Then, when you load a comm module, its exported interfaces gets added
+ * to "commodules". When you unload a comm module, its exported
+ * interfaces get removed from "commodules"
+ *
+ * If there are simple changes that would be useful for this generic
+ * plugin manager, then "patches are being accepted" :-)
+ *
+ * On the other hand, If you don't like the way this plugin manager works
+ * in a broader way, you're free to write your own - it's just another
+ * plugin ;-)
+ */
+#endif
diff --git a/include/pils/interface.h b/include/pils/interface.h
new file mode 100644
index 0000000..5a5114e
--- /dev/null
+++ b/include/pils/interface.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+#ifndef PILS_INTERFACE_H
+# define PILS_INTERFACE_H
+# ifndef PILS_PLUGIN_H
+# include <pils/plugin.h>
+# endif
+
+/*****************************************************************************
+ *
+ * The most basic interface type is the "IFManager" interface.
+ * Each interface manager registers and deals with interfaces of a given type.
+ *
+ * Such an interface must be loaded before any plugins of it's type can
+ * be loaded.
+ *
+ * In order to register any plugin of type "foo", we must load a interface of
+ * type "Interface" named "foo". This interface then manages the
+ * registration of all interfaces of type foo.
+ *
+ * To bootstrap, we load a interface of type "Interface" named "Interface"
+ * during the initialization of the plugin system.
+ *
+ * IFManagers will be autoloaded if certain conditions are met...
+ *
+ * If a IFManager is to be autoloaded, there must be one interface manager
+ * per file, and the file must be named according to the type of the
+ * interface it implements, and loaded in the directory named PI_IFMANAGER
+ * ("Interface").
+ *
+ */
+
+
+/*
+ * I'm unsure exactly which of the following structures
+ * are needed to write a interface, or a interface manager.
+ * We'll get that figured out and scope the defintions accordingly...
+ */
+
+/*
+ * PILInterface (AKA struct PILInterface_s) holds the information
+ * we use to track a single interface manager.
+ */
+
+
+struct PILInterface_s {
+ unsigned long MagicNum;
+ PILInterfaceType* interfacetype; /* Parent pointer */
+ char * interfacename; /* malloced interface name */
+ PILInterface* ifmanager; /* plugin managing us */
+ void* exports; /* Exported Functions */
+ /* for this interface */
+ PILInterfaceFun if_close; /* Interface close operation*/
+ void* ud_interface; /* per-interface user data */
+ int refcnt; /* Ref count for plugin */
+ PILPlugin* loadingpi; /* Plugin that loaded us */
+};
+/*
+ * PILInterfaceType (AKA struct PILInterfaceType_s) holds the info
+ * we use to track the set of all interfaces of a single kind.
+ */
+struct PILInterfaceType_s {
+ unsigned long MagicNum;
+ char* typename; /* Our interface type name */
+ GHashTable* interfaces; /* The set of interfaces
+ * of our type. The
+ * "values" are all
+ * PILInterface * objects
+ */
+ void* ud_if_type; /* per-interface-type user
+ data*/
+ PILInterfaceUniv* universe; /* Pointer to parent (up) */
+ PILInterface* ifmgr_ref; /* Pointer to our interface
+ manager */
+};
+
+/*
+ * PILInterfaceUniv (AKA struct PILInterfaceUniv_s) holds the information
+ * for all interfaces of all types. From our point of view this is
+ * our universe ;-)
+ */
+
+struct PILInterfaceUniv_s{
+ unsigned long MagicNum;
+ GHashTable* iftypes; /*
+ * Set of Interface Types
+ * The values are all
+ * PILInterfaceType objects
+ */
+ struct PILPluginUniv_s* piuniv; /* parallel universe of
+ * plugins
+ */
+};
+
+#ifdef ENABLE_PLUGIN_MANAGER_PRIVATE
+/*
+ * From here to the end is specific to interface managers.
+ * This data is only needed by interface managers, and the interface
+ * management system itself.
+ *
+ */
+typedef struct PILInterfaceOps_s PILInterfaceOps;
+
+
+/* Interfaces imported by a IFManager interface */
+struct PILInterfaceImports_s {
+
+ /* Return current reference count */
+ int (*RefCount)(PILInterface * eifinfo);
+
+ /* Incr/Decr reference count */
+ int (*ModRefCount)(PILInterface*eifinfo, int plusminus);
+
+ /* Unregister us as a interface */
+ void (*ForceUnRegister)(PILInterface *eifinfo);
+
+ /* For each client */
+ void (*ForEachClientDel)(PILInterface* manangerif
+ , gboolean(*f)(PILInterface* clientif, void * other)
+ , void* other);
+
+};
+
+/* Interfaces exported by an InterfaceManager interface */
+struct PILInterfaceOps_s{
+/*
+ * These are the interfaces exported by an InterfaceManager to the
+ * interface management infrastructure. These are not imported
+ * by interfaces - only the interface management infrastructure.
+ */
+
+ /* RegisterInterface - register this interface */
+ PIL_rc (*RegisterInterface)(PILInterface* newif
+ , void** imports);
+
+ PIL_rc (*UnRegisterInterface)(PILInterface*ifinfo); /* Unregister IF*/
+ /* And destroy PILInterface object */
+};
+
+#endif /* ENABLE_PLUGIN_MANAGER_PRIVATE */
+#endif /* PILS_INTERFACE_H */
diff --git a/include/pils/plugin.h.in b/include/pils/plugin.h.in
new file mode 100644
index 0000000..cb67d91
--- /dev/null
+++ b/include/pils/plugin.h.in
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+#ifndef PILS_PLUGIN_H
+# define PILS_PLUGIN_H
+# include <ltdl.h>
+# include <glue_config.h>
+
+/* Glib headers generate warnings - so we make them go away */
+
+#define time FOOtime
+#define index FOOindex
+#include <glib.h>
+#undef index
+#undef time
+
+/*****************************************************************************
+ * PILS - Universal Plugin and Interface loading system
+ *****************************************************************************
+ *
+ * An Overview of PILS...
+ *
+ * PILS is fairly general and reasonably interesting plugin loading system.
+ * We manage both plugins and their interfaces
+ *
+ * This plugin / interface management system is quite general, and should be
+ * directly usable by basically any project on any platform on which it runs
+ * - which should be many, since everything is build with automake
+ * and libtool.
+ *
+ * Some terminology...
+ *
+ * There are two basic kinds of objects we deal with here:
+ *
+ * Plugins: dynamically loaded chunks of code which implement one or more
+ * interfaces. The system treats all plugins as the same.
+ * In UNIX, these are dynamically loaded ".so" files.
+ *
+ * Interface: A set of functions which implement a particular capability
+ * (or interface)
+ * Generally interfaces are registered as part of a plugin.
+ * The system treats all interfaces of the same type the same.
+ * It is common to have exactly one interface inside of each plugin.
+ * In this case, the interface name should match the plugin name.
+ *
+ * Each interface implementation exports certain functions for its clients
+ * to use. We refer to these those "Ops". Every interface of the same type
+ * "imports" the same interfaces from its interface manager,
+ * and exports the same "Ops".
+ *
+ * Each interface implementation is provided certain interfaces which it
+ * imports when it from its interface manager when it is registered.
+ * We refer to these as "Imports". Every interface of a given type
+ * imports the same interfaces.
+ *
+ * The story with plugins is a little different...
+ *
+ * Every plugin exports a certain set of interfaces, regardless of what type
+ * of interfaces is implemented by it. These are described in the
+ * PILPluginOps structure.
+ *
+ * Every plugin imports a certain set of interfaces, regardless of what type
+ * of interfaces it may implement. These are described by the
+ * PILPluginImports structure.
+ *
+ * In the function parameters below, the following notation will
+ * sometimes appear:
+ *
+ * (OP) == Output Parameter - a parameter which is modified by the
+ * function being called
+ *
+ *
+ *****************************************************************************
+ *
+ * The basic structures we maintain about plugins are as follows:
+ *
+ * PILPlugin The data which represents a plugin.
+ * PILPluginType The data common to all plugins of a given type
+ * PILPluginUniv The set of all plugin types in the Universe
+ * (well... at least *this* universe)
+ *
+ * The basic structures we maintain about interfaces are as follows:
+ * PILInterface The data which represents a interface
+ * PILInterfaceType The data which is common to all
+ * interfaces of a given type
+ * PILPluginUniv The set of all interface types in the Universe
+ * (well... at least *this* universe)
+ *
+ * Regarding "Universe"s. It is our intent that a given program can deal
+ * with plugins in more than one universe. This might occur if you have two
+ * independent libraries each of which uses the plugin loading environment
+ * to manage their own independent interface components. There should be
+ * no restriction in creating a program which uses both of these libraries.
+ * At least that's what we hope ;-)
+ *
+ *
+ ***************************************************************************
+ * SOME MORE DETAILS ABOUT PLUGINS...
+ ***************************************************************************
+ *
+ * Going back to more detailed data structures about plugins...
+ *
+ * PILPluginImports The set of standard functions all plugins
+ * import.
+ * This includes:
+ * register_plugin()
+ * unregister_plugin()
+ * register_interface()
+ * unregister_interface()
+ * load_plugin()
+ * log() Preferred logging function
+ *
+ * PILPluginOps The set of standard operations all plugins
+ * export.
+ * This includes:
+ * pluginversion()
+ * pluginname()
+ * getdebuglevel()
+ * setdebuglevel()
+ * close() Prepare for unloading...
+ *
+ * Although we treat plugins pretty much the same, they are still
+ * categorized into "types" - one type per directory. These types
+ * generally correspond to interface types.
+ *
+ * One can only cause a plugin to be loaded - not a interface. But it is
+ * common to assume that loading a plugin named foo of type bar will
+ * cause a interface named foo of type bar to be registered. If one
+ * wants to implement automatic plugin loading in a given interface type,
+ * this assumption is necessary.
+ *
+ * The general way this works is...
+ *
+ * - A request is made to load a particular plugin of a particular type.
+ *
+ * - The plugin is loaded from the appropriate directory for plugins
+ * of that type.
+ *
+ * - The ml_plugin_init() function is called once when the plugin is
+ * loaded.
+ *
+ * The ml_plugin_init() function is passed a vector of functions which
+ * point to functions it can call to register itself, etc.
+ * (it's of type PILPluginImports)
+ *
+ * The ml_plugin_init function then uses this set of imported functions
+ * to register itself and its interfaces.
+ *
+ * The mechanism of registering a interface is largely the same for
+ * every interface. However, the semantics of registering a interfaces
+ * is determined by the interface manager for the particular type of
+ * interface being discussed.
+ *
+ ***************************************************************************
+ * SOME MORE DETAILS ABOUT PLUGINS...
+ ***************************************************************************
+ *
+ * There is only one built in type of interface. That's the Interface
+ * manager interface.
+ * The interface manager for the interface of type "InterfaceMgr",
+ * named "InterfaceMgr" inserts itself into the system in order
+ * to bootstrap things...
+ *
+ * When an attempt is made to register a interface of an unknown type,
+ * then the appropriate Interface manager is loaded automatically.
+ *
+ * The name of an interface manager determines the type of
+ * interface it manages.
+ *
+ * It handles requests for interfaces whose type is the same
+ * as its interface name. If the interface manager's interface name
+ * is foo, then it is the interface manager for all interfaces whose
+ * type is foo.
+ *
+ * Types associated with interfaces of type Interface
+ *
+ * PILInterfaceOps The set of interfaces that every interface
+ * manager exports
+ * PILInterfaceImports The set of interfaces which are supplied to
+ * (imported by) every interface of type
+ * Interface. (that is, every interface
+ * manager).
+ *
+ *****************************************************************************
+ *
+ * Each plugin has only one entry point which is exported directly, regardless
+ * of what kind of interface(s) it may implement...
+ *
+ * This entrypoint is named ml_plugin_init() {more or less - see below}
+ *
+ * The ml_plugin_init() function is called once when the plugin is loaded.
+ *
+ *
+ * All other function pointers are registered (exported) through parameters
+ * passed to ml_plugin_init()
+ *
+ * It is the purpose of the Ml_plugin_init() to register the plugin,
+ * and all the interfaces which this plugin implements. A pointer to
+ * the registration function is in the parameters which are passed
+ * to ml_plugin_init().
+ *
+ *****************************************************************************
+ *
+ * THINGS IN THIS DESIGN WHICH ARE PROBABLY BROKEN...
+ *
+ * It may also be the case that the plugin loading environment needs
+ * to be able to have some kind of user_data passed to it which it can
+ * also pass along to any interface ...
+ *
+ * Maybe this should be handled by a sort of global user_data registration
+ * structure, so globals can be passed to interfaces when they're registered.
+ *
+ * A sort of "user_data" registry. One for each interface type and one
+ * for each interface... Or maybe it could be even more flexible...
+ *
+ * This is all so that these nice pristene, beautiful concepts can come out
+ * and work well in the real world where interfaces need to interact with
+ * some kind of global system view, and with each other...
+ *
+ * Probably need some better way of managing interface versions, etc.
+ *
+ ****************************************************************************
+ */
+
+/*
+ * If you want to use this funky export stuff, then you need to #define
+ * PIL_PLUGINTYPE and PIL_PLUGIN *before* including this file.
+ *
+ * The way to use this stuff is to declare your primary entry point this way:
+ *
+ * This example is for an plugin of type "auth" named "sha1"
+ *
+ * #define PIL_PLUGINTYPE auth
+ * #define PIL_PLUGIN sha1
+ * #include <upmls/PILPlugin.h>
+ *
+ * static const char* Ourpluginversion (void);
+ * static const char* Ourpluginname (void);
+ * static int Ourgetdebuglevel(void);
+ * static void Oursetdebuglevel(int);
+ * static void Ourclose (PILPlugin*);
+ *
+ * static struct PILPluginOps our_exported_plugin_operations =
+ * { Ourpluginversion,
+ * , Ourpluginname
+ * , Ourgetdebuglevel
+ * , Oursetdebuglevel
+ * , Ourclose
+ * };
+ *
+ * static const PILPluginImports* PluginOps;
+ * static PILPlugin* OurPlugin;
+ *
+ * // Our plugin initialization and registration function
+ * // It gets called when the plugin gets loaded.
+ * PIL_rc
+ * PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+ * {
+ * PluginOps = imports;
+ * OurPlugin = us;
+ *
+ * // Register ourself as a plugin * /
+ * imports->register_plugin(us, &our_exported_plugin_operations);
+ *
+ * // Register our interfaces
+ * imports->register_interface(us, "interfacetype", "interfacename"
+ * // Be sure and define "OurExports" and OurImports
+ * // above...
+ * , &OurExports
+ * , &OurImports);
+ * // Repeat for all interfaces in this plugin...
+ *
+ * }
+ *
+ * Except for the PIL_PLUGINTYPE and the PIL_PLUGIN definitions, and changing
+ * the names of various static variables and functions, every single plugin is
+ * set up pretty much the same way
+ *
+ */
+
+/*
+ * No doubt there is a fancy preprocessor trick for avoiding these
+ * duplications but I don't have time to figure it out. Patches are
+ * being accepted...
+ */
+#define mlINIT_FUNC _pil_plugin_init
+#define mlINIT_FUNC_STR "_pil_plugin_init"
+#define PIL_INSERT _LTX_
+#define PIL_INSERT_STR "_LTX_"
+
+/*
+ * snprintf-style format string for initialization entry point name:
+ * arguments are: (plugintype, pluginname)
+ */
+#define PIL_FUNC_FMT "%s" PIL_INSERT_STR "%s" mlINIT_FUNC_STR
+
+#ifdef __STDC__
+# define EXPORTHELPER1(plugintype, insert, pluginname, function) \
+ plugintype##insert##pluginname##function
+#else
+# define EXPORTHELPER1(plugintype, insert, pluginname, function) \
+ plugintype/**/insert/**/pluginname/**/function
+#endif
+
+#define EXPORTHELPER2(a, b, c, d) EXPORTHELPER1(a, b, c, d)
+#define PIL_PLUGIN_INIT \
+ EXPORTHELPER2(PIL_PLUGINTYPE,PIL_INSERT,PIL_PLUGIN,mlINIT_FUNC)
+
+/*
+ * Plugin loading return codes. OK will always be zero.
+ *
+ * There are many ways to fail, but only one kind of success ;-)
+ */
+
+typedef enum {
+ PIL_OK=0, /* Success */
+ PIL_INVAL=1, /* Invalid Parameters */
+ PIL_BADTYPE=2, /* Bad plugin/interface type */
+ PIL_EXIST=3, /* Duplicate Plugin/Interface name */
+ PIL_OOPS=4, /* Internal Error */
+ PIL_NOPLUGIN=5 /* No such plugin or Interface */
+}PIL_rc; /* Return code from Plugin fns*/
+
+const char * PIL_strerror(PIL_rc rc);
+
+typedef struct PILPluginImports_s PILPluginImports;
+typedef struct PILPluginOps_s PILPluginOps;
+typedef struct PILPlugin_s PILPlugin;
+typedef struct PILPluginUniv_s PILPluginUniv;
+typedef struct PILPluginType_s PILPluginType;
+
+typedef struct PILInterface_s PILInterface;
+typedef struct PILInterfaceImports_s PILInterfaceImports;
+typedef struct PILInterfaceUniv_s PILInterfaceUniv;
+typedef struct PILInterfaceType_s PILInterfaceType;
+
+typedef PIL_rc(*PILInterfaceFun)(PILInterface*, void* ud_interface);
+
+#define PIL_MAGIC_PLUGIN 0xFEEDBEEFUL
+#define PIL_MAGIC_PLUGINTYPE 0xFEEDCEEFUL
+#define PIL_MAGIC_PLUGINUNIV 0xFEEDDEEFUL
+#define PIL_MAGIC_INTERFACE 0xFEEDEEEFUL
+#define PIL_MAGIC_INTERFACETYPE 0xFEEDFEEFUL
+#define PIL_MAGIC_INTERFACEUNIV 0xFEED0EEFUL
+
+#define IS_PILPLUGIN(s) ((s)->MagicNum == PIL_MAGIC_PLUGIN)
+#define IS_PILPLUGINTYPE(s) ((s)->MagicNum == PIL_MAGIC_PLUGINTYPE)
+#define IS_PILPLUGINUNIV(s) ((s)->MagicNum == PIL_MAGIC_PLUGINUNIV)
+#define IS_PILINTERFACE(s) ((s)->MagicNum == PIL_MAGIC_INTERFACE)
+#define IS_PILINTERFACETYPE(s) ((s)->MagicNum == PIL_MAGIC_INTERFACETYPE)
+#define IS_PILINTERFACEUNIV(s) ((s)->MagicNum == PIL_MAGIC_INTERFACEUNIV)
+
+/* The type of a Plugin Initialization Function */
+typedef PIL_rc (*PILPluginInitFun) (PILPlugin*us
+, PILPluginImports* imports
+, void* plugin_user_data);
+
+/*
+ * struct PILPluginOps_s (typedef PILPluginOps) defines the set of functions
+ * exported by all plugins...
+ */
+struct PILPluginOps_s {
+ const char* (*pluginversion) (void);
+ int (*getdebuglevel) (void);
+ void (*setdebuglevel) (int);
+ const char* (*license) (void);
+ const char* (*licenseurl) (void);
+ void (*close) (PILPlugin*);
+};
+
+/*
+ * Logging levels for the "standard" log interface.
+ */
+
+typedef enum {
+ PIL_FATAL= 1, /* BOOM! Causes program to stop */
+ PIL_CRIT = 2, /* Critical -- serious error */
+ PIL_WARN = 3, /* Warning */
+ PIL_INFO = 4, /* Informative message */
+ PIL_DEBUG= 5 /* Debug message */
+}PILLogLevel;
+typedef void (*PILLogFun)(PILLogLevel priority, const char * fmt, ...)
+ G_GNUC_PRINTF(2,3);
+
+/*
+ * The size glib2 type du jour?
+ * (once, this used to be size_t, so this change could break
+ * distributions with older glib2 versions; if so, just add an
+ * #ifelse below)
+ */
+#if GLIB_MINOR_VERSION <= 14
+ typedef gulong glib_size_t;
+#else
+ typedef gsize glib_size_t;
+#endif
+
+/*
+ * struct PILPluginImports_s (typedef PILPluginImports) defines
+ * the functions and capabilities that every plugin imports when it is loaded.
+ */
+
+
+struct PILPluginImports_s {
+ PIL_rc (*register_plugin)(PILPlugin* piinfo
+ , const PILPluginOps* commonops);
+ PIL_rc (*unregister_plugin)(PILPlugin* piinfo);
+/*
+ * A little explanation of the close_func parameter to register_interface
+ * is in order.
+ *
+ * It is an exported operation function, just like the Ops structure.
+ * However, the Ops vector is exported to applications that
+ * are using the interface. Unlike the Ops structure, close_func is
+ * exported only to the interface system, since applications shouldn't
+ * call it directly, but should manage the reference counts for the
+ * interfaces instead.
+ * The generic interface system doesn't have any idea how to call
+ * any functions in the operations vector. So, it's a separate
+ * parameter for two good reasons.
+ */
+ PIL_rc (*register_interface)(PILPlugin* piinfo
+ , const char * interfacetype /* Type of interface */
+ , const char * interfacename /* Name of interface */
+ , void* Ops /* Info (functions) exported
+ by this interface */
+ /* Function to call to shut down this interface */
+ , PILInterfaceFun close_func
+
+ , PILInterface** interfaceid /* Interface id (OP) */
+ , void** Imports
+ , void* ud_interface); /* interface user data */
+
+ PIL_rc (*unregister_interface)(PILInterface* interfaceid);
+ PIL_rc (*load_plugin)(PILPluginUniv* universe
+ , const char * plugintype, const char * pluginname
+ , void* plugin_private);
+
+ void (*log) (PILLogLevel priority, const char * fmt, ...)
+ G_GNUC_PRINTF(2,3);
+ gpointer (*alloc)(glib_size_t size);
+ gpointer (*mrealloc)(gpointer space, glib_size_t size);
+ void (*mfree)(gpointer space);
+ char* (*mstrdup)(const char *s);
+};
+
+/*
+ * Function for logging with the given logging function
+ * The reason why it's here is so we can get printf arg checking
+ * You can't get that when you call a function pointer directly.
+ */
+void PILCallLog(PILLogFun logfun, PILLogLevel priority, const char * fmt, ...)
+ G_GNUC_PRINTF(3,4);
+
+/*
+ * EXPORTED INTERFACES...
+ */
+
+/* Create a new plugin universe - start the plugin loading system up */
+PILPluginUniv* NewPILPluginUniv(const char * baseplugindirectory);
+
+/* Change memory allocation functions right after creating universe */
+void PilPluginUnivSetMemalloc(PILPluginUniv*
+, gpointer (*alloc)(glib_size_t size)
+, gpointer (*mrealloc)(gpointer, glib_size_t size)
+, void (*mfree)(void* space)
+, char* (*mstrdup)(const char *s));
+
+
+void PilPluginUnivSetLog(PILPluginUniv*
+, void (*log) (PILLogLevel priority, const char * fmt, ...)
+ G_GNUC_PRINTF(2,3));
+
+
+/* Delete a plugin universe - shut the plugin loading system down */
+/* Best if used carefully ;-) */
+void DelPILPluginUniv(PILPluginUniv*);
+
+/* Set the debug level for the plugin system itself */
+void PILpisysSetDebugLevel (int level);
+
+/* Return a list of plugins of the given type */
+char ** PILListPlugins(PILPluginUniv* u, const char *plugintype
+, int* plugincount /*can be NULL*/);
+
+/* Free the plugin list returned by PILFreeListPlugins */
+void PILFreePluginList(char ** pluginlist);
+
+/* Load the requested plugin */
+PIL_rc PILLoadPlugin(PILPluginUniv* piuniv
+, const char * plugintype
+, const char * pluginname
+, void * pi_private);
+
+/* Return PIL_OK if the given plugin exists */
+PIL_rc PILPluginExists(PILPluginUniv* piuniv
+, const char * plugintype
+, const char * pluginname);
+
+/* Either or both of pitype and piname may be NULL */
+void PILSetDebugLevel(PILPluginUniv*u, const char * pitype
+, const char * piname
+, int level);
+
+/* Neither pitype nor piname may be NULL */
+int PILGetDebugLevel(PILPluginUniv* u, const char * pitype
+, const char * piname);
+
+PIL_rc PILIncrIFRefCount(PILPluginUniv* piuniv
+, const char * interfacetype
+, const char * interfacename
+, int plusminus);
+
+int PILGetIFRefCount(PILPluginUniv* piuniv
+, const char * interfacetype
+, const char * interfacename);
+
+void PILLogMemStats(void);
+/* The plugin/interface type of a interface manager */
+
+#define PI_IFMANAGER "InterfaceMgr"
+#define PI_IFMANAGER_TYPE InterfaceMgr
+
+/*
+ * These functions are standard exported functions for all plugins.
+ */
+
+#define PIL_PLUGIN_BOILERPLATE_PROTOTYPES_GENERIC(PluginVersion, DebugName) \
+/* \
+ * Prototypes for boilerplate functions \
+ */ \
+static const char* Ourpluginversion(void); \
+static int GetOurDebugLevel(void); \
+static void SetOurDebugLevel(int); \
+static const char * ReturnOurLicense(void); \
+static const char * ReturnOurLicenseURL(void);
+
+#define PIL_PLUGIN_BOILERPLATE_FUNCS(PluginVersion, DebugName) \
+/* \
+ * Definitions of boilerplate functions \
+ */ \
+static const char* \
+Ourpluginversion(void) \
+{ return PluginVersion; } \
+ \
+static int DebugName = 0; \
+ \
+static int \
+GetOurDebugLevel(void) \
+{ return DebugName; } \
+ \
+static void \
+SetOurDebugLevel(int level) \
+{ DebugName = level; } \
+ \
+static const char * \
+ReturnOurLicense(void) \
+{ return PIL_PLUGINLICENSE; } \
+ \
+static const char * \
+ReturnOurLicenseURL(void) \
+{ return PIL_PLUGINLICENSEURL; }
+
+#define PIL_PLUGIN_BOILERPLATE(PluginVersion, DebugName, CloseName) \
+PIL_PLUGIN_BOILERPLATE_PROTOTYPES_GENERIC(PluginVersion, DebugName) \
+static void CloseName(PILPlugin*); \
+/* \
+ * Initialize Plugin Exports structure \
+ */ \
+static PILPluginOps OurPIExports = \
+{ Ourpluginversion \
+, GetOurDebugLevel \
+, SetOurDebugLevel \
+, ReturnOurLicense \
+, ReturnOurLicenseURL \
+, CloseName \
+}; \
+PIL_PLUGIN_BOILERPLATE_FUNCS(PluginVersion, DebugName)
+
+#define PIL_PLUGIN_BOILERPLATE2(PluginVersion, DebugName) \
+PIL_PLUGIN_BOILERPLATE_PROTOTYPES_GENERIC(PluginVersion, DebugName) \
+/* \
+ * Initialize Plugin Exports structure \
+ */ \
+static PILPluginOps OurPIExports = \
+{ Ourpluginversion \
+, GetOurDebugLevel \
+, SetOurDebugLevel \
+, ReturnOurLicense \
+, ReturnOurLicenseURL \
+, NULL \
+}; \
+PIL_PLUGIN_BOILERPLATE_FUNCS(PluginVersion, DebugName)
+
+
+/* A few sample licenses and URLs. We can easily add to this */
+
+#define LICENSE_GPL "gpl"
+#define URL_GPL "http://www.fsf.org/licenses/gpl.html"
+
+#define LICENSE_LGPL "lgpl"
+#define URL_LGPL "http://www.fsf.org/licenses/lgpl.html"
+
+#define LICENSE_X11 "x11"
+#define URL_X11 "http://www.x.org/terms.htm"
+
+#define LICENSE_PUBDOM "publicdomain"
+#define URL_PUBDOM "file:///dev/null"
+
+#define LICENSE_MODBSD "modbsd"
+#define URL_MODBSD "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5"
+
+#define LICENSE_OLDBSD "origbsd"
+#define URL_OLDBSD "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#6"
+
+#define LICENSE_EXPAT "expat"
+#define URL_EXPAT "http://www.jclark.com/xml/copying.txt"
+
+#define LICENSE_ZLIB "zlib"
+#define URL_ZLIB "http://www.gzip.org/zlib/zlib_license.html"
+
+#define LICENSE_APACHE_10 "apache1_0"
+#define URL_APACHE_10 "http://www.apache.org/LICENSE-1.0"
+
+#define LICENSE_APACHE_11 "apache1_1"
+#define URL_APACHE_11 "http://www.apache.org/LICENSE-1.1"
+
+#define LICENSE_MPL "mpl"
+#define URL_MPL "http://www.mozilla.org/MPL/MPL-1.1.html"
+
+#define LICENSE_PROP "proprietary"
+#define URL_PROP ""
+
+#define LICENSE_IBMPL "ibmpl"
+#define URL_IBMPL "http://oss.software.ibm.com/developerworks/opensource/license10.html"
+
+#ifdef ENABLE_PIL_DEFS_PRIVATE
+/* Perhaps these should be moved to a different header file */
+
+/*
+ * PILPluginType is the "class" for the basic plugin loading mechanism.
+ *
+ * To enable loading of plugins from a particular plugin type
+ * one calls NewPILPluginType with the plugin type name, the plugin
+ * base directory, and the set of functions to be imported to the plugin.
+ *
+ *
+ * The general idea of these structures is as follows:
+ *
+ * The PILPluginUniv object contains information about all plugins of
+ * all types.
+ *
+ * The PILPluginType object contains information about all the plugins of a
+ * specific type.
+ *
+ * Note: for plugins which implement a single interface, the plugin type name
+ * should be the same as the interface type name.
+ *
+ * For other plugins that implement more than one interface, one of
+ * the interface names should normally match the plugin name.
+ */
+
+
+/*
+ * struct PILPlugin_s (typedef PILPlugin) is the structure which
+ * represents/defines a plugin, and is used to identify which plugin is
+ * being referred to in various function calls.
+ *
+ * NOTE: It may be the case that this definition should be moved to
+ * another header file - since no one ought to be messing with them anyway ;-)
+ *
+ * I'm not sure that we're putting the right stuff in here, either...
+ */
+
+struct PILPlugin_s {
+ unsigned long MagicNum;
+ char* plugin_name;
+ PILPluginType* plugintype; /* Parent structure */
+ int refcnt; /* Reference count for this plugin */
+ lt_dlhandle dlhandle; /* Reference to D.L. object */
+ PILPluginInitFun dlinitfun; /* Initialization function */
+ const PILPluginOps* pluginops; /* Exported plugin operations */
+
+ void* ud_plugin; /* Plugin-Private data */
+ /* Other stuff goes here ... (?) */
+};
+
+/*
+ * PILPluginType Information about all plugins of a given type.
+ * (i.e., in a given directory)
+ * (AKA struct PILPluginType_s)
+ */
+
+struct PILPluginType_s {
+ unsigned long MagicNum;
+ char * plugintype;
+ PILPluginUniv* piuniv; /* The universe to which we belong */
+ GHashTable* Plugins;
+ /* Key is plugin type, value is PILPlugin */
+
+ char** (*listplugins)(PILPluginType*, int* listlen);
+};
+
+/*
+ * PILPluginUniv (aka struct PILPluginUniv_s) is the structure which
+ * represents the universe of all PILPluginType objects.
+ * There is one PILPluginType object for each Plugin type.
+ */
+
+struct PILPluginUniv_s {
+ unsigned long MagicNum;
+ char ** rootdirlist;
+ /* key is plugin type, data is PILPluginType* */
+ GHashTable* PluginTypes;
+ struct PILInterfaceUniv_s*ifuniv; /* Universe of interfaces */
+ PILPluginImports* imports;
+};
+
+# endif /* ENABLE_PIL_DEFS_PRIVATE */
+#endif /*PILS_PLUGIN_H */
diff --git a/include/replace_uuid.h b/include/replace_uuid.h
new file mode 100644
index 0000000..d6bca89
--- /dev/null
+++ b/include/replace_uuid.h
@@ -0,0 +1,50 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * uuid: wrapper declarations.
+ *
+ * heartbeat originally used "uuid" functionality by calling directly,
+ * and only, onto the "e2fsprogs" implementation.
+ *
+ * The run-time usages in the code have since been abstracted, funnelled
+ * through a thin, common interface layer: a Good Thing.
+ *
+ * Similarly, the compile-time usages of "include <uuid/uuid.h>" are
+ * replaced, being funnelled through a reference to this header file.
+ *
+ * This header file interfaces onto the actual underlying implementation.
+ * In the case of the "e2fsprogs" implementation, it is simply a stepping
+ * stone onto "<uuid/uuid.h>". As other implementations are accommodated,
+ * so their header requirements can be accommodated here.
+ *
+ * Copyright (C) 2004 David Lee <t.d.lee@durham.ac.uk>
+ */
+
+#ifndef REPLACE_UUID_H
+#define REPLACE_UUID_H
+
+typedef unsigned char uuid_t[16];
+void uuid_clear(uuid_t uu);
+int uuid_compare(const uuid_t uu1, const uuid_t uu2);
+void uuid_copy(uuid_t dst, const uuid_t src);
+void uuid_generate(uuid_t out);
+void uuid_generate_random(uuid_t out);
+int uuid_is_null(const uuid_t uu);
+int uuid_parse(const char *in, uuid_t uu);
+void uuid_unparse(const uuid_t uu, char *out);
+
+#endif /* REPLACE_UUID_H */
diff --git a/include/stonith/Makefile.am b/include/stonith/Makefile.am
new file mode 100644
index 0000000..9e67a2a
--- /dev/null
+++ b/include/stonith/Makefile.am
@@ -0,0 +1,25 @@
+#
+# linux-ha: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Michael Moerz
+# This instance created by Horms
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+idir=$(includedir)/stonith
+
+i_HEADERS = expect.h stonith.h stonith_plugin.h st_ttylock.h
diff --git a/include/stonith/expect.h b/include/stonith/expect.h
new file mode 100644
index 0000000..6084ef1
--- /dev/null
+++ b/include/stonith/expect.h
@@ -0,0 +1,61 @@
+/*
+ * Expect simple tokens. Simple expect infrastructure for STONITH API
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#ifndef __EXPECT_H
+# define __EXPECT_H
+/*
+ * If we find any of the given tokens in the input stream,
+ * we return it's "toktype", so we can tell which one was
+ * found.
+ *
+ */
+
+struct Etoken {
+ const char * string; /* The token to look for */
+ int toktype; /* The type to return on match */
+ int matchto; /* Modified during matches */
+};
+
+int ExpectToken(int fd
+, struct Etoken * toklist /* List of tokens to match against */
+ /* Final token has NULL string */
+, int to_secs /* Timeout value in seconds */
+, char * buf /* If non-NULL, then all the text
+ * matched/skipped over by this match */
+, int maxline,
+, int debug); /* debug level */
+
+
+/*
+ * A handy little routine. It runs the given process
+ * with it's standard output redirected into our *readfd, and
+ * its standard input redirected from our *writefd
+ *
+ * Doing this with all the pipes, etc. required for doing this
+ * is harder than it sounds :-)
+ */
+
+int StartProcess(const char * cmd, int* readfd, int* writefd);
+
+#ifndef EOS
+# define EOS '\0'
+#endif
+#endif /*__EXPECT_H*/
diff --git a/include/stonith/st_ttylock.h b/include/stonith/st_ttylock.h
new file mode 100644
index 0000000..5b5c7fd
--- /dev/null
+++ b/include/stonith/st_ttylock.h
@@ -0,0 +1,21 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __STONITH_ST_TTYLOCK_H
+# define __STONITH_ST_TTYLOCK_H
+int st_ttylock(const char *serial_device);
+int st_ttyunlock(const char *serial_device);
+#endif /*__STONITH_ST_TTYLOCK_H*/
diff --git a/include/stonith/stonith.h b/include/stonith/stonith.h
new file mode 100644
index 0000000..93fbaac
--- /dev/null
+++ b/include/stonith/stonith.h
@@ -0,0 +1,187 @@
+/*
+ * S hoot
+ * T he
+ * O ther
+ * N ode
+ * I n
+ * T he
+ * H ead
+ *
+ * Cause the other machine to reboot or die - now.
+ *
+ * We guarantee that when we report that the machine has been
+ * rebooted, then it has been (barring misconfiguration or hardware
+ * errors)
+ *
+ * A machine which we have STONITHed won't do anything more to its
+ * peripherials etc. until it goes through the reboot cycle.
+ */
+
+/*
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ * Copyright (c) 2004 International Business Machines, Inc.
+ *
+ * Author: Alan Robertson
+ *
+ * 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
+ *
+ */
+
+#ifndef __STONITH_H
+# define __STONITH_H
+#include <glib.h>
+#include <ctype.h>
+
+#include <pils/plugin.h>
+#define STONITH_VERS 2
+
+/*
+ * Return codes from "Stonith" operations
+ */
+
+#define S_OK 0 /* Machine correctly reset */
+#define S_BADCONFIG 1 /* Bad config info given */
+#define S_ACCESS 2 /* Can't access STONITH device */
+ /* (login/passwd problem?) */
+#define S_INVAL 3 /* Bad/illegal argument */
+#define S_BADHOST 4 /* Bad/illegal host/node name */
+#define S_RESETFAIL 5 /* Reset failed */
+#define S_TIMEOUT 6 /* Timed out in the dialogues */
+#define S_ISOFF 7 /* Can't reboot: Outlet is off */
+#define S_OOPS 8 /* Something strange happened */
+
+typedef struct stonith {
+ char * stype;
+}Stonith;
+
+/* An array of StonithNVpairs is terminated by a NULL s_name */
+typedef struct {
+ char * s_name;
+ char * s_value;
+}StonithNVpair;
+
+/*
+ * Operation requested by reset_req()
+ */
+#define ST_GENERIC_RESET 1 /* Reset the machine any way you can */
+#define ST_POWERON 2 /* Power the node on */
+#define ST_POWEROFF 3 /* Power the node off */
+/*
+ * Type of information requested by the get_info() call
+ */
+#define ST_CONF_XML 1 /* XML config info */
+#define ST_DEVICEID 2 /* Device Type Identification */
+#define ST_DEVICENAME 3 /* Unique Individual Device Identification */
+ /* (only after stonith_set_config() call) */
+#define ST_DEVICEDESCR 4 /* Device Description text */
+#define ST_DEVICEURL 5 /* Manufacturer/Device URL */
+
+extern PILPluginUniv *StonithPIsys;
+
+char ** stonith_types(void); /* NULL-terminated list */
+ /* valid until next call of stonith_types() */
+Stonith*stonith_new(const char * type);
+void stonith_delete(Stonith *);
+
+const char * const * stonith_get_confignames (Stonith* s);
+ /* static/global return */
+ /* Return number and list of valid s_names */
+
+const char* /* static/global return - lots of things! */
+ stonith_get_info (Stonith* s, int infotype);
+
+void stonith_set_debug (Stonith* s, int debuglevel);
+void stonith_set_log (Stonith* s
+ , PILLogFun);
+
+int stonith_set_config (Stonith* s, StonithNVpair* list);
+int stonith_set_config_file(Stonith* s, const char * configname);
+ /* uses get_confignames to determine which
+ * names to look for in file configname, which
+ * is passed in by the -F option */
+int stonith_set_config_info(Stonith* s, const char * info);
+ /* uses get_confignames to determine which
+ * names to look for in string info, which
+ * is passed in by the -p option */
+ /*
+ * Must call stonith_set_config() before calling functions below...
+ */
+char** stonith_get_hostlist (Stonith* s);
+void stonith_free_hostlist (char** hostlist);
+int stonith_get_status (Stonith* s);
+int stonith_req_reset (Stonith* s, int operation, const char* node);
+
+StonithNVpair* stonith_env_to_NVpair(Stonith* s);
+
+/* Stonith 1 compatibility: Convert string to an NVpair set */
+StonithNVpair*
+ stonith1_compat_string_to_NVpair(Stonith* s, const char * str);
+StonithNVpair*
+ stonith_ghash_to_NVpair(GHashTable* stringtable);
+void free_NVpair(StonithNVpair*); /* Free result from above 2 functions */
+void strdown(char *str); /* simple replacement for g_strdown */
+
+/*
+ * The ST_DEVICEID info call is intended to return the type of the Stonith
+ * device. Note that it may return a different result once it has attempted
+ * to talk to the device (like after a status() call). This is because
+ * a given STONITH module may be able to talk to more than one kind of
+ * model of STONITH device, and can't tell which type is out there
+ * to until it talks to it. For example, Baytech 3, Baytech 5 and
+ * Baytech 5a are all supported by one module, and this module actually
+ * captures the particular model number after it talks to it.
+ *
+ * The ST_DEVICEDESCR info call is intended to return information identifying
+ * the type of STONITH device supported by this STONITH object. This is so
+ * users can tell if they have this kind of device or not.
+ *
+ * SHOULD THIS BE IN THE XML SO IT CAN BE SUPPLIED IN SEVERAL LANGUAGES??
+ * But, this would mean the STONITH command would have to parse XML.
+ * Sigh... I'd rather not... Or maybe it can be supplied duplicately
+ * in the XML if that is thought to be desirable...
+ *
+ * The ST_DEVICEURL info call is intended to return the URL of a web site
+ * related to the device in question. This might be the manufacturer,
+ * a pointer to the product line, or the individual product itself.
+ *
+ * A good way for a GUI to work which configures STONITH devices would be to
+ * use the result of the stonith_types() call in a pulldown menu.
+ *
+ * Once the type is selected, create a Stonith object of the selected type.
+ * One can then create a dialog box to create the configuration info for the
+ * device using return from the ST_CONF_XML info call to direct the
+ * GUI in what information to ask for to fill up the StonithNVpair
+ * argument to the stonith_set_config() call. This information would then
+ * be prompted for according to the XML information, and then put into
+ * a NULL-terminated array of StonithNVpair objects.
+ *
+ * Once this has been done, it can be tested for syntactic
+ * validity with stonith_set_config().
+ *
+ * If it passes set_config(), it can be further validated using status()
+ * which will then actually try and talk to the STONITH device. If status()
+ * returns S_OK, then communication with the device was successfully
+ * established.
+ *
+ * Normally that would mean that logins, passwords, device names, and IP
+ * addresses, etc. have been validated as required by the particular device.
+ *
+ * At this point, you can ask the device which machines it knows how to reset
+ * using the stonith_get_hostlist() function.
+ *
+ */
+
+#endif /*__STONITH_H*/
diff --git a/include/stonith/stonith_plugin.h b/include/stonith/stonith_plugin.h
new file mode 100644
index 0000000..9091a6e
--- /dev/null
+++ b/include/stonith/stonith_plugin.h
@@ -0,0 +1,125 @@
+/*
+ * S hoot
+ * T he
+ * O ther
+ * N ode
+ * I n
+ * T he
+ * H ead
+ *
+ * Cause the other machine to reboot or die - now.
+ *
+ * We guarantee that when we report that the machine has been
+ * rebooted, then it has been (barring misconfiguration or hardware errors)
+ *
+ * A machine which we have STONITHed won't do anything more to its
+ * peripherials etc. until it goes through the reboot cycle.
+ */
+
+/*
+ *
+ * Copyright (c) 2004 International Business Machines, Inc.
+ *
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#ifndef __STONITH_PLUGIN_H
+# define __STONITH_PLUGIN_H
+
+#include <stonith/stonith.h>
+#include <glib.h>
+
+typedef struct stonith_plugin StonithPlugin;
+
+#define NUM_STONITH_FNS 7
+
+struct stonith_ops {
+ StonithPlugin * (*new) (const char*); /* mini-Constructor */
+ void (*destroy) (StonithPlugin*); /*(full) Destructor */
+
+ const char* (*get_info) (StonithPlugin*, int infotype);
+ const char * const * (*get_confignames) (StonithPlugin*);
+ int (*set_config) (StonithPlugin*, StonithNVpair* list);
+ /* Finishes construction */
+ /*
+ * Must call set_config before calling any of
+ * the member functions below...
+ */
+
+ int (*get_status) (StonithPlugin*s);
+ int (*req_reset) (StonithPlugin*, int op, const char* node);
+
+
+ char** (*get_hostlist) (StonithPlugin*);
+ /* Returns list of hosts it supports */
+};
+
+struct stonith_plugin {
+ Stonith s;
+ struct stonith_ops* s_ops;
+ gboolean isconfigured;
+};
+
+#define STONITH_TYPE stonith2
+#define STONITH_TYPE_S "stonith2"
+typedef struct StonithImports_s StonithImports;
+
+struct Etoken {
+ const char * string; /* The token to look for */
+ int toktype; /* The type to return on match */
+ int matchto; /* Modified during matches */
+};
+
+/* An array of StonithNamesToGet is terminated by a NULL s_name */
+typedef struct {
+ const char * s_name;
+ char * s_value;
+}StonithNamesToGet;
+
+#define TELNET_PORT 23
+#define TELNET_SERVICE "telnet"
+
+struct StonithImports_s {
+ int (*ExpectToken)(int fd, struct Etoken * toklist, int to_secs
+ , char * buf, int maxline, int debug);
+ int (*StartProcess)(const char * cmd, int * readfd, int * writefd);
+ int (*OpenStreamSocket) (const char * host, int port
+ , const char * service);
+ /* Service can be NULL, port can be <= 0, but not both... */
+ const char* (*GetValue)(StonithNVpair*, const char * name);
+ int (*CopyAllValues) (StonithNamesToGet* out, StonithNVpair* in);
+ char **(*StringToHostList)(const char * hlstring);
+ char **(*CopyHostList)(const char * const * hlstring);
+ void (*FreeHostList)(char** hostlist);
+ int (*TtyLock)(const char* tty);
+ int (*TtyUnlock)(const char* tty);
+};
+
+
+/*
+ * A few standardized parameter names
+ */
+
+#define ST_HOSTLIST "hostlist"
+#define ST_IPADDR "ipaddr"
+#define ST_LOGIN "login"
+#define ST_PASSWD "password"
+#define ST_COMMUNITY "community" /* SNMP community */
+#define ST_TTYDEV "ttydev" /* TTY device name */
+
+#endif /*__STONITH__PLUGIN_H*/
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..5047e1d
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+SUBDIRS = pils clplumbing lrm stonith plugins
diff --git a/lib/clplumbing/GSource.c b/lib/clplumbing/GSource.c
new file mode 100644
index 0000000..48bb198
--- /dev/null
+++ b/lib/clplumbing/GSource.c
@@ -0,0 +1,1864 @@
+/*
+ * Copyright (c) 2002 Alan Robertson <alanr@unix.sh>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/GSource_internal.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/timers.h>
+
+#ifdef events
+# undef events
+#endif
+#ifdef revents
+# undef revents
+#endif
+
+#define DEFAULT_MAXDISPATCH 0
+#define DEFAULT_MAXDELAY 0
+#define OTHER_MAXDELAY 100
+
+/*
+ * On architectures with alignment constraints, our casting between
+ * "(GSource*)" and "(GFDSource_s*)" etc. causes trouble, because of
+ * the massive alignment requirements of "longclock_t".
+ *
+ * Use the following to store and fetch.
+ */
+static
+void
+lc_store(char *destptr, longclock_t value) {
+ longclock_t _ltt;
+ _ltt = value;
+ memcpy((destptr), &_ltt, sizeof(longclock_t));
+}
+
+static
+longclock_t
+lc_fetch(char *ptr) {
+ longclock_t _ltt;
+ memcpy(&_ltt, (ptr), sizeof(longclock_t));
+ return _ltt;
+}
+
+#define ERR_EVENTS (G_IO_ERR|G_IO_NVAL)
+#define INPUT_EVENTS (G_IO_IN|G_IO_PRI|G_IO_HUP)
+#define OUTPUT_EVENTS (G_IO_OUT)
+#define DEF_EVENTS (INPUT_EVENTS|ERR_EVENTS)
+
+#define WARN_DELAY(ms, mx, input) cl_log(LOG_WARNING \
+ , "%s: Dispatch function for %s was delayed" \
+ " %lu ms (> %lu ms) before being called (GSource: 0x%lx)" \
+ , __FUNCTION__, (input)->description, ms, mx \
+ , POINTER_TO_ULONG(input))
+
+#define EXPLAINDELAY(started, detected) cl_log(LOG_INFO \
+ , "%s: started at %llu should have started at %llu" \
+ , __FUNCTION__, (unsigned long long)started \
+ , (unsigned long long)detected)
+
+
+#define WARN_TOOLONG(ms, mx, input) cl_log(LOG_WARNING \
+ , "%s: Dispatch function for %s took too long to execute" \
+ ": %lu ms (> %lu ms) (GSource: 0x%lx)" \
+ , __FUNCTION__, (input)->description, ms, mx \
+ , POINTER_TO_ULONG(input))
+
+#define CHECK_DISPATCH_DELAY(i) { \
+ unsigned long ms; \
+ longclock_t dettime; \
+ dispstart = time_longclock(); \
+ dettime = lc_fetch((i)->detecttime); \
+ ms = longclockto_ms(sub_longclock(dispstart,dettime)); \
+ if ((i)->maxdispatchdelayms > 0 \
+ && ms > (i)->maxdispatchdelayms) { \
+ WARN_DELAY(ms, (i)->maxdispatchdelayms, (i)); \
+ EXPLAINDELAY(dispstart, dettime); \
+ } \
+}
+
+#define CHECK_DISPATCH_TIME(i) { \
+ unsigned long ms; \
+ longclock_t dispend = time_longclock(); \
+ ms = longclockto_ms(sub_longclock(dispend, dispstart)); \
+ if ((i)->maxdispatchms > 0 && ms > (i)->maxdispatchms) { \
+ WARN_TOOLONG(ms, (i)->maxdispatchms, (i)); \
+ } \
+ lc_store(((i)->detecttime), zero_longclock); \
+}
+
+#define WARN_TOOMUCH(ms, mx, input) cl_log(LOG_WARNING \
+ , "%s: working on %s took %ld ms (> %ld ms)" \
+ , __FUNCTION__, (input)->description, ms, mx);
+
+#define SAVESTART {funstart = time_longclock();}
+
+#define CHECKEND(input) { \
+ longclock_t funend = time_longclock(); \
+ long ms; \
+ ms = longclockto_ms(sub_longclock(funend, funstart)); \
+ if (ms > OTHER_MAXDELAY){ \
+ WARN_TOOMUCH(ms, ((long) OTHER_MAXDELAY), input); \
+ } \
+} \
+
+
+#ifndef _NSIG
+# define _NSIG 2*NSIG
+#endif
+
+static gboolean G_fd_prepare(GSource* source,
+ gint* timeout);
+static gboolean G_fd_check(GSource* source);
+static gboolean G_fd_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_fd_destroy(GSource* source);
+
+static GSourceFuncs G_fd_SourceFuncs = {
+ G_fd_prepare,
+ G_fd_check,
+ G_fd_dispatch,
+ G_fd_destroy,
+};
+
+GSource*
+G_main_add_input(int priority,
+ gboolean can_recurse,
+ GSourceFuncs* funcs)
+{
+ GSource * input_source = g_source_new(funcs, sizeof(GSource));
+ if (input_source == NULL){
+ cl_log(LOG_ERR, "create glib source for input failed!");
+ }else {
+ g_source_set_priority(input_source, priority);
+ g_source_set_can_recurse(input_source, can_recurse);
+ if(g_source_attach(input_source, NULL) == 0){
+ cl_log(LOG_ERR, "attaching input_source to main context"
+ " failed!! ");
+ }
+ }
+
+ return input_source;
+}
+
+
+/*
+ * Add the given file descriptor to the gmainloop world.
+ */
+
+
+GFDSource*
+G_main_add_fd(int priority, int fd, gboolean can_recurse
+, gboolean (*dispatch)(int fd, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify)
+{
+
+ GSource* source = g_source_new(&G_fd_SourceFuncs,
+ sizeof(GFDSource));
+ GFDSource* ret = (GFDSource*)source;
+
+ ret->magno = MAG_GFDSOURCE;
+ ret->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ ret->maxdispatchms = DEFAULT_MAXDISPATCH;
+ ret->udata = userdata;
+ ret->dispatch = dispatch;
+ ret->gpfd.fd = fd;
+ ret->gpfd.events = DEF_EVENTS;
+ ret->gpfd.revents = 0;
+ ret->dnotify = notify;
+ lc_store((ret->detecttime), zero_longclock);
+
+ g_source_add_poll(source, &ret->gpfd);
+
+
+ g_source_set_priority(source, priority);
+
+ g_source_set_can_recurse(source, can_recurse);
+
+ ret->gsourceid = g_source_attach(source, NULL);
+ ret->description = "file descriptor";
+
+ if (ret->gsourceid == 0) {
+ g_source_remove_poll(source, &ret->gpfd);
+ memset(ret, 0, sizeof(GFDSource));
+ g_source_unref(source);
+ source = NULL;
+ ret = NULL;
+ }
+ return ret;
+}
+
+gboolean
+G_main_del_fd(GFDSource* fdp)
+{
+ GSource * source = (GSource*) fdp;
+
+
+ if (fdp->gsourceid <= 0) {
+ return FALSE;
+ }
+
+ g_source_remove_poll(source, &fdp->gpfd);
+ g_source_remove(fdp->gsourceid);
+ fdp->gsourceid = 0;
+ g_source_unref(source);
+
+ return TRUE;
+
+}
+
+void
+g_main_output_is_blocked(GFDSource* fdp)
+{
+ fdp->gpfd.events |= OUTPUT_EVENTS;
+}
+
+
+/*
+ * For pure file descriptor events, return FALSE because we
+ * have to poll to get events.
+ *
+ * Note that we don't modify 'timeout' either.
+ */
+static gboolean
+G_fd_prepare(GSource* source,
+ gint* timeout)
+{
+ GFDSource* fdp = (GFDSource*)source;
+ g_assert(IS_FDSOURCE(fdp));
+ return FALSE;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+static gboolean
+G_fd_check(GSource* source)
+
+{
+ GFDSource* fdp = (GFDSource*)source;
+ g_assert(IS_FDSOURCE(fdp));
+ if (fdp->gpfd.revents) {
+ lc_store((fdp->detecttime), time_longclock());
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+static gboolean
+G_fd_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+
+ GFDSource* fdp = (GFDSource*)source;
+ longclock_t dispstart;
+ g_assert(IS_FDSOURCE(fdp));
+ CHECK_DISPATCH_DELAY(fdp);
+
+
+ /*
+ * Is output now unblocked?
+ *
+ * If so, turn off OUTPUT_EVENTS to avoid going into
+ * a tight poll(2) loop.
+ */
+ if (fdp->gpfd.revents & OUTPUT_EVENTS) {
+ fdp->gpfd.events &= ~OUTPUT_EVENTS;
+ }
+
+ if(fdp->dispatch) {
+ if(!(fdp->dispatch(fdp->gpfd.fd, fdp->udata))){
+ g_source_remove_poll(source,&fdp->gpfd);
+ g_source_unref(source);
+ CHECK_DISPATCH_TIME(fdp);
+ return FALSE;
+ }
+ CHECK_DISPATCH_TIME(fdp);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_fd_destroy(GSource* source)
+{
+ GFDSource* fdp = (GFDSource*)source;
+ g_assert(IS_FDSOURCE(fdp));
+ fdp->gsourceid = 0;
+ if (fdp->dnotify) {
+ fdp->dnotify(fdp->udata);
+ }
+}
+
+
+/************************************************************
+ * Functions for IPC_Channels
+ ***********************************************************/
+gboolean G_CH_prepare_int(GSource* source,
+ gint* timeout);
+gboolean G_CH_check_int(GSource* source);
+
+gboolean G_CH_dispatch_int(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+void G_CH_destroy_int(GSource* source);
+
+
+static GSourceFuncs G_CH_SourceFuncs = {
+ G_CH_prepare_int,
+ G_CH_check_int,
+ G_CH_dispatch_int,
+ G_CH_destroy_int,
+};
+
+
+
+
+void
+set_IPC_Channel_dnotify(GCHSource* chp,
+ GDestroyNotify notify){
+ chp->dnotify = notify;
+}
+
+/*
+ * Add an IPC_channel to the gmainloop world...
+ */
+GCHSource*
+G_main_IPC_Channel_constructor(GSource* source, IPC_Channel* ch
+ , gpointer userdata
+ , GDestroyNotify notify)
+{
+ int rfd, wfd;
+ GCHSource* chp;
+
+ if( !source ) {
+ cl_log(LOG_WARNING, "%s:%d: got null source", __FUNCTION__,__LINE__);
+ return NULL;
+ }
+ if( !ch ) {
+ cl_log(LOG_WARNING, "%s:%d: got null channel", __FUNCTION__,__LINE__);
+ return NULL;
+ }
+ chp = (GCHSource*)source;
+
+ chp->magno = MAG_GCHSOURCE;
+ chp->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ chp->maxdispatchms = DEFAULT_MAXDISPATCH;
+ lc_store((chp->detecttime), zero_longclock);
+ ch->refcount++;
+ chp->ch = ch;
+ chp->udata=userdata;
+ chp->dnotify = notify;
+ chp->dontread = FALSE;
+
+ rfd = ch->ops->get_recv_select_fd(ch);
+ wfd = ch->ops->get_send_select_fd(ch);
+
+ chp->fd_fdx = (rfd == wfd);
+
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "%s(sock=%d,%d)",__FUNCTION__, rfd,wfd);
+ }
+ chp->infd.fd = rfd;
+ chp->infd.events = DEF_EVENTS;
+ g_source_add_poll(source, &chp->infd);
+ if (!chp->fd_fdx) {
+ chp->outfd.fd = wfd;
+ chp->outfd.events = DEF_EVENTS;
+ g_source_add_poll(source, &chp->outfd);
+ }
+ chp->dispatch = NULL;
+ chp->description = "IPC channel(base)";
+ chp->gsourceid = 0;
+ return chp;
+}
+
+GCHSource*
+G_main_add_IPC_Channel(int priority, IPC_Channel* ch
+ , gboolean can_recurse
+ , gboolean (*dispatch)(IPC_Channel* source_data,
+ gpointer user_data)
+ , gpointer userdata
+ , GDestroyNotify notify)
+{
+ GCHSource *chp;
+ GSource *source;
+
+ if( !ch ) {
+ cl_log(LOG_WARNING, "%s:%d: got null channel", __FUNCTION__,__LINE__);
+ return NULL;
+ }
+ source = g_source_new(&G_CH_SourceFuncs,
+ sizeof(GCHSource));
+ G_main_IPC_Channel_constructor(source,ch,userdata,notify);
+
+ chp = (GCHSource*)source;
+ chp->dispatch = dispatch;
+
+ g_source_set_priority(source, priority);
+ g_source_set_can_recurse(source, can_recurse);
+
+ chp->gsourceid = g_source_attach(source, NULL);
+ chp->description = "IPC channel";
+
+
+ if (chp->gsourceid == 0) {
+ g_source_remove_poll(source, &chp->infd);
+ if (!chp->fd_fdx) {
+ g_source_remove_poll(source, &chp->outfd);
+ }
+ g_source_unref(source);
+ source = NULL;
+ chp = NULL;
+ }
+ return chp;
+}
+
+
+void /* Suspend reading from far end writer (flow control) */
+G_main_IPC_Channel_pause(GCHSource* chp)
+{
+ if (chp == NULL){
+ cl_log(LOG_ERR, "%s: invalid input", __FUNCTION__);
+ return;
+ }
+
+ chp->dontread = TRUE;
+ return;
+}
+
+
+void /* Resume reading from far end writer (un-flow-control) */
+G_main_IPC_Channel_resume(GCHSource* chp)
+{
+ if (chp == NULL){
+ cl_log(LOG_ERR, "%s: invalid input", __FUNCTION__);
+ return;
+ }
+
+ chp->dontread = FALSE;
+ return;
+
+}
+
+/*
+ * Delete an IPC_channel from the gmainloop world...
+ */
+gboolean
+G_main_del_IPC_Channel(GCHSource* chp)
+{
+ GSource* source = (GSource*) chp;
+
+ if (chp == NULL || chp->gsourceid <= 0) {
+ return FALSE;
+ }
+
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "%s(sock=%d)",__FUNCTION__, chp->infd.fd);
+ }
+ g_source_remove(chp->gsourceid);
+ chp->gsourceid = 0;
+ /* chp should (may) now be undefined */
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+/*
+ * For IPC_CHANNEL events, enable output checking when needed
+ * and note when unread input is already queued.
+ *
+ * Note that we don't modify 'timeout' either.
+ */
+gboolean
+G_CH_prepare_int(GSource* source,
+ gint* timeout)
+{
+ GCHSource* chp = (GCHSource*)source;
+ longclock_t funstart;
+ gboolean ret;
+
+ g_assert(IS_CHSOURCE(chp));
+ SAVESTART;
+
+
+ if (chp->ch->ops->is_sending_blocked(chp->ch)) {
+ if (chp->fd_fdx) {
+ chp->infd.events |= OUTPUT_EVENTS;
+ }else{
+ chp->outfd.events |= OUTPUT_EVENTS;
+ }
+ }
+
+ if (chp->ch->recv_queue->current_qlen < chp->ch->recv_queue->max_qlen) {
+ chp->infd.events |= INPUT_EVENTS;
+ }else{
+ /*
+ * This also disables EOF events - until we
+ * read some of the packets we've already gotten
+ * This prevents a tight loop in poll(2).
+ */
+ chp->infd.events &= ~INPUT_EVENTS;
+ }
+
+ if (chp->dontread){
+ return FALSE;
+ }
+ ret = chp->ch->ops->is_message_pending(chp->ch);
+ if (ret) {
+ lc_store((chp->detecttime), time_longclock());
+ }
+ CHECKEND(chp);
+ return ret;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+gboolean
+G_CH_check_int(GSource* source)
+{
+
+ GCHSource* chp = (GCHSource*)source;
+ gboolean ret;
+ longclock_t funstart;
+
+ g_assert(IS_CHSOURCE(chp));
+ SAVESTART;
+
+
+ if (chp->dontread){
+ /* Make sure output gets unblocked */
+ chp->ch->ops->resume_io(chp->ch);
+ return FALSE;
+ }
+
+ ret = (chp->infd.revents != 0
+ || (!chp->fd_fdx && chp->outfd.revents != 0)
+ || chp->ch->ops->is_message_pending(chp->ch));
+ if (ret) {
+ lc_store((chp->detecttime), time_longclock());
+ }
+ CHECKEND(chp);
+ return ret;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+gboolean
+G_CH_dispatch_int(GSource * source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GCHSource* chp = (GCHSource*)source;
+ longclock_t dispstart;
+ longclock_t resume_start = zero_longclock;
+
+ g_assert(IS_CHSOURCE(chp));
+ CHECK_DISPATCH_DELAY(chp);
+
+
+ if (chp->dontread){
+ return TRUE;
+ }
+
+ /* Is output now unblocked?
+ *
+ * If so, turn off OUTPUT_EVENTS to avoid going into
+ * a tight poll(2) loop.
+ */
+ if (chp->fd_fdx) {
+ if (chp->infd.revents & OUTPUT_EVENTS) {
+ chp->infd.events &= ~OUTPUT_EVENTS;
+ }
+ }else if (chp->outfd.revents & OUTPUT_EVENTS) {
+ chp->outfd.events &= ~OUTPUT_EVENTS;
+ }
+
+ if (ANYDEBUG) {
+ resume_start = time_longclock();
+ }
+
+ chp->ch->ops->resume_io(chp->ch);
+
+ if (ANYDEBUG) {
+ longclock_t resume_end = time_longclock();
+ unsigned long ms;
+ ms = longclockto_ms(sub_longclock(resume_end
+ , resume_start));
+ if (ms > 10) {
+ cl_log(LOG_WARNING
+ , "%s: resume_io() for %s took %lu ms"
+ , __FUNCTION__
+ , chp->description, ms);
+ }
+ }
+
+
+ if(chp->dispatch && chp->ch->ops->is_message_pending(chp->ch)) {
+ if(!(chp->dispatch(chp->ch, chp->udata))){
+ g_source_remove_poll(source, &chp->infd);
+ if (!chp->fd_fdx) {
+ g_source_remove_poll(source, &chp->outfd);
+ }
+ CHECK_DISPATCH_TIME(chp);
+ g_source_unref(source);
+ return FALSE;
+ }
+ }
+ CHECK_DISPATCH_TIME(chp);
+
+ if (chp->ch->ch_status == IPC_DISCONNECT){
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+void
+G_CH_destroy_int(GSource* source)
+{
+ GCHSource* chp = (GCHSource*)source;
+
+ g_assert(IS_CHSOURCE(chp));
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "%s(chp=0x%lx, sock=%d) {", __FUNCTION__
+ , (unsigned long)chp, chp->infd.fd);
+ }
+
+ if (chp->dnotify) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling dnotify(sock=%d, arg=0x%lx) function"
+ , __FUNCTION__, chp->infd.fd, (unsigned long)chp->udata);
+ }
+ chp->dnotify(chp->udata);
+ }else{
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: NOT calling dnotify(sock=%d) function"
+ , __FUNCTION__, chp->infd.fd);
+ }
+ }
+ if (chp->ch) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: calling IPC destroy (chp->ch=0x%lx, sock=%d)"
+ , __FUNCTION__ , (unsigned long)chp->ch, chp->infd.fd);
+ }
+ chp->ch->ops->destroy(chp->ch);
+ chp->ch = NULL;
+ }
+ /*chp->gsourceid = 0; ?*/
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "}/*%s(sock=%d)*/", __FUNCTION__, chp->infd.fd);
+ }
+}
+
+
+/************************************************************
+ * Functions for IPC_WaitConnections
+ ***********************************************************/
+static gboolean G_WC_prepare(GSource * source,
+ gint* timeout);
+static gboolean G_WC_check(GSource* source);
+static gboolean G_WC_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_WC_destroy(GSource* source);
+
+static GSourceFuncs G_WC_SourceFuncs = {
+ G_WC_prepare,
+ G_WC_check,
+ G_WC_dispatch,
+ G_WC_destroy,
+};
+
+
+/*
+ * Add an IPC_WaitConnection to the gmainloop world...
+ */
+GWCSource*
+G_main_add_IPC_WaitConnection(int priority
+, IPC_WaitConnection* wch
+, IPC_Auth* auth_info
+, gboolean can_recurse
+, gboolean (*dispatch)(IPC_Channel* wch
+, gpointer user_data)
+, gpointer userdata
+, GDestroyNotify notify)
+{
+
+ GWCSource* wcp;
+ GSource * source = g_source_new(&G_WC_SourceFuncs,
+ sizeof(GWCSource));
+
+ wcp = (GWCSource*)source;
+
+ wcp->magno = MAG_GWCSOURCE;
+ wcp->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ wcp->maxdispatchms = DEFAULT_MAXDISPATCH;
+ lc_store((wcp->detecttime), zero_longclock);
+ wcp->udata = userdata;
+ wcp->gpfd.fd = wch->ops->get_select_fd(wch);
+ wcp->gpfd.events = DEF_EVENTS;
+ wcp->gpfd.revents = 0;
+ wcp->wch = wch;
+ wcp->dnotify = notify;
+ wcp->auth_info = auth_info;
+ wcp->dispatch = dispatch;
+
+ g_source_add_poll(source, &wcp->gpfd);
+
+ g_source_set_priority(source, priority);
+
+ g_source_set_can_recurse(source, can_recurse);
+
+ wcp->gsourceid = g_source_attach(source, NULL);
+ wcp->description = "IPC wait for connection";
+
+ if (wcp->gsourceid == 0) {
+ g_source_remove_poll(source, &wcp->gpfd);
+ g_source_unref(source);
+ source = NULL;
+ wcp = NULL;
+ }
+ return wcp;
+}
+
+
+/* Delete the given IPC_WaitConnection from the gmainloop world */
+gboolean
+G_main_del_IPC_WaitConnection(GWCSource* wcp)
+{
+
+ GSource* source = (GSource*) wcp;
+
+
+ if (wcp->gsourceid <= 0) {
+ return FALSE;
+ }
+
+ g_source_remove(wcp->gsourceid);
+ wcp->gsourceid = 0;
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+
+
+/*
+ * For IPC_WaitConnection events, return FALSE because we
+ * have to poll to get events.
+ *
+ * We don't modify 'timeout' either.
+ */
+static gboolean
+G_WC_prepare(GSource* source,
+ gint* timeout)
+{
+ GWCSource* wcp = (GWCSource*)source;
+ g_assert(IS_WCSOURCE(wcp));
+ return FALSE;
+}
+
+/*
+ * Did we notice any I/O (connection pending) events?
+ */
+
+static gboolean
+G_WC_check(GSource * source)
+{
+ GWCSource* wcp = (GWCSource*)source;
+ g_assert(IS_WCSOURCE(wcp));
+
+ if (wcp->gpfd.revents != 0) {
+ lc_store((wcp->detecttime), time_longclock());
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Someone is trying to connect.
+ * Try to accept the connection and notify the user.
+ */
+static gboolean
+G_WC_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GWCSource* wcp = (GWCSource*)source;
+ IPC_Channel* ch;
+ gboolean rc = TRUE;
+ int count = 0;
+ longclock_t dispstart;
+
+ g_assert(IS_WCSOURCE(wcp));
+ CHECK_DISPATCH_DELAY(wcp);
+
+ while(1) {
+ ch = wcp->wch->ops->accept_connection(wcp->wch, wcp->auth_info);
+ if (ch == NULL) {
+ if (errno == EBADF) {
+ cl_perror("%s: Stopping accepting connections(socket=%d)!!"
+ , __FUNCTION__, wcp->gpfd.fd);
+ rc = FALSE;
+ }
+ break;
+ }
+ ++count;
+
+ if(!wcp->dispatch) {
+ continue;
+ }
+
+ rc = wcp->dispatch(ch, wcp->udata);
+ if(!rc) {
+ g_source_remove_poll(source, &wcp->gpfd);
+ g_source_unref(source);
+ break;
+ }
+ }
+ CHECK_DISPATCH_TIME(wcp);
+ return rc;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_WC_destroy(GSource* source)
+{
+
+ GWCSource* wcp = (GWCSource*)source;
+ wcp->gsourceid = 0;
+ g_assert(IS_WCSOURCE(wcp));
+ wcp->wch->ops->destroy(wcp->wch);
+ if (wcp->dnotify) {
+ wcp->dnotify(wcp->udata);
+ }
+}
+
+
+/************************************************************
+ * Functions for Signals
+ ***********************************************************/
+static gboolean G_SIG_prepare(GSource* source,
+ gint* timeout);
+static gboolean G_SIG_check(GSource* source);
+
+static gboolean G_SIG_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_SIG_destroy(GSource* source);
+
+static void G_main_signal_handler(int nsig);
+
+static GSourceFuncs G_SIG_SourceFuncs = {
+ G_SIG_prepare,
+ G_SIG_check,
+ G_SIG_dispatch,
+ G_SIG_destroy,
+};
+
+static GSIGSource *G_main_signal_list[_NSIG];
+
+void
+set_SignalHandler_dnotify(GSIGSource* sig_src, GDestroyNotify notify)
+{
+ sig_src->dnotify = notify;
+}
+
+/*
+ * Add an Signal to the gmainloop world...
+ */
+GSIGSource*
+G_main_add_SignalHandler(int priority, int signal,
+ gboolean (*dispatch)(int nsig, gpointer user_data),
+ gpointer userdata, GDestroyNotify notify)
+{
+ GSIGSource* sig_src;
+ GSource * source = g_source_new(&G_SIG_SourceFuncs, sizeof(GSIGSource));
+ gboolean failed = FALSE;
+
+ sig_src = (GSIGSource*)source;
+
+ sig_src->magno = MAG_GSIGSOURCE;
+ sig_src->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ sig_src->maxdispatchms = DEFAULT_MAXDISPATCH;
+ sig_src->signal = signal;
+ sig_src->dispatch = dispatch;
+ sig_src->udata = userdata;
+ sig_src->dnotify = notify;
+
+ sig_src->signal_triggered = FALSE;
+
+ g_source_set_priority(source, priority);
+ g_source_set_can_recurse(source, FALSE);
+
+ if(G_main_signal_list[signal] != NULL) {
+ cl_log(LOG_ERR
+ , "%s: Handler already present for signal %d"
+ , __FUNCTION__, signal);
+ failed = TRUE;
+ }
+ if(!failed) {
+ sig_src->gsourceid = g_source_attach(source, NULL);
+ sig_src->description = "signal";
+ if (sig_src->gsourceid < 1) {
+ cl_log(LOG_ERR
+ , "%s: Could not attach source for signal %d (%d)"
+ , __FUNCTION__
+ , signal, sig_src->gsourceid);
+ failed = TRUE;
+ }
+ }
+
+ if(failed) {
+ cl_log(LOG_ERR
+ , "%s: Signal handler for signal %d NOT added"
+ , __FUNCTION__, signal);
+ g_source_remove(sig_src->gsourceid);
+ g_source_unref(source);
+ source = NULL;
+ sig_src = NULL;
+ } else {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Added signal handler for signal %d"
+ , __FUNCTION__, signal);
+ }
+ G_main_signal_list[signal] = sig_src;
+ CL_SIGNAL(signal, G_main_signal_handler);
+ /*
+ * If we don't set this on, then the mainloop poll(2) call
+ * will never be interrupted by this signal - which sort of
+ * defeats the whole purpose of a signal handler in a
+ * mainloop program
+ */
+ cl_signal_set_interrupt(signal, TRUE);
+ }
+ return sig_src;
+}
+
+
+/*
+ * Delete a Signal from the gmainloop world...
+ */
+gboolean
+G_main_del_SignalHandler(GSIGSource* sig_src)
+{
+ GSource* source = (GSource*) sig_src;
+
+ if (sig_src->gsourceid <= 0) {
+ return FALSE;
+ }
+ if(_NSIG <= sig_src->signal) {
+ g_assert(_NSIG > sig_src->signal);
+ return FALSE;
+ }
+
+ CL_SIGNAL(sig_src->signal, NULL);
+
+ sig_src->signal_triggered = FALSE;
+ g_source_remove(sig_src->gsourceid);
+ G_main_signal_list[sig_src->signal] = NULL;
+ sig_src->gsourceid = 0;
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+static gboolean
+G_SIG_prepare(GSource* source, gint* timeoutms)
+{
+ GSIGSource* sig_src = (GSIGSource*)source;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+
+ /* Don't let a timing window keep us in poll() forever
+ *
+ * The timing window in question looks like this:
+ * No signal has occurred up to the point of prepare being called.
+ * Signal comes in _after_ prepare was called, but _before_ poll.
+ * signal_detected gets set, but no one checks it before going into poll
+ * We wait in poll forever... It's not a pretty sight :-(.
+ */
+ *timeoutms = 1000; /* Sigh... */
+
+ if (sig_src->signal_triggered) {
+ clock_t now;
+ clock_t diff;
+
+ /* detecttime is reset in the dispatch function */
+ if (cmp_longclock(lc_fetch(sig_src->detecttime), zero_longclock) != 0) {
+ cl_log(LOG_ERR, "%s: detecttime already set?", __FUNCTION__);
+ return TRUE;
+ }
+ /* Otherwise, this is when it was first detected */
+ now = cl_times();
+ diff = now - sig_src->sh_detecttime; /* How long since signal occurred? */
+ lc_store(
+ sig_src->detecttime,
+ sub_longclock(time_longclock(), (longclock_t)diff)
+ );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+static gboolean
+G_SIG_check(GSource* source)
+{
+
+ GSIGSource* sig_src = (GSIGSource*)source;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+
+ if (sig_src->signal_triggered) {
+ clock_t now;
+ clock_t diff;
+ if (cmp_longclock(lc_fetch(sig_src->detecttime), zero_longclock) != 0){
+ return TRUE;
+ }
+ /* Otherwise, this is when it was first detected */
+ now = cl_times();
+ diff = now - sig_src->sh_detecttime;
+ lc_store(
+ sig_src->detecttime,
+ sub_longclock(time_longclock(), (longclock_t)diff)
+ );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+static gboolean
+G_SIG_dispatch(GSource * source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GSIGSource* sig_src = (GSIGSource*)source;
+ longclock_t dispstart;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+ CHECK_DISPATCH_DELAY(sig_src);
+
+ sig_src->sh_detecttime = 0UL;
+ sig_src->signal_triggered = FALSE;
+
+ if(sig_src->dispatch) {
+ if(!(sig_src->dispatch(sig_src->signal, sig_src->udata))){
+ G_main_del_SignalHandler(sig_src);
+ CHECK_DISPATCH_TIME(sig_src);
+ return FALSE;
+ }
+ }
+ CHECK_DISPATCH_TIME(sig_src);
+
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_SIG_destroy(GSource* source)
+{
+ GSIGSource* sig_src = (GSIGSource*)source;
+
+ g_assert(IS_SIGSOURCE(sig_src));
+ sig_src->gsourceid = 0;
+
+ if (sig_src->dnotify) {
+ sig_src->dnotify(sig_src->udata);
+ }
+}
+
+/* Find and set the correct mainloop input */
+
+static void
+G_main_signal_handler(int nsig)
+{
+ GSIGSource* sig_src = NULL;
+
+ if(G_main_signal_list == NULL) {
+ g_assert(G_main_signal_list != NULL);
+ return;
+ }
+ if(_NSIG <= nsig) {
+ g_assert(_NSIG > nsig);
+ return;
+ }
+
+ sig_src = G_main_signal_list[nsig];
+
+ if(sig_src == NULL) {
+ /* cl_log(LOG_CRIT, "No handler for signal -%d", nsig); */
+ return;
+ }
+
+ g_assert(IS_SIGSOURCE(sig_src));
+ /* Time from first occurance of signal */
+ if (!sig_src->signal_triggered) {
+ /* Avoid calling longclock_time() on a signal */
+ sig_src->sh_detecttime=cl_times();
+ }
+ sig_src->signal_triggered = TRUE;
+}
+
+/*
+ * Functions to handle child process
+ */
+
+#define WAITALARM 5000L /* milliseconds */
+
+static int alarm_count = 0;
+static void
+G_main_alarm_helper(int nsig)
+{
+ ++alarm_count;
+}
+
+static gboolean
+child_death_dispatch(int sig, gpointer notused)
+{
+ int status;
+ pid_t pid;
+ const int waitflags = WNOHANG;
+ struct sigaction saveaction;
+ int childcount = 0;
+
+ /*
+ * wait3(WNOHANG) isn't _supposed_ to hang
+ * Unfortunately, it seems to do just that on some OSes.
+ *
+ * The workaround is to set an alarm. I don't think for this purpose
+ * that it matters if siginterrupt(SIGALRM) is set TRUE or FALSE since
+ * the tiniest little excuse seems to cause the wait3() to finish.
+ */
+
+ memset(&saveaction, 0, sizeof(saveaction));
+ cl_signal_set_simple_handler(SIGALRM, G_main_alarm_helper, &saveaction);
+
+ alarm_count = 0;
+ cl_signal_set_interrupt(SIGALRM, TRUE);
+ setmsrepeattimer(WAITALARM); /* Might as well be persistent ;-) */
+ while((pid=wait3(&status, waitflags, NULL)) > 0
+ || (pid < 0 && errno == EINTR)) {
+ cancelmstimer();
+ if (pid > 0) {
+ ++childcount;
+ ReportProcHasDied(pid, status);
+ }
+ setmsrepeattimer(WAITALARM); /* Let's be persistent ;-) */
+ }
+ cancelmstimer();
+ cl_signal_set_simple_handler(SIGALRM, saveaction.sa_handler, &saveaction);
+
+ if (pid < 0 && errno != ECHILD) {
+ cl_perror("%s: wait3() failed"
+ , __FUNCTION__);
+ }
+#if defined(DEBUG)
+ if (childcount < 1) {
+ /*
+ * This happens when we receive a SIGCHLD after we clear
+ * 'sig_src->signal_triggered' in G_SIG_dispatch() but
+ * before the last wait3() call returns no child above.
+ */
+ cl_log(LOG_DEBUG, "NOTE: %s called without children to wait on"
+ , __FUNCTION__);
+ }
+#endif
+ if (alarm_count) {
+ cl_log(LOG_ERR
+ , "%s: wait3() call hung %d times. childcount = %d"
+ , __FUNCTION__, alarm_count, childcount);
+ alarm_count = 0;
+ }
+ return TRUE;
+}
+
+void
+set_sigchld_proctrack(int priority, unsigned long maxdisptime)
+{
+ GSIGSource* src = G_main_add_SignalHandler(priority, SIGCHLD
+ , child_death_dispatch, NULL, NULL);
+
+ G_main_setmaxdispatchdelay((GSource*) src, 100);
+ G_main_setmaxdispatchtime((GSource*) src, maxdisptime);
+ G_main_setdescription((GSource*)src, "SIGCHLD");
+ return;
+}
+
+
+/************************************************************
+ * Functions for Trigger inputs
+ ***********************************************************/
+static gboolean G_TRIG_prepare(GSource* source,
+ gint* timeout);
+static gboolean G_TRIG_check(GSource* source);
+
+static gboolean G_TRIG_dispatch(GSource* source,
+ GSourceFunc callback,
+ gpointer user_data);
+static void G_TRIG_destroy(GSource* source);
+
+static GSourceFuncs G_TRIG_SourceFuncs = {
+ G_TRIG_prepare,
+ G_TRIG_check,
+ G_TRIG_dispatch,
+ G_TRIG_destroy
+};
+
+void
+set_TriggerHandler_dnotify(GTRIGSource* trig_src, GDestroyNotify notify)
+{
+ trig_src->dnotify = notify;
+}
+
+/*
+ * Add an Trigger to the gmainloop world...
+ */
+GTRIGSource*
+G_main_add_TriggerHandler(int priority,
+ gboolean (*dispatch)(gpointer user_data),
+ gpointer userdata, GDestroyNotify notify)
+{
+ GTRIGSource* trig_src = NULL;
+ GSource * source = g_source_new(&G_TRIG_SourceFuncs, sizeof(GTRIGSource));
+ gboolean failed = FALSE;
+
+ trig_src = (GTRIGSource*)source;
+
+ trig_src->magno = MAG_GTRIGSOURCE;
+ trig_src->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ trig_src->maxdispatchms = DEFAULT_MAXDISPATCH;
+ trig_src->dispatch = dispatch;
+ trig_src->udata = userdata;
+ trig_src->dnotify = notify;
+ lc_store((trig_src->detecttime), zero_longclock);
+
+ trig_src->manual_trigger = FALSE;
+
+ g_source_set_priority(source, priority);
+ g_source_set_can_recurse(source, FALSE);
+
+ if(!failed) {
+ trig_src->gsourceid = g_source_attach(source, NULL);
+ trig_src->description = "trigger";
+ if (trig_src->gsourceid < 1) {
+ cl_log(LOG_ERR, "G_main_add_TriggerHandler: Could not attach new source (%d)",
+ trig_src->gsourceid);
+ failed = TRUE;
+ }
+ }
+
+ if(failed) {
+ cl_log(LOG_ERR, "G_main_add_TriggerHandler: Trigger handler NOT added");
+ g_source_remove(trig_src->gsourceid);
+ g_source_unref(source);
+ source = NULL;
+ trig_src = NULL;
+ } else {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "G_main_add_TriggerHandler: Added signal manual handler");
+ }
+ }
+
+ return trig_src;
+}
+
+void
+G_main_set_trigger(GTRIGSource* source)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+
+ trig_src->manual_trigger = TRUE;
+ lc_store((trig_src->detecttime), time_longclock());
+}
+
+
+/*
+ * Delete a Trigger from the gmainloop world...
+ */
+gboolean
+G_main_del_TriggerHandler(GTRIGSource* trig_src)
+{
+ GSource* source = (GSource*) trig_src;
+
+ if (trig_src->gsourceid <= 0) {
+ return FALSE;
+ }
+ trig_src->gsourceid = 0;
+ trig_src->manual_trigger = FALSE;
+ g_source_remove(trig_src->gsourceid);
+ g_source_unref(source);
+
+ return TRUE;
+}
+
+static gboolean
+G_TRIG_prepare(GSource* source, gint* timeout)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+
+
+ if (trig_src->manual_trigger
+ && cmp_longclock(lc_fetch(trig_src->detecttime), zero_longclock) == 0) {
+ lc_store((trig_src->detecttime), time_longclock());
+ }
+ return trig_src->manual_trigger;
+}
+
+/*
+ * Did we notice any I/O events?
+ */
+
+static gboolean
+G_TRIG_check(GSource* source)
+{
+
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+ if (trig_src->manual_trigger
+ && cmp_longclock(lc_fetch(trig_src->detecttime), zero_longclock) == 0) {
+ lc_store((trig_src->detecttime), time_longclock());
+ }
+ return trig_src->manual_trigger;
+}
+
+/*
+ * Some kind of event occurred - notify the user.
+ */
+static gboolean
+G_TRIG_dispatch(GSource * source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+ longclock_t dispstart;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+ CHECK_DISPATCH_DELAY(trig_src);
+
+ trig_src->manual_trigger = FALSE;
+
+ if(trig_src->dispatch) {
+ if(!(trig_src->dispatch(trig_src->udata))){
+ G_main_del_TriggerHandler(trig_src);
+ CHECK_DISPATCH_TIME(trig_src);
+ return FALSE;
+ }
+ CHECK_DISPATCH_TIME(trig_src);
+ }
+ lc_store((trig_src->detecttime), zero_longclock);
+
+ return TRUE;
+}
+
+/*
+ * Free up our data, and notify the user process...
+ */
+static void
+G_TRIG_destroy(GSource* source)
+{
+ GTRIGSource* trig_src = (GTRIGSource*)source;
+
+ g_assert(IS_TRIGSOURCE(trig_src));
+ trig_src->gsourceid = 0;
+
+ if (trig_src->dnotify) {
+ trig_src->dnotify(trig_src->udata);
+ }
+}
+/*
+ * Glib mainloop timeout handling code.
+ *
+ * These functions work correctly even if someone resets the
+ * time-of-day clock. The g_main_timeout_add() function does not have
+ * this property, since it relies on gettimeofday().
+ *
+ * Our functions have the same semantics - except they always work ;-)
+ *
+ * This is because we use longclock_t for our time values.
+ *
+ */
+
+
+static gboolean
+Gmain_timeout_prepare(GSource* src, gint* timeout);
+
+static gboolean
+Gmain_timeout_check(GSource* src);
+
+static gboolean
+Gmain_timeout_dispatch(GSource* src, GSourceFunc func, gpointer user_data);
+
+static GSourceFuncs Gmain_timeout_funcs = {
+ Gmain_timeout_prepare,
+ Gmain_timeout_check,
+ Gmain_timeout_dispatch,
+};
+
+
+struct GTimeoutAppend {
+ COMMON_STRUCTSTART;
+ longclock_t nexttime;
+ guint interval;
+};
+
+#define GTIMEOUT(GS) ((struct GTimeoutAppend*)((void*)(GS)))
+
+guint
+Gmain_timeout_add(guint interval
+, GSourceFunc function
+, gpointer data)
+{
+ return Gmain_timeout_add_full(G_PRIORITY_DEFAULT
+ , interval, function, data, NULL);
+}
+
+guint
+Gmain_timeout_add_full(gint priority
+, guint interval
+, GSourceFunc function
+, gpointer data
+, GDestroyNotify notify)
+{
+
+ struct GTimeoutAppend* append;
+
+ GSource* source = g_source_new( &Gmain_timeout_funcs,
+ sizeof(struct GTimeoutAppend));
+
+ append = GTIMEOUT(source);
+ append->magno = MAG_GTIMEOUTSRC;
+ append->maxdispatchms = DEFAULT_MAXDISPATCH;
+ append->maxdispatchdelayms = DEFAULT_MAXDELAY;
+ append->description = "(timeout)";
+ lc_store((append->detecttime), zero_longclock);
+ append->udata = NULL;
+
+ append->nexttime = add_longclock(time_longclock()
+ , msto_longclock(interval));
+ append->interval = interval;
+
+ g_source_set_priority(source, priority);
+
+ g_source_set_can_recurse(source, FALSE);
+
+ g_source_set_callback(source, function, data, notify);
+
+ append->gsourceid = g_source_attach(source, NULL);
+ g_source_unref(source);
+ return append->gsourceid;
+
+}
+
+void
+Gmain_timeout_remove(guint tag)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,tag);
+ struct GTimeoutAppend* append = GTIMEOUT(source);
+
+ if (source == NULL){
+ cl_log(LOG_ERR, "Attempt to remove timeout (%u)"
+ " with NULL source", tag);
+ }else{
+ g_assert(IS_TIMEOUTSRC(append));
+ g_source_remove(tag);
+ }
+
+ return;
+}
+
+/* g_main_loop-style prepare function */
+static gboolean
+Gmain_timeout_prepare(GSource* src, gint* timeout)
+{
+
+ struct GTimeoutAppend* append = GTIMEOUT(src);
+ longclock_t lnow = time_longclock();
+ longclock_t remain;
+
+ g_assert(IS_TIMEOUTSRC(append));
+ if (cmp_longclock(lnow, append->nexttime) >= 0) {
+ *timeout = 0L;
+ return TRUE;
+ }
+ /* This is safe - we will always have a positive result */
+ remain = sub_longclock(append->nexttime, lnow);
+ /* This is also safe - we started out in 'ms' */
+ *timeout = longclockto_ms(remain);
+ return ((*timeout) == 0);
+}
+
+/* g_main_loop-style check function */
+static gboolean
+Gmain_timeout_check (GSource* src)
+{
+ struct GTimeoutAppend* append = GTIMEOUT(src);
+ longclock_t lnow = time_longclock();
+
+ g_assert(IS_TIMEOUTSRC(append));
+ if (cmp_longclock(lnow, append->nexttime) >= 0) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* g_main_loop-style dispatch function */
+static gboolean
+Gmain_timeout_dispatch(GSource* src, GSourceFunc func, gpointer user_data)
+{
+ struct GTimeoutAppend* append = GTIMEOUT(src);
+ longclock_t dispstart;
+ gboolean ret;
+
+ g_assert(IS_TIMEOUTSRC(append));
+ lc_store(append->detecttime, append->nexttime);
+ CHECK_DISPATCH_DELAY(append);
+
+
+ /* Schedule our next dispatch */
+ append->nexttime = add_longclock(time_longclock()
+ , msto_longclock(append->interval));
+
+ /* Then call the user function */
+ ret = func(user_data);
+
+ CHECK_DISPATCH_TIME(append);
+ return ret;
+}
+void
+G_main_setmaxdispatchdelay(GSource* s, unsigned long delayms)
+{
+ GFDSource* fdp = (GFDSource*)s;
+ if (!IS_ONEOFOURS(fdp)) {
+ cl_log(LOG_ERR
+ , "Attempt to set max dispatch delay on wrong object");
+ return;
+ }
+ fdp->maxdispatchdelayms = delayms;
+}
+void
+G_main_setmaxdispatchtime(GSource* s, unsigned long dispatchms)
+{
+ GFDSource* fdp = (GFDSource*)s;
+ if (!IS_ONEOFOURS(fdp)) {
+ cl_log(LOG_ERR
+ , "Attempt to set max dispatch time on wrong object");
+ return;
+ }
+ fdp->maxdispatchms = dispatchms;
+}
+void
+G_main_setdescription(GSource* s, const char * description)
+{
+ GFDSource* fdp = (GFDSource*)s;
+ if (!IS_ONEOFOURS(fdp)) {
+ cl_log(LOG_ERR
+ , "Attempt to set max dispatch time on wrong object");
+ return;
+ }
+ fdp->description = description;
+}
+void
+G_main_setmaxdispatchdelay_id(guint id, unsigned long delayms)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,id);
+
+ if (source) {
+ G_main_setmaxdispatchdelay(source, delayms);
+ }
+}
+void
+G_main_setmaxdispatchtime_id(guint id, unsigned long dispatchms)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,id);
+
+ if (source) {
+ G_main_setmaxdispatchtime(source, dispatchms);
+ }
+}
+void
+G_main_setdescription_id(guint id, const char * description)
+{
+ GSource* source = g_main_context_find_source_by_id(NULL,id);
+
+ if (source) {
+ G_main_setdescription(source, description);
+ }
+}
+void
+G_main_setall_id(guint id, const char * description, unsigned long delay
+, unsigned long elapsed)
+{
+ G_main_setdescription_id(id, description);
+ G_main_setmaxdispatchdelay_id(id, delay);
+ G_main_setmaxdispatchtime_id(id, elapsed);
+}
+
+static void TempProcessRegistered(ProcTrack* p);
+static void TempProcessDied(ProcTrack* p, int status, int signo
+, int exitcode, int waslogged);
+static const char* TempProcessName(ProcTrack* p);
+
+/***********************************************************************
+ * Track our temporary child processes...
+ *
+ * We run no more than one of each type at once.
+ * If we need to run some and one is still running we run another one
+ * when this one exits.
+ *
+ * Requests to run a child process don't add up. So, 3 requests to run
+ * a child while one is running only cause it to be run once more, not
+ * three times.
+ *
+ * The only guarantee is that a new child process will run after a request
+ * was made.
+ *
+ * To create the possibility of running a particular type of child process
+ * call G_main_add_tempproc_trigger().
+ *
+ * To cause it to be run, call G_main_set_trigger().
+ *
+ ***********************************************************************/
+
+static ProcTrack_ops TempProcessTrackOps = {
+ TempProcessDied,
+ TempProcessRegistered,
+ TempProcessName
+};
+
+/*
+ * Information for tracking our generic temporary child processes.
+ */
+struct tempproc_track {
+ const char * procname; /* name of the process*/
+ GTRIGSource* trigger; /* Trigger for this event */
+ int (*fun)(gpointer userdata); /* Function to call
+ * in child process */
+ void (*prefork)(gpointer userdata);/* Call before fork */
+ void (*postfork)(gpointer userdata);/* Call after fork */
+ void (*complete)(gpointer userdata, int status, int signo, int exitcode);/* Call after complete */
+ gpointer userdata; /* Info to pass 'fun' */
+ gboolean isrunning; /* TRUE if child is running */
+ gboolean runagain; /* TRUE if we need to run
+ * again after child process
+ * finishes.
+ */
+};
+static void
+TempProcessRegistered(ProcTrack* p)
+{
+ return; /* Don't need to do much here... */
+}
+
+static void
+TempProcessDied(ProcTrack* p, int status, int signo, int exitcode
+, int waslogged)
+{
+ struct tempproc_track * pt = p->privatedata;
+
+ if (pt->complete) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling 'complete' for temp process %s"
+ , __FUNCTION__, pt->procname);
+ }
+ pt->complete(pt->userdata, status, signo, exitcode);
+ }
+
+ pt->isrunning=FALSE;
+ if (pt->runagain) {
+ pt->runagain=FALSE;
+
+ /* Do it again, Sam! */
+ G_main_set_trigger(pt->trigger);
+
+ /* Note that we set the trigger for this, we don't
+ * fork or call the function now.
+ *
+ * This allows the mainloop scheduler to decide
+ * when the fork should happen according to the priority
+ * of this trigger event - NOT according to the priority
+ * of general SIGCHLD handling.
+ */
+ }
+ p->privatedata = NULL; /* Don't free until trigger is destroyed */
+ return;
+}
+
+static const char *
+TempProcessName(ProcTrack* p)
+{
+ struct tempproc_track * pt = p->privatedata;
+ return pt->procname;
+}
+/*
+ * Make sure only one copy is running at a time...
+ */
+static gboolean
+TempProcessTrigger(gpointer ginfo)
+{
+ struct tempproc_track* info = ginfo;
+ int pid;
+
+ /* Make sure only one copy is running at a time. */
+ /* This avoids concurrency problems. */
+ if (info->isrunning) {
+ info->runagain = TRUE;
+ return TRUE;
+ }
+ info->isrunning = TRUE;
+
+ if (info->prefork) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling prefork for temp process %s"
+ , __FUNCTION__, info->procname);
+ }
+ info->prefork(info->userdata);
+ }
+ if (ANYDEBUG) {
+ cl_log(LOG_DEBUG, "Forking temp process %s", info->procname);
+ }
+ switch ((pid=fork())) {
+ int rc;
+ case -1: cl_perror("%s: Can't fork temporary child"
+ " process [%s]!", __FUNCTION__
+ , info->procname);
+ info->isrunning = FALSE;
+ break;
+
+ case 0: /* Child */
+ if ((rc=info->fun(info->userdata)) == HA_OK) {
+ exit(0);
+ }
+ cl_log(LOG_WARNING
+ , "%s: %s returns %d", __FUNCTION__
+ , info->procname, rc);
+ exit(1);
+ break;
+ default:
+ /* Fall out */;
+
+ }
+ if (pid > 0) {
+ NewTrackedProc(pid, 0, (ANYDEBUG? PT_LOGVERBOSE : PT_LOGNORMAL)
+ , ginfo, &TempProcessTrackOps);
+ if (info->postfork) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: Calling postfork for temp process %s"
+ , __FUNCTION__, info->procname);
+ }
+ info->postfork(info->userdata);
+ }
+ }
+ return TRUE;
+}
+
+static void
+tempproc_destroy_notify(gpointer userdata)
+{
+ if (userdata != NULL) {
+ free(userdata);
+ userdata = NULL;
+ }
+}
+
+GTRIGSource*
+G_main_add_tempproc_trigger(int priority
+, int (*triggerfun) (gpointer p)
+, const char * procname
+, gpointer userdata
+, void (*prefork)(gpointer p)
+, void (*postfork)(gpointer p)
+, void (*complete)(gpointer userdata, int status, int signo, int exitcode))
+{
+
+ struct tempproc_track* p;
+ GTRIGSource* ret;
+
+ p = (struct tempproc_track *) malloc(sizeof(struct tempproc_track));
+ if (p == NULL) {
+ return NULL;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->procname = procname;
+ p->fun = triggerfun;
+ p->userdata = userdata;
+ p->prefork = prefork;
+ p->postfork = postfork;
+ p->complete = complete;
+
+ ret = G_main_add_TriggerHandler(priority
+ , TempProcessTrigger, p, tempproc_destroy_notify);
+
+ if (ret == NULL) {
+ free(p);
+ p = NULL;
+ }else{
+ p->trigger = ret;
+ }
+ return ret;
+}
diff --git a/lib/clplumbing/Makefile.am b/lib/clplumbing/Makefile.am
new file mode 100644
index 0000000..1b504fc
--- /dev/null
+++ b/lib/clplumbing/Makefile.am
@@ -0,0 +1,99 @@
+#
+# plumbing: OCF general plumbing libraries
+#
+# Copyright (C) 2002 Alan Robertson, International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+
+halibdir = $(libdir)/@HB_PKG@
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+## libraries
+
+lib_LTLIBRARIES = libplumb.la libplumbgpl.la
+
+libplumb_la_SOURCES = \
+ base64.c \
+ cl_compress.c \
+ cl_log.c \
+ cl_misc.c \
+ cl_msg.c \
+ cl_msg_types.c \
+ cl_netstring.c \
+ cl_pidfile.c \
+ cl_poll.c \
+ cl_random.c \
+ cl_signal.c \
+ cl_syslog.c \
+ cl_uuid.c \
+ cl_plugin.c \
+ cl_reboot.c \
+ coredumps.c \
+ cpulimits.c \
+ GSource.c \
+ ipcsocket.c \
+ longclock.c \
+ md5.c \
+ mkstemp_mode.c \
+ ocf_ipc.c \
+ proctrack.c \
+ realtime.c \
+ replytrack.c \
+ timers.c \
+ uids.c
+
+libplumb_la_LIBADD = $(top_builddir)/replace/libreplace.la \
+ $(top_builddir)/lib/pils/libpils.la
+libplumb_la_LDFLAGS = -version-info 3:0:1
+
+libplumbgpl_la_SOURCES = setproctitle.c
+libplumbgpl_la_LIBADD = $(top_builddir)/replace/libreplace.la \
+ $(top_builddir)/lib/pils/libpils.la
+libplumbgpl_la_LDFLAGS = -version-info 2:0:0
+
+testdir = $(libdir)/@HB_PKG@
+test_PROGRAMS = ipctest ipctransientclient ipctransientserver base64_md5_test
+test_SCRIPTS = transient-test.sh
+
+ipctest_SOURCES = ipctest.c
+ipctest_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \
+ $(top_builddir)/lib/pils/libpils.la
+
+noinst_HEADERS = ipctransient.h
+
+#ipctransient_SOURCES = ipctransient.c
+#ipctransient_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(top_builddir)/heartbeat/libhbclient.la $(GLIBLIB)
+
+ipctransientclient_SOURCES = ipctransientclient.c ipctransientlib.c
+ipctransientclient_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \
+ $(top_builddir)/lib/pils/libpils.la
+
+ipctransientserver_SOURCES = ipctransientserver.c ipctransientlib.c
+ipctransientserver_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB) \
+ $(top_builddir)/lib/pils/libpils.la
+
+#netstring_test_SOURCES = netstring_test.c
+#netstring_test_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la libhbclient.la $(gliblib)
+
+base64_md5_test_SOURCES = base64_md5_test.c
+base64_md5_test_LDADD = libplumb.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+EXTRA_DIST = $(test_SCRIPTS)
diff --git a/lib/clplumbing/base64.c b/lib/clplumbing/base64.c
new file mode 100644
index 0000000..c8ad325
--- /dev/null
+++ b/lib/clplumbing/base64.c
@@ -0,0 +1,422 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "clplumbing/base64.h"
+
+/*
+ *
+ * Base64 conversion functions.
+ * They convert from a binary array into a single string
+ * in base 64. This is almost (but not quite) like section 5.2 of RFC 1341
+ * The only difference is that we don't care about line lengths.
+ * We do use their encoding algorithm.
+ *
+ */
+
+
+static char b64chars[]
+= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define EQUALS '='
+#define MASK6 (077)
+#define MASK24 (077777777)
+
+
+/* Convert from binary to a base64 string (~ according to RFC1341) */
+int
+binary_to_base64(const void * data, int nbytes, char * output, int outlen)
+{
+ int requiredlen = B64_stringlen(nbytes)+1; /* EOS */
+ char * outptr;
+ const unsigned char * inmax;
+ const unsigned char * inlast;
+ const unsigned char * inptr;
+ int bytesleft;
+
+ if (outlen < requiredlen) {
+ syslog(LOG_ERR, "binary_to_base64: output area too small.");
+ return -1;
+ }
+
+ inptr = data;
+ /* Location of last whole 3-byte chunk */
+ inmax = inptr + ((nbytes / B64inunit)*B64inunit);
+ inlast = inptr + nbytes;
+ outptr = output;
+
+
+ /* Convert whole 3-byte chunks */
+ for (;inptr < inmax; inptr += B64inunit) {
+ unsigned long chunk;
+ unsigned int sixbits;
+
+ chunk = ((*inptr) << 16
+ | ((*(inptr+1)) << 8)
+ | (*(inptr+2))) & MASK24;
+
+ sixbits = (chunk >> 18) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk >> 12) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk >> 6) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ sixbits = (chunk & MASK6);
+ *outptr = b64chars[sixbits]; ++outptr;
+ }
+
+ /* Do we have anything left over? */
+
+ bytesleft = inlast - inptr;
+ if (bytesleft > 0) {
+ /* bytesleft can only be 1 or 2 */
+
+ unsigned long chunk;
+ unsigned int sixbits;
+
+
+ /* Grab first byte */
+ chunk = (*inptr) << 16;
+
+ if (bytesleft == 2) {
+ /* Grab second byte */
+ chunk |= ((*(inptr+1)) << 8);
+ }
+ chunk &= MASK24;
+
+ /* OK, now we have our chunk... */
+ sixbits = (chunk >> 18) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+ sixbits = (chunk >> 12) & MASK6;
+ *outptr = b64chars[sixbits]; ++outptr;
+
+ if (bytesleft == 2) {
+ sixbits = (chunk >> 6) & MASK6;
+ *outptr = b64chars[sixbits];
+ }else{
+ *outptr = EQUALS;
+ }
+ ++outptr;
+
+ *outptr = EQUALS; ++outptr;
+ }
+ *outptr = EOS; /* Don't increment */
+ return (outptr - output);
+}
+
+
+/* This macro is only usable by the base64_to_binary() function.
+ *
+ * There are faster ways of doing this, but I didn't spend the time
+ * to implement one of them. If we have an array of six bit values,
+ * sized by 256 or so, then we could look it up.
+ * FIXME: This is how it really ought to be done...
+ */
+
+static unsigned char b64values [256];
+#define BADVALUE 0xff
+
+static void
+init_b64_values(void)
+{
+ int j;
+ memset(b64values, BADVALUE, sizeof(b64values));
+
+ for (j=0; b64chars[j] != EOS; ++j) {
+ b64values[(int)b64chars[j]] = (unsigned char)j;
+ }
+}
+
+
+#define Char2SixBits(in, out) { \
+ unsigned char ch; \
+ ch = b64values[(unsigned int)in]; \
+ if (ch == BADVALUE) { \
+ syslog(LOG_ERR \
+ , "base64_to_binary: invalid input [%c]!" \
+ , in); \
+ return -1; \
+ } \
+ out = ch; \
+ } \
+
+
+/* Convert from a base64 string (~ according to RFC1341) to binary */
+int
+base64_to_binary(const char * in, int inlen, void * output, int outlen)
+{
+ int maxbinlen = B64_maxbytelen(inlen); /* Worst case size */
+ const char * input = in;
+ const char * lastinput = in + inlen - B64outunit;
+ int equalcount = 0;
+ unsigned char * startout;
+ unsigned char * out;
+ unsigned sixbits1;
+ unsigned sixbits2;
+ unsigned sixbits3;
+ unsigned sixbits4;
+ unsigned long chunk;
+ static int inityet = 0;
+
+ if (!inityet) {
+ inityet=1;
+ init_b64_values();
+ }
+
+ /* Make sure we have enough room */
+ if (outlen < maxbinlen) {
+ int residue = maxbinlen - outlen;
+
+ if (residue > 2
+ || input[inlen-1] != EQUALS
+ || (residue == 2 && input[inlen-2] != EQUALS)) {
+ syslog(LOG_ERR
+ , "base64_to_binary: output area too small.");
+ return -1;
+ }
+ }
+ if ((inlen % 4) != 0) {
+ syslog(LOG_ERR
+ , "base64_to_binary: input length invalid.");
+ return -1;
+ }
+
+ if (inlen == 0) {
+ return 0;
+ }
+
+ /* We have enough space. We are happy :-) */
+
+ startout = out = (unsigned char *)output;
+
+ while (input < lastinput) {
+ Char2SixBits(*input, sixbits1); ++input;
+ Char2SixBits(*input, sixbits2); ++input;
+ Char2SixBits(*input, sixbits3); ++input;
+ Char2SixBits(*input, sixbits4); ++input;
+
+ chunk = (sixbits1 << 18)
+ | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4;
+
+ *out = ((chunk >> 16) & 0xff); ++out;
+ *out = ((chunk >> 8) & 0xff); ++out;
+ *out = (chunk & 0xff); ++out;
+ }
+
+ /* Process last 4 chars of input (1 to 3 bytes of output) */
+
+ /* The first two input chars must be normal chars */
+ Char2SixBits(*input, sixbits1); ++input;
+ Char2SixBits(*input, sixbits2); ++input;
+
+ /* We should find one of: (char,char) (char,=) or (=,=) */
+ /* We then output: (3 bytes) (2 bytes) (1 byte) */
+
+ if (*input == EQUALS) {
+ /* The (=,=): 1 byte case */
+ equalcount=2;
+ sixbits3 = 0;
+ sixbits4 = 0;
+ /* We assume the 2nd char is an = sign :-) */
+ }else{
+ /* We have either the (char,char) or (char,=) case */
+ Char2SixBits(*input, sixbits3); ++input;
+ if (*input == EQUALS) {
+ /* The (char, =): 2 bytes case */
+ equalcount=1;
+ sixbits4 = 0;
+ }else{
+ /* The (char, char): 3 bytes case */
+ Char2SixBits(*input, sixbits4); ++input;
+ equalcount=0;
+ }
+ }
+
+ chunk = (sixbits1 << 18)
+ | (sixbits2 << 12) | (sixbits3 << 6) | sixbits4;
+
+ /* We always have one more char to output... */
+ *out = ((chunk >> 16) & 0xff); ++out;
+
+ if (equalcount < 2) {
+ /* Zero or one equal signs: total of 2 or 3 bytes output */
+ *out = ((chunk >> 8) & 0xff); ++out;
+
+ if (equalcount < 1) {
+ /* No equal signs: total of 3 bytes output */
+ *out = (chunk & 0xff); ++out;
+ }
+ }
+
+ return out - startout;
+}
+
+#if 0
+#define RAND(upb) (rand()%(upb))
+
+void dumpbin(void * Bin, int length);
+void randbin(void * Bin, int length);
+
+void
+dumpbin(void * Bin, int length)
+{
+ unsigned char * bin = Bin;
+
+ int j;
+
+ for (j=0; j < length; ++j) {
+ fprintf(stderr, "%02x ", bin[j]);
+ if ((j % 32) == 31) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+void
+randbin(void * Bin, int length)
+{
+ unsigned char * bin = Bin;
+ int j;
+
+ for (j=0; j < length; ++j) {
+ bin[j] = (unsigned char)RAND(256);
+ }
+
+}
+
+#define MAXLEN 320
+#define MAXSTRING B64_stringlen(MAXLEN)+1
+#define MAXITER 300000
+int
+main(int argc, char ** argv)
+{
+ int errcount = 0;
+ char origbin[MAXLEN+1];
+ char sourcebin[MAXLEN+1];
+ char destbin[MAXLEN+1];
+ char deststr[MAXSTRING];
+ int maxiter = MAXITER;
+ int j;
+
+ for (j=0; j < maxiter; ++j) {
+ int iterlen = RAND(MAXLEN+1);
+ int slen;
+ int blen;
+
+ if ((j%100) == 99) {
+ fprintf(stderr, "+");
+ }
+
+ memset(origbin, 0, MAXLEN+1);
+ memset(sourcebin, 0, MAXLEN+1);
+ memset(destbin, 0, MAXLEN+1);
+ randbin(origbin, iterlen);
+ origbin[iterlen] = 0x1;
+ memcpy(sourcebin, origbin, iterlen);
+ sourcebin[iterlen] = 0x2;
+ slen = binary_to_base64(sourcebin, iterlen, deststr, MAXSTRING);
+ if (slen < 0) {
+ fprintf(stderr
+ , "binary_to_base64 failure: length %d\n"
+ , iterlen);
+ ++errcount;
+ continue;
+ }
+ if (strlen(deststr) != slen) {
+ fprintf(stderr
+ , "binary_to_base64 failure: length was %d (strlen) vs %d (ret value)\n"
+ , strlen(deststr), slen);
+ fprintf(stderr, "binlen: %d, deststr: [%s]\n"
+ , iterlen, deststr);
+ continue;
+ ++errcount;
+ }
+ destbin[iterlen] = 0x3;
+ blen = base64_to_binary(deststr, slen, destbin, iterlen);
+
+ if (blen != iterlen) {
+ fprintf(stderr
+ , "base64_to_binary failure: length was %d vs %d\n"
+ , blen, iterlen);
+ dumpbin(origbin, iterlen);
+ fprintf(stderr
+ , "Base64 intermediate: [%s]\n", deststr);
+ ++errcount;
+ continue;
+ }
+ if (memcmp(destbin, origbin, iterlen) != 0) {
+ fprintf(stderr
+ , "base64_to_binary mismatch. Orig:\n");
+ dumpbin(origbin, iterlen);
+ fprintf(stderr, "Dest:\n");
+ dumpbin(destbin, iterlen);
+ fprintf(stderr
+ , "Base64 intermediate: [%s]\n", deststr);
+ ++errcount;
+ }
+ if (destbin[iterlen] != 0x3) {
+ fprintf(stderr
+ , "base64_to_binary corruption. dest byte: 0x%02x\n"
+ , destbin[iterlen]);
+ ++errcount;
+ }
+
+ if (sourcebin[iterlen] != 0x2) {
+ fprintf(stderr
+ , "base64_to_binary corruption. source byte: 0x%02x\n"
+ , sourcebin[iterlen]);
+ ++errcount;
+ }
+ sourcebin[iterlen] = 0x0;
+ origbin[iterlen] = 0x0;
+ if (memcmp(sourcebin, origbin, MAXLEN+1) != 0) {
+ fprintf(stderr
+ , "base64_to_binary corruption. origbin:\n");
+ dumpbin(origbin, MAXLEN+1);
+ fprintf(stderr, "sourcebin:\n");
+ dumpbin(sourcebin, MAXLEN+1);
+ ++errcount;
+ }
+
+ }
+
+ fprintf(stderr, "\n%d iterations, %d errors.\n"
+ , maxiter, errcount);
+
+ return errcount;
+}
+/* HA-logging function */
+void
+ha_log(int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s\n", buf);
+
+}
+#endif
diff --git a/lib/clplumbing/base64_md5_test.c b/lib/clplumbing/base64_md5_test.c
new file mode 100644
index 0000000..d536776
--- /dev/null
+++ b/lib/clplumbing/base64_md5_test.c
@@ -0,0 +1,113 @@
+/* File: base64_md5_test.c
+ * Description: base64 and md5 algorithm tests
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2005 International Business Machines
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/md5.h>
+
+#define MD5LEN 16 /* md5 buffer */
+#define BASE64_BUF_LEN 32
+
+/* gcc -o base64_md5_test base64_md5_test.c -lplumb */
+int main(void)
+{
+ int error_count = 0;
+
+ const char base64_encode[] = "YWJjZGVmZ2g=";
+ const char raw_data[] = "abcdefgh";
+
+ /* A test case from
+ * RFC 1321 - The MD5 Message-Digest Algorithm
+ */
+ const char * data1 = "message digest";
+ const char digest_rfc1321[(MD5LEN+1)*2+1]
+ = "f96b697d7cb7938d525a2f31aaf161d0";
+
+ /* A test case from
+ * RFC 2104 - HMAC: Keyed-Hashing for Message Authentication
+ */
+ const char *key = "Jefe";
+ const char *data2 = "what do ya want for nothing?";
+ const char digest_rfc2104[(MD5LEN+1)*2+1]
+ = "750c783e6ab0b503eaa86e310a5db738";
+
+ char buffer_tmp[BASE64_BUF_LEN];
+
+ char md[(MD5LEN+1)*2+1];
+ unsigned char digest[MD5LEN];
+ char * md_tmp;
+ int rc,i;
+
+ /* base64 encode test */
+ binary_to_base64(raw_data, strlen(raw_data), buffer_tmp
+ , BASE64_BUF_LEN);
+ /* printf("base64_encode = %s\n", buffer_tmp); */
+ if (0 != strncmp(buffer_tmp, base64_encode, strlen(buffer_tmp)) ) {
+ cl_log(LOG_ERR, "binary_to_base64 works bad.");
+ error_count++;
+ }
+
+ /* base64 decode test */
+ memset(buffer_tmp, 0, BASE64_BUF_LEN);
+ base64_to_binary(base64_encode, strlen(base64_encode)
+ , buffer_tmp, BASE64_BUF_LEN);
+ /* printf("decoded data= %s\n", buffer_tmp); */
+ if (0 != strncmp(buffer_tmp, raw_data, strlen(buffer_tmp)) ) {
+ cl_log(LOG_ERR, "base64_to_binary works bad.");
+ error_count++;
+ }
+
+ rc = MD5((const unsigned char *)data1, strlen(data1), digest);
+
+ md_tmp = md;
+ for (i = 0; i < MD5LEN; i++) {
+ snprintf(md_tmp, sizeof(md), "%02x", digest[i]);
+ md_tmp += 2;
+ }
+ *md_tmp = '\0';
+ /* printf("rc=%d MD5=%s\n", rc, md); */
+
+ if (0 != strncmp(md, digest_rfc1321, MD5LEN*2) ) {
+ cl_log(LOG_ERR, "The md5-rfc1321 algorithm works bad.");
+ error_count++;
+ }
+
+ rc = HMAC((const unsigned char *)key, strlen(key)
+ , (const unsigned char *)data2, strlen(data2), digest);
+ md_tmp = md;
+ for (i = 0; i < MD5LEN; i++) {
+ sprintf(md_tmp,"%02x", digest[i]);
+ md_tmp += 2;
+ }
+ *md_tmp = '\0';
+ /* printf("rc=%d HMAC=%s\n", rc, md); */
+
+ if (0 != strncmp(md, digest_rfc2104, MD5LEN*2) ) {
+ cl_log(LOG_ERR, "The md5-rfc2104 algorithm works bad.");
+ error_count++;
+ }
+
+ (void) rc; /* Suppress -Werror=unused-but-set-variable */
+ return error_count;
+}
diff --git a/lib/clplumbing/cl_compress.c b/lib/clplumbing/cl_compress.c
new file mode 100644
index 0000000..6b56ad6
--- /dev/null
+++ b/lib/clplumbing/cl_compress.c
@@ -0,0 +1,500 @@
+
+/*
+ * compress.c: Compression functions for Linux-HA
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Compression is designed to handle big messages, right now with 4 nodes
+ * cib message can go up to 64 KB or more. I expect much larger messages
+ * when the number of node increase. This makes message compression necessary.
+ *
+ *
+ * Compression is handled in field level. One can add a struct field using
+ * ha_msg_addstruct() -- the field will not get compressed, or using
+ * ha_msg_addstruct_compress(), and the field will get compressed when
+ * the message is converted to wire format, i.e. when msg2wirefmt() is called.
+ * The compressed field will stay compressed until it reached the desination.
+ * It will finally decompressed when the user start to get the field value.
+ * It is designed this way so that the compression/decompression only happens
+ * in end users so that heartbeat itself can save cpu cycle and memory.
+ * (more info about compression can be found in cl_msg_types.c about FT_COMPRESS
+ * FT_UNCOMPRESS types)
+ *
+ * compression has another legacy mode, which is there so it can be compatible
+ * to old ways of compression. In the old way, no field is compressed individually
+ * and the messages is compressed before it is sent out, and it will be decompressed
+ * in the receiver side immediately. So in each IPC channel, the message is compressed
+ * and decompressed once. This way will cost a lot of cpu time and memory and it is
+ * discouraged.
+ *
+ * If use_traditional_compression is true, then it is using the legacy mode, otherwise
+ * it is using the new compression. For back compatibility, the default is legacy mode.
+ *
+ * The real compression work is done by compression plugins. There are two plugins right
+ * now: zlib and bz2, they are in lib/plugins/compress
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <assert.h>
+#include <glib.h>
+#include <compress.h>
+#include <ha_msg.h>
+#include <clplumbing/netstring.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+#define COMPRESSED_FIELD "_compressed_payload"
+#define COMPRESS_NAME "_compression_algorithm"
+#define HACOMPRESSNAME "HA_COMPRESSION"
+#define DFLT_COMPRESS_PLUGIN "bz2"
+
+static struct hb_compress_fns* msg_compress_fns = NULL;
+static char* compress_name = NULL;
+GHashTable* CompressFuncs = NULL;
+
+static PILGenericIfMgmtRqst Reqs[] =
+ {
+ {"compress", &CompressFuncs, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+static PILPluginUniv* CompressPIsys = NULL;
+
+static int
+init_pluginsys(void){
+
+ if (CompressPIsys) {
+ return TRUE;
+ }
+
+ CompressPIsys = NewPILPluginUniv(HA_PLUGIN_DIR);
+
+ if (CompressPIsys) {
+ if (PILLoadPlugin(CompressPIsys, PI_IFMANAGER, "generic", Reqs)
+ != PIL_OK){
+ cl_log(LOG_ERR, "generic plugin load failed\n");
+ DelPILPluginUniv(CompressPIsys);
+ CompressPIsys = NULL;
+ }
+ }else{
+ cl_log(LOG_ERR, "pi univ creation failed\n");
+ }
+ return CompressPIsys != NULL;
+
+}
+
+int
+cl_compress_remove_plugin(const char* pluginname)
+{
+ return HA_OK;
+}
+
+int
+cl_compress_load_plugin(const char* pluginname)
+{
+ struct hb_compress_fns* funcs = NULL;
+
+ if (!init_pluginsys()){
+ return HA_FAIL;
+ }
+
+ if ((funcs = g_hash_table_lookup(CompressFuncs, pluginname))
+ == NULL){
+ if (PILPluginExists(CompressPIsys, HB_COMPRESS_TYPE_S,
+ pluginname) == PIL_OK){
+ PIL_rc rc;
+ if ((rc = PILLoadPlugin(CompressPIsys,
+ HB_COMPRESS_TYPE_S,
+ pluginname,
+ NULL))!= PIL_OK){
+ cl_log(LOG_ERR,
+ "Cannot load compress plugin %s[%s]",
+ pluginname,
+ PIL_strerror(rc));
+ return HA_FAIL;
+ }
+ funcs = g_hash_table_lookup(CompressFuncs,
+ pluginname);
+ }
+
+ }
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "Compression module(%s) not found", pluginname);
+ return HA_FAIL;
+ }
+
+ /* set the environment variable so that later programs can
+ * load the appropriate plugin
+ */
+ setenv(HACOMPRESSNAME,pluginname,1);
+ msg_compress_fns = funcs;
+
+ return HA_OK;
+}
+
+int
+cl_set_compress_fns(const char* pluginname)
+{
+ /* this function was unnecessary duplication of the
+ * code in cl_compress_load_plugin
+ */
+ return cl_compress_load_plugin(pluginname);
+}
+
+struct hb_compress_fns*
+cl_get_compress_fns(void)
+{
+ static int try_dflt = 1;
+
+ if (try_dflt && !msg_compress_fns) {
+ try_dflt = 0;
+ cl_log(LOG_INFO, "%s: user didn't set compression type, "
+ "loading %s plugin",
+ __FUNCTION__, DFLT_COMPRESS_PLUGIN);
+ cl_compress_load_plugin(DFLT_COMPRESS_PLUGIN);
+ }
+ return msg_compress_fns;
+}
+
+static struct hb_compress_fns*
+get_compress_fns(const char* pluginname)
+{
+ struct hb_compress_fns* funcs = NULL;
+
+ if (cl_compress_load_plugin(pluginname) != HA_OK){
+ cl_log(LOG_ERR, "%s: loading compression module"
+ "(%s) failed",
+ __FUNCTION__, pluginname);
+ return NULL;
+ }
+
+ funcs = g_hash_table_lookup(CompressFuncs, pluginname);
+ return funcs;
+}
+
+void cl_realtime_malloc_check(void);
+
+char*
+cl_compressmsg(struct ha_msg* m, size_t* len)
+{
+ char* src;
+ char* dest;
+ size_t destlen;
+ int rc;
+ char* ret = NULL;
+ struct ha_msg* tmpmsg;
+ size_t datalen;
+
+ destlen = MAXMSG;
+
+ dest = malloc(destlen);
+ if (!dest) {
+ cl_log(LOG_ERR, "%s: failed to allocate destination buffer",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ if (msg_compress_fns == NULL){
+ cl_log(LOG_ERR, "%s: msg_compress_fns is NULL!",
+ __FUNCTION__);
+ goto out;
+ }
+ if ( get_netstringlen(m) > MAXUNCOMPRESSED
+ || get_stringlen(m) > MAXUNCOMPRESSED){
+ cl_log(LOG_ERR, "%s: msg too big(stringlen=%d,"
+ "netstringlen=%d)",
+ __FUNCTION__,
+ get_stringlen(m),
+ get_netstringlen(m));
+ goto out;
+ }
+
+
+ if ((src = msg2wirefmt_noac(m, &datalen)) == NULL){
+ cl_log(LOG_ERR,"%s: converting msg"
+ " to wirefmt failed", __FUNCTION__);
+ goto out;
+ }
+
+ rc = msg_compress_fns->compress(dest, &destlen,
+ src, datalen);
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ free(src);
+
+ tmpmsg =ha_msg_new(0);
+ rc = ha_msg_addbin(tmpmsg, COMPRESSED_FIELD, dest, destlen)/*discouraged function*/;
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: adding binary to msg failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ rc = ha_msg_add(tmpmsg, COMPRESS_NAME,
+ msg_compress_fns->getname());
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: adding compress name to msg failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+
+ ret = msg2netstring(tmpmsg, len);
+ ha_msg_del(tmpmsg);
+
+#if 0
+ cl_log(LOG_INFO, "------original stringlen=%d, netstringlen=%d,"
+ "compressed_datalen=%d,current len=%d",
+ get_stringlen(m), get_netstringlen(m),(int)destlen, (int)*len);
+
+#endif
+
+out:
+ if (dest) {
+ free(dest);
+ }
+
+ return ret;
+}
+
+
+gboolean
+is_compressed_msg(struct ha_msg* m)
+{
+ if( cl_get_binary(m, COMPRESSED_FIELD, NULL) /*discouraged function*/
+ != NULL){
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+/* the decompressmsg function is not exactly the reverse
+ * operation of compressmsg, it starts when the prorgram
+ * detects there is compressed_field in a msg
+ */
+
+struct ha_msg*
+cl_decompressmsg(struct ha_msg* m)
+{
+ const char* src;
+ size_t srclen;
+ char *dest = NULL;
+ size_t destlen = MAXUNCOMPRESSED;
+ int rc;
+ struct ha_msg* ret = NULL;
+ const char* decompress_name;
+ struct hb_compress_fns* funcs = NULL;
+
+ dest = malloc(destlen);
+
+ if (!dest) {
+ cl_log(LOG_ERR, "%s: Failed to allocate buffer.", __FUNCTION__);
+ return NULL;
+ }
+
+ if (m == NULL){
+ cl_log(LOG_ERR, "%s: NULL message", __FUNCTION__);
+ goto out;
+ }
+ src = cl_get_binary(m, COMPRESSED_FIELD, &srclen)/*discouraged function*/;
+ if (src == NULL){
+ cl_log(LOG_ERR, "%s: compressed-field is NULL",
+ __FUNCTION__);
+ goto out;
+ }
+
+ if (srclen > MAXMSG){
+ cl_log(LOG_ERR, "%s: field too long(%d)",
+ __FUNCTION__, (int)srclen);
+ goto out;
+ }
+
+ decompress_name = ha_msg_value(m, COMPRESS_NAME);
+ if (decompress_name == NULL){
+ cl_log(LOG_ERR, "compress name not found");
+ goto out;
+ }
+
+
+ funcs = get_compress_fns(decompress_name);
+
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "%s: compress method(%s) is not"
+ " supported in this machine",
+ __FUNCTION__, decompress_name);
+ goto out;
+ }
+
+ rc = funcs->decompress(dest, &destlen, src, srclen);
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ ret = wirefmt2msg(dest, destlen, 0);
+
+#if 0
+ cl_log(LOG_INFO, "%s: srclen =%d, destlen=%d",
+ __FUNCTION__,
+ srclen, destlen);
+#endif
+
+out:
+ if (dest) {
+ free(dest);
+ }
+
+ return ret;
+}
+
+
+int
+cl_decompress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen)
+{
+ char* value;
+ int vallen;
+ int rc;
+ const char* decompress_name;
+ struct hb_compress_fns* funcs;
+
+ if ( msg == NULL|| index >= msg->nfields){
+ cl_log(LOG_ERR, "%s: wrong argument",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ value = msg->values[index];
+ vallen = msg->vlens[index];
+
+ decompress_name = ha_msg_value(msg, COMPRESS_NAME);
+ if (decompress_name == NULL){
+ cl_log(LOG_ERR, "compress name not found");
+ return HA_FAIL;
+ }
+
+
+ funcs = get_compress_fns(decompress_name);
+
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "%s: compress method(%s) is not"
+ " supported in this machine",
+ __FUNCTION__, decompress_name);
+ return HA_FAIL;
+ }
+
+ rc = funcs->decompress(buf, buflen, value, vallen);
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+
+int
+cl_compress_field(struct ha_msg* msg, int index, char* buf, size_t* buflen)
+{
+ char* src;
+ size_t srclen;
+ int rc;
+
+ if ( msg == NULL|| index >= msg->nfields
+ || msg->types[index] != FT_UNCOMPRESS){
+ cl_log(LOG_ERR, "%s: wrong argument",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if (msg_compress_fns == NULL){
+ if (compress_name == NULL){
+ compress_name = getenv(HACOMPRESSNAME);
+ }
+
+ if (compress_name == NULL){
+ cl_log(LOG_ERR, "%s: no compression module name found",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if(cl_set_compress_fns(compress_name) != HA_OK){
+ cl_log(LOG_ERR, "%s: loading compression module failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+ }
+
+ if (msg_compress_fns == NULL){
+ cl_log(LOG_ERR, "%s: msg_compress_fns is NULL!",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ src = msg2wirefmt_noac(msg->values[index], &srclen);
+ if (src == NULL){
+ cl_log(LOG_ERR,"%s: converting msg"
+ " to wirefmt failed", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ rc = msg_compress_fns->compress(buf, buflen,
+ src, srclen);
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+
+ rc = ha_msg_mod(msg, COMPRESS_NAME,
+ msg_compress_fns->getname());
+
+ if (rc != HA_OK){
+ cl_log(LOG_ERR, "%s: adding compress name to msg failed",
+ __FUNCTION__);
+ return HA_FAIL;;
+ }
+
+ free(src);
+ src = NULL;
+
+ return HA_OK;
+
+}
diff --git a/lib/clplumbing/cl_log.c b/lib/clplumbing/cl_log.c
new file mode 100644
index 0000000..213e760
--- /dev/null
+++ b/lib/clplumbing/cl_log.c
@@ -0,0 +1,1261 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/utsname.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/loggingdaemon.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/uids.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_syslog.h>
+#include <ha_msg.h>
+
+#ifndef MAXLINE
+# define MAXLINE 512
+#endif
+/*
+ * <syslog.h> might not contain LOG_PRI...
+ * So, we define it ourselves, or error out if we can't...
+ */
+
+#ifndef LOG_PRI
+# ifdef LOG_PRIMASK
+ /* David Lee <T.D.Lee@durham.ac.uk> reports this works on Solaris */
+# define LOG_PRI(p) ((p) & LOG_PRIMASK)
+# else
+# error "Syslog.h does not define either LOG_PRI or LOG_PRIMASK."
+# endif
+#endif
+
+#define DFLT_ENTITY "cluster"
+#define DFLT_PREFIX ""
+#define NULLTIME 0
+#define QUEUE_SATURATION_FUZZ 10
+
+static IPC_Channel* logging_daemon_chan = NULL;
+static gboolean syslogformatfile = TRUE;
+/*
+ * If true, then output messages more or less like this...
+ * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null
+ */
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+
+static int LogToLoggingDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+static IPC_Message* ChildLogIPCMessage(int priority, const char *buf, int bstrlen,
+ gboolean use_priority_str, IPC_Channel* ch);
+static void FreeChildLogIPCMessage(IPC_Message* msg);
+static gboolean send_dropped_message(gboolean use_pri_str, IPC_Channel *chan);
+static int cl_set_logging_wqueue_maxlen(int qlen);
+
+static int use_logging_daemon = FALSE;
+static int conn_logd_time = 0;
+static char cl_log_entity[MAXENTITY]= DFLT_ENTITY;
+static char cl_log_syslogprefix[MAXENTITY] = DFLT_PREFIX;
+static char common_log_entity[MAXENTITY]= DFLT_ENTITY;
+static int cl_log_facility = LOG_USER;
+static int use_buffered_io = 0;
+
+static void cl_opensyslog(void);
+static int syslog_enabled = 0;
+static int stderr_enabled = 0;
+static int stdout_enabled = 0;
+static const char* logfile_name = NULL;
+static const char* debugfile_name = NULL;
+static int cl_process_pid = -1;
+int debug_level = 0;
+static GDestroyNotify destroy_logging_channel_callback;
+static void (*create_logging_channel_callback)(IPC_Channel* chan);
+static gboolean logging_chan_in_main_loop = FALSE;
+
+/***********************
+ *debug use only, do not use this function in your program
+ */
+IPC_Channel * get_log_chan(void);
+
+IPC_Channel* get_log_chan(void){
+ return logging_daemon_chan;
+}
+/*************************/
+
+/**************************
+ * check if the fd is in use for logging
+ **************************/
+int
+cl_log_is_logd_fd(int fd)
+{
+ return logging_daemon_chan && (
+ fd == logging_daemon_chan->ops->get_send_select_fd(logging_daemon_chan)
+ ||
+ fd == logging_daemon_chan->ops->get_recv_select_fd(logging_daemon_chan)
+ );
+}
+
+void
+cl_log_enable_stderr(int truefalse)
+{
+ stderr_enabled = truefalse;
+}
+
+void
+cl_log_enable_stdout(int truefalse)
+{
+ stdout_enabled = truefalse;
+}
+
+void
+cl_log_set_uselogd(int truefalse)
+{
+ use_logging_daemon = truefalse;
+}
+void
+cl_log_enable_syslog_filefmt(int truefalse)
+{
+ syslogformatfile = (gboolean)truefalse;
+}
+
+gboolean
+cl_log_get_uselogd(void)
+{
+ return use_logging_daemon;
+}
+
+
+int
+cl_log_get_logdtime(void)
+{
+ return conn_logd_time;
+
+}
+
+void
+cl_log_set_logdtime(int logdtime)
+{
+ conn_logd_time = logdtime;
+ return;
+}
+
+void
+cl_log_use_buffered_io(int truefalse)
+{
+ use_buffered_io = truefalse;
+ cl_log_close_log_files();
+}
+
+#define ENVPRE "HA_"
+
+#define ENV_HADEBUGVAL "HA_debug"
+#define ENV_LOGFENV "HA_logfile" /* well-formed log file :-) */
+#define ENV_DEBUGFENV "HA_debugfile" /* Debug log file */
+#define ENV_LOGFACILITY "HA_logfacility"/* Facility to use for logger */
+#define ENV_SYSLOGFMT "HA_syslogmsgfmt"/* TRUE if we should use syslog message formatting */
+#define ENV_LOGDAEMON "HA_use_logd"
+#define ENV_CONNINTVAL "HA_conn_logd_time"
+#define TRADITIONAL_COMPRESSION "HA_traditional_compression"
+#define COMPRESSION "HA_compression"
+
+static void
+inherit_compress(void)
+{
+ char* inherit_env = NULL;
+
+ inherit_env = getenv(TRADITIONAL_COMPRESSION);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean value;
+
+ if (cl_str_to_boolean(inherit_env, &value)!= HA_OK){
+ cl_log(LOG_ERR, "inherit traditional_compression failed");
+ }else{
+ cl_set_traditional_compression(value);
+ }
+ }
+
+}
+
+void
+cl_inherit_logging_environment(int logqueuemax)
+{
+ char * inherit_env = NULL;
+
+ /* Donnot need to free the return pointer from getenv */
+ inherit_env = getenv(ENV_HADEBUGVAL);
+ if (inherit_env != NULL && atoi(inherit_env) != 0 ) {
+ debug_level = atoi(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_LOGFENV);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ cl_log_set_logfile(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_DEBUGFENV);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ cl_log_set_debugfile(inherit_env);
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_LOGFACILITY);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ int facility = -1;
+ facility = cl_syslogfac_str2int(inherit_env);
+ if ( facility >= 0 ) {
+ cl_log_set_facility(facility);
+ }
+ inherit_env = NULL;
+ }
+
+ inherit_env = getenv(ENV_SYSLOGFMT);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean truefalse;
+ if (cl_str_to_boolean(inherit_env, &truefalse) == HA_OK) {
+ cl_log_enable_syslog_filefmt(truefalse);
+ }
+ }
+
+ inherit_env = getenv(ENV_LOGDAEMON);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ gboolean uselogd;
+ cl_str_to_boolean(inherit_env, &uselogd);
+ cl_log_set_uselogd(uselogd);
+ if (uselogd) {
+ if (logqueuemax > 0) {
+ cl_set_logging_wqueue_maxlen(logqueuemax);
+ }
+ }
+ }
+
+ inherit_env = getenv(ENV_CONNINTVAL);
+ if (inherit_env != NULL && *inherit_env != EOS) {
+ int logdtime;
+ logdtime = cl_get_msec(inherit_env);
+ cl_log_set_logdtime(logdtime);
+ }
+
+ inherit_compress();
+ return;
+}
+
+
+static void
+add_logging_channel_mainloop(IPC_Channel* chan)
+{
+ GCHSource* chp=
+ G_main_add_IPC_Channel( G_PRIORITY_DEFAULT,
+ chan,
+ FALSE,
+ NULL,
+ NULL,
+ destroy_logging_channel_callback);
+
+ if (chp == NULL){
+ cl_log(LOG_INFO, "adding logging channel to mainloop failed");
+ }
+
+ logging_chan_in_main_loop = TRUE;
+
+
+ return;
+}
+
+static void
+remove_logging_channel_mainloop(gpointer userdata)
+{
+ logging_chan_in_main_loop = FALSE;
+
+ return;
+}
+
+
+static IPC_Channel*
+create_logging_channel(void)
+{
+ GHashTable* attrs;
+ char path[] = IPC_PATH_ATTR;
+ char sockpath[] = HA_LOGDAEMON_IPC;
+ IPC_Channel* chan;
+ static gboolean complained_yet = FALSE;
+
+ attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(attrs, path, sockpath);
+
+ chan =ipc_channel_constructor(IPC_ANYTYPE, attrs);
+
+ g_hash_table_destroy(attrs);
+
+ if (chan == NULL) {
+ cl_log(LOG_ERR, "create_logging_channel:"
+ "contructing ipc channel failed");
+ return NULL;
+ }
+
+ if (chan->ops->initiate_connection(chan) != IPC_OK) {
+ if (!complained_yet) {
+ complained_yet = TRUE;
+ cl_log(LOG_WARNING, "Initializing connection"
+ " to logging daemon failed."
+ " Logging daemon may not be running");
+ }
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+
+ return NULL;
+ }
+ complained_yet = FALSE;
+
+ if (create_logging_channel_callback){
+ create_logging_channel_callback(chan);
+ }
+
+
+ return chan;
+
+}
+
+gboolean
+cl_log_test_logd(void)
+{
+ IPC_Channel* chan = logging_daemon_chan;
+
+ if (chan && chan->ops->get_chan_status(chan) == IPC_CONNECT){
+ return TRUE;
+ }
+ if (chan ){
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = chan = NULL;
+ }
+
+ logging_daemon_chan = chan = create_logging_channel();
+
+ if (chan == NULL){
+ return FALSE;
+ }
+
+ if(chan->ops->get_chan_status(chan) != IPC_CONNECT){
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = chan = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+/* FIXME: This is way too ugly to bear */
+
+void
+cl_log_set_facility(int facility)
+{
+ if (syslog_enabled && facility == cl_log_facility) {
+ return;
+ }
+ cl_log_facility = facility;
+ closelog();
+ syslog_enabled = 0;
+ if (facility > 0) {
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_entity(const char * entity)
+{
+ if (entity == NULL) {
+ entity = DFLT_ENTITY;
+ }
+ strncpy(cl_log_entity, entity, MAXENTITY);
+ cl_log_entity[MAXENTITY-1] = '\0';
+ if (syslog_enabled) {
+ syslog_enabled = 0;
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_syslogprefix(const char *prefix)
+{
+ if (prefix == NULL) {
+ prefix = DFLT_PREFIX;
+ }
+ strncpy(cl_log_syslogprefix, prefix, MAXENTITY);
+ cl_log_syslogprefix[MAXENTITY-1] = '\0';
+ if (syslog_enabled) {
+ syslog_enabled = 0;
+ cl_opensyslog();
+ }
+}
+
+void
+cl_log_set_logfile(const char * path)
+{
+ if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+ path = NULL;
+ }
+ logfile_name = path;
+ cl_log_close_log_files();
+}
+void
+cl_log_set_debugfile(const char * path)
+{
+ if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+ path = NULL;
+ }
+ debugfile_name = path;
+ cl_log_close_log_files();
+}
+
+
+/*
+ * This function sets two callback functions.
+ * One for creating a channel and
+ * the other for destroying a channel*
+ */
+int
+cl_log_set_logd_channel_source( void (*create_callback)(IPC_Channel* chan),
+ GDestroyNotify destroy_callback)
+{
+ IPC_Channel* chan = logging_daemon_chan ;
+
+ if (destroy_callback == NULL){
+ destroy_logging_channel_callback = remove_logging_channel_mainloop;
+ }else{
+ destroy_logging_channel_callback = destroy_callback;
+ }
+
+ if (create_callback == NULL){
+ create_logging_channel_callback = add_logging_channel_mainloop;
+ }else{
+ create_logging_channel_callback = create_callback;
+ }
+
+ if (chan != NULL
+ && chan->ops->get_chan_status(chan) == IPC_CONNECT){
+ add_logging_channel_mainloop(chan);
+ }
+
+ return 0;
+}
+
+const char *
+prio2str(int priority)
+{
+ static const char *log_prio[8] = {
+ "EMERG",
+ "ALERT",
+ "CRIT",
+ "ERROR",
+ "WARN",
+ "notice",
+ "info",
+ "debug"
+ };
+ int logpri;
+
+ logpri = LOG_PRI(priority);
+
+ return (logpri < 0 || logpri >= DIMOF(log_prio)) ?
+ "(undef)" : log_prio[logpri];
+}
+
+/* print log line to a FILE *f */
+#define print_logline(fp,entity,entity_pid,ts,pristr,buf) { \
+ fprintf(fp, "%s[%d]: %s ",entity,entity_pid,ha_timestamp(ts)); \
+ if (pristr) \
+ fprintf(fp,"%s: %s\n",pristr,buf); \
+ else \
+ fprintf(fp,"%s\n",buf); \
+ }
+
+static char * syslog_timestamp(TIME_T t);
+static void cl_limit_log_update(struct msg_ctrl *ml, time_t ts);
+
+static void
+append_log(FILE * fp, const char * entity, int entity_pid
+, TIME_T timestamp, const char * pristr, const char * msg)
+{
+ static int got_uname = FALSE;
+ static struct utsname un;
+
+ if (!syslogformatfile) {
+ print_logline(fp, entity, entity_pid, timestamp, pristr, msg);
+ return;
+ }
+ if (!got_uname) {
+ uname(&un);
+ }
+ /*
+ * Jul 14 21:45:18 beam logd: [1056]: info: setting log file to /dev/null
+ */
+ fprintf(fp, "%s %s %s: [%d]: %s%s%s\n"
+ , syslog_timestamp(timestamp)
+ , un.nodename, entity, entity_pid
+ , (pristr ? pristr : "")
+ , (pristr ? ": " : "")
+ , msg);
+}
+
+/* As performance optimization we try to keep the file descriptor
+ * open all the time, but as logrotation needs to work, the calling
+ * program actually needs a signal handler.
+ *
+ * To be able to keep files open even without signal handler,
+ * we remember the stat info, and close/reopen if the inode changed.
+ * We keep the number of stat() calls to one per file per minute.
+ * logrotate should be configured for delayed compression, if any.
+ */
+
+struct log_file_context {
+ FILE *fp;
+ struct stat stat_buf;
+};
+
+static struct log_file_context log_file, debug_file;
+
+static void close_log_file(struct log_file_context *lfc)
+{
+ /* ignore errors, we cannot do anything about them anyways */
+ fflush(lfc->fp);
+ fsync(fileno(lfc->fp));
+ fclose(lfc->fp);
+ lfc->fp = NULL;
+}
+
+void cl_log_close_log_files(void)
+{
+ if (log_file.fp)
+ close_log_file(&log_file);
+ if (debug_file.fp)
+ close_log_file(&debug_file);
+}
+
+static void maybe_close_log_file(const char *fname, struct log_file_context *lfc)
+{
+ struct stat buf;
+ if (!lfc->fp)
+ return;
+ if (stat(fname, &buf) || buf.st_ino != lfc->stat_buf.st_ino) {
+ close_log_file(lfc);
+ cl_log(LOG_INFO, "log-rotate detected on logfile %s", fname);
+ }
+}
+
+/* Default to unbuffered IO. logd or others can use cl_log_use_buffered_io(1)
+ * to enable fully buffered mode, and then use fflush appropriately.
+ */
+static void open_log_file(const char *fname, struct log_file_context *lfc)
+{
+ lfc->fp = fopen(fname ,"a");
+ if (!lfc->fp) {
+ syslog(LOG_ERR, "Failed to open log file %s: %s\n" ,
+ fname, strerror(errno));
+ } else {
+ setvbuf(lfc->fp, NULL,
+ use_buffered_io ? _IOFBF : _IONBF,
+ BUFSIZ);
+ fstat(fileno(lfc->fp), &lfc->stat_buf);
+ }
+}
+
+static void maybe_reopen_log_files(const char *log_fname, const char *debug_fname)
+{
+ static TIME_T last_stat_time;
+
+ if (log_file.fp || debug_file.fp) {
+ TIME_T now = time(NULL);
+ if (now - last_stat_time > 59) {
+ /* Don't use an exact minute, have it jitter around a
+ * bit against cron or others. Note that, if there
+ * is no new log message, it can take much longer
+ * than this to notice logrotation and actually close
+ * our file handle on the possibly already rotated,
+ * or even deleted.
+ *
+ * As long as at least one minute pases between
+ * renaming the log file, and further processing,
+ * no message will be lost, so this should do fine:
+ * (mv ha-log ha-log.1; sleep 60; gzip ha-log.1)
+ */
+ maybe_close_log_file(log_fname, &log_file);
+ maybe_close_log_file(debug_fname, &debug_file);
+ last_stat_time = now;
+ }
+ }
+
+ if (log_fname && !log_file.fp)
+ open_log_file(log_fname, &log_file);
+
+ if (debug_fname && !debug_file.fp)
+ open_log_file(debug_fname, &debug_file);
+}
+
+/*
+ * This function can cost us realtime unless use_logging_daemon
+ * is enabled. Then we log everything through a child process using
+ * non-blocking IPC.
+ */
+
+/* Cluster logging function */
+void
+cl_direct_log(int priority, const char* buf, gboolean use_priority_str,
+ const char* entity, int entity_pid, TIME_T ts)
+{
+ const char * pristr;
+ int needprivs = !cl_have_full_privs();
+
+ pristr = use_priority_str ? prio2str(priority) : NULL;
+
+ if (!entity)
+ entity = *cl_log_entity ? cl_log_entity : DFLT_ENTITY;
+
+ if (needprivs) {
+ return_to_orig_privs();
+ }
+
+ if (syslog_enabled) {
+ snprintf(common_log_entity, MAXENTITY, "%s",
+ *cl_log_syslogprefix ? cl_log_syslogprefix : entity);
+
+ /* The extra trailing '\0' is supposed to work around some
+ * "known syslog bug that ends up concatinating entries".
+ * Knowledge about which syslog package, version, platform and
+ * what exactly the bug was has been lost, but leaving it in
+ * won't do any harm either. */
+ syslog(priority, "%s[%d]: %s%s%s%c",
+ *cl_log_syslogprefix ? entity : "",
+ entity_pid,
+ pristr ?: "", pristr ? ": " : "",
+ buf, 0);
+ }
+
+ maybe_reopen_log_files(logfile_name, debugfile_name);
+
+ if (debug_file.fp)
+ append_log(debug_file.fp, entity, entity_pid, ts, pristr, buf);
+
+ if (priority != LOG_DEBUG && log_file.fp)
+ append_log(log_file.fp, entity, entity_pid, ts, pristr, buf);
+
+ if (needprivs) {
+ return_to_dropped_privs();
+ }
+ return;
+}
+
+void cl_log_do_fflush(int do_fsync)
+{
+ if (log_file.fp) {
+ fflush(log_file.fp);
+ if (do_fsync)
+ fsync(fileno(log_file.fp));
+ }
+ if (debug_file.fp) {
+ fflush(debug_file.fp);
+ if (do_fsync)
+ fsync(fileno(debug_file.fp));
+ }
+}
+
+/*
+ * This function can cost us realtime unless use_logging_daemon
+ * is enabled. Then we log everything through a child process using
+ * non-blocking IPC.
+ */
+
+static int cl_log_depth = 0;
+
+/* Cluster logging function */
+void
+cl_log(int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+ ssize_t nbytes;
+
+ cl_process_pid = (int)getpid();
+
+ cl_log_depth++;
+
+ buf[MAXLINE-1] = EOS;
+ va_start(ap, fmt);
+ nbytes=vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (nbytes >= (ssize_t)sizeof(buf)){
+ nbytes = sizeof(buf) -1 ;
+ }
+
+ if (stderr_enabled) {
+ append_log(stderr, cl_log_entity,cl_process_pid,
+ NULLTIME, prio2str(priority), buf);
+ }
+
+ if (stdout_enabled) {
+ append_log(stdout, cl_log_entity,cl_process_pid,
+ NULLTIME, prio2str(priority), buf);
+ }
+
+ if (use_logging_daemon && cl_log_depth <= 1) {
+ LogToLoggingDaemon(priority, buf, nbytes, TRUE);
+ }else{
+ /* this may cause blocking... maybe should make it optional? */
+ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+ }
+
+ cl_log_depth--;
+ return;
+}
+
+/*
+ * Log a message only if there were not too many messages of this
+ * kind recently. This is too prevent log spamming in case a
+ * condition persists over a long period of time. The maximum
+ * number of messages for the timeframe and other details are
+ * provided in struct logspam (see cl_log.h).
+ *
+ * Implementation details:
+ * - max number of time_t slots is allocated; slots keep time
+ * stamps of previous max number of messages
+ * - we check if the difference between now (i.e. new message just
+ * arrived) and the oldest message is _less_ than the window
+ * timeframe
+ * - it's up to the user to do cl_limit_log_new and afterwards
+ * cl_limit_log_destroy, though the latter is usually not
+ * necessary; the memory allocated with cl_limit_log_new stays
+ * constant during the lifetime of the process
+ *
+ * NB on Thu Aug 4 15:26:49 CEST 2011:
+ * This interface is very new, use with caution and report bugs.
+ */
+
+struct msg_ctrl *
+cl_limit_log_new(struct logspam *lspam)
+{
+ struct msg_ctrl *ml;
+
+ ml = (struct msg_ctrl *)malloc(sizeof(struct msg_ctrl));
+ if (!ml) {
+ cl_log(LOG_ERR, "%s:%d: out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ ml->msg_slots = (time_t *)calloc(lspam->max, sizeof(time_t));
+ if (!ml->msg_slots) {
+ cl_log(LOG_ERR, "%s:%d: out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ ml->lspam = lspam;
+ cl_limit_log_reset(ml);
+ return ml; /* to be passed later to cl_limit_log() */
+}
+
+void
+cl_limit_log_destroy(struct msg_ctrl *ml)
+{
+ if (!ml)
+ return;
+ g_free(ml->msg_slots);
+ g_free(ml);
+}
+
+void
+cl_limit_log_reset(struct msg_ctrl *ml)
+{
+ ml->last = -1;
+ ml->cnt = 0;
+ ml->suppress_t = (time_t)0;
+ memset(ml->msg_slots, 0, ml->lspam->max * sizeof(time_t));
+}
+
+static void
+cl_limit_log_update(struct msg_ctrl *ml, time_t ts)
+{
+ ml->last = (ml->last + 1) % ml->lspam->max;
+ *(ml->msg_slots + ml->last) = ts;
+ if (ml->cnt < ml->lspam->max)
+ ml->cnt++;
+}
+
+void
+cl_limit_log(struct msg_ctrl *ml, int priority, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+ time_t last_ts, now = time(NULL);
+
+ if (!ml)
+ goto log_msg;
+ if (ml->suppress_t) {
+ if ((now - ml->suppress_t) < ml->lspam->reset_time)
+ return;
+ /* message blocking expired */
+ cl_limit_log_reset(ml);
+ }
+ last_ts = ml->last != -1 ? *(ml->msg_slots + ml->last) : (time_t)0;
+ if (
+ ml->cnt < ml->lspam->max || /* not so many messages logged */
+ (now - last_ts) > ml->lspam->window /* messages far apart */
+ ) {
+ cl_limit_log_update(ml, now);
+ goto log_msg;
+ } else {
+ cl_log(LOG_INFO
+ , "'%s' messages logged too often, "
+ "suppressing messages of this kind for %ld seconds"
+ , ml->lspam->id, ml->lspam->reset_time);
+ cl_log(priority, "%s", ml->lspam->advice);
+ ml->suppress_t = now;
+ return;
+ }
+
+log_msg:
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+ cl_log(priority, "%s", buf);
+}
+
+void
+cl_perror(const char * fmt, ...)
+{
+ const char * err;
+
+ va_list ap;
+ char buf[MAXLINE];
+
+ err = strerror(errno);
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ va_end(ap);
+
+ cl_log(LOG_ERR, "%s: %s", buf, err);
+
+}
+void
+cl_glib_msg_handler(const gchar *log_domain, GLogLevelFlags log_level
+, const gchar *message, gpointer user_data)
+{
+ GLogLevelFlags level = (log_level & G_LOG_LEVEL_MASK);
+ int ha_level;
+
+ switch(level) {
+ case G_LOG_LEVEL_ERROR: ha_level = LOG_ERR; break;
+ case G_LOG_LEVEL_CRITICAL: ha_level = LOG_ERR; break;
+ case G_LOG_LEVEL_WARNING: ha_level = LOG_WARNING; break;
+ case G_LOG_LEVEL_MESSAGE: ha_level = LOG_NOTICE; break;
+ case G_LOG_LEVEL_INFO: ha_level = LOG_INFO; break;
+ case G_LOG_LEVEL_DEBUG: ha_level = LOG_DEBUG; break;
+
+ default: ha_level = LOG_WARNING; break;
+ }
+
+
+ cl_log(ha_level, "glib: %s", message);
+}
+static char *
+syslog_timestamp(TIME_T t)
+{
+ static char ts[64];
+ struct tm* ttm;
+ TIME_T now;
+ time_t nowtt;
+ static const char* monthstr [12] = {
+ "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+ };
+
+ /* Work around various weridnesses in different OSes and time_t definitions */
+ if (t == 0){
+ now = time(NULL);
+ }else{
+ now = t;
+ }
+
+ nowtt = (time_t)now;
+ ttm = localtime(&nowtt);
+
+ snprintf(ts, sizeof(ts), "%3s %02d %02d:%02d:%02d"
+ , monthstr[ttm->tm_mon], ttm->tm_mday
+ , ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
+ return(ts);
+}
+
+
+
+char *
+ha_timestamp(TIME_T t)
+{
+ static char ts[64];
+ struct tm* ttm;
+ TIME_T now;
+ time_t nowtt;
+
+ /* Work around various weridnesses in different OSes and time_t definitions */
+ if (t == 0){
+ now = time(NULL);
+ }else{
+ now = t;
+ }
+
+ nowtt = (time_t)now;
+ ttm = localtime(&nowtt);
+
+ snprintf(ts, sizeof(ts), "%04d/%02d/%02d_%02d:%02d:%02d"
+ , ttm->tm_year+1900, ttm->tm_mon+1, ttm->tm_mday
+ , ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
+ return(ts);
+}
+
+
+static int
+cl_set_logging_wqueue_maxlen(int qlen)
+{
+ int sendrc;
+ IPC_Channel* chan = logging_daemon_chan;
+
+ if (chan == NULL){
+ chan = logging_daemon_chan = create_logging_channel();
+ }
+
+ if (chan == NULL){
+ return HA_FAIL;
+ }
+
+ if (chan->ch_status != IPC_CONNECT){
+ cl_log(LOG_ERR, "cl_set_logging_wqueue_maxle:"
+ "channel is not connected");
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = NULL;
+ return HA_FAIL;
+ }
+
+ sendrc = chan->ops->set_send_qlen(logging_daemon_chan, qlen);
+
+ if (sendrc == IPC_OK) {
+ return HA_OK;
+ }else {
+ return HA_FAIL;
+ }
+}
+
+
+
+int
+LogToDaemon(int priority, const char * buf,
+ int bufstrlen, gboolean use_pri_str)
+{
+ int rc;
+
+ cl_log_depth++;
+
+ rc= LogToLoggingDaemon(priority, buf, bufstrlen, use_pri_str);
+
+ cl_log_depth--;
+
+ return rc;
+}
+
+static int drop_msg_num = 0;
+
+void
+cl_flush_logs(void)
+{
+ if(logging_daemon_chan == NULL) {
+ return;
+ }
+ logging_daemon_chan->ops->waitout(logging_daemon_chan);
+}
+
+static int
+LogToLoggingDaemon(int priority, const char * buf,
+ int bufstrlen, gboolean use_pri_str)
+{
+ IPC_Channel* chan = logging_daemon_chan;
+ static longclock_t nexttime = 0;
+ IPC_Message* msg;
+ int sendrc = IPC_FAIL;
+ int intval = conn_logd_time;
+
+ /* make sure we don't hold file descriptors open
+ * we don't intend to use again */
+ cl_log_close_log_files();
+
+ if (chan == NULL) {
+ longclock_t lnow = time_longclock();
+
+ if (cmp_longclock(lnow, nexttime) >= 0){
+ nexttime = add_longclock(
+ lnow, msto_longclock(intval));
+
+ logging_daemon_chan = chan = create_logging_channel();
+ }
+ }
+
+ if (chan == NULL){
+ cl_direct_log(
+ priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+ return HA_FAIL;
+ }
+
+ msg = ChildLogIPCMessage(priority, buf, bufstrlen, use_pri_str, chan);
+ if (msg == NULL) {
+ drop_msg_num++;
+ return HA_FAIL;
+ }
+
+ if (chan->ch_status == IPC_CONNECT){
+
+ if (chan->ops->is_sending_blocked(chan)) {
+ chan->ops->resume_io(chan);
+ }
+ /* Make sure there is room for the drop message _and_ the
+ * one we wish to log. Otherwise there is no point.
+ *
+ * Try to avoid bouncing on the limit by additionally
+ * waiting until there is room for QUEUE_SATURATION_FUZZ
+ * messages.
+ */
+ if (drop_msg_num > 0
+ && chan->send_queue->current_qlen
+ < (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ))
+ {
+ /* have to send it this way so the order is correct */
+ send_dropped_message(use_pri_str, chan);
+ }
+
+ /* Don't log a debug message if we're
+ * approaching the queue limit and already
+ * dropped a message
+ */
+ if (drop_msg_num == 0
+ || chan->send_queue->current_qlen <
+ (chan->send_queue->max_qlen -1 -QUEUE_SATURATION_FUZZ)
+ || priority != LOG_DEBUG )
+ {
+ sendrc = chan->ops->send(chan, msg);
+ }
+ }
+
+ if (sendrc == IPC_OK) {
+ return HA_OK;
+
+ } else {
+
+ if (chan->ops->get_chan_status(chan) != IPC_CONNECT) {
+ if (!logging_chan_in_main_loop){
+ chan->ops->destroy(chan);
+ }
+ logging_daemon_chan = NULL;
+ cl_direct_log(priority, buf, TRUE, NULL, cl_process_pid, NULLTIME);
+
+ if (drop_msg_num > 0){
+ /* Direct logging here is ok since we're
+ * switching to that for everything
+ * "for a while"
+ */
+ cl_log(LOG_ERR,
+ "cl_log: %d messages were dropped"
+ " : channel destroyed", drop_msg_num);
+ }
+
+ drop_msg_num=0;
+ FreeChildLogIPCMessage(msg);
+ return HA_FAIL;
+ }
+
+ drop_msg_num++;
+
+ }
+
+ FreeChildLogIPCMessage(msg);
+ return HA_FAIL;
+}
+
+
+static gboolean
+send_dropped_message(gboolean use_pri_str, IPC_Channel *chan)
+{
+ int sendrc;
+ char buf[64];
+ int buf_len = 0;
+ IPC_Message *drop_msg = NULL;
+
+ memset(buf, 0, 64);
+ snprintf(buf, 64, "cl_log: %d messages were dropped", drop_msg_num);
+ buf_len = strlen(buf)+1;
+ drop_msg = ChildLogIPCMessage(LOG_ERR, buf, buf_len, use_pri_str, chan);
+
+ if(drop_msg == NULL || drop_msg->msg_len == 0) {
+ return FALSE;
+ }
+
+ sendrc = chan->ops->send(chan, drop_msg);
+
+ if(sendrc == IPC_OK) {
+ drop_msg_num = 0;
+ }else{
+ FreeChildLogIPCMessage(drop_msg);
+ }
+ return sendrc == IPC_OK;
+}
+
+
+static IPC_Message*
+ChildLogIPCMessage(int priority, const char *buf, int bufstrlen,
+ gboolean use_prio_str, IPC_Channel* ch)
+{
+ IPC_Message* ret;
+ LogDaemonMsgHdr logbuf;
+ int msglen;
+ char* bodybuf;
+
+
+ if (ch->msgpad > MAX_MSGPAD){
+ cl_log(LOG_ERR, "ChildLogIPCMessage: invalid msgpad(%d)",
+ ch->msgpad);
+ return NULL;
+ }
+
+
+ ret = (IPC_Message*)malloc(sizeof(IPC_Message));
+
+ if (ret == NULL) {
+ return ret;
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ /* Compute msg len: including room for the EOS byte */
+ msglen = sizeof(LogDaemonMsgHdr)+bufstrlen + 1;
+ bodybuf = malloc(msglen + ch->msgpad);
+ if (bodybuf == NULL) {
+ free(ret);
+ return NULL;
+ }
+
+ memset(bodybuf, 0, msglen + ch->msgpad);
+ memset(&logbuf, 0, sizeof(logbuf));
+ logbuf.msgtype = LD_LOGIT;
+ logbuf.facility = cl_log_facility;
+ logbuf.priority = priority;
+ logbuf.use_pri_str = use_prio_str;
+ logbuf.entity_pid = getpid();
+ logbuf.timestamp = time(NULL);
+ if (*cl_log_entity){
+ strncpy(logbuf.entity,cl_log_entity,MAXENTITY);
+ }else {
+ strncpy(logbuf.entity,DFLT_ENTITY,MAXENTITY);
+ }
+
+ logbuf.msglen = bufstrlen + 1;
+ memcpy(bodybuf + ch->msgpad, &logbuf, sizeof(logbuf));
+ memcpy(bodybuf + ch->msgpad + sizeof(logbuf),
+ buf,
+ bufstrlen);
+
+ ret->msg_len = msglen;
+ ret->msg_buf = bodybuf;
+ ret->msg_body = bodybuf + ch->msgpad;
+ ret->msg_done = FreeChildLogIPCMessage;
+ ret->msg_ch = ch;
+
+ return ret;
+}
+
+
+static void
+FreeChildLogIPCMessage(IPC_Message* msg)
+{
+ if (msg == NULL) {
+ return;
+ }
+ memset(msg->msg_body, 0, msg->msg_len);
+ free(msg->msg_buf);
+
+ memset(msg, 0, sizeof (*msg));
+ free(msg);
+
+ return;
+
+}
+
+
+
+static void
+cl_opensyslog(void)
+{
+ if (*cl_log_entity == '\0' || cl_log_facility < 0) {
+ return;
+ }
+ syslog_enabled = 1;
+ strncpy(common_log_entity, cl_log_entity, MAXENTITY);
+ openlog(common_log_entity, LOG_CONS, cl_log_facility);
+}
+
+
+void
+cl_log_args(int argc, char **argv)
+{
+ int lpc = 0;
+ int len = 0;
+ int existing_len = 0;
+ char *arg_string = NULL;
+
+ if(argc == 0 || argv == NULL) {
+ return;
+ }
+
+ for(;lpc < argc; lpc++) {
+ if(argv[lpc] == NULL) {
+ break;
+ }
+
+ len = 2 + strlen(argv[lpc]); /* +1 space, +1 EOS */
+ if(arg_string) {
+ existing_len = strlen(arg_string);
+ }
+
+ arg_string = realloc(arg_string, len + existing_len);
+ sprintf(arg_string + existing_len, "%s ", argv[lpc]);
+ }
+ cl_log(LOG_INFO, "Invoked: %s", arg_string);
+ free(arg_string);
+}
diff --git a/lib/clplumbing/cl_malloc.c b/lib/clplumbing/cl_malloc.c
new file mode 100644
index 0000000..ca6dc0b
--- /dev/null
+++ b/lib/clplumbing/cl_malloc.c
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define HA_MALLOC_ORIGINAL
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+#include <string.h>
+#include <errno.h>
+#ifndef BSD
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif
+#endif
+#include <clplumbing/cl_malloc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/longclock.h>
+
+#include <ltdl.h>
+
+#ifndef _CLPLUMBING_CLMALLOC_NATIVE_H
+static cl_mem_stats_t default_memstats;
+static volatile cl_mem_stats_t * memstats = &default_memstats;
+
+/*
+ * Compile time malloc debugging switches:
+ *
+ * MARK_PRISTINE - puts known byte pattern in freed memory
+ * Good at finding "use after free" cases
+ * Cheap in memory, but expensive in CPU
+ *
+ * MAKE_GUARD - puts a known pattern *after* allocated memory
+ * Good at finding overrun problems after the fact
+ * Cheap in CPU, adds a few bytes to each malloc item
+ *
+ */
+
+#define MARK_PRISTINE 1 /* Expensive in CPU time */
+#undef MARK_PRISTINE
+#define MAKE_GUARD 1 /* Adds 'n' bytes memory - cheap in CPU*/
+#define USE_ASSERTS 1
+#define DUMPONERR 1
+#define RETURN_TO_MALLOC 1
+#undef RETURN_TO_MALLOC
+
+#ifndef DUMPONERR
+# define DUMPIFASKED() /* nothing */
+#else
+# define DUMPIFASKED() {abort();}
+#endif
+
+
+/*
+ *
+ * Malloc wrapper functions
+ *
+ * I wrote these so we can better track memory leaks, etc. and verify
+ * that the system is stable in terms of memory usage.
+ *
+ * For our purposes, these functions are a somewhat faster than using
+ * malloc directly (although they use a bit more memory)
+ *
+ * The general strategy is loosely related to the buddy system,
+ * except very simple, well-suited to our continuous running
+ * nature, and the constancy of the requests and messages.
+ *
+ * We keep an array of linked lists, each for a different size
+ * buffer. If we need a buffer larger than the largest one provided
+ * by the list, we go directly to malloc.
+ *
+ * Otherwise, we keep return them to the appropriate linked list
+ * when we're done with them, and reuse them from the list.
+ *
+ * We never coalesce buffers on our lists, and we never free them.
+ *
+ * It's very simple. We get usage stats. It makes me happy.
+ */
+
+#define HA_MALLOC_MAGIC 0xFEEDBEEFUL
+#define HA_FREE_MAGIC 0xDEADBEEFUL
+
+
+/*
+ * We put a struct cl_mhdr in front of every malloc item.
+ * This means each malloc item is at least 12 bytes bigger than it theoretically
+ * needs to be. But, it allows this code to be fast and recognize
+ * multiple free attempts, and memory corruption *before* the object
+ *
+ * It's probably possible to combine these fields a bit,
+ * since bucket and reqsize are only needed for allocated items,
+ * both are bounded in value, and fairly strong integrity checks apply
+ * to them. But then we wouldn't be able to tell *quite* as reliably
+ * if someone gave us an item to free that we didn't allocate...
+ *
+ * Could even make the bucket and reqsize objects into 16-bit ints...
+ *
+ * The idea of getting it all down into 32-bits of overhead is
+ * an interesting thought...
+ *
+ * But some architectures have alignment constraints. For instance, sparc
+ * requires that double-word accesses be aligned on double-word boundaries.
+ * Thus if the requested space is bigger than a double-word, then cl_mhdr
+ * should, for safety, be a double-word multiple (minimum 8bytes, 64bits).
+
+*/
+
+#ifdef HA_MALLOC_TRACK
+# define HA_MALLOC_OWNER 64
+struct cl_bucket;
+#endif
+
+struct cl_mhdr {
+# ifdef HA_MALLOC_MAGIC
+ unsigned long magic; /* Must match HA_*_MAGIC */
+#endif
+# ifdef HA_MALLOC_TRACK
+ char owner[HA_MALLOC_OWNER];
+ struct cl_bucket * left;
+ struct cl_bucket * right;
+ int dumped;
+ longclock_t mtime;
+#endif
+ size_t reqsize;
+ int bucket;
+};
+
+struct cl_bucket {
+ struct cl_mhdr hdr;
+ struct cl_bucket * next;
+};
+
+#define NUMBUCKS 12
+#define NOBUCKET (NUMBUCKS)
+
+static struct cl_bucket* cl_malloc_buckets[NUMBUCKS];
+static size_t cl_bucket_sizes[NUMBUCKS];
+static size_t buckminpow2 = 0L;
+
+static int cl_malloc_inityet = 0;
+static size_t cl_malloc_hdr_offset = sizeof(struct cl_mhdr);
+
+static void* cl_new_mem(size_t size, int numbuck);
+static void cl_malloc_init(void);
+static void cl_dump_item(const struct cl_bucket*b);
+
+#ifdef MARK_PRISTINE
+# define PRISTVALUE 0xff
+ static int cl_check_is_pristine(const void* v, unsigned size);
+ static void cl_mark_pristine(void* v, unsigned size);
+ static int pristoff;
+#endif
+
+#define BHDR(p) ((struct cl_bucket*)(void*)(((char*)p)-cl_malloc_hdr_offset))
+#define CBHDR(p) ((const struct cl_bucket*)(const void*)(((const char*)p)-cl_malloc_hdr_offset))
+#define MEMORYSIZE(p)(CBHDR(p)->hdr.reqsize)
+
+#define MALLOCSIZE(allocsize) ((allocsize) + cl_malloc_hdr_offset + GUARDSIZE)
+#define MAXMALLOC (SIZE_MAX-(MALLOCSIZE(0)+1))
+
+#ifdef MAKE_GUARD
+# define GUARDLEN 4
+ static const unsigned char cl_malloc_guard[] =
+#if GUARDLEN == 1
+ {0xA5};
+#endif
+#if GUARDLEN == 2
+ {0x5A, 0xA5};
+#endif
+#if GUARDLEN == 4
+ {0x5A, 0xA5, 0x5A, 0xA5};
+#endif
+# define GUARDSIZE sizeof(cl_malloc_guard)
+# define ADD_GUARD(cp) (memcpy((((char*)cp)+MEMORYSIZE(cp)), cl_malloc_guard, sizeof(cl_malloc_guard)))
+# define GUARD_IS_OK(cp) (memcmp((((const char*)cp)+MEMORYSIZE(cp)), \
+ cl_malloc_guard, sizeof(cl_malloc_guard)) == 0)
+# define CHECK_GUARD_BYTES(cp, msg) { \
+ if (!GUARD_IS_OK(cp)) { \
+ cl_log(LOG_ERR, "%s: guard corrupted at 0x%lx", msg \
+ , (unsigned long)cp); \
+ cl_dump_item(CBHDR(cp)); \
+ DUMPIFASKED(); \
+ } \
+ }
+#else
+# define GUARDSIZE 0
+# define ADD_GUARD(cp) /* */
+# define GUARD_IS_OK(cp) (1)
+# define CHECK_GUARD_BYTES(cp, msg) /* */
+#endif
+
+#define MALLOCROUND 4096 /* Round big mallocs up to a multiple of this size */
+
+
+#ifdef HA_MALLOC_TRACK
+
+static struct cl_bucket * cl_malloc_track_root = NULL;
+
+static void
+cl_ptr_tag(void *ptr, const char *file, const char *function, const int line)
+{
+ struct cl_bucket* bhdr = BHDR(ptr);
+ snprintf(bhdr->hdr.owner, HA_MALLOC_OWNER, "%s:%s:%d",
+ file, function, line);
+}
+
+static void
+cl_ptr_track(void *ptr)
+{
+ struct cl_bucket* bhdr = BHDR(ptr);
+
+#if defined(USE_ASSERTS)
+ g_assert(bhdr->hdr.left == NULL);
+ g_assert(bhdr->hdr.right == NULL);
+ g_assert((cl_malloc_track_root == NULL) || (cl_malloc_track_root->hdr.left == NULL));
+#endif
+
+ bhdr->hdr.dumped = 0;
+ bhdr->hdr.mtime = time_longclock();
+
+ if (cl_malloc_track_root == NULL) {
+ cl_malloc_track_root = bhdr;
+ } else {
+ bhdr->hdr.right = cl_malloc_track_root;
+ cl_malloc_track_root->hdr.left = bhdr;
+ cl_malloc_track_root = bhdr;
+ }
+}
+
+static void
+cl_ptr_release(void *ptr)
+{
+ struct cl_bucket* bhdr = BHDR(ptr);
+
+/* cl_log(LOG_DEBUG, "cl_free: Freeing memory belonging to %s"
+ , bhdr->hdr.owner); */
+
+#if defined(USE_ASSERTS)
+ g_assert(cl_malloc_track_root != NULL);
+ g_assert(cl_malloc_track_root->hdr.left == NULL);
+#endif
+
+ if (bhdr->hdr.left != NULL) {
+ bhdr->hdr.left->hdr.right=bhdr->hdr.right;
+ }
+
+ if (bhdr->hdr.right != NULL) {
+ bhdr->hdr.right->hdr.left=bhdr->hdr.left;
+ }
+
+ if (cl_malloc_track_root == bhdr) {
+ cl_malloc_track_root=bhdr->hdr.right;
+ }
+
+ bhdr->hdr.left = NULL;
+ bhdr->hdr.right = NULL;
+}
+
+static void
+cl_ptr_init(void)
+{
+ cl_malloc_track_root = NULL;
+}
+
+int
+cl_malloc_dump_allocated(int log_level, gboolean filter)
+{
+ int lpc = 0;
+ struct cl_bucket* cursor = cl_malloc_track_root;
+ longclock_t time_diff;
+
+ cl_log(LOG_INFO, "Dumping allocated memory buffers:");
+
+ while (cursor != NULL) {
+ if(filter && cursor->hdr.dumped) {
+
+ } else if(log_level > LOG_DEBUG) {
+ } else if(filter) {
+ lpc++;
+ cl_log(log_level, "cl_malloc_dump: %p owner %s, size %d"
+ , cursor+cl_malloc_hdr_offset
+ , cursor->hdr.owner
+ , (int)cursor->hdr.reqsize);
+ } else {
+ lpc++;
+ time_diff = sub_longclock(time_longclock(), cursor->hdr.mtime);
+ cl_log(log_level, "cl_malloc_dump: %p owner %s, size %d, dumped %d, age %lu ms"
+ , cursor+cl_malloc_hdr_offset
+ , cursor->hdr.owner
+ , (int)cursor->hdr.reqsize
+ , cursor->hdr.dumped
+ , longclockto_long(time_diff));
+ }
+ cursor->hdr.dumped = 1;
+ cursor = cursor->hdr.right;
+ }
+
+ cl_log(LOG_INFO, "End dump.");
+ return lpc;
+}
+#endif
+static const int LogTable256[] =
+{
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+};
+#define POW2BYTE(b) (LogTable256[b])
+#define BYTE3(i) (((i)&0xFF000000)>>24)
+#define BYTE2(i) (((i)&0x00FF0000)>>16)
+#define BYTE1(i) (((i)&0x0000FF00)>>8)
+#define BYTE0(i) ((i)&0x000000FF)
+
+/* Works for malloc bucket sizes up to 2^8 */
+#define POW21BYTE(i) (POW2BYTE(BYTE0(i)))
+
+/* Works for malloc bucket sizes up to 2^16 */
+#define POW22BYTE(i) ((BYTE1(i) != 0x00)? (POW2BYTE(BYTE1(i))+8) \
+ : (POW21BYTE(i)))
+
+/* Works for malloc bucket sizes up to 2^24 */
+#define POW23BYTE(i) ((BYTE2(i) != 0x00)? (POW2BYTE(BYTE2(i))+16) \
+ : POW22BYTE(i))
+
+/* Works for malloc bucket sizes up to 2^32 */
+#define POW24BYTE(i) ((BYTE3(i) != 0x00)? (POW2BYTE(BYTE3(i))+24) \
+ : POW23BYTE(i))
+
+/* #define INT2POW2(i) POW24BYTE(i) / * This would allow 2G in our largest malloc chain */
+ /* which I don't think we need */
+#define INT2POW2(i) POW23BYTE(i) /* This allows up to about 16 Mbytes in our largest malloc chain */
+ /* and it's a little faster than the one above */
+
+
+/*
+ * cl_malloc: malloc clone
+ */
+
+void *
+cl_malloc(size_t size)
+{
+#if 0
+ int j;
+#endif
+ int numbuck = NOBUCKET;
+ struct cl_bucket* buckptr = NULL;
+ void* ret;
+
+ if(!size) {
+ cl_log(LOG_ERR
+ , "%s: refusing to allocate zero sized block"
+ , __FUNCTION__
+ );
+ return NULL;
+ }
+ if (size > MAXMALLOC) {
+ return NULL;
+ }
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+
+#if 1
+ /*
+ * NOTE: This restricts bucket sizes to be powers of two
+ * - which is OK with me - and how the code has always worked :-D
+ */
+ numbuck = INT2POW2(size-1)-buckminpow2;
+ numbuck = MAX(0, numbuck);
+ if (numbuck < NUMBUCKS) {
+ if (size <= cl_bucket_sizes[numbuck]
+ || (numbuck > 0 && size <= (cl_bucket_sizes[numbuck]/2))) {
+ buckptr = cl_malloc_buckets[numbuck];
+ }else{
+ cl_log(LOG_ERR
+ , "%s: bucket size bug: %lu bytes in %lu byte bucket #%d"
+ , __FUNCTION__
+ , (unsigned long)size
+ , (unsigned long)cl_bucket_sizes[numbuck]
+ , numbuck);
+
+ }
+ }
+#else
+ /*
+ * Find which bucket would have buffers of the requested size
+ */
+ for (j=0; j < NUMBUCKS; ++j) {
+ if (size <= cl_bucket_sizes[j]) {
+ numbuck = j;
+ buckptr = cl_malloc_buckets[numbuck];
+ break;
+ }
+ }
+#endif
+
+ /*
+ * Pull it out of the linked list of free buffers if we can...
+ */
+
+ if (buckptr == NULL) {
+ ret = cl_new_mem(size, numbuck);
+ }else{
+ cl_malloc_buckets[numbuck] = buckptr->next;
+ buckptr->hdr.reqsize = size;
+ ret = (((char*)buckptr)+cl_malloc_hdr_offset);
+
+#ifdef MARK_PRISTINE
+ {
+ int bucksize = cl_bucket_sizes[numbuck];
+ if (!cl_check_is_pristine(ret, bucksize)) {
+ cl_log(LOG_ERR
+ , "attempt to allocate memory"
+ " which is not pristine.");
+ cl_dump_item(buckptr);
+ DUMPIFASKED();
+ }
+ }
+#endif
+
+#ifdef HA_MALLOC_MAGIC
+ switch (buckptr->hdr.magic) {
+
+ case HA_FREE_MAGIC:
+ break;
+
+ case HA_MALLOC_MAGIC:
+ cl_log(LOG_ERR
+ , "attempt to allocate memory"
+ " already allocated at 0x%lx"
+ , (unsigned long)ret);
+ cl_dump_item(buckptr);
+ DUMPIFASKED();
+ ret=NULL;
+ break;
+
+ default:
+ cl_log(LOG_ERR
+ , "corrupt malloc buffer at 0x%lx"
+ , (unsigned long)ret);
+ cl_dump_item(buckptr);
+ DUMPIFASKED();
+ ret=NULL;
+ break;
+ }
+ buckptr->hdr.magic = HA_MALLOC_MAGIC;
+#endif /* HA_MALLOC_MAGIC */
+ if (memstats) {
+ memstats->nbytes_req += size;
+ memstats->nbytes_alloc
+ += MALLOCSIZE(cl_bucket_sizes[numbuck]);
+ }
+
+ }
+
+ if (ret && memstats) {
+#if 0 && defined(HAVE_MALLINFO)
+ /* mallinfo is too expensive to use :-( */
+ struct mallinfo i = mallinfo();
+ memstats->arena = i.arena;
+#endif
+ memstats->numalloc++;
+ }
+ if (ret) {
+#ifdef HA_MALLOC_TRACK
+ /* If we were _always_ called via the wrapper functions,
+ * this wouldn't be necessary, but we aren't, some use
+ * function pointers directly to cl_malloc() */
+ cl_ptr_track(ret);
+ cl_ptr_tag(ret, "cl_malloc.c", "cl_malloc", 0);
+#endif
+ ADD_GUARD(ret);
+ }
+ return(ret);
+}
+
+int
+cl_is_allocated(const void *ptr)
+{
+#ifdef HA_MALLOC_MAGIC
+ if (NULL == ptr || CBHDR(ptr)->hdr.magic != HA_MALLOC_MAGIC) {
+ return FALSE;
+ }else if (GUARD_IS_OK(ptr)) {
+ return TRUE;
+ }
+ cl_log(LOG_ERR
+ , "cl_is_allocated: supplied storage is guard-corrupted at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(CBHDR(ptr));
+ DUMPIFASKED();
+ return FALSE;
+#else
+ return (ptr != NULL);
+#endif
+}
+
+/*
+ * cl_free: "free" clone
+ */
+
+void
+cl_free(void *ptr)
+{
+ int bucket;
+ struct cl_bucket* bhdr;
+
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+
+ if (ptr == NULL) {
+ cl_log(LOG_ERR, "attempt to free NULL pointer in cl_free()");
+ DUMPIFASKED();
+ return;
+ }
+
+ /* Find the beginning of our "hidden" structure */
+
+ bhdr = BHDR(ptr);
+
+#ifdef HA_MALLOC_MAGIC
+ switch (bhdr->hdr.magic) {
+ case HA_MALLOC_MAGIC:
+ break;
+
+ case HA_FREE_MAGIC:
+ cl_log(LOG_ERR
+ , "cl_free: attempt to free already-freed"
+ " object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return;
+ break;
+ default:
+ cl_log(LOG_ERR, "cl_free: Bad magic number"
+ " in object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return;
+ break;
+ }
+#endif
+ if (!GUARD_IS_OK(ptr)) {
+ cl_log(LOG_ERR
+ , "cl_free: attempt to free guard-corrupted"
+ " object at 0x%lx", (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return;
+ }
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_release(ptr);
+#endif
+ bucket = bhdr->hdr.bucket;
+#ifdef HA_MALLOC_MAGIC
+ bhdr->hdr.magic = HA_FREE_MAGIC;
+#endif
+
+ /*
+ * Return it to the appropriate bucket (linked list), or just free
+ * it if it didn't come from one of our lists...
+ */
+
+#ifndef RETURN_TO_MALLOC
+ if (bucket >= NUMBUCKS) {
+#endif
+#ifdef MARK_PRISTINE
+ /* Is this size right? */
+ cl_mark_pristine(ptr, bhdr->hdr.reqsize);
+#endif
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_alloc -= MALLOCSIZE(bhdr->hdr.reqsize);
+ memstats->mallocbytes -= MALLOCSIZE(bhdr->hdr.reqsize);
+ }
+ free(bhdr);
+#ifndef RETURN_TO_MALLOC
+ }else{
+ int bucksize = cl_bucket_sizes[bucket];
+#if defined(USE_ASSERTS)
+ g_assert(bhdr->hdr.reqsize <= cl_bucket_sizes[bucket]);
+# endif
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_alloc -= MALLOCSIZE(bucksize);
+ }
+ bhdr->next = cl_malloc_buckets[bucket];
+ cl_malloc_buckets[bucket] = bhdr;
+#ifdef MARK_PRISTINE
+ cl_mark_pristine(ptr, bucksize);
+# endif
+ }
+#endif /* RETURN_TO_MALLOC */
+ if (memstats) {
+ memstats->numfree++;
+ }
+}
+
+void*
+cl_realloc(void *ptr, size_t newsize)
+{
+ struct cl_bucket* bhdr;
+ int bucket;
+ size_t bucksize;
+
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+
+ if (memstats) {
+ memstats->numrealloc++;
+ }
+ if (ptr == NULL) {
+ /* NULL is a legal 'ptr' value for realloc... */
+ return cl_malloc(newsize);
+ }
+ if (newsize == 0) {
+ /* realloc() is the most redundant interface ever */
+ cl_free(ptr);
+ return NULL;
+ }
+
+ /* Find the beginning of our "hidden" structure */
+
+ bhdr = BHDR(ptr);
+
+#ifdef HA_MALLOC_MAGIC
+ switch (bhdr->hdr.magic) {
+ case HA_MALLOC_MAGIC:
+ break;
+
+ case HA_FREE_MAGIC:
+ cl_log(LOG_ERR
+ , "cl_realloc: attempt to realloc already-freed"
+ " object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return NULL;
+ break;
+ default:
+ cl_log(LOG_ERR, "cl_realloc: Bad magic number"
+ " in object at 0x%lx"
+ , (unsigned long)ptr);
+ cl_dump_item(bhdr);
+ DUMPIFASKED();
+ return NULL;
+ break;
+ }
+#endif
+ CHECK_GUARD_BYTES(ptr, "cl_realloc");
+
+ bucket = bhdr->hdr.bucket;
+
+ /*
+ * Figure out which bucket it came from... If any...
+ */
+
+ if (bucket >= NUMBUCKS) {
+ /* Not from our bucket-area... Call realloc... */
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_alloc -= MALLOCSIZE(bhdr->hdr.reqsize);
+ memstats->mallocbytes -= MALLOCSIZE(bhdr->hdr.reqsize);
+ memstats->nbytes_req += newsize;
+ memstats->nbytes_alloc += MALLOCSIZE(newsize);
+ memstats->mallocbytes += MALLOCSIZE(newsize);
+ }
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_release(ptr);
+#endif
+ bhdr = realloc(bhdr, newsize + cl_malloc_hdr_offset + GUARDSIZE);
+ if (!bhdr) {
+ return NULL;
+ }
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_track(ptr);
+ cl_ptr_tag(ptr, "cl_malloc.c", "realloc", 0);
+#endif
+ bhdr->hdr.reqsize = newsize;
+ ptr = (((char*)bhdr)+cl_malloc_hdr_offset);
+ ADD_GUARD(ptr);
+ CHECK_GUARD_BYTES(ptr, "cl_realloc - real realloc return value");
+ /* Not really a memory leak... BEAM thinks so though... */
+ return ptr; /*memory leak*/
+ }
+ bucksize = cl_bucket_sizes[bucket];
+#if defined(USE_ASSERTS)
+ g_assert(bhdr->hdr.reqsize <= bucksize);
+#endif
+ if (newsize > bucksize) {
+ /* Need to allocate new space for it */
+ void* newret = cl_malloc(newsize);
+ if (newret != NULL) {
+ memcpy(newret, ptr, bhdr->hdr.reqsize);
+ CHECK_GUARD_BYTES(newret, "cl_realloc - cl_malloc case");
+ }
+ cl_free(ptr);
+ return newret;
+ }
+
+ /* Amazing! It fits into the space previously allocated for it! */
+ bhdr->hdr.reqsize = newsize;
+ if (memstats) {
+ memstats->nbytes_req -= bhdr->hdr.reqsize;
+ memstats->nbytes_req += newsize;
+ }
+ ADD_GUARD(ptr);
+ CHECK_GUARD_BYTES(ptr, "cl_realloc - fits in existing space");
+ return ptr;
+}
+
+/*
+ * cl_new_mem: use the real malloc to allocate some new memory
+ */
+
+static void*
+cl_new_mem(size_t size, int numbuck)
+{
+ struct cl_bucket* hdrret;
+ size_t allocsize;
+ size_t mallocsize;
+
+ if (numbuck < NUMBUCKS) {
+ allocsize = cl_bucket_sizes[numbuck];
+ }else{
+ allocsize = size;
+ }
+
+ mallocsize = MALLOCSIZE(allocsize);
+ if (numbuck == NOBUCKET) {
+ mallocsize = (((mallocsize + (MALLOCROUND-1))/MALLOCROUND)*MALLOCROUND);
+ }
+
+ if ((hdrret = malloc(mallocsize)) == NULL) {
+ return NULL;
+ }
+
+ hdrret->hdr.reqsize = size;
+ hdrret->hdr.bucket = numbuck;
+#ifdef HA_MALLOC_MAGIC
+ hdrret->hdr.magic = HA_MALLOC_MAGIC;
+#endif
+#ifdef HA_MALLOC_TRACK
+ hdrret->hdr.left = NULL;
+ hdrret->hdr.right = NULL;
+ hdrret->hdr.owner[0] = '\0';
+ hdrret->hdr.dumped = 0;
+#endif
+
+ if (memstats) {
+ memstats->nbytes_alloc += mallocsize;
+ memstats->nbytes_req += size;
+ memstats->mallocbytes += mallocsize;
+ }
+ /* BEAM BUG -- this is NOT a leak */
+ return(((char*)hdrret)+cl_malloc_hdr_offset); /*memory leak*/
+}
+
+
+/*
+ * cl_calloc: calloc clone
+ */
+
+void *
+cl_calloc(size_t nmemb, size_t size)
+{
+ void * ret = cl_malloc(nmemb*size);
+
+ if (ret != NULL) {
+ memset(ret, 0, nmemb*size);
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_tag(ret, "cl_malloc.c", "cl_calloc", 0);
+#endif
+ }
+
+ return(ret);
+}
+
+#ifdef HA_MALLOC_TRACK
+void *
+cl_calloc_track(size_t nmemb, size_t size,
+ const char *file, const char *function, const int line)
+{
+ void* ret;
+
+ ret = cl_calloc(nmemb, size);
+
+ if (ret) {
+ cl_ptr_tag(ret, file, function, line);
+ }
+
+ return ret;
+}
+
+void*
+cl_realloc_track(void *ptr, size_t newsize,
+ const char *file, const char *function, const int line)
+{
+ void* ret;
+
+ ret = cl_realloc(ptr, newsize);
+
+ if (ret) {
+ cl_ptr_tag(ret, file, function, line);
+ }
+
+ return ret;
+}
+
+void *
+cl_malloc_track(size_t size,
+ const char *file, const char *function, const int line)
+{
+ void* ret;
+
+ ret = cl_malloc(size);
+ if (ret) {
+ /* Retag with the proper owner. */
+ cl_ptr_tag(ret, file, function, line);
+ }
+
+ return ret;
+}
+
+#endif
+
+/*
+ * cl_strdup: strdup clone
+ */
+
+char *
+cl_strdup(const char *s)
+{
+ void * ret;
+
+ if (!s) {
+ cl_log(LOG_ERR, "cl_strdup(NULL)");
+ return(NULL);
+ }
+ ret = cl_malloc((strlen(s) + 1) * sizeof(char));
+
+ if (ret) {
+ strcpy(ret, s);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * cl_malloc_init(): initialize our malloc wrapper things
+ */
+
+static void
+cl_malloc_init()
+{
+ int j;
+ size_t cursize = 32;
+ int llcount = 1;
+
+ cl_malloc_inityet = 1;
+
+ /* cl_malloc_hdr_offset should be a double-word multiple */
+ while (cl_malloc_hdr_offset > (llcount * sizeof(long long))) {
+ llcount++;
+ }
+ cl_malloc_hdr_offset = llcount * sizeof(long long);
+
+
+ for (j=0; j < NUMBUCKS; ++j) {
+ cl_malloc_buckets[j] = NULL;
+
+ cl_bucket_sizes[j] = cursize;
+ cursize <<= 1;
+ }
+ buckminpow2 = INT2POW2(cl_bucket_sizes[0]-1);
+#ifdef MARK_PRISTINE
+ {
+ struct cl_bucket b;
+ pristoff = (unsigned char*)&(b.next)-(unsigned char*)&b;
+ pristoff += sizeof(b.next);
+ }
+#endif
+#ifdef HA_MALLOC_TRACK
+ cl_ptr_init();
+#endif
+}
+
+void
+cl_malloc_setstats(volatile cl_mem_stats_t *stats)
+{
+ if (memstats && stats) {
+ *stats = *memstats;
+ }
+ memstats = stats;
+}
+
+volatile cl_mem_stats_t *
+cl_malloc_getstats(void)
+{
+ return memstats;
+}
+
+static void
+cl_dump_item(const struct cl_bucket*b)
+{
+ const unsigned char * cbeg;
+ const unsigned char * cend;
+ const unsigned char * cp;
+ cl_log(LOG_INFO, "Dumping cl_malloc item @ 0x%lx, bucket address: 0x%lx"
+ , ((unsigned long)b)+cl_malloc_hdr_offset, (unsigned long)b);
+#ifdef HA_MALLOC_TRACK
+ cl_log(LOG_INFO, "Owner: %s"
+ , b->hdr.owner);
+#endif
+#ifdef HA_MALLOC_MAGIC
+ cl_log(LOG_INFO, "Magic number: 0x%lx reqsize=%ld"
+ ", bucket=%d, bucksize=%ld"
+ , b->hdr.magic
+ , (long)b->hdr.reqsize, b->hdr.bucket
+ , (long)(b->hdr.bucket >= NUMBUCKS ? 0
+ : cl_bucket_sizes[b->hdr.bucket]));
+#else
+ cl_log(LOG_INFO, "reqsize=%ld"
+ ", bucket=%d, bucksize=%ld"
+ , (long)b->hdr.reqsize, b->hdr.bucket
+ , (long)(b->hdr.bucket >= NUMBUCKS ? 0
+ : cl_bucket_sizes[b->hdr.bucket]));
+#endif
+ cbeg = ((const unsigned char *)b)+cl_malloc_hdr_offset;
+ cend = cbeg+b->hdr.reqsize+GUARDSIZE;
+
+ for (cp=cbeg; cp < cend; cp+= sizeof(unsigned)) {
+ cl_log(LOG_INFO, "%02x %02x %02x %02x \"%c%c%c%c\""
+ , (unsigned)cp[0], (unsigned)cp[1]
+ , (unsigned)cp[2], (unsigned)cp[3]
+ , cp[0], cp[1], cp[2], cp[3]);
+ }
+}
+
+/* The only reason these functions exist is because glib uses non-standard
+ * types (gsize)in place of size_t. Since size_t is 64-bits on some
+ * machines where gsize (unsigned int) is 32-bits, this is annoying.
+ */
+
+static gpointer
+cl_malloc_glib(gsize n_bytes)
+{
+ return (gpointer)cl_malloc((size_t)n_bytes);
+}
+
+static void
+cl_free_glib(gpointer mem)
+{
+ cl_free((void*)mem);
+}
+
+static void *
+cl_realloc_glib(gpointer mem, gsize n_bytes)
+{
+ return cl_realloc((void*)mem, (size_t)n_bytes);
+}
+
+
+/* Call before using any glib functions(!) */
+/* See also: g_mem_set_vtable() */
+void
+cl_malloc_forced_for_glib(void)
+{
+ static GMemVTable vt = {
+ cl_malloc_glib,
+ cl_realloc_glib,
+ cl_free_glib,
+ NULL,
+ NULL,
+ NULL,
+ };
+ if (!cl_malloc_inityet) {
+ cl_malloc_init();
+ }
+ g_mem_set_vtable(&vt);
+}
+
+#ifdef MARK_PRISTINE
+static int
+cl_check_is_pristine(const void* v, unsigned size)
+{
+ const unsigned char * cp;
+ const unsigned char * last;
+ cp = v;
+ last = cp + size;
+ cp += pristoff;
+
+ for (;cp < last; ++cp) {
+ if (*cp != PRISTVALUE) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+static void
+cl_mark_pristine(void* v, unsigned size)
+{
+ unsigned char * cp = v;
+ memset(cp+pristoff, PRISTVALUE, size-pristoff);
+}
+#endif
+
+#endif /* _CLPLUMBING_CLMALLOC_NATIVE_H */
diff --git a/lib/clplumbing/cl_misc.c b/lib/clplumbing/cl_misc.c
new file mode 100644
index 0000000..be6441d
--- /dev/null
+++ b/lib/clplumbing/cl_misc.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <lha_internal.h>
+
+#include <strings.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_log.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <sys/time.h>
+
+int
+cl_str_to_boolean(const char * s, int * ret)
+{
+ if(s == NULL) {
+ return HA_FAIL;
+ }
+
+ if ( strcasecmp(s, "true") == 0
+ || strcasecmp(s, "on") == 0
+ || strcasecmp(s, "yes") == 0
+ || strcasecmp(s, "y") == 0
+ || strcasecmp(s, "1") == 0){
+ *ret = TRUE;
+ return HA_OK;
+ }
+ if ( strcasecmp(s, "false") == 0
+ || strcasecmp(s, "off") == 0
+ || strcasecmp(s, "no") == 0
+ || strcasecmp(s, "n") == 0
+ || strcasecmp(s, "0") == 0){
+ *ret = FALSE;
+ return HA_OK;
+ }
+ return HA_FAIL;
+}
+
+int
+cl_file_exists(const char* filename)
+{
+ struct stat st;
+
+ if (filename == NULL){
+ cl_log(LOG_ERR, "%s: NULL filename",
+ __FUNCTION__);
+ return FALSE;
+ }
+
+ if (lstat(filename, &st) == 0){
+ return S_ISREG(st.st_mode);
+ }
+
+ return FALSE;
+}
+
+char*
+cl_get_env(const char* env_name)
+{
+ if (env_name == NULL){
+ cl_log(LOG_ERR, "%s: null name",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ return getenv(env_name);
+}
+
+
+int
+cl_binary_to_int(const char* data, int len)
+{
+ const char *p = data;
+ const char *pmax = p + len;
+ guint h = *p;
+
+ if (h){
+ for (p += 1; p < pmax; p++){
+ h = (h << 5) - h + *p;
+ }
+ }
+
+ return h;
+}
+
+/*
+ * Convert a string into a positive, rounded number of milliseconds.
+ *
+ * Returns -1 on error.
+ *
+ * Permissible forms:
+ * [0-9]+ units are seconds
+ * [0-9]*.[0-9]+ units are seconds
+ * [0-9]+ *[Mm][Ss] units are milliseconds
+ * [0-9]*.[0-9]+ *[Mm][Ss] units are milliseconds
+ * [0-9]+ *[Uu][Ss] units are microseconds
+ * [0-9]*.[0-9]+ *[Uu][Ss] units are microseconds
+ *
+ * Examples:
+ *
+ * 1 = 1000 milliseconds
+ * 1000ms = 1000 milliseconds
+ * 1000000us = 1000 milliseconds
+ * 0.1 = 100 milliseconds
+ * 100ms = 100 milliseconds
+ * 100000us = 100 milliseconds
+ * 0.001 = 1 millisecond
+ * 1ms = 1 millisecond
+ * 1000us = 1 millisecond
+ * 499us = 0 milliseconds
+ * 501us = 1 millisecond
+ */
+
+#define NUMCHARS "0123456789."
+#define WHITESPACE " \t\n\r\f"
+#define EOS '\0'
+
+long
+cl_get_msec(const char * input)
+{
+ const char * cp = input;
+ const char * units;
+ long multiplier = 1000;
+ long divisor = 1;
+ long ret = -1;
+ double dret;
+
+ cp += strspn(cp, WHITESPACE);
+ units = cp + strspn(cp, NUMCHARS);
+ units += strspn(units, WHITESPACE);
+
+ if (strchr(NUMCHARS, *cp) == NULL) {
+ return ret;
+ }
+
+ if (strncasecmp(units, "ms", 2) == 0
+ || strncasecmp(units, "cl_get_msec", 4) == 0) {
+ multiplier = 1;
+ divisor = 1;
+ }else if (strncasecmp(units, "us", 2) == 0
+ || strncasecmp(units, "usec", 4) == 0) {
+ multiplier = 1;
+ divisor = 1000;
+ }else if (*units != EOS && *units != '\n'
+ && *units != '\r') {
+ return ret;
+ }
+ dret = atof(cp);
+ dret *= (double)multiplier;
+ dret /= (double)divisor;
+ dret += 0.5;
+ ret = (long)dret;
+ return(ret);
+}
diff --git a/lib/clplumbing/cl_msg.c b/lib/clplumbing/cl_msg.c
new file mode 100644
index 0000000..22f00e3
--- /dev/null
+++ b/lib/clplumbing/cl_msg.c
@@ -0,0 +1,2537 @@
+/*
+ * Heartbeat messaging object.
+ *
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <ha_msg.h>
+#include <unistd.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/netstring.h>
+#include <glib.h>
+#include <clplumbing/cl_uuid.h>
+#include <compress.h>
+#include <clplumbing/timers.h>
+#include <clplumbing/cl_signal.h>
+
+#define MAXMSGLINE 512
+#define MINFIELDS 30
+#define NEWLINE "\n"
+
+
+#define NEEDAUTH 1
+#define NOAUTH 0
+#define MAX_INT_LEN 64
+#define MAX_NAME_LEN 255
+#define UUID_SLEN 64
+#define MAXCHILDMSGLEN 512
+
+static int compression_threshold = (128*1024);
+
+static enum cl_msgfmt msgfmt = MSGFMT_NVPAIR;
+static gboolean use_traditional_compression = FALSE;
+
+const char*
+FT_strings[]={
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9"
+};
+
+#undef DOAUDITS
+#define DOAUDITS
+
+#undef DOPARANOIDAUDITS
+/* #define DOPARANOIDAUDITS */
+
+#ifdef DOAUDITS
+void ha_msg_audit(const struct ha_msg* msg);
+# define AUDITMSG(msg) ha_msg_audit(msg)
+# ifdef DOPARANOIDAUDITS
+# define PARANOIDAUDITMSG(msg) ha_msg_audit(msg)
+# else
+# define PARANOIDAUDITMSG(msg) /*nothing*/
+# endif
+#else
+# define AUDITMSG(msg) /*nothing*/
+# define PARANOIDAUDITMSG(msg) /*nothing*/
+#endif
+
+
+static volatile hb_msg_stats_t* msgstats = NULL;
+
+gboolean cl_msg_quiet_fmterr = FALSE;
+
+extern int netstring_format;
+
+static struct ha_msg* wirefmt2msg_ll(const char* s, size_t length, int need_auth);
+
+struct ha_msg* string2msg_ll(const char * s, size_t length, int need_auth, int depth);
+
+extern int struct_stringlen(size_t namlen, size_t vallen, const void* value);
+extern int struct_netstringlen(size_t namlen, size_t vallen, const void* value);
+extern int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen);
+static char* msg2wirefmt_ll(struct ha_msg*m, size_t* len, gboolean need_compress);
+extern GHashTable* CompressFuncs;
+
+
+void
+cl_set_traditional_compression(gboolean value)
+{
+ use_traditional_compression = value;
+ if (use_traditional_compression && CompressFuncs) {
+ cl_log(LOG_WARNING
+ , "Traditional compression selected"
+ ". Realtime behavior will likely be impacted(!)");
+ cl_log(LOG_INFO
+ , "See %s for more information."
+ , HAURL("Ha.cf#traditional_compression_-_controls_compression_mode"));
+ }
+}
+
+void
+cl_set_compression_threshold(size_t threadhold)
+{
+ compression_threshold = threadhold;
+
+}
+
+void
+cl_msg_setstats(volatile hb_msg_stats_t* stats)
+{
+ msgstats = stats;
+}
+
+static int msg_stats_fd = -1;
+
+static int
+cl_msg_stats_open(const char* filename)
+{
+ if (filename == NULL){
+ cl_log(LOG_ERR, "%s: filename is NULL", __FUNCTION__);
+ return -1;
+ }
+
+ return open(filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
+
+}
+
+static int
+cl_msg_stats_close(void)
+{
+ if (msg_stats_fd > 0){
+ close(msg_stats_fd);
+ }
+
+ msg_stats_fd = -1;
+
+ return HA_OK;
+}
+
+#define STATSFILE "/var/log/ha_msg_stats"
+int
+cl_msg_stats_add(longclock_t time, int size)
+{
+ char buf[MAXLINE];
+ int len;
+
+ if (msg_stats_fd < 0){
+ msg_stats_fd = cl_msg_stats_open(STATSFILE);
+ if (msg_stats_fd < 0){
+ cl_log(LOG_ERR, "%s:opening file failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+ }
+
+
+ sprintf(buf, "%lld %d\n", (long long)time, size);
+ len = strnlen(buf, MAXLINE);
+ if (write(msg_stats_fd, buf, len) == len){
+ cl_msg_stats_close();
+ return HA_OK;
+ }
+
+ cl_msg_stats_close();
+
+ return HA_FAIL;;
+
+}
+
+
+/* Set default messaging format */
+void
+cl_set_msg_format(enum cl_msgfmt mfmt)
+{
+ msgfmt = mfmt;
+}
+
+void
+cl_dump_msgstats(void)
+{
+ if (msgstats){
+ cl_log(LOG_INFO, "dumping msg stats: "
+ "allocmsgs=%lu",
+ msgstats->allocmsgs);
+ }
+ return;
+}
+void
+list_cleanup(GList* list)
+{
+ size_t i;
+ for (i = 0; i < g_list_length(list); i++){
+ char* element = g_list_nth_data(list, i);
+ if (element == NULL){
+ cl_log(LOG_WARNING, "list_cleanup:"
+ "element is NULL");
+ continue;
+ }
+ free(element);
+ }
+ g_list_free(list);
+}
+
+
+
+/* Create a new (empty) message */
+struct ha_msg *
+ha_msg_new(int nfields)
+{
+ struct ha_msg * ret;
+ int nalloc;
+
+ ret = MALLOCT(struct ha_msg);
+ if (ret) {
+ ret->nfields = 0;
+
+ if (nfields > MINFIELDS) {
+ nalloc = nfields;
+ } else {
+ nalloc = MINFIELDS;
+ }
+
+ ret->nalloc = nalloc;
+ ret->names = (char **)calloc(sizeof(char *), nalloc);
+ ret->nlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ ret->values = (void **)calloc(sizeof(void *), nalloc);
+ ret->vlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ ret->types = (int*)calloc(sizeof(int), nalloc);
+
+ if (ret->names == NULL || ret->values == NULL
+ || ret->nlens == NULL || ret->vlens == NULL
+ || ret->types == NULL) {
+
+ cl_log(LOG_ERR, "%s"
+ , "ha_msg_new: out of memory for ha_msg");
+ /* It is safe to give this to ha_msg_del() */
+ /* at this point. It's well-enough-formed */
+ ha_msg_del(ret); /*violated property*/
+ ret = NULL;
+ }else if (msgstats) {
+ msgstats->allocmsgs++;
+ msgstats->totalmsgs++;
+ msgstats->lastmsg = time_longclock();
+ }
+ }
+ return(ret);
+}
+
+/* Delete (destroy) a message */
+void
+ha_msg_del(struct ha_msg *msg)
+{
+ if (msg) {
+ int j;
+ PARANOIDAUDITMSG(msg);
+ if (msgstats) {
+ msgstats->allocmsgs--;
+ }
+ if (msg->names) {
+ for (j=0; j < msg->nfields; ++j) {
+ if (msg->names[j]) {
+ free(msg->names[j]);
+ msg->names[j] = NULL;
+ }
+ }
+ free(msg->names);
+ msg->names = NULL;
+ }
+ if (msg->values) {
+ for (j=0; j < msg->nfields; ++j) {
+
+ if (msg->values[j] == NULL){
+ continue;
+ }
+
+ if(msg->types[j] < DIMOF(fieldtypefuncs)){
+ fieldtypefuncs[msg->types[j]].memfree(msg->values[j]);
+ }
+ }
+ free(msg->values);
+ msg->values = NULL;
+ }
+ if (msg->nlens) {
+ free(msg->nlens);
+ msg->nlens = NULL;
+ }
+ if (msg->vlens) {
+ free(msg->vlens);
+ msg->vlens = NULL;
+ }
+ if (msg->types){
+ free(msg->types);
+ msg->types = NULL;
+ }
+ msg->nfields = -1;
+ msg->nalloc = -1;
+ free(msg);
+ }
+}
+struct ha_msg*
+ha_msg_copy(const struct ha_msg *msg)
+{
+ struct ha_msg* ret;
+ int j;
+
+
+ PARANOIDAUDITMSG(msg);
+ if (msg == NULL || (ret = ha_msg_new(msg->nalloc)) == NULL) {
+ return NULL;
+ }
+
+ ret->nfields = msg->nfields;
+
+ memcpy(ret->nlens, msg->nlens, sizeof(msg->nlens[0])*msg->nfields);
+ memcpy(ret->vlens, msg->vlens, sizeof(msg->vlens[0])*msg->nfields);
+ memcpy(ret->types, msg->types, sizeof(msg->types[0])*msg->nfields);
+
+ for (j=0; j < msg->nfields; ++j) {
+
+ if ((ret->names[j] = malloc(msg->nlens[j]+1)) == NULL) {
+ goto freeandleave;
+ }
+ memcpy(ret->names[j], msg->names[j], msg->nlens[j]+1);
+
+
+ if(msg->types[j] < DIMOF(fieldtypefuncs)){
+ ret->values[j] = fieldtypefuncs[msg->types[j]].dup(msg->values[j],
+ msg->vlens[j]);
+ if (!ret->values[j]){
+ cl_log(LOG_ERR,"duplicating the message field failed");
+ goto freeandleave;
+ }
+ }
+ }
+ return ret;
+
+freeandleave:
+ /*
+ * ha_msg_del nicely handles partially constructed ha_msgs
+ * so, there's not really a memory leak here at all, but BEAM
+ * thinks there is.
+ */
+ ha_msg_del(ret);/* memory leak */ ret=NULL;
+ return ret;
+}
+
+#ifdef DOAUDITS
+void
+ha_msg_audit(const struct ha_msg* msg)
+{
+ int doabort = FALSE;
+ int j;
+
+ if (!msg) {
+ return;
+ }
+ if (!msg) {
+ cl_log(LOG_CRIT, "Message @ %p is not allocated"
+ , msg);
+ abort();
+ }
+ if (msg->nfields < 0) {
+ cl_log(LOG_CRIT, "Message @ %p has negative fields (%d)"
+ , msg, msg->nfields);
+ doabort = TRUE;
+ }
+ if (msg->nalloc < 0) {
+ cl_log(LOG_CRIT, "Message @ %p has negative nalloc (%d)"
+ , msg, msg->nalloc);
+ doabort = TRUE;
+ }
+
+ if (!msg->names) {
+ cl_log(LOG_CRIT
+ , "Message names @ %p is not allocated"
+ , msg->names);
+ doabort = TRUE;
+ }
+ if (!msg->values) {
+ cl_log(LOG_CRIT
+ , "Message values @ %p is not allocated"
+ , msg->values);
+ doabort = TRUE;
+ }
+ if (!msg->nlens) {
+ cl_log(LOG_CRIT
+ , "Message nlens @ %p is not allocated"
+ , msg->nlens);
+ doabort = TRUE;
+ }
+ if (!msg->vlens) {
+ cl_log(LOG_CRIT
+ , "Message vlens @ %p is not allocated"
+ , msg->vlens);
+ doabort = TRUE;
+ }
+ if (doabort) {
+ cl_log_message(LOG_INFO,msg);
+ abort();
+ }
+ for (j=0; j < msg->nfields; ++j) {
+
+ if (msg->nlens[j] == 0){
+ cl_log(LOG_ERR, "zero namelen found in msg");
+ abort();
+ }
+
+ if (msg->types[j] == FT_STRING){
+ if (msg->vlens[j] != strlen(msg->values[j])){
+ cl_log(LOG_ERR, "stringlen does not match");
+ cl_log_message(LOG_INFO,msg);
+ abort();
+ }
+ }
+
+ if (!msg->names[j]) {
+ cl_log(LOG_CRIT, "Message name[%d] @ 0x%p"
+ " is not allocated." ,
+ j, msg->names[j]);
+ abort();
+ }
+ if (msg->types[j] != FT_LIST && !msg->values[j]) {
+ cl_log(LOG_CRIT, "Message value [%d] @ 0x%p"
+ " is not allocated.", j, msg->values[j]);
+ cl_log_message(LOG_INFO, msg);
+ abort();
+ }
+ }
+}
+#endif
+
+
+
+int
+ha_msg_expand(struct ha_msg* msg )
+{
+ char ** names ;
+ size_t *nlens ;
+ void ** values ;
+ size_t* vlens ;
+ int * types ;
+ int nalloc;
+
+ if(!msg){
+ cl_log(LOG_ERR, "ha_msg_expand:"
+ "input msg is null");
+ return HA_FAIL;
+ }
+
+ names = msg->names;
+ nlens = msg->nlens;
+ values = msg->values;
+ vlens = msg->vlens;
+ types = msg->types;
+
+ nalloc = msg->nalloc + MINFIELDS;
+ msg->names = (char **)calloc(sizeof(char *), nalloc);
+ msg->nlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ msg->values = (void **)calloc(sizeof(void *), nalloc);
+ msg->vlens = (size_t *)calloc(sizeof(size_t), nalloc);
+ msg->types= (int*)calloc(sizeof(int), nalloc);
+
+ if (msg->names == NULL || msg->values == NULL
+ || msg->nlens == NULL || msg->vlens == NULL
+ || msg->types == NULL) {
+
+ cl_log(LOG_ERR, "%s"
+ , " out of memory for ha_msg");
+ return(HA_FAIL);
+ }
+
+ memcpy(msg->names, names, msg->nalloc*sizeof(char *));
+ memcpy(msg->nlens, nlens, msg->nalloc*sizeof(size_t));
+ memcpy(msg->values, values, msg->nalloc*sizeof(void *));
+ memcpy(msg->vlens, vlens, msg->nalloc*sizeof(size_t));
+ memcpy(msg->types, types, msg->nalloc*sizeof(int));
+
+ free(names);
+ free(nlens);
+ free(values);
+ free(vlens);
+ free(types);
+
+ msg->nalloc = nalloc;
+
+ return HA_OK;
+}
+
+int
+cl_msg_remove_value(struct ha_msg* msg, const void* value)
+{
+ int j;
+
+ if (msg == NULL || value == NULL){
+ cl_log(LOG_ERR, "cl_msg_remove: invalid argument");
+ return HA_FAIL;
+ }
+
+ for (j = 0; j < msg->nfields; ++j){
+ if (value == msg->values[j]){
+ break;
+ }
+ }
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_remove: field %p not found", value);
+ return HA_FAIL;
+ }
+ return cl_msg_remove_offset(msg, j);
+
+}
+
+
+int
+cl_msg_remove(struct ha_msg* msg, const char* name)
+{
+ int j;
+
+ if (msg == NULL || name == NULL){
+ cl_log(LOG_ERR, "cl_msg_remove: invalid argument");
+ return HA_FAIL;
+ }
+
+ for (j = 0; j < msg->nfields; ++j){
+ if (strcmp(name, msg->names[j]) == 0){
+ break;
+ }
+ }
+
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_remove: field %s not found", name);
+ return HA_FAIL;
+ }
+ return cl_msg_remove_offset(msg, j);
+}
+
+int
+cl_msg_remove_offset(struct ha_msg* msg, int offset)
+{
+ int j = offset;
+ int i;
+
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_remove: field %d not found", j);
+ return HA_FAIL;
+ }
+
+ free(msg->names[j]);
+ fieldtypefuncs[msg->types[j]].memfree(msg->values[j]);
+
+ for (i= j + 1; i < msg->nfields ; i++){
+ msg->names[i -1] = msg->names[i];
+ msg->nlens[i -1] = msg->nlens[i];
+ msg->values[i -1] = msg->values[i];
+ msg->vlens[i-1] = msg->vlens[i];
+ msg->types[i-1] = msg->types[i];
+ }
+ msg->nfields--;
+
+
+ return HA_OK;
+}
+
+
+
+/* low level implementation for ha_msg_add
+ the caller is responsible to allocate/free memories
+ for @name and @value.
+
+*/
+
+static int
+ha_msg_addraw_ll(struct ha_msg * msg, char * name, size_t namelen,
+ void * value, size_t vallen, int type, int depth)
+{
+
+ size_t startlen = sizeof(MSG_START)-1;
+
+
+ int (*addfield) (struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth);
+
+ if (!msg || msg->names == NULL || (msg->values == NULL) ) {
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: cannot add field to ha_msg");
+ return(HA_FAIL);
+ }
+
+ if (msg->nfields >= msg->nalloc) {
+ if( ha_msg_expand(msg) != HA_OK){
+ cl_log(LOG_ERR, "message expanding failed");
+ return(HA_FAIL);
+ }
+
+ }
+
+ if (namelen >= startlen
+ && name[0] == '>'
+ && strncmp(name, MSG_START, startlen) == 0) {
+ if(!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: illegal field");
+ }
+ return(HA_FAIL);
+ }
+
+ if (name == NULL || (value == NULL)
+ || namelen <= 0 || vallen < 0) {
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: "
+ "cannot add name/value to ha_msg");
+ return(HA_FAIL);
+ }
+
+ HA_MSG_ASSERT(type < DIMOF(fieldtypefuncs));
+
+ addfield = fieldtypefuncs[type].addfield;
+ if (!addfield ||
+ addfield(msg, name, namelen, value, vallen,depth) != HA_OK){
+ cl_log(LOG_ERR, "ha_msg_addraw_ll: addfield failed");
+ return(HA_FAIL);
+ }
+
+ PARANOIDAUDITMSG(msg);
+
+ return(HA_OK);
+
+
+}
+
+static int
+ha_msg_addraw(struct ha_msg * msg, const char * name, size_t namelen,
+ const void * value, size_t vallen, int type, int depth)
+{
+
+ char *cpvalue = NULL;
+ char *cpname = NULL;
+ int ret;
+
+
+ if (namelen == 0){
+ cl_log(LOG_ERR, "%s: Adding a field with 0 name length", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if ((cpname = malloc(namelen+1)) == NULL) {
+ cl_log(LOG_ERR, "ha_msg_addraw: no memory for string (name)");
+ return(HA_FAIL);
+ }
+ strncpy(cpname, name, namelen);
+ cpname[namelen] = EOS;
+
+ HA_MSG_ASSERT(type < DIMOF(fieldtypefuncs));
+
+ if (fieldtypefuncs[type].dup){
+ cpvalue = fieldtypefuncs[type].dup(value, vallen);
+ }
+ if (cpvalue == NULL){
+ cl_log(LOG_ERR, "ha_msg_addraw: copying message failed");
+ free(cpname);
+ return(HA_FAIL);
+ }
+
+ ret = ha_msg_addraw_ll(msg, cpname, namelen, cpvalue, vallen
+ , type, depth);
+
+ if (ret != HA_OK){
+ cl_log(LOG_ERR, "ha_msg_addraw(): ha_msg_addraw_ll failed");
+ free(cpname);
+ fieldtypefuncs[type].memfree(cpvalue);
+ }
+
+ return(ret);
+
+}
+
+/*Add a null-terminated name and binary value to a message*/
+int
+ha_msg_addbin(struct ha_msg * msg, const char * name,
+ const void * value, size_t vallen)
+{
+
+ return(ha_msg_addraw(msg, name, strlen(name),
+ value, vallen, FT_BINARY, 0));
+
+}
+
+int
+ha_msg_adduuid(struct ha_msg* msg, const char *name, const cl_uuid_t* u)
+{
+ return(ha_msg_addraw(msg, name, strlen(name),
+ u, sizeof(cl_uuid_t), FT_BINARY, 0));
+}
+
+/*Add a null-terminated name and struct value to a message*/
+int
+ha_msg_addstruct(struct ha_msg * msg, const char * name, const void * value)
+{
+ const struct ha_msg* childmsg = (const struct ha_msg*) value;
+
+ if (get_netstringlen(childmsg) > MAXCHILDMSGLEN
+ || get_stringlen(childmsg) > MAXCHILDMSGLEN) {
+ /*cl_log(LOG_WARNING,
+ "%s: childmsg too big (name=%s, nslen=%d, len=%d)."
+ " Use ha_msg_addstruct_compress() instead.",
+ __FUNCTION__, name, get_netstringlen(childmsg),
+ get_stringlen(childmsg));
+ */
+ }
+
+ return ha_msg_addraw(msg, name, strlen(name), value,
+ sizeof(struct ha_msg), FT_STRUCT, 0);
+}
+
+int
+ha_msg_addstruct_compress(struct ha_msg * msg, const char * name, const void * value)
+{
+
+ if (use_traditional_compression){
+ return ha_msg_addraw(msg, name, strlen(name), value,
+ sizeof(struct ha_msg), FT_STRUCT, 0);
+ }else{
+ return ha_msg_addraw(msg, name, strlen(name), value,
+ sizeof(struct ha_msg), FT_UNCOMPRESS, 0);
+ }
+}
+
+int
+ha_msg_add_int(struct ha_msg * msg, const char * name, int value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%d", value);
+ return (ha_msg_add(msg, name, buf));
+}
+
+int
+ha_msg_mod_int(struct ha_msg * msg, const char * name, int value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%d", value);
+ return (cl_msg_modstring(msg, name, buf));
+}
+
+int
+ha_msg_value_int(const struct ha_msg * msg, const char * name, int* value)
+{
+ const char* svalue = ha_msg_value(msg, name);
+ if(NULL == svalue) {
+ return HA_FAIL;
+ }
+ *value = atoi(svalue);
+ return HA_OK;
+}
+
+int
+ha_msg_add_ul(struct ha_msg * msg, const char * name, unsigned long value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%lu", value);
+ return (ha_msg_add(msg, name, buf));
+}
+
+int
+ha_msg_mod_ul(struct ha_msg * msg, const char * name, unsigned long value)
+{
+ char buf[MAX_INT_LEN];
+ snprintf(buf, MAX_INT_LEN, "%lu", value);
+ return (cl_msg_modstring(msg, name, buf));
+}
+
+int
+ha_msg_value_ul(const struct ha_msg * msg, const char * name, unsigned long* value)
+{
+ const char* svalue = ha_msg_value(msg, name);
+ if(NULL == svalue) {
+ return HA_FAIL;
+ }
+ *value = strtoul(svalue, NULL, 10);
+ return HA_OK;
+}
+
+/*
+ * ha_msg_value_str_list()/ha_msg_add_str_list():
+ * transform a string list suitable for putting into an ha_msg is by a convention
+ * of naming the fields into the following format:
+ * listname1=foo
+ * listname2=bar
+ * listname3=stuff
+ * etc.
+ */
+
+GList*
+ha_msg_value_str_list(struct ha_msg * msg, const char * name)
+{
+
+ int i = 1;
+ int len = 0;
+ const char* value;
+ char* element;
+ GList* list = NULL;
+
+
+ if( NULL==msg||NULL==name||strnlen(name, MAX_NAME_LEN)>=MAX_NAME_LEN ){
+ return NULL;
+ }
+ len = cl_msg_list_length(msg,name);
+ for(i=0; i<len; i++) {
+ value = cl_msg_list_nth_data(msg,name,i);
+ if (NULL == value) {
+ break;
+ }
+ element = g_strdup(value);
+ list = g_list_append(list, element);
+ }
+ return list;
+}
+
+
+
+static void
+pair_to_msg(gpointer key, gpointer value, gpointer user_data)
+{
+ struct ha_msg* msg = (struct ha_msg*)user_data;
+ if( HA_OK != ha_msg_add(msg, key, value)) {
+ cl_log(LOG_ERR, "ha_msg_add in pair_to_msg failed");
+ }
+}
+
+
+static struct ha_msg*
+str_table_to_msg(GHashTable* hash_table)
+{
+ struct ha_msg* hash_msg;
+
+ if ( NULL == hash_table) {
+ return NULL;
+ }
+
+ hash_msg = ha_msg_new(5);
+ g_hash_table_foreach(hash_table, pair_to_msg, hash_msg);
+ return hash_msg;
+}
+
+
+static GHashTable*
+msg_to_str_table(struct ha_msg * msg)
+{
+ int i;
+ GHashTable* hash_table;
+
+ if ( NULL == msg) {
+ return NULL;
+ }
+
+ hash_table = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i = 0; i < msg->nfields; i++) {
+ if( FT_STRING != msg->types[i] ) {
+ continue;
+ }
+ g_hash_table_insert(hash_table,
+ g_strndup(msg->names[i],msg->nlens[i]),
+ g_strndup(msg->values[i],msg->vlens[i]));
+ }
+ return hash_table;
+}
+
+GHashTable*
+ha_msg_value_str_table(struct ha_msg * msg, const char * name)
+{
+ struct ha_msg* hash_msg;
+ GHashTable * hash_table = NULL;
+
+ if (NULL == msg || NULL == name) {
+ return NULL;
+ }
+
+ hash_msg = cl_get_struct(msg, name);
+ if (NULL == hash_msg) {
+ return NULL;
+ }
+ hash_table = msg_to_str_table(hash_msg);
+ return hash_table;
+}
+
+int
+ha_msg_add_str_table(struct ha_msg * msg, const char * name,
+ GHashTable* hash_table)
+{
+ struct ha_msg* hash_msg;
+ if (NULL == msg || NULL == name || NULL == hash_table) {
+ return HA_FAIL;
+ }
+
+ hash_msg = str_table_to_msg(hash_table);
+ if( HA_OK != ha_msg_addstruct(msg, name, hash_msg)) {
+ ha_msg_del(hash_msg);
+ cl_log(LOG_ERR
+ , "ha_msg_addstruct in ha_msg_add_str_table failed");
+ return HA_FAIL;
+ }
+ ha_msg_del(hash_msg);
+ return HA_OK;
+}
+
+int
+ha_msg_mod_str_table(struct ha_msg * msg, const char * name,
+ GHashTable* hash_table)
+{
+ struct ha_msg* hash_msg;
+ if (NULL == msg || NULL == name || NULL == hash_table) {
+ return HA_FAIL;
+ }
+
+ hash_msg = str_table_to_msg(hash_table);
+ if( HA_OK != cl_msg_modstruct(msg, name, hash_msg)) {
+ ha_msg_del(hash_msg);
+ cl_log(LOG_ERR
+ , "ha_msg_modstruct in ha_msg_mod_str_table failed");
+ return HA_FAIL;
+ }
+ ha_msg_del(hash_msg);
+ return HA_OK;
+}
+
+int
+cl_msg_list_add_string(struct ha_msg* msg, const char* name, const char* value)
+{
+ GList* list = NULL;
+ int ret;
+
+ if(!msg || !name || !value){
+ cl_log(LOG_ERR, "cl_msg_list_add_string: input invalid");
+ return HA_FAIL;
+ }
+
+
+ list = g_list_append(list, UNCONST_CAST_POINTER(gpointer, value));
+ if (!list){
+ cl_log(LOG_ERR, "cl_msg_list_add_string: append element to"
+ "a glist failed");
+ return HA_FAIL;
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+
+ g_list_free(list);
+
+ return ret;
+
+}
+
+/* Add a null-terminated name and value to a message */
+int
+ha_msg_add(struct ha_msg * msg, const char * name, const char * value)
+{
+ if(name == NULL || value == NULL) {
+ return HA_FAIL;
+ }
+ return(ha_msg_nadd(msg, name, strlen(name), value, strlen(value)));
+}
+
+/* Add a name/value pair to a message (with sizes for name and value) */
+int
+ha_msg_nadd(struct ha_msg * msg, const char * name, int namelen
+ , const char * value, int vallen)
+{
+ return(ha_msg_addraw(msg, name, namelen, value, vallen, FT_STRING, 0));
+
+}
+
+/* Add a name/value/type to a message (with sizes for name and value) */
+int
+ha_msg_nadd_type(struct ha_msg * msg, const char * name, int namelen
+ , const char * value, int vallen, int type)
+{
+ return(ha_msg_addraw(msg, name, namelen, value, vallen, type, 0));
+
+}
+
+
+
+/* Add a "name=value" line to the name, value pairs in a message */
+static int
+ha_msg_add_nv_depth(struct ha_msg* msg, const char * nvline,
+ const char * bufmax, int depth)
+{
+ int namelen;
+ const char * valp;
+ int vallen;
+
+ if (!nvline) {
+ cl_log(LOG_ERR, "ha_msg_add_nv: NULL nvline");
+ return(HA_FAIL);
+ }
+ /* How many characters before the '='? */
+ if ((namelen = strcspn(nvline, EQUAL)) <= 0
+ || nvline[namelen] != '=') {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "ha_msg_add_nv_depth: line doesn't contain '='");
+ cl_log(LOG_INFO, "%s", nvline);
+ }
+ return(HA_FAIL);
+ }
+ valp = nvline + namelen +1; /* Point just *past* the '=' */
+ if (valp >= bufmax){
+ return HA_FAIL;
+ }
+ vallen = strcspn(valp, NEWLINE);
+ if ((valp + vallen) >= bufmax){
+ return HA_FAIL;
+ }
+
+ if (vallen == 0){
+ valp = NULL;
+ }
+ /* Call ha_msg_nadd to actually add the name/value pair */
+ return(ha_msg_addraw(msg, nvline, namelen, valp, vallen
+ , FT_STRING, depth));
+
+}
+
+int
+ha_msg_add_nv(struct ha_msg* msg, const char * nvline,
+ const char * bufmax)
+{
+
+ return(ha_msg_add_nv_depth(msg, nvline, bufmax, 0));
+
+}
+
+
+static void *
+cl_get_value(const struct ha_msg * msg, const char * name,
+ size_t * vallen, int *type)
+{
+
+ int j;
+ if (!msg || !msg->names || !msg->values) {
+ cl_log(LOG_ERR, "%s: wrong argument (%s)",
+ __FUNCTION__, name);
+ return(NULL);
+ }
+
+ PARANOIDAUDITMSG(msg);
+ for (j=0; j < msg->nfields; ++j) {
+ const char *local_name = msg->names[j];
+ if (name[0] == local_name[0]
+ && strcmp(name, local_name) == 0) {
+ if (vallen){
+ *vallen = msg->vlens[j];
+ }
+ if (type){
+ *type = msg->types[j];
+ }
+ return(msg->values[j]);
+ }
+ }
+ return(NULL);
+}
+
+static void *
+cl_get_value_mutate(struct ha_msg * msg, const char * name,
+ size_t * vallen, int *type)
+{
+
+ int j;
+ if (!msg || !msg->names || !msg->values) {
+ cl_log(LOG_ERR, "%s: wrong argument",
+ __FUNCTION__);
+ return(NULL);
+ }
+
+ AUDITMSG(msg);
+ for (j=0; j < msg->nfields; ++j) {
+ if (strcmp(name, msg->names[j]) == 0) {
+ int tp = msg->types[j];
+ if (fieldtypefuncs[tp].pregetaction){
+ fieldtypefuncs[tp].pregetaction(msg, j);
+ }
+
+ if (vallen){
+ *vallen = msg->vlens[j];
+ }
+ if (type){
+ *type = msg->types[j];
+ }
+ return(msg->values[j]);
+ }
+ }
+ return(NULL);
+}
+
+
+const void *
+cl_get_binary(const struct ha_msg *msg,
+ const char * name, size_t * vallen)
+{
+
+ const void *ret;
+ int type;
+
+ ret = cl_get_value( msg, name, vallen, &type);
+
+ if (ret == NULL){
+ /*
+ cl_log(LOG_WARNING, "field %s not found", name);
+ cl_log_message(msg);
+ */
+ return(NULL);
+ }
+ if ( type != FT_BINARY){
+ cl_log(LOG_WARNING, "field %s is not binary", name);
+ cl_log_message(LOG_WARNING, msg);
+ return(NULL);
+ }
+
+ return(ret);
+}
+
+/* UUIDs are stored with a machine-independent byte ordering (even though it's binary) */
+int
+cl_get_uuid(const struct ha_msg *msg, const char * name, cl_uuid_t* retval)
+{
+ const void * vret;
+ size_t vretsize;
+
+ cl_uuid_clear(retval);
+
+ if ((vret = cl_get_binary(msg, name, &vretsize)/*discouraged function*/) == NULL) {
+ /* But perfectly portable in this case */
+ return HA_FAIL;
+ }
+ if (vretsize != sizeof(cl_uuid_t)) {
+ cl_log(LOG_WARNING, "Binary field %s is not a uuid.", name);
+ cl_log(LOG_INFO, "expecting %d bytes, got %d bytes",
+ (int)sizeof(cl_uuid_t), (int)vretsize);
+ cl_log_message(LOG_INFO, msg);
+ return HA_FAIL;
+ }
+ memcpy(retval, vret, sizeof(cl_uuid_t));
+ return HA_OK;
+}
+
+const char *
+cl_get_string(const struct ha_msg *msg, const char *name)
+{
+
+ const void *ret;
+ int type;
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if (ret == NULL || type != FT_STRING){
+ return(NULL);
+ }
+
+ return(ret);
+
+}
+
+int
+cl_get_type(const struct ha_msg *msg, const char *name)
+{
+
+ const void *ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if (ret == NULL) {
+ return -1;
+ }
+ if (type < 0){
+ cl_log(LOG_WARNING, "field %s not a valid type"
+ , name);
+ return(-1);
+ }
+
+ return(type);
+
+}
+
+/*
+struct ha_msg *
+cl_get_struct(const struct ha_msg *msg, const char* name)
+{
+ struct ha_msg* ret;
+ int type;
+ size_t vallen;
+
+ ret = cl_get_value(msg, name, &vallen, &type);
+
+ if (ret == NULL ){
+ return(NULL);
+ }
+
+ switch(type){
+
+ case FT_STRUCT:
+ break;
+
+ default:
+ cl_log(LOG_ERR, "%s: field %s is not a struct (%d)",
+ __FUNCTION__, name, type);
+ return NULL;
+ }
+
+ return ret;
+}
+*/
+
+
+struct ha_msg *
+cl_get_struct(struct ha_msg *msg, const char* name)
+{
+ struct ha_msg* ret;
+ int type = -1;
+ size_t vallen;
+
+ ret = cl_get_value_mutate(msg, name, &vallen, &type);
+
+ if (ret == NULL ){
+ return(NULL);
+ }
+
+ switch(type){
+
+ case FT_UNCOMPRESS:
+ case FT_STRUCT:
+ break;
+
+ default:
+ cl_log(LOG_ERR, "%s: field %s is not a struct (%d)",
+ __FUNCTION__, name, type);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+int
+cl_msg_list_length(struct ha_msg* msg, const char* name)
+{
+ GList* ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if ( ret == NULL || type != FT_LIST){
+ return -1;
+ }
+
+ return g_list_length(ret);
+
+}
+
+
+void*
+cl_msg_list_nth_data(struct ha_msg* msg, const char* name, int n)
+{
+ GList* ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if ( ret == NULL || type != FT_LIST){
+ cl_log(LOG_WARNING, "field %s not found "
+ " or type mismatch", name);
+ return NULL;
+ }
+
+ return g_list_nth_data(ret, n);
+
+}
+
+int
+cl_msg_add_list(struct ha_msg* msg, const char* name, GList* list)
+{
+ int ret;
+
+ if(msg == NULL|| name ==NULL || list == NULL){
+ cl_log(LOG_ERR, "cl_msg_add_list:"
+ "invalid arguments");
+ return HA_FAIL;
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+
+ return ret;
+}
+
+GList*
+cl_msg_get_list(struct ha_msg* msg, const char* name)
+{
+ GList* ret;
+ int type;
+
+ ret = cl_get_value( msg, name, NULL, &type);
+
+ if ( ret == NULL || type != FT_LIST){
+ cl_log(LOG_WARNING, "field %s not found "
+ " or type mismatch", name);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+int
+cl_msg_add_list_str(struct ha_msg* msg, const char* name,
+ char** buf, size_t n)
+{
+ GList* list = NULL;
+ int i;
+ int ret = HA_FAIL;
+
+ if (n <= 0 || buf == NULL|| name ==NULL ||msg == NULL){
+ cl_log(LOG_ERR, "%s:"
+ "invalid parameter(%s)",
+ !n <= 0?"n is negative or zero":
+ !buf?"buf is NULL":
+ !name?"name is NULL":
+ "msg is NULL",__FUNCTION__);
+ return HA_FAIL;
+ }
+
+ for ( i = 0; i < n; i++){
+ if (buf[i] == NULL){
+ cl_log(LOG_ERR, "%s: %dth element in buf is null",
+ __FUNCTION__, i);
+ goto free_and_out;
+ }
+ list = g_list_append(list, buf[i]);
+ if (list == NULL){
+ cl_log(LOG_ERR, "%s:adding integer to list failed",
+ __FUNCTION__);
+ goto free_and_out;
+ }
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+
+ free_and_out:
+ if (list){
+ g_list_free(list);
+ list = NULL;
+ }
+ return ret;
+}
+
+static void
+list_element_free(gpointer data, gpointer userdata)
+{
+ if (data){
+ g_free(data);
+ }
+}
+
+int
+cl_msg_add_list_int(struct ha_msg* msg, const char* name,
+ int* buf, size_t n)
+{
+
+ GList* list = NULL;
+ size_t i;
+ int ret = HA_FAIL;
+
+ if (n <= 0 || buf == NULL|| name ==NULL ||msg == NULL){
+ cl_log(LOG_ERR, "cl_msg_add_list_int:"
+ "invalid parameter(%s)",
+ !n <= 0?"n is negative or zero":
+ !buf?"buf is NULL":
+ !name?"name is NULL":
+ "msg is NULL");
+ goto free_and_out;
+ }
+
+ for ( i = 0; i < n; i++){
+ char intstr[MAX_INT_LEN];
+ sprintf(intstr,"%d", buf[i]);
+ list = g_list_append(list, g_strdup(intstr));
+ if (list == NULL){
+ cl_log(LOG_ERR, "cl_msg_add_list_int:"
+ "adding integer to list failed");
+ goto free_and_out;
+ }
+ }
+
+ ret = ha_msg_addraw(msg, name, strlen(name), list,
+ string_list_pack_length(list),
+ FT_LIST, 0);
+ free_and_out:
+ if (list){
+ g_list_foreach(list,list_element_free , NULL);
+ g_list_free(list);
+ list = NULL;
+ }
+
+ return ret;
+}
+int
+cl_msg_get_list_int(struct ha_msg* msg, const char* name,
+ int* buf, size_t* n)
+{
+ GList* list;
+ size_t len;
+ int i;
+ GList* list_element;
+
+
+ if (n == NULL || buf == NULL|| name ==NULL ||msg == NULL){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "invalid parameter(%s)",
+ !n?"n is NULL":
+ !buf?"buf is NULL":
+ !name?"name is NULL":
+ "msg is NULL");
+ return HA_FAIL;
+ }
+
+ list = cl_msg_get_list(msg, name);
+ if (list == NULL){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "list of integers %s not found", name);
+ return HA_FAIL;
+ }
+
+ len = g_list_length(list);
+ if (len > *n){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "buffer too small: *n=%ld, required len=%ld",
+ (long)*n, (long)len);
+ *n = len;
+ return HA_FAIL;
+ }
+
+ *n = len;
+ i = 0;
+ list_element = g_list_first(list);
+ while( list_element != NULL){
+ char* intstr = list_element->data;
+ if (intstr == NULL){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "element data is NULL");
+ return HA_FAIL;
+ }
+
+ if (sscanf(intstr,"%d", &buf[i]) != 1){
+ cl_log(LOG_ERR, "cl_msg_get_list_int:"
+ "element data is NULL");
+ return HA_FAIL;
+ }
+
+ i++;
+ list_element = g_list_next(list_element);
+ }
+
+ return HA_OK;
+}
+
+int
+cl_msg_replace_value(struct ha_msg* msg, const void *old_value,
+ const void* value, size_t vlen, int type)
+{
+ int j;
+
+ if (msg == NULL || old_value == NULL) {
+ cl_log(LOG_ERR, "cl_msg_replace: invalid argument");
+ return HA_FAIL;
+ }
+
+ for (j = 0; j < msg->nfields; ++j){
+ if (old_value == msg->values[j]){
+ break;
+ }
+ }
+ if (j == msg->nfields){
+ cl_log(LOG_ERR, "cl_msg_replace: field %p not found", old_value);
+ return HA_FAIL;
+ }
+ return cl_msg_replace(msg, j, value, vlen, type);
+}
+
+/*this function is for internal use only*/
+int
+cl_msg_replace(struct ha_msg* msg, int index,
+ const void* value, size_t vlen, int type)
+{
+ void * newv ;
+ int newlen = vlen;
+ int oldtype;
+
+ PARANOIDAUDITMSG(msg);
+ if (msg == NULL || value == NULL) {
+ cl_log(LOG_ERR, "%s: NULL input.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if(type >= DIMOF(fieldtypefuncs)){
+ cl_log(LOG_ERR, "%s:"
+ "invalid type(%d)",__FUNCTION__, type);
+ return HA_FAIL;
+ }
+
+ if (index >= msg->nfields){
+ cl_log(LOG_ERR, "%s: index(%d) out of range(%d)",
+ __FUNCTION__,index, msg->nfields);
+ return HA_FAIL;
+ }
+
+ oldtype = msg->types[index];
+
+ newv = fieldtypefuncs[type].dup(value,vlen);
+ if (!newv){
+ cl_log(LOG_ERR, "%s: duplicating message fields failed"
+ "value=%p, vlen=%d, msg->names[i]=%s",
+ __FUNCTION__,value, (int)vlen, msg->names[index]);
+ return HA_FAIL;
+ }
+
+ fieldtypefuncs[oldtype].memfree(msg->values[index]);
+
+ msg->values[index] = newv;
+ msg->vlens[index] = newlen;
+ msg->types[index] = type;
+ PARANOIDAUDITMSG(msg);
+ return(HA_OK);
+
+}
+
+
+static int
+cl_msg_mod(struct ha_msg * msg, const char * name,
+ const void* value, size_t vlen, int type)
+{
+ int j;
+ int rc;
+
+ PARANOIDAUDITMSG(msg);
+ if (msg == NULL || name == NULL || value == NULL) {
+ cl_log(LOG_ERR, "cl_msg_mod: NULL input.");
+ return HA_FAIL;
+ }
+
+ if(type >= DIMOF(fieldtypefuncs)){
+ cl_log(LOG_ERR, "cl_msg_mod:"
+ "invalid type(%d)", type);
+ return HA_FAIL;
+ }
+
+ for (j=0; j < msg->nfields; ++j) {
+ if (strcmp(name, msg->names[j]) == 0) {
+
+ char * newv ;
+ int newlen = vlen;
+
+ if (type != msg->types[j]){
+ cl_log(LOG_ERR, "%s: type mismatch(%d %d)",
+ __FUNCTION__, type, msg->types[j]);
+ return HA_FAIL;
+ }
+
+ newv = fieldtypefuncs[type].dup(value,vlen);
+ if (!newv){
+ cl_log(LOG_ERR, "duplicating message fields failed"
+ "value=%p, vlen=%d, msg->names[j]=%s",
+ value, (int)vlen, msg->names[j]);
+ return HA_FAIL;
+ }
+
+ fieldtypefuncs[type].memfree(msg->values[j]);
+ msg->values[j] = newv;
+ msg->vlens[j] = newlen;
+ PARANOIDAUDITMSG(msg);
+ return(HA_OK);
+ }
+ }
+
+ rc = ha_msg_nadd_type(msg, name,strlen(name), value, vlen, type);
+
+ PARANOIDAUDITMSG(msg);
+ return rc;
+}
+
+int
+cl_msg_modstruct(struct ha_msg * msg, const char* name,
+ const struct ha_msg* value)
+{
+ return cl_msg_mod(msg, name, value, 0, FT_STRUCT);
+}
+
+int
+cl_msg_modbin(struct ha_msg * msg, const char* name,
+ const void* value, size_t vlen)
+{
+ return cl_msg_mod(msg, name, value, vlen, FT_BINARY);
+
+}
+int
+cl_msg_moduuid(struct ha_msg * msg, const char* name,
+ const cl_uuid_t* uuid)
+{
+ return cl_msg_mod(msg, name, uuid, sizeof(cl_uuid_t), FT_BINARY);
+}
+
+
+
+/* Modify the value associated with a particular name */
+int
+cl_msg_modstring(struct ha_msg * msg, const char * name, const char * value)
+{
+ return cl_msg_mod(msg, name, value, strlen(value), FT_STRING);
+}
+
+
+
+/* Return the next message found in the stream */
+struct ha_msg *
+msgfromstream(FILE * f)
+{
+ char buf[MAXMSGLINE];
+ char * getsret;
+ clearerr(f);
+ /* Skip until we find a MSG_START (hopefully we skip nothing) */
+ while(1) {
+ getsret = fgets(buf, sizeof(buf), f);
+ if (!getsret) {
+ break;
+ }
+ if (strcmp(buf, MSG_START) == 0) {
+ return msgfromstream_string(f);
+
+ }
+ if (strcmp(buf, MSG_START_NETSTRING) == 0){
+ return msgfromstream_netstring(f);
+ }
+
+ }
+
+ return NULL;
+}
+
+/* Return the next message found in the stream with string format */
+struct ha_msg *
+msgfromstream_string(FILE * f)
+{
+ char buf[MAXMSGLINE];
+ const char * bufmax = buf + sizeof(buf);
+ struct ha_msg* ret;
+ char * getsret;
+
+
+ if ((ret = ha_msg_new(0)) == NULL) {
+ /* Getting an error with EINTR is pretty normal */
+ /* (so is EOF) */
+ if ( (!ferror(f) || (errno != EINTR && errno != EAGAIN))
+ && !feof(f)) {
+ cl_log(LOG_ERR, "msgfromstream: cannot get message");
+ }
+ return(NULL);
+ }
+
+ /* Add Name=value pairs until we reach MSG_END or EOF */
+ while(1) {
+ getsret = fgets(buf, MAXMSGLINE, f);
+ if (!getsret) {
+ break;
+ }
+
+ if (strnlen(buf, MAXMSGLINE) > MAXMSGLINE - 2) {
+ cl_log(LOG_DEBUG
+ , "msgfromstream: field too long [%s]"
+ , buf);
+ }
+
+ if (!strcmp(buf, MSG_END)) {
+ break;
+ }
+
+
+ /* Add the "name=value" string on this line to the message */
+ if (ha_msg_add_nv(ret, buf, bufmax) != HA_OK) {
+ cl_log(LOG_ERR, "NV failure (msgfromsteam): [%s]"
+ , buf);
+ ha_msg_del(ret); ret=NULL;
+ return(NULL);
+ }
+ }
+ return(ret);
+}
+
+
+/* Return the next message found in the stream with netstring format*/
+
+struct ha_msg *
+msgfromstream_netstring(FILE * f)
+{
+ struct ha_msg * ret;
+
+ if ((ret = ha_msg_new(0)) == NULL) {
+ /* Getting an error with EINTR is pretty normal */
+ /* (so is EOF) */
+ if ( (!ferror(f) || (errno != EINTR && errno != EAGAIN))
+ && !feof(f)) {
+ cl_log(LOG_ERR
+ , "msgfromstream_netstring(): cannot get message");
+ }
+ return(NULL);
+ }
+
+ while(1) {
+ char* nvpair;
+ int nvlen;
+ int n;
+
+ if (fscanf(f, "%d:", &nvlen) <= 0 || nvlen <= 0){
+ return(ret);
+ }
+
+ nvpair = malloc(nvlen + 2);
+
+ if ((n =fread(nvpair, 1, nvlen + 1, f)) != nvlen + 1){
+ cl_log(LOG_WARNING, "msgfromstream_netstring()"
+ ": Can't get enough nvpair,"
+ "expecting %d bytes long, got %d bytes",
+ nvlen + 1, n);
+ ha_msg_del(ret);
+ return(NULL);
+ }
+
+ process_netstring_nvpair(ret, nvpair, nvlen);
+
+ }
+
+}
+
+static gboolean ipc_timer_expired = FALSE;
+
+static void cl_sigalarm_handler(int signum)
+{
+ if (signum == SIGALRM) {
+ ipc_timer_expired = TRUE;
+ }
+}
+
+int
+cl_ipc_wait_timeout(
+ IPC_Channel *chan, int (*waitfun)(IPC_Channel *chan), unsigned int timeout)
+{
+ int rc = IPC_FAIL;
+ struct sigaction old_action;
+
+ memset(&old_action, 0, sizeof(old_action));
+ cl_signal_set_simple_handler(SIGALRM, cl_sigalarm_handler, &old_action);
+
+ ipc_timer_expired = FALSE;
+
+ alarm(timeout);
+ rc = waitfun(chan);
+ if (rc == IPC_INTR && ipc_timer_expired) {
+ rc = IPC_TIMEOUT;
+ }
+
+ alarm(0); /* ensure it expires */
+ cl_signal_set_simple_handler(SIGALRM, old_action.sa_handler, &old_action);
+
+
+ return rc;
+}
+
+/* Return the next message found in the IPC channel */
+static struct ha_msg*
+msgfromIPC_ll(IPC_Channel * ch, int flag, unsigned int timeout, int *rc_out)
+{
+ int rc;
+ IPC_Message* ipcmsg;
+ struct ha_msg* hmsg;
+ int need_auth = flag & MSG_NEEDAUTH;
+ int allow_intr = flag & MSG_ALLOWINTR;
+
+ startwait:
+ if(timeout > 0) {
+ rc = cl_ipc_wait_timeout(ch, ch->ops->waitin, timeout);
+ } else {
+ rc = ch->ops->waitin(ch);
+ }
+
+ if(rc_out) {
+ *rc_out = rc;
+ }
+
+ switch(rc) {
+ default:
+ case IPC_FAIL:
+ cl_perror("msgfromIPC: waitin failure");
+ return NULL;
+
+ case IPC_TIMEOUT:
+ return NULL;
+
+ case IPC_BROKEN:
+ sleep(1);
+ return NULL;
+
+ case IPC_INTR:
+ if ( allow_intr){
+ goto startwait;
+ }else{
+ return NULL;
+ }
+
+ case IPC_OK:
+ break;
+ }
+
+
+ ipcmsg = NULL;
+ rc = ch->ops->recv(ch, &ipcmsg);
+#if 0
+ if (DEBUGPKTCONT) {
+ cl_log(LOG_DEBUG, "msgfromIPC: recv returns %d ipcmsg = 0x%lx"
+ , rc, (unsigned long)ipcmsg);
+ }
+#endif
+ if(rc_out) {
+ *rc_out = rc;
+ }
+
+ if (rc != IPC_OK) {
+ return NULL;
+ }
+
+ hmsg = wirefmt2msg_ll((char *)ipcmsg->msg_body, ipcmsg->msg_len, need_auth);
+ if (ipcmsg->msg_done) {
+ ipcmsg->msg_done(ipcmsg);
+ }
+
+ AUDITMSG(hmsg);
+ return hmsg;
+}
+
+/* Return the next message found in the IPC channel */
+struct ha_msg*
+msgfromIPC_timeout(IPC_Channel *ch, int flag, unsigned int timeout, int *rc_out)
+{
+ return msgfromIPC_ll(ch, flag, timeout, rc_out);
+}
+
+struct ha_msg*
+msgfromIPC(IPC_Channel * ch, int flag)
+{
+ return msgfromIPC_ll(ch, flag, 0, NULL);
+}
+
+
+struct ha_msg*
+msgfromIPC_noauth(IPC_Channel * ch)
+{
+ int flag = 0;
+
+ flag |= MSG_ALLOWINTR;
+ return msgfromIPC_ll(ch, flag, 0, NULL);
+}
+
+/* Return the next message found in the IPC channel */
+IPC_Message *
+ipcmsgfromIPC(IPC_Channel * ch)
+{
+ int rc;
+ IPC_Message* ipcmsg;
+
+ rc = ch->ops->waitin(ch);
+
+ switch(rc) {
+ default:
+ case IPC_FAIL:
+ cl_perror("msgfromIPC: waitin failure");
+ return NULL;
+
+ case IPC_BROKEN:
+ sleep(1);
+ return NULL;
+
+ case IPC_INTR:
+ return NULL;
+
+ case IPC_OK:
+ break;
+ }
+
+
+ ipcmsg = NULL;
+ rc = ch->ops->recv(ch, &ipcmsg);
+#if 0
+ if (DEBUGPKTCONT) {
+ cl_log(LOG_DEBUG, "msgfromIPC: recv returns %d ipcmsg = 0x%lx"
+ , rc, (unsigned long)ipcmsg);
+ }
+#endif
+ if (rc != IPC_OK) {
+ return NULL;
+ }
+
+
+ return(ipcmsg);
+}
+
+
+/* Writes a message into a stream - used for serial lines */
+int
+msg2stream(struct ha_msg* m, FILE * f)
+{
+ size_t len;
+ char * s = msg2wirefmt(m, &len);
+
+ if (s != NULL) {
+ int rc = HA_OK;
+ if (fputs(s, f) == EOF) {
+ rc = HA_FAIL;
+ cl_perror("msg2stream: fputs failure");
+ }
+ if (fflush(f) == EOF) {
+ cl_perror("msg2stream: fflush failure");
+ rc = HA_FAIL;
+ }
+ free(s);
+ return(rc);
+ }else{
+ return(HA_FAIL);
+ }
+}
+static void ipcmsg_done(IPC_Message* m);
+
+static int clmsg_ipcmsg_allocated = 0;
+static int clmsg_ipcmsg_freed = 0;
+
+void dump_clmsg_ipcmsg_stats(void);
+void
+dump_clmsg_ipcmsg_stats(void)
+{
+ cl_log(LOG_INFO, "clmsg ipcmsg allocated=%d, freed=%d, diff=%d",
+ clmsg_ipcmsg_allocated,
+ clmsg_ipcmsg_freed,
+ clmsg_ipcmsg_allocated - clmsg_ipcmsg_freed);
+
+ return;
+}
+
+static void
+ipcmsg_done(IPC_Message* m)
+{
+ if (!m) {
+ return;
+ }
+ if (m->msg_buf) {
+ free(m->msg_buf);
+ }
+ free(m);
+ m = NULL;
+ clmsg_ipcmsg_freed ++;
+}
+
+
+
+/*
+ * create an ipcmsg and copy the data
+ */
+
+IPC_Message*
+wirefmt2ipcmsg(void* p, size_t len, IPC_Channel* ch)
+{
+ IPC_Message* ret = NULL;
+
+ if (p == NULL){
+ return(NULL);
+ }
+
+ ret = MALLOCT(IPC_Message);
+ if (!ret) {
+ return(NULL);
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ if (NULL == (ret->msg_buf = malloc(len + ch->msgpad))) {
+ free(ret);
+ return NULL;
+ }
+ ret->msg_body = (char*)ret->msg_buf + ch->msgpad;
+ memcpy(ret->msg_body, p, len);
+
+ ret->msg_done = ipcmsg_done;
+ ret->msg_private = NULL;
+ ret->msg_ch = ch;
+ ret->msg_len = len;
+
+ clmsg_ipcmsg_allocated ++;
+
+ return ret;
+
+}
+
+IPC_Message*
+hamsg2ipcmsg(struct ha_msg* m, IPC_Channel* ch)
+{
+ size_t len;
+ char * s = msg2wirefmt_ll(m, &len, MSG_NEEDCOMPRESS);
+ IPC_Message* ret = NULL;
+
+ if (s == NULL) {
+ return ret;
+ }
+ ret = MALLOCT(IPC_Message);
+ if (!ret) {
+ free(s);
+ return ret;
+ }
+
+ memset(ret, 0, sizeof(IPC_Message));
+
+ if (NULL == (ret->msg_buf = malloc(len + ch->msgpad))) {
+ free(s);
+ free(ret);
+ return NULL;
+ }
+ ret->msg_body = (char*)ret->msg_buf + ch->msgpad;
+ memcpy(ret->msg_body, s, len);
+ free(s);
+
+ ret->msg_done = ipcmsg_done;
+ ret->msg_private = NULL;
+ ret->msg_ch = ch;
+ ret->msg_len = len;
+
+ clmsg_ipcmsg_allocated ++;
+
+ return ret;
+}
+
+struct ha_msg*
+ipcmsg2hamsg(IPC_Message*m)
+{
+ struct ha_msg* ret = NULL;
+
+
+ ret = wirefmt2msg(m->msg_body, m->msg_len,MSG_NEEDAUTH);
+ return ret;
+}
+
+int
+msg2ipcchan(struct ha_msg*m, IPC_Channel*ch)
+{
+ IPC_Message* imsg;
+
+ if (m == NULL || ch == NULL) {
+ cl_log(LOG_ERR, "Invalid msg2ipcchan argument");
+ errno = EINVAL;
+ return HA_FAIL;
+ }
+
+ if ((imsg = hamsg2ipcmsg(m, ch)) == NULL) {
+ cl_log(LOG_ERR, "hamsg2ipcmsg() failure");
+ return HA_FAIL;
+ }
+
+ if (ch->ops->send(ch, imsg) != IPC_OK) {
+ if (ch->ch_status == IPC_CONNECT) {
+ snprintf(ch->failreason,MAXFAILREASON,
+ "send failed,farside_pid=%d, sendq length=%ld(max is %ld)",
+ ch->farside_pid, (long)ch->send_queue->current_qlen,
+ (long)ch->send_queue->max_qlen);
+ }
+ imsg->msg_done(imsg);
+ return HA_FAIL;
+ }
+ return HA_OK;
+}
+
+static gboolean (*msg_authentication_method)(const struct ha_msg* ret) = NULL;
+
+
+void
+cl_set_oldmsgauthfunc(gboolean (*authfunc)(const struct ha_msg*))
+{
+ msg_authentication_method = authfunc;
+}
+
+
+
+/* Converts a string (perhaps received via UDP) into a message */
+struct ha_msg *
+string2msg_ll(const char * s, size_t length, int depth, int need_auth)
+{
+ struct ha_msg* ret;
+ int startlen;
+ int endlen;
+ const char * sp = s;
+ const char * smax = s + length;
+
+
+ if ((ret = ha_msg_new(0)) == NULL) {
+ cl_log(LOG_ERR, "%s: creating new msg failed", __FUNCTION__);
+ return(NULL);
+ }
+
+ startlen = sizeof(MSG_START)-1;
+ if (strncmp(sp, MSG_START, startlen) != 0) {
+ /* This can happen if the sender gets killed */
+ /* at just the wrong time... */
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING, "string2msg_ll: no MSG_START");
+ cl_log(LOG_WARNING, "%s: s=%s", __FUNCTION__, s);
+ cl_log(LOG_WARNING, "depth=%d", depth);
+ }
+ ha_msg_del(ret);
+ return(NULL);
+ }else{
+ sp += startlen;
+ }
+
+ endlen = sizeof(MSG_END)-1;
+
+ /* Add Name=value pairs until we reach MSG_END or end of string */
+
+ while (*sp != EOS && strncmp(sp, MSG_END, endlen) != 0) {
+
+ if (sp >= smax) {
+ cl_log(LOG_ERR, "%s: buffer overflow(sp=%p, smax=%p)",
+ __FUNCTION__, sp, smax);
+ return(NULL);
+ }
+ /* Skip over initial CR/NL things */
+ sp += strspn(sp, NEWLINE);
+ if (sp >= smax) {
+ cl_log(LOG_ERR, "%s: buffer overflow after NEWLINE(sp=%p, smax=%p)",
+ __FUNCTION__, sp, smax);
+ return(NULL);
+ }
+ /* End of message marker? */
+ if (strncmp(sp, MSG_END, endlen) == 0) {
+ break;
+ }
+ /* Add the "name=value" string on this line to the message */
+ if (ha_msg_add_nv_depth(ret, sp, smax, depth) != HA_OK) {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR, "NV failure (string2msg_ll):");
+ cl_log(LOG_ERR, "Input string: [%s]", s);
+ cl_log(LOG_ERR, "sp=%s", sp);
+ cl_log(LOG_ERR, "depth=%d", depth);
+ cl_log_message(LOG_ERR,ret);
+ }
+ ha_msg_del(ret);
+ return(NULL);
+ }
+ if (sp >= smax) {
+ cl_log(LOG_ERR, "%s: buffer overflow after adding field(sp=%p, smax=%p)",
+ __FUNCTION__, sp, smax);
+ return(NULL);
+ }
+ sp += strcspn(sp, NEWLINE);
+ }
+
+ if (need_auth && msg_authentication_method
+ && !msg_authentication_method(ret)) {
+ const char* from = ha_msg_value(ret, F_ORIG);
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING,
+ "string2msg_ll: node [%s]"
+ " failed authentication", from ? from : "?");
+ }
+ ha_msg_del(ret);
+ ret = NULL;
+ }
+ return(ret);
+}
+
+
+
+struct ha_msg *
+string2msg(const char * s, size_t length)
+{
+ return(string2msg_ll(s, length, 0, MSG_NEEDAUTH));
+}
+
+
+
+
+
+
+/* Converts a message into a string (for sending out UDP interface)
+
+ used in two places:
+
+ 1.called by msg2string as a implementation for computing string for a
+ message provided the buffer
+
+ 2.called by is_authentic. In this case, there are no start/end string
+ and the "auth" field is not included in the string
+
+*/
+
+#define NOROOM { \
+ cl_log(LOG_ERR, "%s:%d: out of memory bound" \
+ ", bp=%p, buf + len=%p, len=%ld" \
+ , __FUNCTION__, __LINE__ \
+ , bp, buf + len, (long)len); \
+ cl_log_message(LOG_ERR, m); \
+ return(HA_FAIL); \
+ }
+
+#define CHECKROOM_CONST(c) CHECKROOM_INT(STRLEN_CONST(c))
+#define CHECKROOM_STRING(s) CHECKROOM_INT(strnlen(s, len))
+#define CHECKROOM_STRING_INT(s,i) CHECKROOM_INT(strnlen(s, len)+(i))
+#define CHECKROOM_INT(i) { \
+ if ((bp + (i)) > maxp) { \
+ NOROOM; \
+ } \
+ }
+
+
+int
+msg2string_buf(const struct ha_msg *m, char* buf, size_t len
+, int depth,int needhead)
+{
+
+ char * bp = NULL;
+ int j;
+ char* maxp = buf + len;
+
+ buf[0]=0;
+ bp = buf;
+
+ if (needhead){
+ CHECKROOM_CONST(MSG_START);
+ strcpy(bp, MSG_START);
+ bp += STRLEN_CONST(MSG_START);
+ }
+
+ for (j=0; j < m->nfields; ++j) {
+
+ int truelen;
+ int (*tostring)(char*, char*, void*, size_t, int);
+
+ if (needhead == NOHEAD && strcmp(m->names[j], F_AUTH) == 0) {
+ continue;
+ }
+
+ if (m->types[j] != FT_STRING){
+ CHECKROOM_STRING_INT(FT_strings[m->types[j]],2);
+ strcat(bp, "(");
+ bp++;
+ strcat(bp, FT_strings[m->types[j]]);
+ bp++;
+ strcat(bp,")");
+ bp++;
+ }
+
+ CHECKROOM_STRING_INT(m->names[j],1);
+ strcat(bp, m->names[j]);
+ bp += m->nlens[j];
+ strcat(bp, "=");
+ bp++;
+
+ if(m->types[j] < DIMOF(fieldtypefuncs)){
+ tostring = fieldtypefuncs[m->types[j]].tostring;
+ } else {
+ cl_log(LOG_ERR, "type(%d) unrecognized", m->types[j]);
+ return HA_FAIL;
+ }
+ if (!tostring ||
+ (truelen = tostring(bp, maxp, m->values[j], m->vlens[j], depth))
+ < 0){
+ cl_log(LOG_ERR, "tostring failed for field %d", j);
+ return HA_FAIL;
+ }
+
+ CHECKROOM_INT(truelen+1);
+ bp +=truelen;
+
+ strcat(bp,"\n");
+ bp++;
+ }
+ if (needhead){
+ CHECKROOM_CONST(MSG_END);
+ strcat(bp, MSG_END);
+ bp += strlen(MSG_END);
+ }
+
+ CHECKROOM_INT(1);
+ bp[0] = EOS;
+
+ return(HA_OK);
+}
+
+
+char *
+msg2string(const struct ha_msg *m)
+{
+ void *buf;
+ int len;
+
+ AUDITMSG(m);
+ if (m->nfields <= 0) {
+ cl_log(LOG_ERR, "msg2string: Message with zero fields");
+ return(NULL);
+ }
+
+ len = get_stringlen(m);
+
+ buf = malloc(len);
+
+ if (buf == NULL) {
+ cl_log(LOG_ERR, "msg2string: no memory for string");
+ return(NULL);
+ }
+
+ if (msg2string_buf(m, buf, len ,0, NEEDHEAD) != HA_OK){
+ cl_log(LOG_ERR, "msg2string: msg2string_buf failed");
+ free(buf);
+ return(NULL);
+ }
+
+ return(buf);
+}
+
+gboolean
+must_use_netstring(const struct ha_msg* msg)
+{
+ int i;
+
+ for ( i = 0; i < msg->nfields; i++){
+ if (msg->types[i] == FT_COMPRESS
+ || msg->types[i] == FT_UNCOMPRESS
+ || msg->types[i] == FT_STRUCT){
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+}
+
+#define use_netstring(m) (msgfmt == MSGFMT_NETSTRING || must_use_netstring(m))
+
+static char*
+msg2wirefmt_ll(struct ha_msg*m, size_t* len, int flag)
+{
+
+ int wirefmtlen;
+ int i;
+ int netstg = use_netstring(m);
+
+ wirefmtlen = netstg ? get_netstringlen(m) : get_stringlen(m);
+ if (use_traditional_compression
+ &&(flag & MSG_NEEDCOMPRESS)
+ && (wirefmtlen> compression_threshold)
+ && cl_get_compress_fns() != NULL){
+ return cl_compressmsg(m, len);
+ }
+
+ if (flag & MSG_NEEDCOMPRESS){
+ for (i=0 ;i < m->nfields; i++){
+ int type = m->types[i];
+ if (fieldtypefuncs[type].prepackaction){
+ fieldtypefuncs[type].prepackaction(m,i);
+ }
+ }
+ }
+
+ wirefmtlen = netstg ? get_netstringlen(m) : get_stringlen(m);
+ if (wirefmtlen >= MAXMSG){
+ if (flag&MSG_NEEDCOMPRESS) {
+ if (cl_get_compress_fns() != NULL)
+ return cl_compressmsg(m, len);
+ }
+ cl_log(LOG_ERR, "%s: msg too big(%d)",
+ __FUNCTION__, wirefmtlen);
+ return NULL;
+ }
+ if (flag & MSG_NEEDAUTH) {
+ return msg2netstring(m, len);
+ }
+ return msg2wirefmt_noac(m, len);
+}
+
+char*
+msg2wirefmt(struct ha_msg*m, size_t* len){
+ return msg2wirefmt_ll(m, len, MSG_NEEDAUTH|MSG_NEEDCOMPRESS);
+}
+
+char*
+msg2wirefmt_noac(struct ha_msg*m, size_t* len)
+{
+ if (use_netstring(m)) {
+ return msg2netstring_noauth(m, len);
+ } else {
+ char *tmp;
+
+ tmp = msg2string(m);
+ if(tmp == NULL){
+ *len = 0;
+ return NULL;
+ }
+ *len = strlen(tmp) + 1;
+ return tmp;
+ }
+}
+
+static struct ha_msg*
+wirefmt2msg_ll(const char* s, size_t length, int need_auth)
+{
+
+ size_t startlen;
+ struct ha_msg* msg = NULL;
+
+
+ startlen = sizeof(MSG_START)-1;
+
+ if (startlen > length){
+ return NULL;
+ }
+
+ if (strncmp( s, MSG_START, startlen) == 0) {
+ msg = string2msg_ll(s, length, 0, need_auth);
+ goto out;
+ }
+
+ startlen = sizeof(MSG_START_NETSTRING) - 1;
+
+ if (startlen > length){
+ return NULL;
+ }
+
+ if (strncmp(s, MSG_START_NETSTRING, startlen) == 0) {
+ msg = netstring2msg(s, length, need_auth);
+ goto out;
+ }
+
+out:
+ if (msg && is_compressed_msg(msg)){
+ struct ha_msg* ret;
+ if ((ret = cl_decompressmsg(msg))==NULL){
+ cl_log(LOG_ERR, "decompress msg failed");
+ ha_msg_del(msg);
+ return NULL;
+ }
+ ha_msg_del(msg);
+ return ret;
+ }
+ return msg;
+
+}
+
+
+
+
+struct ha_msg*
+wirefmt2msg(const char* s, size_t length, int flag)
+{
+ return wirefmt2msg_ll(s, length, flag& MSG_NEEDAUTH);
+
+}
+
+
+void
+cl_log_message (int log_level, const struct ha_msg *m)
+{
+ int j;
+
+ if(m == NULL) {
+ cl_log(log_level, "MSG: No message to dump");
+ return;
+ }
+
+ cl_log(log_level, "MSG: Dumping message with %d fields", m->nfields);
+
+ for (j=0; j < m->nfields; ++j) {
+
+ if(m->types[j] < DIMOF(fieldtypefuncs)){
+ fieldtypefuncs[m->types[j]].display(log_level, j,
+ m->names[j],
+ m->values[j],
+ m->vlens[j]);
+ }
+ }
+}
+
+
+#ifdef TESTMAIN_MSGS
+int
+main(int argc, char ** argv)
+{
+ struct ha_msg* m;
+ while (!feof(stdin)) {
+ if ((m=controlfifo2msg(stdin)) != NULL) {
+ fprintf(stderr, "Got message!\n");
+ if (msg2stream(m, stdout) == HA_OK) {
+ fprintf(stderr, "Message output OK!\n");
+ }else{
+ fprintf(stderr, "Could not output Message!\n");
+ }
+ }else{
+ fprintf(stderr, "Could not get message!\n");
+ }
+ }
+ return(0);
+}
+#endif
diff --git a/lib/clplumbing/cl_msg_types.c b/lib/clplumbing/cl_msg_types.c
new file mode 100644
index 0000000..56cf56a
--- /dev/null
+++ b/lib/clplumbing/cl_msg_types.c
@@ -0,0 +1,1736 @@
+/*
+ * Heartbeat message type functions
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <ha_msg.h>
+#include <unistd.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/netstring.h>
+#include <glib.h>
+
+#ifndef MAX
+# define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+
+extern const char* FT_strings[];
+
+
+
+#define NL_TO_SYM 0
+#define SYM_TO_NL 1
+
+static const int SPECIAL_SYMS[MAXDEPTH] = {
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31,
+ 15,
+ 16,
+ 17,
+ 18,
+};
+
+#define SPECIAL_SYM 19
+
+struct ha_msg* string2msg_ll(const char*, size_t, int, int);
+int compose_netstring(char*, const char*, const char*, size_t, size_t*);
+int msg2netstring_buf(const struct ha_msg*, char*, size_t, size_t*);
+int struct_display_print_spaces(char *buffer, int depth);
+int struct_display_as_xml(int log_level, int depth, struct ha_msg *data,
+ const char *prefix, gboolean formatted);
+int struct_stringlen(size_t namlen, size_t vallen, const void* value);
+int struct_netstringlen(size_t namlen, size_t vallen, const void* value);
+int convert_nl_sym(char* s, int len, char sym, int direction);
+int bytes_for_int(int x);
+
+int
+bytes_for_int(int x)
+{
+ int len = 0;
+ if(x < 0) {
+ x = 0-x;
+ len=1;
+ }
+ while(x > 9) {
+ x /= 10;
+ len++;
+ }
+ return len+1;
+}
+
+int
+netstring_extra(int x)
+{
+ return (bytes_for_int(x) + x + 2);
+}
+
+int
+get_netstringlen(const struct ha_msg *m)
+{
+ int i;
+ int total_len =0 ;
+
+ if (m == NULL){
+ cl_log(LOG_ERR, "get_netstringlen:"
+ "asking netstringlen of a NULL message");
+ return 0;
+ }
+
+ total_len = sizeof(MSG_START_NETSTRING)
+ + sizeof(MSG_END_NETSTRING) -2 ;
+
+
+ for (i = 0; i < m->nfields; i++){
+ int len;
+ len = fieldtypefuncs[m->types[i]].netstringlen(m->nlens[i],
+ m->vlens[i],
+ m->values[i]);
+ total_len += netstring_extra(len);
+ }
+
+
+ return total_len;
+
+
+}
+
+
+
+int
+get_stringlen(const struct ha_msg *m)
+{
+ int i;
+ int total_len =0 ;
+
+ if (m == NULL){
+ cl_log(LOG_ERR, "get_stringlen:"
+ "asking stringlen of a NULL message");
+ return 0;
+ }
+
+ total_len = sizeof(MSG_START)+sizeof(MSG_END)-1;
+
+ for (i = 0; i < m->nfields; i++){
+ total_len += fieldtypefuncs[m->types[i]].stringlen(m->nlens[i],
+ m->vlens[i],
+ m->values[i]);
+ }
+
+ return total_len;
+}
+
+
+
+/*
+ compute the total size of the resulted string
+ if the string list is to be converted
+
+*/
+size_t
+string_list_pack_length(const GList* _list)
+{
+ size_t i;
+ GList* list = UNCONST_CAST_POINTER(GList *, _list);
+ size_t total_length = 0;
+
+ if (list == NULL){
+ cl_log(LOG_WARNING, "string_list_pack_length():"
+ "list is NULL");
+
+ return 0;
+ }
+ for (i = 0; i < g_list_length(list) ; i++){
+
+ int len = 0;
+ char * element = g_list_nth_data(list, i);
+ if (element == NULL){
+ cl_log(LOG_ERR, "string_list_pack_length: "
+ "%luth element of the string list is NULL"
+ , (unsigned long)i);
+ return 0;
+ }
+ len = strlen(element);
+ total_length += bytes_for_int(len) + len + 2;
+ /* 2 is for ":" and "," */
+ }
+ return total_length ;
+}
+
+
+
+/*
+ convert a string list into a single string
+ the format to convert is similar to netstring:
+ <length> ":" <the actual string> ","
+
+ for example, a list containing two strings "abc" "defg"
+ will be converted into
+ 3:abc,4:defg,
+ @list: the list to be converted
+ @buf: the converted string should be put in the @buf
+ @maxp: max pointer
+*/
+
+
+int
+string_list_pack(GList* list, char* buf, char* maxp)
+{
+ size_t i;
+ char* p = buf;
+
+ for (i = 0; i < g_list_length(list) ; i++){
+ char * element = g_list_nth_data(list, i);
+ int element_len;
+
+ if (element == NULL){
+ cl_log(LOG_ERR, "string_list_pack: "
+ "%luth element of the string list is NULL"
+ , (unsigned long)i);
+ return 0;
+ }
+ element_len = strlen(element);
+ if (p + 2 + element_len + bytes_for_int(element_len)> maxp){
+ cl_log(LOG_ERR, "%s: memory out of boundary",
+ __FUNCTION__);
+ return 0;
+ }
+ p += sprintf(p, "%d:%s,", element_len,element);
+
+ if (p > maxp){
+ cl_log(LOG_ERR, "string_list_pack: "
+ "buffer overflowed ");
+ return 0;
+ }
+ }
+
+
+ return (p - buf);
+}
+
+
+
+/*
+ this is reverse process of pack_string_list
+*/
+GList*
+string_list_unpack(const char* packed_str_list, size_t length)
+{
+ GList* list = NULL;
+ const char* psl = packed_str_list;
+ const char * maxp= packed_str_list + length;
+ int len = 0;
+
+
+ while(TRUE){
+ char* buf;
+
+ if (*psl == '\0' || psl >= maxp){
+ break;
+ }
+
+ if (sscanf( psl, "%d:", &len) <= 0 ){
+ break;
+ }
+
+ if (len <=0){
+ cl_log(LOG_ERR, "unpack_string_list:"
+ "reading len of string error");
+ if (list){
+ list_cleanup(list);
+ }
+ return NULL;
+ }
+
+ while (*psl != ':' && *psl != '\0' ){
+ psl++;
+ }
+
+ if (*psl == '\0'){
+ break;
+ }
+
+ psl++;
+
+ buf = malloc(len + 1);
+ if (buf == NULL){
+ cl_log(LOG_ERR, "unpack_string_list:"
+ "unable to allocate buf");
+ if(list){
+ list_cleanup(list);
+ }
+ return NULL;
+
+ }
+ memcpy(buf, psl, len);
+ buf[len] = '\0';
+ list = g_list_append(list, buf);
+ psl +=len;
+
+ if (*psl != ','){
+ cl_log(LOG_ERR, "unpack_string_list:"
+ "wrong format, s=%s",packed_str_list);
+ }
+ psl++;
+ }
+
+ return list;
+
+}
+
+
+static void
+string_memfree(void* value)
+{
+ if (value){
+ free(value);
+ }else {
+ cl_log(LOG_ERR, "string_memfree: "
+ "value is NULL");
+ }
+
+
+ return;
+}
+
+static void
+binary_memfree(void* value)
+{
+ string_memfree(value);
+}
+
+
+static void
+struct_memfree( void* value)
+{
+ struct ha_msg* msg;
+
+ if (!value){
+ cl_log(LOG_ERR,
+ "value is NULL");
+ return ;
+ }
+
+ msg = (struct ha_msg*) value;
+ ha_msg_del(msg);
+ return ;
+}
+
+static void
+list_memfree(void* value)
+{
+
+ if (!value){
+ cl_log(LOG_ERR,
+ "value is NULL");
+ return ;
+ }
+
+ list_cleanup(value);
+
+}
+
+
+static void*
+binary_dup(const void* value, size_t len)
+{
+
+ char* dupvalue;
+
+ /* 0 byte binary field is allowed*/
+
+ if (value == NULL && len > 0){
+ cl_log(LOG_ERR, "binary_dup:"
+ "NULL value with non-zero len=%d",
+ (int)len);
+ return NULL;
+ }
+
+ dupvalue = malloc(len + 1);
+ if (dupvalue == NULL){
+ cl_log(LOG_ERR, "binary_dup:"
+ "malloc failed");
+ return NULL;
+ }
+
+ if (value != NULL) {
+ memcpy(dupvalue, value, len);
+ }
+
+ dupvalue[len] =0;
+
+ return dupvalue;
+}
+
+static void*
+string_dup(const void* value, size_t len)
+{
+ return binary_dup(value, len);
+}
+
+
+static void*
+struct_dup(const void* value, size_t len)
+{
+ char* dupvalue;
+
+ (void)len;
+
+ if (!value){
+ cl_log(LOG_ERR,"struct_dup:"
+ "value is NULL");
+ return NULL ;
+ }
+
+
+ dupvalue = (void*)ha_msg_copy((const struct ha_msg*)value);
+ if (dupvalue == NULL){
+ cl_log(LOG_ERR, "struct_dup: "
+ "ha_msg_copy failed");
+ return NULL;
+ }
+
+ return dupvalue;
+}
+
+static GList*
+list_copy(const GList* _list)
+{
+ size_t i;
+ GList* newlist = NULL;
+ GList* list = UNCONST_CAST_POINTER(GList *, _list);
+
+ for (i = 0; i < g_list_length(list); i++){
+ char* dup_element = NULL;
+ char* element = g_list_nth_data(list, i);
+ int len;
+ if (element == NULL){
+ cl_log(LOG_WARNING, "list_copy:"
+ "element is NULL");
+ continue;
+ }
+
+ len = strlen(element);
+ dup_element= malloc(len + 1);
+ if ( dup_element == NULL){
+ cl_log(LOG_ERR, "duplicate element failed");
+ continue;
+ }
+ memcpy(dup_element, element,len);
+ dup_element[len] = 0;
+
+ newlist = g_list_append(newlist, dup_element);
+ }
+
+ return newlist;
+}
+
+static void*
+list_dup( const void* value, size_t len)
+{
+ char* dupvalue;
+
+ (void)len;
+ if (!value){
+ cl_log(LOG_ERR,"struct_dup:"
+ "value is NULL");
+ return NULL ;
+ }
+
+ dupvalue = (void*)list_copy((const GList*)value);
+
+ if (!dupvalue){
+ cl_log(LOG_ERR, "list_dup: "
+ "list_copy failed");
+ return NULL;
+ }
+
+ return dupvalue;
+}
+
+
+static void
+general_display(int log_level, int seq, char* name, void* value, int vlen, int type)
+{
+ int netslen;
+ int slen;
+ HA_MSG_ASSERT(value);
+ HA_MSG_ASSERT(name);
+
+ slen = fieldtypefuncs[type].stringlen(strlen(name), vlen, value);
+ netslen = fieldtypefuncs[type].netstringlen(strlen(name), vlen, value);
+ cl_log(log_level, "MSG[%d] : [(%s)%s=%p(%d %d)]",
+ seq, FT_strings[type],
+ name, value, slen, netslen);
+
+}
+static void
+string_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ HA_MSG_ASSERT(name);
+ HA_MSG_ASSERT(value);
+ cl_log(log_level, "MSG[%d] : [%s=%s]",
+ seq, name, (const char*)value);
+ return;
+}
+
+static void
+binary_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ general_display(log_level, seq, name, value, vlen, FT_BINARY);
+}
+
+static void
+compress_display(int log_level, int seq, char* name, void* value, int vlen){
+ general_display(log_level, seq, name, value, vlen, FT_COMPRESS);
+}
+
+
+static void
+general_struct_display(int log_level, int seq, char* name, void* value, int vlen, int type)
+{
+ int slen;
+ int netslen;
+
+ HA_MSG_ASSERT(name);
+ HA_MSG_ASSERT(value);
+
+ slen = fieldtypefuncs[type].stringlen(strlen(name), vlen, value);
+ netslen = fieldtypefuncs[type].netstringlen(strlen(name), vlen, value);
+
+ cl_log(log_level, "MSG[%d] : [(%s)%s=%p(%d %d)]",
+ seq, FT_strings[type],
+ name, value, slen, netslen);
+ if(cl_get_string((struct ha_msg*) value, F_XML_TAGNAME) == NULL) {
+ cl_log_message(log_level, (struct ha_msg*) value);
+ } else {
+ /* use a more friendly output format for nested messages */
+ struct_display_as_xml(log_level, 0, value, NULL, TRUE);
+ }
+}
+static void
+struct_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ general_struct_display(log_level, seq, name, value, vlen, FT_STRUCT);
+
+}
+static void
+uncompress_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ general_struct_display(log_level, seq, name, value, vlen, FT_UNCOMPRESS);
+}
+
+#define update_buffer_head(buffer, len) if(len < 0) { \
+ (*buffer) = EOS; return -1; \
+ } else { \
+ buffer += len; \
+ }
+
+int
+struct_display_print_spaces(char *buffer, int depth)
+{
+ int lpc = 0;
+ int spaces = 2*depth;
+ /* <= so that we always print 1 space - prevents problems with syslog */
+ for(lpc = 0; lpc <= spaces; lpc++) {
+ if(sprintf(buffer, "%c", ' ') < 1) {
+ return -1;
+ }
+ buffer += 1;
+ }
+ return lpc;
+}
+
+int
+struct_display_as_xml(
+ int log_level, int depth, struct ha_msg *data,
+ const char *prefix, gboolean formatted)
+{
+ int lpc = 0;
+ int printed = 0;
+ gboolean has_children = FALSE;
+ char print_buffer[1000];
+ char *buffer = print_buffer;
+ const char *name = cl_get_string(data, F_XML_TAGNAME);
+
+ if(data == NULL) {
+ return 0;
+
+ } else if(name == NULL) {
+ cl_log(LOG_WARNING, "Struct at depth %d had no name", depth);
+ cl_log_message(log_level, data);
+ return 0;
+ }
+
+ if(formatted) {
+ printed = struct_display_print_spaces(buffer, depth);
+ update_buffer_head(buffer, printed);
+ }
+
+ printed = sprintf(buffer, "<%s", name);
+ update_buffer_head(buffer, printed);
+
+ for (lpc = 0; lpc < data->nfields; lpc++) {
+ const char *prop_name = data->names[lpc];
+ const char *prop_value = data->values[lpc];
+ if(data->types[lpc] != FT_STRING) {
+ continue;
+ } else if(prop_name == NULL) {
+ continue;
+ } else if(prop_name[0] == '_' && prop_name[1] == '_') {
+ continue;
+ }
+ printed = sprintf(buffer, " %s=\"%s\"", prop_name, prop_value);
+ update_buffer_head(buffer, printed);
+ }
+
+ for (lpc = 0; lpc < data->nfields; lpc++) {
+ if(data->types[lpc] == FT_STRUCT) {
+ has_children = TRUE;
+ break;
+ }
+ }
+
+ printed = sprintf(buffer, "%s>", has_children==0?"/":"");
+ update_buffer_head(buffer, printed);
+ cl_log(log_level, "%s%s", prefix?prefix:"", print_buffer);
+ buffer = print_buffer;
+
+ if(has_children == FALSE) {
+ return 0;
+ }
+
+ for (lpc = 0; lpc < data->nfields; lpc++) {
+ if(data->types[lpc] != FT_STRUCT) {
+ continue;
+ } else if(0 > struct_display_as_xml(
+ log_level, depth+1, data->values[lpc],
+ prefix, formatted)) {
+ return -1;
+ }
+ }
+
+ if(formatted) {
+ printed = struct_display_print_spaces(buffer, depth);
+ update_buffer_head(buffer, printed);
+ }
+ cl_log(log_level, "%s%s</%s>", prefix?prefix:"", print_buffer, name);
+
+ return 0;
+}
+
+
+
+
+static int
+liststring(GList* list, char* buf, int maxlen)
+{
+ char* p = buf;
+ char* maxp = buf + maxlen;
+ size_t i;
+
+ for ( i = 0; i < g_list_length(list); i++){
+ char* element = g_list_nth_data(list, i);
+ if (element == NULL) {
+ cl_log(LOG_ERR, "%luth element is NULL "
+ , (unsigned long)i);
+ return HA_FAIL;
+ } else{
+ if (i == 0){
+ p += sprintf(p,"%s",element);
+ }else{
+ p += sprintf(p," %s",element);
+ }
+
+ }
+ if ( p > maxp){
+ cl_log(LOG_ERR, "buffer overflow");
+ return HA_FAIL;
+ }
+
+ }
+
+ return HA_OK;
+}
+
+static void
+list_display(int log_level, int seq, char* name, void* value, int vlen)
+{
+ GList* list;
+ char buf[MAXLENGTH];
+
+ HA_MSG_ASSERT(name);
+ HA_MSG_ASSERT(value);
+
+ list = value;
+
+ if (liststring(list, buf, MAXLENGTH) != HA_OK){
+ cl_log(LOG_ERR, "liststring error");
+ return;
+ }
+ cl_log(log_level, "MSG[%d] :[(%s)%s=%s]",
+ seq, FT_strings[FT_LIST],
+ name, buf);
+
+ return ;
+
+}
+
+
+/*
+ * This function changes each new line in the input string
+ * into a special symbol, or the other way around
+ */
+
+int
+convert_nl_sym(char* s, int len, char sym, int direction)
+{
+ int i;
+
+ if (direction != NL_TO_SYM && direction != SYM_TO_NL){
+ cl_log(LOG_ERR, "convert_nl_sym(): direction not defined!");
+ return(HA_FAIL);
+ }
+
+
+ for (i = 0; i < len && s[i] != EOS; i++){
+
+ switch(direction){
+ case NL_TO_SYM :
+ if (s[i] == '\n'){
+ s[i] = sym;
+ break;
+ }
+
+ if (s[i] == sym){
+ cl_log(LOG_ERR
+ , "convert_nl_sym(): special symbol \'0x%x\' (%c) found"
+ " in string at %d (len=%d)", s[i], s[i], i, len);
+ i -= 10;
+ if(i < 0) {
+ i = 0;
+ }
+ cl_log(LOG_ERR, "convert_nl_sym(): %s", s + i);
+ return(HA_FAIL);
+ }
+
+ break;
+
+ case SYM_TO_NL:
+
+ if (s[i] == sym){
+ s[i] = '\n';
+ break;
+ }
+ break;
+ default:
+ /* nothing, never executed*/;
+
+ }
+ }
+
+ return(HA_OK);
+}
+
+
+/*
+ * This function changes each new line in the input string
+ * into a special symbol, or the other way around
+ */
+
+static int
+convert(char* s, int len, int depth, int direction)
+{
+
+ if (direction != NL_TO_SYM && direction != SYM_TO_NL){
+ cl_log(LOG_ERR, "convert(): direction not defined!");
+ return(HA_FAIL);
+ }
+
+
+ if (depth >= MAXDEPTH ){
+ cl_log(LOG_ERR, "convert(): MAXDEPTH exceeded: %d", depth);
+ return(HA_FAIL);
+ }
+
+ return convert_nl_sym(s, len, SPECIAL_SYMS[depth], direction);
+}
+
+
+
+
+static int
+string_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+
+ HA_MSG_ASSERT(value);
+/* HA_MSG_ASSERT( vallen == strlen(value)); */
+ return namlen + vallen + 2;
+}
+
+static int
+binary_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+ int length;
+
+ HA_MSG_ASSERT(value);
+
+ length = 3 + namlen + 1 + vallen;
+
+ return length;
+}
+
+
+static int
+string_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+ HA_MSG_ASSERT(value);
+ HA_MSG_ASSERT( vallen == strlen(value));
+
+ return binary_netstringlen(namlen, vallen, value);
+}
+
+
+static int
+binary_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+ HA_MSG_ASSERT(value);
+
+ return namlen + B64_stringlen(vallen) + 2 + 3;
+ /*overhead 3 is for type*/
+}
+
+
+
+
+
+int
+struct_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+ const struct ha_msg* childmsg;
+
+ HA_MSG_ASSERT(value);
+
+ (void)vallen;
+ childmsg = (const struct ha_msg*)value;
+
+ return namlen +2 + 3 + get_stringlen(childmsg);
+ /*overhead 3 is for type*/
+}
+
+int
+struct_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+
+ int ret;
+ const struct ha_msg* childmsg;
+ int len;
+
+ HA_MSG_ASSERT(value);
+
+ (void)vallen;
+ childmsg = (const struct ha_msg*)value;
+
+ len = get_netstringlen(childmsg);
+
+ ret = 3 + namlen + 1 + len;
+
+ return ret;
+
+}
+
+
+static int
+list_stringlen(size_t namlen, size_t vallen, const void* value)
+{
+ (void)value;
+ return namlen + vallen + 2 + 3;
+ /*overhead 3 is for type (FT_STRUCT)*/
+}
+
+static int
+list_netstringlen(size_t namlen, size_t vallen, const void* value)
+{
+ int ret;
+ const GList* list;
+
+ list = (const GList*)value;
+
+ ret = 3 + namlen + 1 + string_list_pack_length(list);
+
+ return ret;
+
+}
+
+static int
+add_binary_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_binary_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_BINARY;
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+static int
+add_struct_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_struct_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_STRUCT;
+
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+
+
+static int
+add_list_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+ int next;
+ int j;
+ GList* list = NULL;
+
+ if ( !msg || !name || !value
+ || namelen <= 0
+ || vallen <= 0
+ || depth < 0){
+ cl_log(LOG_ERR, "add_list_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+ for (j=0; j < msg->nfields; ++j) {
+ if (strcmp(name, msg->names[j]) == 0) {
+ break;
+ }
+ }
+
+ if ( j >= msg->nfields){
+ list = (GList*)value;
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_LIST;
+ msg->nfields++;
+
+ } else if( msg->types[j] == FT_LIST ){
+
+ GList* oldlist = (GList*) msg->values[j];
+ int listlen;
+ size_t i;
+
+ for ( i =0; i < g_list_length((GList*)value); i++){
+ list = g_list_append(oldlist, g_list_nth_data((GList*)value, i));
+ }
+ if (list == NULL){
+ cl_log(LOG_ERR, "add_list_field:"
+ " g_list_append() failed");
+ return HA_FAIL;
+ }
+
+ listlen = string_list_pack_length(list);
+
+ msg->values[j] = list;
+ msg->vlens[j] = listlen;
+ g_list_free((GList*)value); /*we don't free each element
+ because they are used in new list*/
+ free(name); /* this name is no longer necessary
+ because msg->names[j] is reused */
+
+ } else {
+ cl_log(LOG_ERR, "field already exists "
+ "with differnt type=%d", msg->types[j]);
+ return (HA_FAIL);
+ }
+
+ return HA_OK;
+}
+
+
+static int
+add_compress_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_binary_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_COMPRESS;
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+
+
+static int
+add_uncompress_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+ int next;
+
+ if ( !msg || !name || !value
+ || depth < 0){
+ cl_log(LOG_ERR, "add_struct_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+ next = msg->nfields;
+ msg->names[next] = name;
+ msg->nlens[next] = namelen;
+ msg->values[next] = value;
+ msg->vlens[next] = vallen;
+ msg->types[next] = FT_UNCOMPRESS;
+
+ msg->nfields++;
+
+ return HA_OK;
+}
+
+
+
+/*print a string to a string,
+ pretty simple one :)
+*/
+static int
+str2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+ char* s = value;
+ char* p = buf;
+ (void)maxp;
+ (void)depth;
+
+ if (buf + len > maxp){
+ cl_log(LOG_ERR, "%s: out of boundary",
+ __FUNCTION__);
+ return -1;
+ }
+
+ if ( strlen(s) != len){
+ cl_log(LOG_ERR, "str2string:"
+ "the input len != string length");
+ return -1;
+ }
+
+ strcat(buf, s);
+ while(*p != '\0'){
+ if (*p == '\n'){
+ *p = SPECIAL_SYM;
+ }
+ p++;
+ }
+
+ return len;
+
+}
+
+/*print a binary value to a string using base64
+ library
+*/
+
+static int
+binary2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+ int baselen;
+ int truelen = 0;
+
+ (void)depth;
+ baselen = B64_stringlen(len) + 1;
+
+ if ( buf + baselen > maxp){
+ cl_log(LOG_ERR, "binary2string: out of bounary");
+ return -1;
+ }
+
+ truelen = binary_to_base64(value, len, buf, baselen);
+
+ return truelen;
+}
+
+/*print a struct(ha_msg) to a string
+ @depth denotes the number of recursion
+*/
+
+static int
+struct2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+
+ struct ha_msg* msg = value;
+ int baselen = get_stringlen(msg);
+
+ (void)len;
+
+ if ( buf + baselen > maxp){
+ cl_log(LOG_ERR, "struct2string: not enough buffer"
+ "for the struct to generate a string");
+ return -1;
+ }
+
+ if (msg2string_buf(msg, buf ,baselen,depth + 1, NEEDHEAD)
+ != HA_OK){
+
+ cl_log(LOG_ERR
+ , "struct2string(): msg2string_buf for"
+ " child message failed");
+ return -1;
+
+ }
+
+ if (convert(buf, baselen, depth, NL_TO_SYM) != HA_OK){
+ cl_log(LOG_ERR , "struct2string(): convert failed");
+ return -1;
+ }
+
+ return strlen(buf);
+}
+
+
+
+
+/* print a list to a string
+ */
+
+static int
+list2string(char* buf, char* maxp, void* value, size_t len, int depth)
+{
+ int listlen;
+ GList* list = (GList*) value;
+
+ (void)len;
+ (void)depth;
+ listlen = string_list_pack(list , buf, maxp);
+ if (listlen == 0){
+ cl_log(LOG_ERR, "list2string():"
+ "string_list_pack() failed");
+ return -1;
+ }
+
+ return listlen;
+
+}
+
+
+static int
+string2str(void* value, size_t len, int depth, void** nv, size_t* nlen )
+{
+ if (!value || !nv || !nlen || depth < 0){
+ cl_log(LOG_ERR, "string2str:invalid input");
+ return HA_FAIL;
+ }
+
+ if (convert_nl_sym(value, len, SPECIAL_SYM, SYM_TO_NL) != HA_OK){
+ cl_log(LOG_ERR, "string2str:convert_nl_sym"
+ "from symbol to new line failed");
+ return HA_FAIL;
+ }
+ *nv = value;
+ *nlen = len;
+
+ return HA_OK;
+}
+
+static int
+string2binary(void* value, size_t len, int depth, void** nv, size_t* nlen)
+{
+ char tmpbuf[MAXLINE];
+ char* buf = NULL;
+ int buf_malloced = 0;
+ int ret = HA_FAIL;
+ if (len > MAXLINE){
+ buf = malloc(len);
+ if (buf == NULL){
+ cl_log(LOG_ERR, "%s: malloc failed",
+ __FUNCTION__);
+ goto out;
+ }
+ buf_malloced = 1;
+ }else {
+ buf = &tmpbuf[0];
+ }
+
+ if (value == NULL && len == 0){
+ *nv = NULL;
+ *nlen = 0;
+ ret = HA_OK;
+ goto out;
+ }
+
+ if ( !value || !nv || depth < 0){
+ cl_log(LOG_ERR, "string2binary:invalid input");
+ ret = HA_FAIL;
+ goto out;
+ }
+
+ memcpy(buf, value, len);
+ *nlen = base64_to_binary(buf, len, value, len);
+
+ *nv = value;
+ ret = HA_OK;
+ out:
+ if (buf_malloced && buf){
+ free(buf);
+ }
+ return ret;
+}
+
+static int
+string2struct(void* value, size_t vallen, int depth, void** nv, size_t* nlen)
+{
+
+ struct ha_msg *tmpmsg;
+
+ if (!value || !nv || depth < 0){
+ cl_log(LOG_ERR, "string2struct:invalid input");
+ return HA_FAIL;
+ }
+
+
+ if (convert(value, vallen, depth,SYM_TO_NL) != HA_OK){
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): convert failed");
+ return(HA_FAIL);
+ }
+
+ tmpmsg = string2msg_ll(value, vallen,depth + 1, 0);
+ if (tmpmsg == NULL){
+ cl_log(LOG_ERR
+ , "string2struct()"
+ ": string2msg_ll failed");
+ return(HA_FAIL);
+ }
+ free(value);
+ *nv = tmpmsg;
+ *nlen = 0;
+
+ return HA_OK;
+
+}
+
+static int
+string2list(void* value, size_t vallen, int depth, void** nv, size_t* nlen)
+{
+ GList* list;
+
+ if (!value || !nv || !nlen || depth < 0){
+ cl_log(LOG_ERR, "string2struct:invalid input");
+ return HA_FAIL;
+ }
+
+ list = string_list_unpack(value, vallen);
+ if (list == NULL){
+ cl_log(LOG_ERR, "ha_msg_addraw_ll():"
+ "unpack_string_list failed: %s", (char*)value);
+ return(HA_FAIL);
+ }
+ free(value);
+
+ *nv = (void*)list;
+ *nlen = string_list_pack_length(list);
+
+ return HA_OK;
+
+}
+
+static int
+fields2netstring(char* sp, char* smax, char* name, size_t nlen,
+ void* value, size_t vallen, int type, size_t* comlen)
+{
+ size_t fieldlen;
+ size_t slen;
+ int ret = HA_OK;
+ char* sp_save = sp;
+ char* tmpsp;
+
+ fieldlen = fieldtypefuncs[type].netstringlen(nlen, vallen, value);
+ /* this check seems to be superfluous because of the next one
+ if (fieldlen > MAXMSG){
+ cl_log(LOG_INFO, "%s: field too big(%d)", __FUNCTION__, (int)fieldlen);
+ return HA_FAIL;
+ }
+ */
+ tmpsp = sp + netstring_extra(fieldlen);
+ if (tmpsp > smax){
+ cl_log(LOG_ERR, "%s: memory out of boundary, tmpsp=%p, smax=%p",
+ __FUNCTION__, tmpsp, smax);
+ return HA_FAIL;
+ }
+ sp += sprintf(sp , "%d:(%d)%s=", (int)fieldlen, type, name);
+ switch (type){
+
+ case FT_STRING:
+ case FT_BINARY:
+ case FT_COMPRESS:
+ memcpy(sp, value, vallen);
+ slen = vallen;
+ break;
+
+ case FT_UNCOMPRESS:
+ case FT_STRUCT:
+ {
+ struct ha_msg* msg = (struct ha_msg*) value;
+ /* infinite recursion? Must say that I got lost at
+ * this point
+ */
+ ret = msg2netstring_buf(msg, sp,get_netstringlen(msg),
+ &slen);
+ break;
+ }
+ case FT_LIST:
+ {
+
+ char buf[MAXLENGTH];
+ GList* list = NULL;
+ int tmplen;
+
+ list = (GList*) value;
+
+ tmplen = string_list_pack_length(list);
+ if (tmplen >= MAXLENGTH){
+ cl_log(LOG_ERR,
+ "string list length exceeds limit");
+ return(HA_FAIL);
+ }
+
+ if (string_list_pack(list, buf, buf + MAXLENGTH)
+ != tmplen ){
+ cl_log(LOG_ERR,
+ "packing string list return wrong length");
+ return(HA_FAIL);
+ }
+
+
+ memcpy(sp, buf, tmplen);
+ slen = tmplen;
+ ret = HA_OK;
+ break;
+ }
+
+ default:
+ ret = HA_FAIL;
+ cl_log(LOG_ERR, "%s: Wrong type (%d)", __FUNCTION__,type);
+ }
+
+ if (ret == HA_FAIL){
+ return ret;
+ }
+
+ sp +=slen;
+ *sp++ = ',';
+ *comlen = sp - sp_save;
+
+ return HA_OK;
+
+
+}
+
+
+static int
+netstring2string(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ char* dupvalue;
+
+ if (value == NULL && vlen == 0){
+ *retvalue = NULL;
+ *ret_vlen = 0;
+ return HA_OK;
+ }
+
+ if ( !value || !retvalue || !ret_vlen){
+ cl_log(LOG_ERR, " netstring2string:"
+ "invalid input arguments");
+ return HA_FAIL;
+ }
+
+ dupvalue = binary_dup(value, vlen);
+ if (!dupvalue){
+ cl_log(LOG_ERR, "netstring2string:"
+ "duplicating value failed");
+ return HA_FAIL;
+ }
+
+ *retvalue = dupvalue;
+ *ret_vlen = vlen;
+
+ return HA_OK;
+}
+
+static int
+netstring2binary(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ return netstring2string(value, vlen, retvalue, ret_vlen);
+
+}
+
+static int
+netstring2struct(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ struct ha_msg* msg;
+
+ if ( !value || !retvalue || !ret_vlen){
+ cl_log(LOG_ERR, " netstring2struct:"
+ "invalid input arguments");
+ return HA_FAIL;
+ }
+
+ msg = netstring2msg(value, vlen, 0);
+ if (!msg){
+ cl_log(LOG_ERR, "netstring2struct:"
+ "netstring2msg failed");
+ return HA_FAIL;
+ }
+
+ *retvalue =(void* ) msg;
+ *ret_vlen = 0;
+
+ return HA_OK;
+
+}
+
+static int
+netstring2list(const void* value, size_t vlen, void** retvalue, size_t* ret_vlen)
+{
+ GList* list;
+
+ if ( !value || !retvalue || !ret_vlen){
+ cl_log(LOG_ERR, " netstring2struct:"
+ "invalid input arguments");
+ return HA_FAIL;
+ }
+
+
+ list = string_list_unpack(value, vlen);
+ if (list == NULL){
+ cl_log(LOG_ERR, "netstring2list: unpacking string list failed");
+ cl_log(LOG_INFO, "thisbuf=%s", (const char*)value);
+ return HA_FAIL;
+ }
+ *retvalue = (void*)list;
+
+ *ret_vlen = string_list_pack_length(list);
+
+ return HA_OK;
+
+}
+
+
+
+
+
+static int
+add_string_field(struct ha_msg* msg, char* name, size_t namelen,
+ void* value, size_t vallen, int depth)
+{
+
+ size_t internal_type;
+ unsigned long tmptype;
+ char *cp_name = NULL;
+ size_t cp_namelen;
+ size_t cp_vallen;
+ void *cp_value = NULL;
+ int next;
+
+ if ( !msg || !name || !value
+ || namelen <= 0
+ || depth < 0){
+ cl_log(LOG_ERR, "add_string_field:"
+ " invalid input argument");
+ return HA_FAIL;
+ }
+
+
+
+ internal_type = FT_STRING;
+ if (name[0] == '('){
+
+ int nlo = 3; /*name length overhead */
+ if (name[2] != ')'){
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): no closing parentheses");
+ }
+ return(HA_FAIL);
+ }
+ tmptype = name[1] - '0';
+ if (tmptype < 0 || tmptype > 9) {
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): not a number.");
+ return(HA_FAIL);
+ }
+
+ internal_type = tmptype;
+
+ if (internal_type == FT_STRING){
+ cl_log(LOG_ERR
+ , "ha_msg_addraw_ll(): wrong type");
+ return(HA_FAIL);
+ }
+
+ cp_name = name;
+ cp_namelen = namelen - nlo ;
+ memmove(cp_name, name + nlo, namelen - nlo);
+ cp_name[namelen - nlo] = EOS;
+ }else {
+ cp_name = name;
+ cp_namelen = namelen;
+
+ }
+
+ if(internal_type < DIMOF(fieldtypefuncs)){
+ int (*stringtofield)(void*, size_t, int depth, void**, size_t* );
+ int (*fieldstringlen)( size_t, size_t, const void*);
+
+ stringtofield= fieldtypefuncs[internal_type].stringtofield;
+
+ if (!stringtofield || stringtofield(value, vallen, depth, &cp_value, &cp_vallen) != HA_OK){
+ cl_log(LOG_ERR, "add_string_field: stringtofield failed");
+ return HA_FAIL;
+ }
+
+ fieldstringlen = fieldtypefuncs[internal_type].stringlen;
+ if (!fieldstringlen ||
+ fieldstringlen(cp_namelen, cp_vallen, cp_value) <= 0 ){
+
+ cl_log(LOG_ERR, "add_string_field: stringlen failed");
+ return HA_FAIL;
+ }
+
+ } else {
+ cl_log(LOG_ERR, "add_string_field():"
+ " wrong type %lu", (unsigned long)internal_type);
+ return HA_FAIL;
+ }
+
+
+ next = msg->nfields;
+ msg->values[next] = cp_value;
+ msg->vlens[next] = cp_vallen;
+ msg->names[next] = cp_name;
+ msg->nlens[next] = cp_namelen;
+ msg->types[next] = internal_type;
+ msg->nfields++;
+
+ return HA_OK;
+
+}
+
+static int
+uncompress2compress(struct ha_msg* msg, int index)
+{
+ char* buf;
+ size_t buflen = MAXMSG;
+ int rc = HA_FAIL;
+
+ buf = malloc(buflen);
+ if (!buf) {
+ cl_log(LOG_ERR, "%s: failed to allocate buffer",
+ __FUNCTION__);
+ goto err;
+ }
+
+ if (msg->types[index] != FT_UNCOMPRESS){
+ cl_log(LOG_ERR, "%s: the %dth field is not FT_UNCOMPRESS type",
+ __FUNCTION__, index);
+ goto err;
+ }
+
+
+ if (cl_compress_field(msg, index, buf, &buflen) != HA_OK){
+ cl_log(LOG_ERR, "%s: compressing %dth field failed", __FUNCTION__, index);
+ goto err;
+ }
+
+ rc = cl_msg_replace(msg, index, buf, buflen, FT_COMPRESS);
+
+err:
+ if (buf) {
+ free(buf);
+ }
+
+ return rc;
+}
+
+static int
+compress2uncompress(struct ha_msg* msg, int index)
+{
+ char *buf = NULL;
+ size_t buflen = MAXUNCOMPRESSED;
+ struct ha_msg* msgfield;
+ int err = HA_FAIL;
+
+ buf = malloc(buflen);
+
+ if (!buf) {
+ cl_log(LOG_ERR, "%s: allocating buffer for uncompression failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ if (cl_decompress_field(msg, index, buf, &buflen) != HA_OK){
+ cl_log(LOG_ERR, "%s: compress field failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ msgfield = wirefmt2msg(buf, buflen, 0);
+ if (msgfield == NULL){
+ cl_log(LOG_ERR, "%s: wirefmt to msg failed",
+ __FUNCTION__);
+ goto out;
+ }
+
+ err = cl_msg_replace(msg, index, (char*)msgfield, 0, FT_UNCOMPRESS);
+
+ ha_msg_del(msgfield);
+
+out:
+ if (buf) {
+ free(buf);
+ }
+
+ return err;
+}
+
+/*
+ * string FT_STRING
+ * string is the basic type used in heartbeat, it is used for printable ascii value
+ *
+ * binary FT_BINARY
+ * binary means the value can be any binary value, including non-printable ascii value
+ *
+ * struct FT_STRUCT
+ * struct means the value is also an ha_msg (actually it is a pointer to an ha message)
+ *
+ * list FT_LIST
+ * LIST means the value is a GList. Right now we only suppport a Glist of strings
+ *
+ * compress FT_COMPRESS
+ * This field and the next one(FT_UNCOMPRESS) is designed to optimize compression in message
+ * (see cl_compression.c for more about compression). This field is similar to the binary field.
+ * It stores a compressed field, which will be an ha_msg if uncompressed. Most of time this field
+ * act like a binary field until compress2uncompress() is called. That function will be called
+ * when someone calls cl_get_struct() to get this field value. After that this field is converted
+ * to a new type FT_UNCOMPRESS
+ *
+ * uncompress FT_UNCOMPRESS
+ * As said above, this field is used to optimize compression. This field is similar to the struct
+ * field. It's value is a pointer to an ha_msg. This field will be converted to a new type FT_COMPRESS
+ * when msg2wirefmt() is called, where uncompress2compress is called to do the field compression
+ */
+
+struct fieldtypefuncs_s fieldtypefuncs[NUM_MSG_TYPES]=
+ { {string_memfree, string_dup, string_display, add_string_field,
+ string_stringlen,string_netstringlen, str2string,fields2netstring,
+ string2str, netstring2string, NULL, NULL},
+
+ {binary_memfree, binary_dup, binary_display, add_binary_field,
+ binary_stringlen,binary_netstringlen, binary2string,fields2netstring,
+ string2binary, netstring2binary, NULL, NULL},
+
+ {struct_memfree, struct_dup, struct_display, add_struct_field,
+ struct_stringlen, struct_netstringlen, struct2string, fields2netstring, \
+ string2struct, netstring2struct, NULL, NULL},
+
+ {list_memfree, list_dup, list_display, add_list_field,
+ list_stringlen, list_netstringlen, list2string, fields2netstring,
+ string2list, netstring2list, NULL, NULL},
+
+ {binary_memfree, binary_dup, compress_display, add_compress_field,
+ binary_stringlen,binary_netstringlen, binary2string ,fields2netstring,
+ string2binary , netstring2binary, NULL, compress2uncompress}, /*FT_COMPRESS*/
+
+ {struct_memfree, struct_dup, uncompress_display, add_uncompress_field,
+ struct_stringlen, struct_netstringlen, NULL , fields2netstring,
+ NULL , netstring2struct, uncompress2compress, NULL}, /*FT_UNCOMPRSS*/
+ };
+
+
diff --git a/lib/clplumbing/cl_netstring.c b/lib/clplumbing/cl_netstring.c
new file mode 100644
index 0000000..f4040e0
--- /dev/null
+++ b/lib/clplumbing/cl_netstring.c
@@ -0,0 +1,570 @@
+/*
+ * netstring implementation
+ *
+ * Copyright (c) 2003 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ha_msg.h>
+#include <unistd.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/netstring.h>
+#include <clplumbing/base64.h>
+#include <assert.h>
+#include <ctype.h>
+
+/*
+ * Avoid sprintf. Use snprintf instead, even if you count your bytes.
+ * It can detect calculation errors (if used properly)
+ * and will not make the security audit tools crazy.
+ */
+
+#define MAX_AUTH_BYTES 64
+
+
+int msg2netstring_buf(const struct ha_msg*, char*, size_t, size_t*);
+int compose_netstring(char*, const char*, const char*, size_t, size_t*);
+int is_auth_netstring(const char*, size_t, const char*, size_t);
+char* msg2netstring(const struct ha_msg*, size_t*);
+int process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen);
+extern int bytes_for_int(int x);
+extern const char * FT_strings[];
+
+static int (*authmethod)(int whichauth
+, const void * data
+, size_t datalen
+, char * authstr
+, size_t authlen) = NULL;
+
+void
+cl_set_authentication_computation_method(int (*method)(int whichauth
+, const void * data
+, size_t datalen
+, char * authstr
+, size_t authlen))
+{
+ authmethod = method;
+}
+
+int cl_parse_int(const char *sp, const char *smax, int* len);
+
+int
+cl_parse_int(const char *sp, const char *smax, int* len)
+{
+ char ch = 0;
+ int offset = 0;
+ *len = 0;
+
+ errno = 0;
+ for( ; sp+offset < smax; offset++) {
+ ch = sp[offset] - '0';
+ if(ch > 9) { /* ch >= 0 is implied by the data type*/
+ break;
+ }
+ *len *= 10;
+ *len += ch;
+ }
+
+ if(offset == 0) {
+ cl_log(LOG_ERR,
+ "cl_parse_int: Couldn't parse an int from: %.5s", sp);
+ }
+ return offset;
+}
+
+int
+compose_netstring(char * s, const char * smax, const char* data,
+ size_t len, size_t* comlen)
+{
+
+ char * sp = s;
+
+ /* 2 == ":" + "," */
+ if (s + len + 2 + bytes_for_int(len) > smax) {
+ cl_log(LOG_ERR,
+ "netstring pointer out of boundary(compose_netstring)");
+ return(HA_FAIL);
+ }
+
+ sp += sprintf(sp, "%ld:", (long)len);
+
+ if(data){
+ memcpy(sp, data, len);
+ }
+ sp += len;
+ *sp++ = ',';
+
+ *comlen = sp - s;
+
+ return(HA_OK);
+}
+
+
+
+/* Converts a message into a netstring */
+
+int
+msg2netstring_buf(const struct ha_msg *m, char *s,
+ size_t buflen, size_t * slen)
+{
+ int i;
+ char * sp;
+ char * smax;
+ int ret = HA_OK;
+
+ sp = s;
+ smax = s + buflen;
+
+ strcpy(sp, MSG_START_NETSTRING);
+
+ sp += strlen(MSG_START_NETSTRING);
+
+ for (i=0; i < m->nfields; i++) {
+ size_t flen;
+ int tmplen;
+
+ /* some of these functions in its turn invoke us again */
+ ret = fieldtypefuncs[m->types[i]].tonetstring(sp,
+ smax,
+ m->names[i],
+ m->nlens[i],
+ m->values[i],
+ m->vlens[i],
+ m->types[i],
+ &flen);
+
+ if (ret != HA_OK){
+ cl_log(LOG_ERR, "encoding msg to netstring failed");
+ cl_log_message(LOG_ERR, m);
+ return ret;
+ }
+
+ tmplen = netstring_extra(fieldtypefuncs[m->types[i]].netstringlen(m->nlens[i],
+ m->vlens[i],
+ m->values[i]));
+
+ if (flen != tmplen ){
+ cl_log(LOG_ERR,"netstring len discrepency: actual usage is %d bytes"
+ "it should use %d", (int)flen, tmplen);
+ }
+ sp +=flen;
+
+ }
+
+ if (sp + strlen(MSG_END_NETSTRING) > smax){
+ cl_log(LOG_ERR, "%s: out of boundary for MSG_END_NETSTRING",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+ strcpy(sp, MSG_END_NETSTRING);
+ sp += sizeof(MSG_END_NETSTRING) -1;
+
+ if (sp > smax){
+ cl_log(LOG_ERR,
+ "msg2netstring: exceed memory boundary sp =%p smax=%p",
+ sp, smax);
+ return(HA_FAIL);
+ }
+
+ *slen = sp - s;
+ return(HA_OK);
+}
+
+
+int get_netstringlen_auth(const struct ha_msg* m);
+
+int get_netstringlen_auth(const struct ha_msg* m)
+{
+ int len = get_netstringlen(m) + MAX_AUTH_BYTES;
+ return len;
+}
+
+
+
+static char *
+msg2netstring_ll(const struct ha_msg *m, size_t * slen, int need_auth)
+{
+ int len;
+ char* s;
+ int authnum;
+ char authtoken[MAXLINE];
+ char authstring[MAXLINE];
+ char* sp;
+ size_t payload_len;
+ char* smax;
+
+ len= get_netstringlen_auth(m) + 1;
+
+ /* use MAXUNCOMPRESSED for the in memory size check */
+ if (len >= MAXUNCOMPRESSED){
+ cl_log(LOG_ERR, "%s: msg is too large; len=%d,"
+ " MAX msg allowed=%d", __FUNCTION__, len, MAXUNCOMPRESSED);
+ return NULL;
+ }
+
+ s = calloc(1, len);
+ if (!s){
+ cl_log(LOG_ERR, "%s: no memory for netstring", __FUNCTION__);
+ return(NULL);
+ }
+
+ smax = s + len;
+
+ if (msg2netstring_buf(m, s, len, &payload_len) != HA_OK){
+ cl_log(LOG_ERR, "%s: msg2netstring_buf() failed", __FUNCTION__);
+ free(s);
+ return(NULL);
+ }
+
+ sp = s + payload_len;
+
+ if ( need_auth && authmethod){
+ int auth_strlen;
+
+ authnum = authmethod(-1, s, payload_len, authtoken,sizeof(authtoken));
+ if (authnum < 0){
+ cl_log(LOG_WARNING
+ , "Cannot compute message authentication!");
+ free(s);
+ return(NULL);
+ }
+
+ sprintf(authstring, "%d %s", authnum, authtoken);
+ auth_strlen = strlen(authstring);
+ if (sp + 2 + auth_strlen + bytes_for_int(auth_strlen) >= smax){
+ cl_log(LOG_ERR, "%s: out of boundary for auth", __FUNCTION__);
+ free(s);
+ return NULL;
+ }
+ sp += sprintf(sp, "%ld:%s,", (long)strlen(authstring), authstring);
+
+ }
+ *slen = sp - s;
+
+ return(s);
+}
+
+char *
+msg2netstring(const struct ha_msg *m, size_t * slen)
+{
+ char* ret;
+ ret = msg2netstring_ll(m, slen, TRUE);
+
+ return ret;
+
+}
+char *
+msg2netstring_noauth(const struct ha_msg *m, size_t * slen)
+{
+ char * ret;
+
+ ret = msg2netstring_ll(m, slen, FALSE);
+
+ return ret;
+}
+
+
+/*
+ * Peel one string off in a netstring
+ */
+
+static int
+peel_netstring(const char * s, const char * smax, int* len,
+ const char ** data, int* parselen )
+{
+ int offset = 0;
+ const char * sp = s;
+
+ if (sp >= smax){
+ return(HA_FAIL);
+ }
+
+ offset = cl_parse_int(sp, smax, len);
+ if (*len < 0 || offset <= 0){
+ cl_log(LOG_ERR, "peel_netstring: Couldn't parse an int starting at: %.5s", sp);
+ return(HA_FAIL);
+ }
+
+ sp = sp+offset;
+ while (*sp != ':' && sp < smax) {
+ sp ++;
+ }
+
+ if (sp >= smax) {
+ return(HA_FAIL);
+ }
+
+ sp ++;
+
+ *data = sp;
+
+ sp += (*len);
+ if (sp >= smax) {
+ return(HA_FAIL);
+ }
+ if (*sp != ','){
+ return(HA_FAIL);
+ }
+ sp++;
+
+ *parselen = sp - s;
+
+ return(HA_OK);
+}
+
+
+int
+process_netstring_nvpair(struct ha_msg* m, const char* nvpair, int nvlen)
+{
+
+ const char *name;
+ int nlen;
+ const char *ns_value;
+ int ns_vlen;
+ void *value;
+ size_t vlen;
+ int type;
+ void (*memfree)(void*);
+ int ret = HA_OK;
+
+ assert(*nvpair == '(');
+ nvpair++;
+
+ type = nvpair[0] - '0';
+ nvpair++;
+
+ /* if this condition is no longer true, change the above to:
+ * nvpair += cl_parse_int(nvpair, nvpair+nvlen, &type)
+ */
+ assert(type >= 0 && type < 10);
+
+ assert(*nvpair == ')');
+ nvpair++;
+
+ if ((nlen = strcspn(nvpair, EQUAL)) <= 0
+ || nvpair[nlen] != '=') {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "%s: line doesn't contain '='", __FUNCTION__);
+ cl_log(LOG_INFO, "%s", nvpair);
+ }
+ return(HA_FAIL);
+ }
+
+ name = nvpair;
+ ns_value = name +nlen + 1;
+ ns_vlen = nvpair + nvlen - ns_value -3 ;
+ if (fieldtypefuncs[type].netstringtofield(ns_value,ns_vlen, &value, &vlen) != HA_OK){
+ cl_log(LOG_ERR, "netstringtofield failed in %s", __FUNCTION__);
+ return HA_FAIL;
+
+ }
+
+ memfree = fieldtypefuncs[type].memfree;
+
+ if (ha_msg_nadd_type(m , name, nlen, value, vlen,type)
+ != HA_OK) {
+ cl_log(LOG_ERR, "ha_msg_nadd fails(netstring2msg_rec)");
+ ret = HA_FAIL;
+ }
+
+
+ if (memfree && value){
+ memfree(value);
+ } else{
+ cl_log(LOG_ERR, "netstring2msg_rec:"
+ "memfree or ret_value is NULL");
+ ret= HA_FAIL;
+ }
+
+ return ret;
+
+
+}
+
+
+/* Converts a netstring into a message*/
+static struct ha_msg *
+netstring2msg_rec(const char *s, size_t length, int* slen)
+{
+ struct ha_msg* ret = NULL;
+ const char * sp = s;
+ const char * smax = s + length;
+ int startlen;
+ int endlen;
+
+ if ((ret = ha_msg_new(0)) == NULL){
+ return(NULL);
+ }
+
+ startlen = sizeof(MSG_START_NETSTRING)-1;
+
+ if (strncmp(sp, MSG_START_NETSTRING, startlen) != 0) {
+ /* This can happen if the sender gets killed */
+ /* at just the wrong time... */
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING, "netstring2msg_rec: no MSG_START");
+ ha_msg_del(ret);
+ }
+ return(NULL);
+ }else{
+ sp += startlen;
+ }
+
+ endlen = sizeof(MSG_END_NETSTRING) - 1;
+
+ while (sp < smax && strncmp(sp, MSG_END_NETSTRING, endlen) !=0 ){
+
+ const char *nvpair;
+ int nvlen;
+ int parselen;
+
+ if (peel_netstring(sp , smax, &nvlen, &nvpair,&parselen) != HA_OK){
+ cl_log(LOG_ERR
+ , "%s:peel_netstring fails for name/value pair", __FUNCTION__);
+ cl_log(LOG_ERR, "sp=%s", sp);
+ ha_msg_del(ret);
+ return(NULL);
+ }
+ sp += parselen;
+
+ if (process_netstring_nvpair(ret, nvpair, nvlen) != HA_OK){
+ cl_log(LOG_ERR, "%s: processing nvpair failed", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ }
+
+
+ sp += sizeof(MSG_END_NETSTRING) -1;
+ *slen = sp - s;
+ return(ret);
+
+}
+
+
+struct ha_msg *
+netstring2msg(const char* s, size_t length, int needauth)
+{
+ const char *sp;
+ struct ha_msg *msg;
+ const char *smax = s + length;
+ int parselen;
+ int authlen;
+ const char *authstring;
+ /*actual string length used excluding auth string*/
+ int slen = 0; /* assign to keep compiler happy */
+
+ msg = netstring2msg_rec(s, length, &slen);
+
+ if (needauth == FALSE || !authmethod){
+ goto out;
+ }
+
+ sp = s + slen;
+
+ if (peel_netstring(sp , smax, &authlen, &authstring, &parselen) !=HA_OK){
+ cl_log(LOG_ERR,
+ "peel_netstring() error in getting auth string");
+ cl_log(LOG_ERR, "sp=%s", sp);
+ cl_log(LOG_ERR, "s=%s", s);
+ ha_msg_del(msg);
+ return(NULL);
+ }
+
+ if (sp + parselen > smax){
+ cl_log(LOG_ERR, " netstring2msg: smax passed");
+ ha_msg_del(msg);
+ return NULL;
+ }
+
+ if (!is_auth_netstring(s, slen, authstring,authlen) ){
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR
+ , "netstring authentication"
+ " failed, s=%s, autotoken=%s"
+ , s, authstring);
+ cl_log_message(LOG_ERR, msg);
+ }
+ ha_msg_del(msg);
+ return(NULL);
+ }
+
+ out:
+ return msg;
+}
+
+
+
+
+int
+is_auth_netstring(const char * datap, size_t datalen,
+ const char * authstring, size_t authlen)
+{
+
+ char authstr[MAXLINE]; /* A copy of authstring */
+ int authwhich;
+ char authtoken[MAXLINE];
+
+
+ /*
+ * If we don't have any authentication method - everything is authentic...
+ */
+ if (!authmethod) {
+ return TRUE;
+ }
+ strncpy(authstr, authstring, MAXLINE);
+ authstr[authlen] = 0;
+ if (sscanf(authstr, "%d %s", &authwhich, authtoken) != 2) {
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "Bad/invalid netstring auth string");
+ }
+ return(0);
+ }
+
+ memset(authstr, 0, MAXLINE);
+ if (authmethod(authwhich, datap, datalen, authstr, MAXLINE)
+ != authwhich) {
+
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_WARNING
+ , "Invalid authentication [%d] in message!"
+ , authwhich);
+ }
+ return(FALSE);
+ }
+
+ if (strcmp(authtoken, authstr) == 0) {
+ return(TRUE);
+ }
+
+ if (!cl_msg_quiet_fmterr) {
+ cl_log(LOG_ERR
+ , "authtoken does not match, authtoken=%s, authstr=%s"
+ , authtoken, authstr);
+ }
+ return(FALSE);
+}
+
diff --git a/lib/clplumbing/cl_pidfile.c b/lib/clplumbing/cl_pidfile.c
new file mode 100644
index 0000000..b94573b
--- /dev/null
+++ b/lib/clplumbing/cl_pidfile.c
@@ -0,0 +1,294 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/lsb_exitcodes.h>
+
+/*
+ * The following information is from the Filesystem Hierarchy Standard
+ * version 2.1 dated 12 April, 2000.
+ *
+ * 5.6 /var/lock : Lock files
+ * Lock files should be stored within the /var/lock directory structure.
+ * Device lock files, such as the serial device lock files that were originally
+ * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
+ * /var/lock. The naming convention which must be used is LCK.. followed by
+ * the base name of the device file. For example, to lock /dev/cua0 the file
+ * LCK..cua0 would be created.
+ *
+ * The format used for device lock files must be the HDB UUCP lock file format.
+ * The HDB format is to store the process identifier (PID) as a ten byte
+ * ASCII decimal number, with a trailing newline. For example, if process 1230
+ * holds a lock file, it would contain the eleven characters: space, space,
+ * space, space, space, space, one, two, three, zero, and newline.
+ * Then, anything wishing to use /dev/cua0 can read the lock file and act
+ * accordingly (all locks in /var/lock should be world-readable).
+ *
+ *
+ * PERMISSIONS NOTE:
+ * Different linux distributions set the mode of the lock directory differently
+ * Any process which wants to create lock files must have write permissions
+ * on FILE_LOCK_D (probably /var/lock). For things like the heartbeat API
+ * code, this may mean allowing the uid of the processes that use this API
+ * to join group uucp, or making the binaries setgid to uucp.
+ */
+
+/* The code in this file originally written by Guenther Thomsen */
+/* Somewhat mangled by Alan Robertson */
+
+/*
+ * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL)
+ * serial_device has to be _the complete path_, i.e. including '/dev/' to the
+ * special file, which denotes the tty to lock -tho
+ * return 0 on success,
+ * -1 if device is locked (lockfile exists and isn't stale),
+ * -2 for temporarily failure, try again,
+ * other negative value, if something unexpected happend (failure anyway)
+ */
+
+
+/* This is what the FHS standard specifies for the size of our lock file */
+#define LOCKSTRLEN 11
+#include <clplumbing/cl_log.h>
+static int IsRunning(long pid)
+{
+ int rc = 0;
+ long mypid;
+ int running = 0;
+ char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
+
+ /* check if pid is running */
+ if (CL_KILL(pid, 0) < 0 && errno == ESRCH) {
+ goto bail;
+ }
+
+#ifndef HAVE_PROC_PID
+ return 1;
+#endif
+
+ /* check to make sure pid hasn't been reused by another process */
+ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
+
+ rc = readlink(proc_path, exe_path, PATH_MAX-1);
+ if(rc < 0) {
+ cl_perror("Could not read from %s", proc_path);
+ goto bail;
+ }
+ exe_path[rc] = 0;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", mypid);
+ rc = readlink(proc_path, myexe_path, PATH_MAX-1);
+ if(rc < 0) {
+ cl_perror("Could not read from %s", proc_path);
+ goto bail;
+ }
+ myexe_path[rc] = 0;
+
+ if(strcmp(exe_path, myexe_path) == 0) {
+ running = 1;
+ }
+
+ bail:
+ return running;
+}
+
+static int
+DoLock(const char *filename)
+{
+ char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
+ int fd;
+ long pid, mypid;
+ int rc;
+ struct stat sbuf;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(lf_name, sizeof(lf_name), "%s",filename);
+
+ snprintf(tf_name, sizeof(tf_name), "%s.%lu",
+ filename, mypid);
+
+ if ((fd = open(lf_name, O_RDONLY)) >= 0) {
+ if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
+ sleep(1); /* if someone was about to create one,
+ * give'm a sec to do so
+ * Though if they follow our protocol,
+ * this won't happen. They should really
+ * put the pid in, then link, not the
+ * other way around.
+ */
+ }
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ /* lockfile empty -> rm it and go on */;
+ } else {
+ if (sscanf(buf, "%lu", &pid) < 1) {
+ /* lockfile screwed up -> rm it and go on */
+ } else {
+ if (pid > 1 && (getpid() != pid)
+ && IsRunning(pid)) {
+ /* is locked by existing process
+ * -> give up */
+ close(fd);
+ return -1;
+ } else {
+ /* stale lockfile -> rm it and go on */
+ }
+ }
+ }
+ unlink(lf_name);
+ close(fd);
+ }
+ if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
+ /* Hmmh, why did we fail? Anyway, nothing we can do about it */
+ return -3;
+ }
+
+ /* Slight overkill with the %*d format ;-) */
+ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
+
+ if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
+ /* Again, nothing we can do about this */
+ rc = -3;
+ close(fd);
+ goto out;
+ }
+ close(fd);
+
+ switch (link(tf_name, lf_name)) {
+ case 0:
+ if (stat(tf_name, &sbuf) < 0) {
+ /* something weird happened */
+ rc = -3;
+ break;
+ }
+ if (sbuf.st_nlink < 2) {
+ /* somehow, it didn't get through - NFS trouble? */
+ rc = -2;
+ break;
+ }
+ rc = 0;
+ break;
+ case EEXIST:
+ rc = -1;
+ break;
+ default:
+ rc = -3;
+ }
+ out:
+ unlink(tf_name);
+ return rc;
+}
+
+static int
+DoUnlock(const char * filename)
+{
+ char lf_name[256];
+
+ snprintf(lf_name, sizeof(lf_name), "%s", filename);
+
+ return unlink(lf_name);
+}
+
+
+int
+cl_read_pidfile(const char*filename)
+{
+ long pid = 0;
+
+ pid = cl_read_pidfile_no_checking(filename);
+
+ if (pid < 0){
+ return - LSB_STATUS_STOPPED;
+ }
+
+ if (IsRunning(pid)){
+ return pid;
+ }else{
+ return -LSB_STATUS_VAR_PID;
+ }
+}
+
+
+int
+cl_read_pidfile_no_checking(const char*filename)
+{
+ int fd;
+ long pid = 0;
+ char buf[LOCKSTRLEN+1];
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ return -1;
+ }
+
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ close(fd);
+ return -1;
+ }
+
+ if (sscanf(buf, "%lu", &pid) <= 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (pid <= 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return pid;
+}
+
+
+int
+cl_lock_pidfile(const char *filename)
+{
+ if (filename == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+ return DoLock(filename);
+}
+
+/*
+ * Unlock a file (remove its lockfile)
+ * do we need to check, if its (still) ours? No, IMHO, if someone else
+ * locked our line, it's his fault -tho
+ * returns 0 on success
+ * <0 if some failure occured
+ */
+
+int
+cl_unlock_pidfile(const char *filename)
+{
+ if (filename == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+
+ return(DoUnlock(filename));
+}
diff --git a/lib/clplumbing/cl_plugin.c b/lib/clplumbing/cl_plugin.c
new file mode 100644
index 0000000..c039a35
--- /dev/null
+++ b/lib/clplumbing/cl_plugin.c
@@ -0,0 +1,140 @@
+
+/*
+ * cl_plugin.c: This file handle plugin loading and deleting
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <assert.h>
+#include <glib.h>
+#include <ha_msg.h>
+#include <clplumbing/netstring.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+/* #include <stonith/stonith.h> */
+/* #include <stonith/stonith_plugin.h> */
+#include <clplumbing/cl_plugin.h>
+
+#define MAXTYPES 16
+#define MAXTYPELEN 64
+
+static GHashTable* funcstable[MAXTYPES];
+
+static PILPluginUniv* plugin_univ = NULL;
+
+static PILGenericIfMgmtRqst reqs[] =
+ {
+ {"compress", &funcstable[0], NULL, NULL, NULL},
+ {"HBcoms", &funcstable[1], NULL, NULL, NULL},
+ {"HBauth", &funcstable[2], NULL, NULL, NULL},
+ {"RAExec", &funcstable[3], NULL, NULL, NULL},
+ {"quorum", &funcstable[4], NULL, NULL, NULL},
+ {"tiebreaker", &funcstable[5], NULL, NULL, NULL},
+ {"quorumd", &funcstable[6], NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+ };
+
+static int
+init_pluginsys(void){
+
+ if (plugin_univ) {
+ return TRUE;
+ }
+
+ plugin_univ = NewPILPluginUniv(HA_PLUGIN_DIR);
+
+ if (plugin_univ) {
+ if (PILLoadPlugin(plugin_univ, PI_IFMANAGER, "generic", reqs)
+ != PIL_OK){
+ cl_log(LOG_ERR, "generic plugin load failed\n");
+ DelPILPluginUniv(plugin_univ);
+ plugin_univ = NULL;
+ }
+ }else{
+ cl_log(LOG_ERR, "pi univ creation failed\n");
+ }
+ return plugin_univ != NULL;
+
+}
+
+int
+cl_remove_plugin(const char* type, const char* pluginname)
+{
+ return HA_OK;
+}
+
+void*
+cl_load_plugin(const char* type, const char* pluginname)
+{
+ void* funcs = NULL;
+ int i = 0;
+ GHashTable** table = NULL;
+
+ while (reqs[i].iftype != NULL){
+ if ( strcmp(reqs[i].iftype,type) != 0){
+ i++;
+ continue;
+ }
+
+ table = reqs[i].ifmap;
+ break;
+ }
+
+ if (table == NULL){
+ cl_log(LOG_ERR, "%s: function table not found",__FUNCTION__);
+ return NULL;
+ }
+
+ if (!init_pluginsys()){
+ cl_log(LOG_ERR, "%s: init plugin universe failed", __FUNCTION__);
+ return NULL;
+ }
+
+ if ((funcs = g_hash_table_lookup(*table, pluginname))
+ == NULL){
+ if (PILPluginExists(plugin_univ, type, pluginname) == PIL_OK){
+ PIL_rc rc;
+ rc = PILLoadPlugin(plugin_univ, type, pluginname, NULL);
+ if (rc != PIL_OK){
+ cl_log(LOG_ERR,
+ "Cannot load plugin %s[%s]",
+ pluginname,
+ PIL_strerror(rc));
+ return NULL;
+ }
+ funcs = g_hash_table_lookup(*table,
+ pluginname);
+ }
+
+ }
+ if (funcs == NULL){
+ cl_log(LOG_ERR, "%s: module(%s) not found",
+ __FUNCTION__, pluginname);
+ return NULL;
+ }
+
+ return funcs;
+
+}
+
diff --git a/lib/clplumbing/cl_poll.c b/lib/clplumbing/cl_poll.c
new file mode 100644
index 0000000..789eb1a
--- /dev/null
+++ b/lib/clplumbing/cl_poll.c
@@ -0,0 +1,809 @@
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <unistd.h>
+/*
+ * Substitute poll(2) function using POSIX real time signals.
+ *
+ * The poll(2) system call often has significant latencies and realtime
+ * impacts (probably because of its variable length argument list).
+ *
+ * These functions let us use real time signals and sigtimedwait(2) instead
+ * of poll - for those files which work with real time signals.
+ * In the 2.4 series of Linux kernels, this does *not* include FIFOs.
+ *
+ * NOTE: We (have to) grab the SIGPOLL signal for our own purposes.
+ * Hope that's OK with you...
+ *
+ * Special caution: We can only incompletely simulate the difference between
+ * the level-triggered interface of poll(2) and the edge-triggered behavior
+ * of I/O signals. As a result you *must* read all previously-indicated
+ * incoming data before calling cl_poll() again. Otherwise you may miss
+ * some incoming data (and possibly hang).
+ *
+ *
+ * Copyright (C) 2003 IBM Corporation
+ *
+ * Author: <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ **************************************************************************/
+
+
+#define __USE_GNU 1
+# include <fcntl.h>
+#undef __USE_GNU
+
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_poll.h>
+#include <clplumbing/cl_signal.h>
+
+
+
+/* Turn on to log odd realtime behavior */
+
+#define TIME_CALLS 1
+#ifdef TIME_CALLS
+# include <clplumbing/longclock.h>
+# include <clplumbing/cl_log.h>
+#endif
+
+static int debug = 0;
+
+int /* Slightly sleazy... */
+cl_glibpoll(GPollFD* ufds, guint nfsd, gint timeout)
+{
+ (void)debug;
+ return cl_poll((struct pollfd*)ufds, nfsd, timeout);
+}
+
+#if defined (F_SETSIG) && defined(F_SETOWN) && defined (O_ASYNC)
+# define HAVE_FCNTL_F_SETSIG
+#endif
+
+#ifndef HAVE_FCNTL_F_SETSIG
+
+/*
+ * Dummy cl_poll() and cl_poll_ignore() functions for systems where
+ * we don't have all the support we need.
+ */
+
+int
+cl_poll(struct pollfd *fds, unsigned int nfds, int timeout)
+{
+ return poll(fds, (nfds_t)nfds, timeout);
+}
+
+int
+cl_poll_ignore(int fd)
+{
+ return 0;
+}
+
+#else /* HAVE_FCNTL_F_SETSIG */
+static void dump_fd_info(struct pollfd *fds, unsigned int nfds, int timeoutms);
+static void check_fd_info(struct pollfd *fds, unsigned int nfds);
+static void cl_real_poll_fd(int fd);
+static void cl_poll_sigpoll_overflow_sigaction(int nsig, siginfo_t* , void*);
+static void cl_poll_sigpoll_overflow(void);
+static int cl_poll_get_sigqlimit(void);
+typedef unsigned char poll_bool;
+
+/*
+ * Here's our strategy:
+ * We have a set of signals which we use for these file descriptors,
+ * and we use sigtimedwait(2) to wait on information from these various
+ * signals.
+ *
+ * If we are ever asked to wait for a particular signal, then we will
+ * enable signals for that file descriptor, and post the events in
+ * our own cache. The next time you include that signal in a call
+ * to cl_poll(), you will get the information delivered
+ * to you in your cl_poll() call.
+ *
+ * If you want to stop monitoring a particular file descriptor, use
+ * cl_poll_ignore() for that purpose. Doing this is a good idea, but
+ * not fatal if omitted...
+ */
+
+/* Information about a file descriptor we're monitoring */
+
+typedef struct poll_fd_info_s {
+ short nsig; /* Which signal goes with it? */
+ short pendevents; /* Pending events */
+}poll_info_t;
+
+static int max_allocated = 0;
+static poll_bool* is_monitored = NULL; /* Sized by max_allocated */
+static poll_info_t* monitorinfo = NULL; /* Sized by max_allocated */
+static int cl_nsig = 0;
+static gboolean SigQOverflow = FALSE;
+
+static int cl_init_poll_sig(struct pollfd *fds, unsigned int nfds);
+static short cl_poll_assignsig(int fd);
+static void cl_poll_sigaction(int nsig, siginfo_t* info, void* v);
+static int cl_poll_prepsig(int nsig);
+
+
+/*
+ * SignalSet is the set of all file descriptors we're monitoring.
+ *
+ * We monitor a file descriptor forever, unless you tell us not to
+ * by calling cl_poll_ignore(), or you (mistakenly) give it to
+ * us to look at in another poll call after you've closed it.
+ */
+
+static sigset_t SignalSet;
+
+/* Select the signal you want us to use (must be a RT signal) */
+int
+cl_poll_setsig(int nsig)
+{
+ if (nsig < SIGRTMIN || nsig >= SIGRTMAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (cl_poll_prepsig(nsig) < 0) {
+ return -1;
+ }
+ cl_nsig = nsig;
+ return 0;
+}
+
+/*
+ * It's harmless to call us multiple times on the same signal.
+ */
+static int
+cl_poll_prepsig(int nsig)
+{
+ static gboolean setinityet=FALSE;
+
+ if (!setinityet) {
+ CL_SIGEMPTYSET(&SignalSet);
+ cl_signal_set_simple_action(SIGPOLL
+ , cl_poll_sigpoll_overflow_sigaction
+ , NULL);
+ setinityet = TRUE;
+ }
+ if (CL_SIGINTERRUPT(nsig, FALSE) < 0) {
+ cl_perror("sig_interrupt(%d, FALSE)", nsig);
+ return -1;
+ }
+ if (CL_SIGADDSET(&SignalSet, nsig) < 0) {
+ cl_perror("sig_addset(&SignalSet, %d)", nsig);
+ return -1;
+ }
+ if (CL_SIGPROCMASK(SIG_BLOCK, &SignalSet, NULL) < 0) {
+ cl_perror("sig_sigprocmask(SIG_BLOCK, sig %d)", nsig);
+ return -1;
+ }
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Signal %d belongs to us...", nsig);
+ cl_log(LOG_DEBUG, "cl_poll_prepsig(%d) succeeded.", nsig);
+ }
+
+ return 0;
+}
+
+#define FD_CHUNKSIZE 64
+
+/* Set of events everyone must monitor whether they want to or not ;-) */
+#define CONSTEVENTS (POLLHUP|POLLERR|POLLNVAL)
+
+#define RECORDFDEVENT(fd, flags) (monitorinfo[fd].pendevents |= (flags))
+
+/*
+ * Initialized our poll-simulation data structures.
+ * This means (among other things) registering any monitored
+ * file descriptors.
+ */
+static int
+cl_init_poll_sig(struct pollfd *fds, unsigned int nfds)
+{
+ unsigned j;
+ int maxmonfd = -1;
+ int nmatch = 0;
+
+
+ if (cl_nsig == 0) {
+ cl_nsig = ((SIGRTMIN+SIGRTMAX)/2);
+ if (cl_poll_setsig(cl_nsig) < 0) {
+ return -1;
+ }
+ }
+ for (j=0; j < nfds; ++j) {
+ const int fd = fds[j].fd;
+
+ if (fd > maxmonfd) {
+ maxmonfd = fd;
+ }
+ }
+
+ /* See if we need to malloc/realloc our data structures */
+
+ if (maxmonfd >= max_allocated) {
+ int newsize;
+ int growthamount;
+
+ newsize = ((maxmonfd + FD_CHUNKSIZE)/FD_CHUNKSIZE)
+ * FD_CHUNKSIZE;
+ growthamount = newsize - max_allocated;
+
+ /* This can't happen ;-) */
+ if (growthamount <= 0 || newsize <= maxmonfd) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Allocate (more) memory! */
+
+ if ((is_monitored = (poll_bool*)realloc(is_monitored
+ , newsize * sizeof(poll_bool))) == NULL
+ || (monitorinfo = (poll_info_t*) realloc(monitorinfo
+ , newsize * sizeof(poll_info_t))) == NULL) {
+
+ if (is_monitored) {
+ free(is_monitored);
+ is_monitored = NULL;
+ }
+ if (monitorinfo) {
+ free(monitorinfo);
+ monitorinfo = NULL;
+ }
+ max_allocated = 0;
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(monitorinfo+max_allocated, 0
+ , growthamount * sizeof(monitorinfo[0]));
+ memset(is_monitored+max_allocated, FALSE
+ , growthamount*sizeof(is_monitored[0]));
+ max_allocated = newsize;
+ }
+
+ if (fds->events != 0 && debug) {
+ cl_log(LOG_DEBUG
+ , "Current event mask for fd [0] {%d} 0x%x"
+ , fds->fd, fds->events);
+ }
+ /*
+ * Examine each fd for the following things:
+ * Is it already monitored?
+ * if not, set it up for monitoring.
+ * Do we have events for it?
+ * if so, post events...
+ */
+
+ for (j=0; j < nfds; ++j) {
+ const int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+ short nsig;
+ int badfd = FALSE;
+
+ is_monitored[fd] = TRUE;
+
+ if (moni->nsig <= 0) {
+ nsig = cl_poll_assignsig(fd);
+ if (nsig < 0) {
+ RECORDFDEVENT(fd, POLLERR);
+ badfd = TRUE;
+ }else{
+ /* Use real poll(2) to get initial
+ * event status
+ */
+ moni->nsig = nsig;
+ cl_real_poll_fd(fd);
+ }
+ }else if (fcntl(fd, F_GETFD) < 0) {
+ cl_log(LOG_ERR, "bad fd(%d)", fd);
+ RECORDFDEVENT(fd, POLLNVAL);
+ badfd = TRUE;
+ }
+
+ /* Look for pending events... */
+
+ fds[j].revents = (moni->pendevents
+ & (fds[j].events|CONSTEVENTS));
+
+ if (fds[j].revents) {
+ ++nmatch;
+ moni->pendevents &= ~(fds[j].revents);
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "revents for fd %d: 0x%x"
+ , fds[j].fd, fds[j].revents);
+ cl_log(LOG_DEBUG
+ , "events for fd %d: 0x%x"
+ , fds[j].fd, fds[j].events);
+ }
+ }else if (fds[j].events && debug) {
+ cl_log(LOG_DEBUG
+ , "pendevents for fd %d: 0x%x"
+ , fds[j].fd, moni->pendevents);
+ }
+ if (badfd) {
+ cl_poll_ignore(fd);
+ }
+ }
+ if (nmatch != 0 && debug) {
+ cl_log(LOG_DEBUG, "Returning %d events from cl_init_poll_sig()"
+ , nmatch);
+ }
+ return nmatch;
+}
+
+/*
+ * Initialize our current state of the world with info from the
+ * real poll(2) call.
+ *
+ * We call this when we first see a particular fd, and after a signal
+ * queue overflow.
+ */
+static void
+cl_real_poll_fd(int fd)
+{
+ struct pollfd pfd[1];
+
+ if (fd >= max_allocated || !is_monitored[fd]) {
+ return;
+ }
+
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Calling poll(2) on fd %d", fd);
+ }
+ /* Get the current state of affaris from poll(2) */
+ pfd[0].fd = fd;
+ pfd[0].revents = 0;
+ pfd[0].events = ~0;
+ if (poll(pfd, 1, 0) >= 0) {
+ RECORDFDEVENT(fd, pfd[0].revents);
+ if (pfd[0].revents & (POLLNVAL|POLLERR)) {
+ cl_log(LOG_INFO, "cl_poll_real_fd(%d): error in revents [%d]"
+ , fd, pfd[0].revents);
+ }
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Old news from poll(2) for fd %d: 0x%x"
+ , fd, pfd[0].revents);
+ }
+ }else{
+ if (fcntl(fd, F_GETFL) < 0) {
+ cl_perror("cl_poll_real_fd(%d): F_GETFL failure"
+ , fd);
+ RECORDFDEVENT(fd, POLLNVAL);
+ }else{
+ RECORDFDEVENT(fd, POLLERR);
+ }
+ }
+}
+
+/*
+ * Assign a signal for monitoring the given file descriptor
+ */
+
+static short
+cl_poll_assignsig(int fd)
+{
+ int flags;
+
+
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "Signal %d monitors fd %d...", cl_nsig, fd);
+ }
+
+ /* Test to see if the file descriptor is good */
+ if ((flags = fcntl(fd, F_GETFL)) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_GETFL failure"
+ , fd);
+ return -1;
+ }
+
+ /* Associate the right signal with the fd */
+
+ if (fcntl(fd, F_SETSIG, cl_nsig) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_SETSIG failure"
+ , fd);
+ return -1;
+ }
+
+ /* Direct the signals to us */
+ if (fcntl(fd, F_SETOWN, getpid()) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_SETOWN failure", fd);
+ return -1;
+ }
+
+ /* OK... Go ahead and send us signals! */
+
+ if (fcntl(fd, F_SETFL, flags|O_ASYNC) < 0) {
+ cl_perror("cl_poll_assignsig(%d) F_SETFL(O_ASYNC) failure"
+ , fd);
+ return -1;
+ }
+
+ return cl_nsig;
+}
+
+
+/*
+ * This is a function we call as a (fake) signal handler.
+ *
+ * It records events to our "monitorinfo" structure.
+ *
+ * Except for the cl_log() call, it could be called in a signal
+ * context.
+ */
+
+static void
+cl_poll_sigaction(int nsig, siginfo_t* info, void* v)
+{
+ int fd;
+
+ /* What do you suppose all the various si_code values mean? */
+
+ fd = info->si_fd;
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "cl_poll_sigaction(nsig=%d fd=%d"
+ ", si_code=%d si_band=0x%lx)"
+ , nsig, fd, info->si_code
+ , (unsigned long)info->si_band);
+ }
+
+ if (fd <= 0) {
+ return;
+ }
+
+
+ if (fd >= max_allocated || !is_monitored[fd]) {
+ return;
+ }
+
+ /* We should not call logging functions in (real) signal handlers */
+ if (nsig != monitorinfo[fd].nsig) {
+ cl_log(LOG_ERR, "cl_poll_sigaction called with signal %d/%d"
+ , nsig, monitorinfo[fd].nsig);
+ }
+
+ /* Record everything as a pending event. */
+ RECORDFDEVENT(fd, info->si_band);
+}
+
+
+
+/*
+ * This is called whenever a file descriptor shouldn't be
+ * monitored any more.
+ */
+int
+cl_poll_ignore(int fd)
+{
+ int flags;
+
+ if (debug) {
+ cl_log(LOG_DEBUG
+ , "cl_poll_ignore(%d)", fd);
+ }
+ if (fd < 0 || fd >= max_allocated) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!is_monitored[fd]) {
+ return 0;
+ }
+
+ is_monitored[fd] = FALSE;
+ memset(monitorinfo+fd, 0, sizeof(monitorinfo[0]));
+
+ if ((flags = fcntl(fd, F_GETFL)) >= 0) {
+ flags &= ~O_ASYNC;
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ return -1;
+ }
+ }else{
+ return flags;
+ }
+ return 0;
+}
+
+
+/*
+ * cl_poll: fake poll routine based on POSIX realtime signals.
+ *
+ * We want to emulate poll as exactly as possible, but poll has a couple
+ * of problems: scaleability, and it tends to sleep in the kernel
+ * because the first argument is an argument of arbitrary size, and
+ * generally requires allocating memory.
+ *
+ * The challenge is that poll is level-triggered, but the POSIX
+ * signals (and sigtimedwait(2)) are edge triggered. This is
+ * one of the reasons why we have the cl_real_poll_fd() function
+ * - to get the current "level" before we start.
+ * Once we have this level we can compute something like the current
+ * level
+ */
+
+int
+cl_poll(struct pollfd *fds, unsigned int nfds, int timeoutms)
+{
+ int nready;
+ struct timespec ts;
+ static const struct timespec zerotime = {0L, 0L};
+ const struct timespec* itertime = &ts;
+ siginfo_t info;
+ int eventcount = 0;
+ unsigned int j;
+ int savederrno = errno;
+ int stw_errno;
+ int rc;
+ longclock_t starttime;
+ longclock_t endtime;
+ const int msfudge
+ = 2* 1000/hz_longclock();
+ int mselapsed = 0;
+
+ /* Do we have any old news to report? */
+ if ((nready=cl_init_poll_sig(fds, nfds)) != 0) {
+ /* Return error or old news to report */
+ if (debug) {
+ cl_log(LOG_DEBUG, "cl_poll: early return(%d)", nready);
+ }
+ return nready;
+ }
+
+ /* Nothing to report yet... */
+
+ /* So, we'll do a sigtimedwait(2) to wait for signals
+ * and see if we can find something to report...
+ *
+ * cl_init_poll() prepared a set of file signals to watch...
+ */
+
+recalcandwaitagain:
+ if (timeoutms >= 0) {
+ ts.tv_sec = timeoutms / 1000;
+ ts.tv_nsec = (((unsigned long)timeoutms) % 1000UL)*1000000UL;
+ }else{
+ ts.tv_sec = G_MAXLONG;
+ ts.tv_nsec = 99999999UL;
+ }
+
+ /*
+ * Perform a timed wait for any of our signals...
+ *
+ * We shouldn't sleep for any call but (possibly) the first one.
+ * Subsequent calls should just pick up other events without
+ * sleeping.
+ */
+
+ starttime = time_longclock();
+ /*
+ * Wait up to the prescribed time for a signal.
+ * If we get a signal, then loop grabbing all other
+ * pending signals. Note that subsequent iterations will
+ * use &zerotime to get the minimum wait time.
+ */
+ if (debug) {
+ check_fd_info(fds, nfds);
+ dump_fd_info(fds, nfds, timeoutms);
+ }
+waitagain:
+ while (sigtimedwait(&SignalSet, &info, itertime) >= 0) {
+ int nsig = info.si_signo;
+
+ /* Call signal handler to simulate signal reception */
+
+ cl_poll_sigaction(nsig, &info, NULL);
+ itertime = &zerotime;
+ }
+ stw_errno=errno; /* Save errno for later use */
+ endtime = time_longclock();
+ mselapsed = longclockto_ms(sub_longclock(endtime, starttime));
+
+#ifdef TIME_CALLS
+ if (timeoutms >= 0 && mselapsed > timeoutms + msfudge) {
+ /* We slept too long... */
+ cl_log(LOG_WARNING
+ , "sigtimedwait() sequence for %d ms took %d ms"
+ , timeoutms, mselapsed);
+ }
+#endif
+
+ if (SigQOverflow) {
+ /* OOPS! Better recover from this! */
+ /* This will use poll(2) to correct our current status */
+ cl_poll_sigpoll_overflow();
+ }
+
+ /* Post observed events and count them... */
+
+ for (j=0; j < nfds; ++j) {
+ int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+ fds[j].revents = (moni->pendevents
+ & (fds[j].events|CONSTEVENTS));
+ if (fds[j].revents) {
+ ++eventcount;
+ moni->pendevents &= ~(fds[j].revents);
+ /* Make POLLHUP persistent */
+ if (fds[j].revents & POLLHUP) {
+ moni->pendevents |= POLLHUP;
+ /* Don't lose input events at EOF */
+ if (fds[j].events & POLLIN) {
+ cl_real_poll_fd(fds[j].fd);
+ }
+ }
+ }
+ }
+ if (eventcount == 0 && stw_errno == EAGAIN && timeoutms != 0) {
+ /* We probably saw an event the user didn't ask to see. */
+ /* Consquently, we may have more waiting to do */
+ if (timeoutms < 0) {
+ /* Restore our infinite wait time */
+ itertime = &ts;
+ goto waitagain;
+ }else if (timeoutms > 0) {
+ if (mselapsed < timeoutms) {
+ timeoutms -= mselapsed;
+ goto recalcandwaitagain;
+ }
+ }
+ }
+ rc = (eventcount > 0 ? eventcount : (stw_errno == EAGAIN ? 0 : -1));
+
+ if (rc >= 0) {
+ errno = savederrno;
+ }
+ return rc;
+}
+/*
+ * Debugging routine for printing current poll arguments, etc.
+ */
+static void
+dump_fd_info(struct pollfd *fds, unsigned int nfds, int timeoutms)
+{
+ unsigned j;
+
+ cl_log(LOG_DEBUG, "timeout: %d milliseconds", timeoutms);
+ for (j=0; j < nfds; ++j) {
+ int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+
+ cl_log(LOG_DEBUG, "fd %d flags: 0%o, signal: %d, events: 0x%x"
+ ", revents: 0x%x, pendevents: 0x%x"
+ , fd, fcntl(fd, F_GETFL), moni->nsig
+ , fds[j].events, fds[j].revents, moni->pendevents);
+ }
+ for (j=SIGRTMIN; j < (unsigned)SIGRTMAX; ++j) {
+ if (!sigismember(&SignalSet, j)) {
+ continue;
+ }
+ cl_log(LOG_DEBUG, "Currently monitoring RT signal %d", j);
+ }
+}
+
+/*
+ * Debugging routine for auditing our file descriptors, etc.
+ */
+static void
+check_fd_info(struct pollfd *fds, unsigned int nfds)
+{
+ unsigned j;
+
+ for (j=0; j < nfds; ++j) {
+ int fd = fds[j].fd;
+ poll_info_t* moni = monitorinfo+fd;
+
+ if (!sigismember(&SignalSet, moni->nsig)) {
+ cl_log(LOG_ERR, "SIGNAL %d not in monitored SignalSet"
+ , moni->nsig);
+ }
+ }
+ for (j=0; j < 10; ++j) {
+ int sig;
+ int flags;
+ int pid;
+ if ((flags = fcntl(j, F_GETFL)) < 0 || (flags & O_ASYNC) ==0){
+ continue;
+ }
+ sig = fcntl(j, F_GETSIG);
+ if (sig == 0) {
+ cl_log(LOG_ERR, "FD %d will get SIGIO", j);
+ }
+ if (!sigismember(&SignalSet, sig)) {
+ cl_log(LOG_ERR, "FD %d (signal %d) is not in SignalSet"
+ , j, sig);
+ }
+ if (sig < SIGRTMIN || sig >= SIGRTMAX) {
+ cl_log(LOG_ERR, "FD %d (signal %d) is not RealTime"
+ , j, sig);
+ }
+ pid = fcntl(j, F_GETOWN);
+ if (pid != getpid()) {
+ cl_log(LOG_ERR, "FD %d (signal %d) owner is pid %d"
+ , j, sig, pid);
+ }
+ }
+}
+
+/* Note that the kernel signalled an event queue overflow */
+static void
+cl_poll_sigpoll_overflow_sigaction(int nsig, siginfo_t* info, void* v)
+{
+ SigQOverflow = TRUE;
+}
+
+#define MAXQNAME "rtsig-max"
+/*
+ * Called when signal queue overflow is suspected.
+ * We then use poll(2) to get the current data. It's slow, but it
+ * should work quite nicely.
+ */
+static void
+cl_poll_sigpoll_overflow(void)
+{
+ int fd;
+ int limit;
+
+ if (!SigQOverflow) {
+ return;
+ }
+ cl_log(LOG_WARNING, "System signal queue overflow.");
+ limit = cl_poll_get_sigqlimit();
+ if (limit > 0) {
+ cl_log(LOG_WARNING, "Increase '%s'. Current limit is %d"
+ " (see sysctl(8)).", MAXQNAME, limit);
+ }
+
+ SigQOverflow = FALSE;
+
+ for (fd = 0; fd < max_allocated; ++fd) {
+ if (is_monitored[fd]) {
+ cl_real_poll_fd(fd);
+ }
+ }
+}
+
+#define PSK "/proc/sys/kernel/"
+
+/* Get current kernel signal queue limit */
+/* This only works on Linux - but that's not a big problem... */
+static int
+cl_poll_get_sigqlimit(void)
+{
+ int limit = -1;
+ int pfd;
+ char result[32];
+
+ pfd = open(PSK MAXQNAME, O_RDONLY);
+ if (pfd >= 0 && read(pfd, result, sizeof(result)) > 1) {
+ limit = atoi(result);
+ if (limit < 1) {
+ limit = -1;
+ }
+ }
+ if (pfd >= 0) {
+ close(pfd);
+ }
+ return limit;
+}
+#endif /* HAVE_FCNTL_F_SETSIG */
diff --git a/lib/clplumbing/cl_random.c b/lib/clplumbing/cl_random.c
new file mode 100644
index 0000000..4bafcfe
--- /dev/null
+++ b/lib/clplumbing/cl_random.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ * Copyright (C) 2005 International Business Machines Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <lha_internal.h>
+#include <strings.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_misc.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/cl_random.h>
+#include <clplumbing/longclock.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <sys/time.h>
+#include <sys/times.h>
+
+/* Used to provide seed to the random number generator */
+unsigned int
+cl_randseed(void)
+{
+ char buf[16];
+ FILE* fs;
+ struct timeval tv;
+ const char * randdevname [] = {"/dev/urandom", "/dev/random"};
+ int idev;
+#if 0
+ long horrid;
+#endif
+
+ /*
+ * Notes, based on reading of man pages of Solaris, FreeBSD and Linux,
+ * and on proof-of-concept tests on Solaris and Linux (32- and 64-bit).
+ *
+ * Reminder of a subtlety: our intention is not to return a random
+ * number, but rather to return a random-enough seed for future
+ * random numbers. So don't bother trying (e.g.) "rand()" and
+ * "random()".
+ *
+ * /dev/random and dev/urandom seem to be a related pair. In the
+ * words of the song: "You can't have one without the other".
+ *
+ * /dev/random is probably the best. But it can block. The Solaris
+ * implementation can apparently honour "O_NONBLOCK" and "O_NDELAY".
+ * But can others? For this reason, I chose not to use it at present.
+ *
+ * /dev/urandom (with the "u") is also good. This doesn't block.
+ * But some OSes may lack it. It is tempting to detect its presence
+ * with autoconf and use the result in a "hash-if" here. BUT... in
+ * at least one OS, its presence can vary depending upon patch levels,
+ * so a binary/package built on an enabled machine might hit trouble
+ * when run on one where it is absent. (And vice versa: a build on a
+ * disabled machine would be unable to take advantage of it on an
+ * enabled machine.) Therefore always try for it at run time.
+ *
+ * "gettimeofday()" returns a random-ish number in its millisecond
+ * component.
+ *
+ * -- David Lee, Jan 2006
+ */
+
+ /*
+ * Each block below is logically of the form:
+ * if good-feature appears present {
+ * try feature
+ * if feature worked {
+ * return its result
+ * }
+ * }
+ * -- fall through to not-quite-so-good feature --
+ */
+
+ /*
+ * Does any of the random device names work?
+ */
+ for (idev=0; idev < DIMOF(randdevname); ++idev) {
+ fs = fopen(randdevname[idev], "r");
+ if (fs == NULL) {
+ cl_log(LOG_INFO, "%s: Opening file %s failed"
+ , __FUNCTION__, randdevname[idev]);
+ }else{
+ if (fread(buf, 1, sizeof(buf), fs)!= sizeof(buf)){
+ cl_log(LOG_INFO, "%s: reading file %s failed"
+ , __FUNCTION__, randdevname[idev]);
+ fclose(fs);
+ }else{
+ fclose(fs);
+ return (unsigned int)cl_binary_to_int(buf, sizeof(buf));
+ }
+ }
+ }
+
+ /*
+ * Try "gettimeofday()"; use its microsecond output.
+ * (Might it be prudent to let, say, the seconds further adjust this,
+ * in case the microseconds are too predictable?)
+ */
+ if (gettimeofday(&tv, NULL) != 0) {
+ cl_log(LOG_INFO, "%s: gettimeofday failed",
+ __FUNCTION__);
+ }else{
+ return (unsigned int) tv.tv_usec;
+ }
+ /*
+ * times(2) returns the number of clock ticks since
+ * boot. Fairly predictable, but not completely so...
+ */
+ return (unsigned int) cl_times();
+
+
+#if 0
+ /*
+ * If all else has failed, return (as a number) the address of
+ * something on the stack.
+ * Poor, but at least it has a chance of some sort of variability.
+ */
+ horrid = (long) &tv;
+ return (unsigned int) horrid; /* pointer to local variable exposed */
+#endif
+}
+
+static gboolean inityet = FALSE;
+
+static void
+cl_init_random(void)
+{
+ if (inityet)
+ return;
+
+ inityet=TRUE;
+ srand(cl_randseed());
+}
+
+int
+get_next_random(void)
+{
+ if (!inityet)
+ cl_init_random();
+
+ return rand();
+}
diff --git a/lib/clplumbing/cl_reboot.c b/lib/clplumbing/cl_reboot.c
new file mode 100644
index 0000000..c4c3ab0
--- /dev/null
+++ b/lib/clplumbing/cl_reboot.c
@@ -0,0 +1,59 @@
+#include <lha_internal.h>
+#include <clplumbing/cl_reboot.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_REBOOT_H
+# include <sys/reboot.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#include <clplumbing/cl_log.h>
+#include <clplumbing/timers.h>
+
+enum rebootopt {
+ REBOOT_DEFAULT = 0,
+ REBOOT_NOCOREDUMP = 1,
+ REBOOT_COREDUMP = 2,
+};
+static enum rebootopt coredump = REBOOT_DEFAULT;
+
+void
+cl_enable_coredump_before_reboot(gboolean yesno)
+{
+ coredump = (yesno ? REBOOT_COREDUMP : REBOOT_NOCOREDUMP);
+}
+
+
+void cl_reboot(int msdelaybeforereboot, const char * reason)
+{
+ int rebootflag = 0;
+ int systemrc = 0;
+#ifdef RB_AUTOBOOT
+ rebootflag = RB_AUTOBOOT;
+#endif
+#ifdef RB_NOSYNC
+ rebootflag = RB_NOSYNC;
+#endif
+#ifdef RB_DUMP
+ if (coredump == REBOOT_COREDUMP) {
+ rebootflag = RB_DUMP;
+ }
+#endif
+ cl_log(LOG_EMERG, "Rebooting system. Reason: %s", reason);
+ sync();
+ mssleep(msdelaybeforereboot);
+#if REBOOT_ARGS == 1
+ reboot(rebootflag);
+#elif REBOOT_ARGS == 2
+ reboot(rebootflag, NULL);
+#else
+#error "reboot() call needs to take one or two args"
+#endif
+ /* Shouldn't ever get here, but just in case... */
+ systemrc=system(REBOOT " " REBOOT_OPTIONS);
+ cl_log(LOG_EMERG, "ALL REBOOT OPTIONS FAILED: %s returned %d"
+ , REBOOT " " REBOOT_OPTIONS, systemrc);
+ exit(1);
+}
diff --git a/lib/clplumbing/cl_signal.c b/lib/clplumbing/cl_signal.c
new file mode 100644
index 0000000..feedb3d
--- /dev/null
+++ b/lib/clplumbing/cl_signal.c
@@ -0,0 +1,209 @@
+/*
+ * cl_signal.c: signal handling routines to be used by Linux-HA programmes
+ *
+ * Copyright (C) 2002 Horms <horms@verge.net.au>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_log.h>
+
+
+int
+cl_signal_set_handler(int sig, void (*handler)(int), sigset_t *mask
+, int flags, struct sigaction *oldact)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = handler;
+ sa.sa_mask = *mask;
+ sa.sa_flags = flags;
+
+ if (sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_handler(): sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ if(sigemptyset(&mask) < 0) {
+ cl_perror("cl_signal_set_simple_handler(): "
+ "sigemptyset()");
+ return(-1);
+ }
+
+ sa.sa_handler = handler;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_simple_handler()"
+ ": sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_action(int sig, void (*action)(int, siginfo_t *, void *)
+, sigset_t *mask, int flags, struct sigaction *oldact)
+{
+ struct sigaction sa;
+
+ sa.sa_sigaction = action;
+ sa.sa_mask = *mask;
+ sa.sa_flags = flags;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_action(): sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_simple_action(int sig, void (*action)(int, siginfo_t *, void *)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ if(sigemptyset(&mask) < 0) {
+ cl_perror("cl_signal_set_simple_action()"
+ ": sigemptyset()");
+ return(-1);
+ }
+
+ sa.sa_sigaction = action;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ cl_perror("cl_signal_set_simple_action()"
+ ": sigaction()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_interrupt(int sig, int flag)
+{
+ if(siginterrupt(sig, flag) < 0) {
+ cl_perror("cl_signal_set_interrupt(): siginterrupt()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_block(int how, int signal, sigset_t *oldset)
+{
+ sigset_t set;
+
+ if(sigemptyset(&set) < 0) {
+ cl_perror("cl_signal_block(): sigemptyset()");
+ return(-1);
+ }
+
+ if(sigaddset(&set, signal) < 0) {
+ cl_perror("cl_signal_block(): sigaddset()");
+ return(-1);
+ }
+
+ if(sigprocmask(how, &set, oldset) < 0) {
+ cl_perror("cl_signal_block(): sigprocmask()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_block_set(int how, const sigset_t *set, sigset_t *oldset)
+{
+ if(sigprocmask(how, set, oldset) < 0) {
+ cl_perror("cl_signal_block_mask(): sigprocmask()");
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+int
+cl_signal_set_handler_mode(const cl_signal_mode_t *mode, sigset_t *set)
+{
+ size_t i;
+ sigset_t our_set;
+ sigset_t *use_set;
+
+ use_set = (set) ? set : &our_set;
+
+ for (i=0; mode[i].sig; ++i) {
+ if(sigaddset(use_set, mode[i].sig) < 0) {
+ cl_perror("cl_signal_set_handler_mode(): "
+ "sigaddset() [signum=%d]", mode[i].sig);
+ return(-1);
+ }
+ }
+
+ if (sigprocmask(SIG_UNBLOCK, use_set, NULL) < 0) {
+ cl_perror("cl_signal_set_handler_mode()"
+ ": sigprocmask()");
+ return(-1);
+ }
+
+ for (i=0; mode[i].sig; ++i) {
+ if(cl_signal_set_handler(mode[i].sig, mode[i]. handler
+ , use_set, SA_NOCLDSTOP, NULL) < 0) {
+ cl_log(LOG_ERR, "cl_signal_set_handler_mode(): "
+ "ha_set_sig_handler()");
+ return(-1);
+ }
+ if(cl_signal_set_interrupt(mode[i].sig, mode[i].interrupt) < 0) {
+ cl_log(LOG_ERR, "cl_signal_set_handler_mode(): "
+ "hb_signal_interrupt()");
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
diff --git a/lib/clplumbing/cl_syslog.c b/lib/clplumbing/cl_syslog.c
new file mode 100644
index 0000000..6920bd5
--- /dev/null
+++ b/lib/clplumbing/cl_syslog.c
@@ -0,0 +1,149 @@
+/*
+ * Functions to support syslog.
+ * David Lee (c) 2005
+ */
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+/*
+ * Some OSes already have tables to convert names into corresponding numbers.
+ * For instance Linux makes these available if SYSLOG_NAMES is defined.
+ */
+#define SYSLOG_NAMES
+#include <stdlib.h>
+#include <clplumbing/cl_syslog.h>
+
+#include <syslog.h>
+#include <string.h>
+
+struct _syslog_code {
+ const char *c_name;
+ int c_val;
+};
+
+#if defined(HAVE_SYSLOG_FACILITYNAMES)
+
+/*
+ * <cl_syslog.h> will have included a table called "facilitynames" structured
+ * as a "struct _syslog_code" but the tag "_syslog_code" may be something else.
+ */
+
+#else
+
+struct _syslog_code facilitynames[] =
+{
+#ifdef LOG_AUTH
+ { "auth", LOG_AUTH },
+ { "security", LOG_AUTH }, /* DEPRECATED */
+#endif
+#ifdef LOG_AUTHPRIV
+ { "authpriv", LOG_AUTHPRIV },
+#endif
+#ifdef LOG_CRON
+ { "cron", LOG_CRON },
+#endif
+#ifdef LOG_DAEMON
+ { "daemon", LOG_DAEMON },
+#endif
+#ifdef LOG_FTP
+ { "ftp", LOG_FTP },
+#endif
+#ifdef LOG_KERN
+ { "kern", LOG_KERN },
+#endif
+#ifdef LOG_LPR
+ { "lpr", LOG_LPR },
+#endif
+#ifdef LOG_MAIL
+ { "mail", LOG_MAIL },
+#endif
+
+/* { "mark", INTERNAL_MARK }, * INTERNAL */
+
+#ifdef LOG_NEWS
+ { "news", LOG_NEWS },
+#endif
+#ifdef LOG_SYSLOG
+ { "syslog", LOG_SYSLOG },
+#endif
+#ifdef LOG_USER
+ { "user", LOG_USER },
+#endif
+#ifdef LOG_UUCP
+ { "uucp", LOG_UUCP },
+#endif
+#ifdef LOG_LOCAL0
+ { "local0", LOG_LOCAL0 },
+#endif
+#ifdef LOG_LOCAL1
+ { "local1", LOG_LOCAL1 },
+#endif
+#ifdef LOG_LOCAL2
+ { "local2", LOG_LOCAL2 },
+#endif
+#ifdef LOG_LOCAL3
+ { "local3", LOG_LOCAL3 },
+#endif
+#ifdef LOG_LOCAL4
+ { "local4", LOG_LOCAL4 },
+#endif
+#ifdef LOG_LOCAL5
+ { "local5", LOG_LOCAL5 },
+#endif
+#ifdef LOG_LOCAL6
+ { "local6", LOG_LOCAL6 },
+#endif
+#ifdef LOG_LOCAL7
+ { "local7", LOG_LOCAL7 },
+#endif
+ { NULL, -1 }
+};
+
+#endif /* HAVE_SYSLOG_FACILITYNAMES */
+
+/* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
+int
+cl_syslogfac_str2int(const char *fname)
+{
+ int i;
+
+ if(fname == NULL || strcmp("none", fname) == 0) {
+ return 0;
+ }
+
+ for (i = 0; facilitynames[i].c_name != NULL; i++) {
+ if (strcmp(fname, facilitynames[i].c_name) == 0) {
+ return facilitynames[i].c_val;
+ }
+ }
+ return -1;
+}
+
+/* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
+const char *
+cl_syslogfac_int2str(int fnum)
+{
+ int i;
+
+ for (i = 0; facilitynames[i].c_name != NULL; i++) {
+ if (facilitynames[i].c_val == fnum) {
+ return facilitynames[i].c_name;
+ }
+ }
+ return NULL;
+}
diff --git a/lib/clplumbing/cl_uuid.c b/lib/clplumbing/cl_uuid.c
new file mode 100644
index 0000000..d0dfcb6
--- /dev/null
+++ b/lib/clplumbing/cl_uuid.c
@@ -0,0 +1,180 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+/*
+ * uuid: wrapper declarations.
+ *
+ * heartbeat originally used "uuid" functionality by calling directly,
+ * and only, onto the "e2fsprogs" implementation.
+ *
+ * The run-time usages in the code have since been abstracted, funnelled
+ * through a thin, common interface layer: a Good Thing.
+ *
+ * Similarly, the compile-time usages of "include <uuid/uuid.h>" are
+ * replaced, being funnelled through a reference to this header file.
+ *
+ * This header file interfaces onto the actual underlying implementation.
+ * In the case of the "e2fsprogs" implementation, it is simply a stepping
+ * stone onto "<uuid/uuid.h>". As other implementations are accommodated,
+ * so their header requirements can be accommodated here.
+ *
+ * Copyright (C) 2004 David Lee <t.d.lee@durham.ac.uk>
+ */
+
+#if defined (HAVE_UUID_UUID_H)
+/*
+ * Almost certainly the "e2fsprogs" implementation.
+ */
+# include <uuid/uuid.h>
+
+/* elif defined(HAVE...UUID_OTHER_1 e.g. OSSP ...) */
+
+/* elif defined(HAVE...UUID_OTHER_2...) */
+#else
+# include <replace_uuid.h>
+#endif
+
+#include <clplumbing/cl_uuid.h>
+#include <clplumbing/cl_log.h>
+#include <assert.h>
+
+void
+cl_uuid_copy(cl_uuid_t* dst, cl_uuid_t* src)
+{
+ if (dst == NULL || src == NULL){
+ cl_log(LOG_ERR, "cl_uuid_copy: "
+ "wrong argument %s is NULL",
+ dst == NULL?"dst":"src");
+ assert(0);
+ }
+
+ uuid_copy(dst->uuid, src->uuid);
+}
+
+void
+cl_uuid_clear(cl_uuid_t* uu)
+{
+ if (uu == NULL){
+ cl_log(LOG_ERR, "cl_uuid_clear: "
+ "wrong argument (uu is NULL)");
+ assert(0);
+ }
+
+ uuid_clear(uu->uuid);
+
+}
+
+int
+cl_uuid_compare(const cl_uuid_t* uu1, const cl_uuid_t* uu2)
+{
+ if (uu1 == NULL || uu2 == NULL){
+ cl_log(LOG_ERR, "cl_uuid_compare: "
+ " wrong argument (%s is NULL)",
+ uu1 == NULL?"uu1":"uu2");
+ assert(0);
+ }
+
+ return uuid_compare(uu1->uuid, uu2->uuid);
+
+}
+
+
+
+void
+cl_uuid_generate(cl_uuid_t* out)
+{
+ if (out == NULL){
+ cl_log(LOG_ERR, "cl_uuid_generate: "
+ " wrong argument (out is NULL)");
+ assert(0);
+ }
+
+ uuid_generate(out->uuid);
+
+}
+
+int
+cl_uuid_is_null(cl_uuid_t* uu)
+{
+ if (uu == NULL){
+ cl_log(LOG_ERR, "cl_uuid_is_null: "
+ "wrong argument (uu is NULL)");
+ assert(0);
+ }
+
+ return uuid_is_null(uu->uuid);
+
+}
+
+int
+cl_uuid_parse( char *in, cl_uuid_t* uu)
+{
+ if (in == NULL || uu == NULL){
+
+ cl_log(LOG_ERR, "cl_uuid_parse: "
+ "wrong argument (%s is NULL)",
+ in == NULL? "in":"uu");
+ assert(0);
+ }
+
+ return uuid_parse(in, uu->uuid);
+}
+
+
+void
+cl_uuid_unparse(const cl_uuid_t* uu, char *out){
+
+ if (uu == NULL || out == NULL){
+ cl_log(LOG_ERR, "cl_uuid_unparse: "
+ "wrong argument (%s is NULL)",
+ uu == NULL? "uu":"out");
+ assert(0);
+ }
+
+ uuid_unparse(uu->uuid, out);
+}
+
+
+guint
+cl_uuid_g_hash(gconstpointer uuid_ptr)
+{
+ guint ret = 0U;
+ guint32 value32;
+ int index;
+ const unsigned char * uuid_char = uuid_ptr;
+
+ /* It is probably not strictly necessary, but I'm trying to get the
+ * same hash result on all platforms. After all, the uuids are the
+ * same on every platform.
+ */
+
+ for (index = 0; index < sizeof(cl_uuid_t); index += sizeof(value32)) {
+ memcpy(&value32, uuid_char+index, sizeof (value32));
+ ret += g_ntohl(value32);
+ }
+ return ret;
+}
+gboolean
+cl_uuid_g_equal(gconstpointer uuid_ptr_a, gconstpointer uuid_ptr_b)
+{
+ return cl_uuid_compare(uuid_ptr_a, uuid_ptr_b) == 0;
+}
diff --git a/lib/clplumbing/coredumps.c b/lib/clplumbing/coredumps.c
new file mode 100644
index 0000000..79da737
--- /dev/null
+++ b/lib/clplumbing/coredumps.c
@@ -0,0 +1,309 @@
+/*
+ * Basic Core dump control functions.
+ *
+ * Author: Alan Robertson
+ *
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
+#endif
+#include <clplumbing/coredumps.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/cl_signal.h>
+
+static char * coreroot = NULL;
+
+/* Set the root directory of our core directory hierarchy */
+int
+cl_set_corerootdir(const char * dir)
+{
+ if (dir == NULL || *dir != '/') {
+ cl_perror("Invalid dir in cl_set_corerootdir() [%s]"
+ , dir ? dir : "<NULL>");
+ errno = EINVAL;
+ return -1;
+ }
+ if (coreroot != NULL) {
+ free(coreroot);
+ coreroot = NULL;
+ }
+ coreroot = strdup(dir);
+ if (coreroot == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Change directory to the directory our core file needs to go in
+ * Call after you establish the userid you're running under.
+ */
+int
+cl_cdtocoredir(void)
+{
+ const char * dir = coreroot;
+ int rc;
+ struct passwd* pwent;
+
+ if (dir == NULL) {
+ dir = HA_COREDIR;
+ }
+ if ((rc=chdir(dir)) < 0) {
+ int errsave = errno;
+ cl_perror("Cannot chdir to [%s]", dir);
+ errno = errsave;
+ return rc;
+ }
+ pwent = getpwuid(getuid());
+ if (pwent == NULL) {
+ int errsave = errno;
+ cl_perror("Cannot get name for uid [%d]", getuid());
+ errno = errsave;
+ return -1;
+ }
+ if ((rc=chdir(pwent->pw_name)) < 0) {
+ int errsave = errno;
+ cl_perror("Cannot chdir to [%s/%s]", dir, pwent->pw_name);
+ errno = errsave;
+ }
+ return rc;
+}
+
+#define CHECKED_KERNEL_CORE_ENV "_PROC_SYS_CORE_CHECKED_"
+#define PROC_SYS_KERNEL_CORE_PID "/proc/sys/kernel/core_uses_pid"
+#define PROC_SYS_KERNEL_CORE_PAT "/proc/sys/kernel/core_pattern"
+
+static void cl_coredump_signal_handler(int nsig);
+
+/*
+ * core_uses_pid():
+ *
+ * returns {-1, 0, 1}
+ * -1: not supported
+ * 0: supported and disabled
+ * 1: supported and enabled
+ */
+#define BUF_MAX 256
+static int
+core_uses_pid(void)
+{
+ const char * uses_pid_pathnames[] = {PROC_SYS_KERNEL_CORE_PID};
+ const char * corepats_pathnames[] = {PROC_SYS_KERNEL_CORE_PAT};
+ const char * goodpats [] = {"%t", "%p"};
+ int j;
+
+
+ for (j=0; j < DIMOF(corepats_pathnames); ++j) {
+ int fd;
+ char buf[BUF_MAX];
+ int rc;
+ int k;
+
+ if ((fd = open(corepats_pathnames[j], O_RDONLY)) < 0) {
+ continue;
+ }
+
+ memset(buf, 0, BUF_MAX);
+ rc = read(fd, buf, BUF_MAX - 1); /* Ensure it is always NULL terminated */
+ close(fd);
+
+ for (k=0; rc > 0 && k < DIMOF(goodpats); ++k) {
+ if (strstr(buf, goodpats[k]) != NULL) {
+ return 1;
+ }
+ }
+
+ break;
+ }
+ for (j=0; j < DIMOF(uses_pid_pathnames); ++j) {
+ int fd;
+ char buf[2];
+ int rc;
+ if ((fd = open(uses_pid_pathnames[j], O_RDONLY)) < 0) {
+ continue;
+ }
+ rc = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (rc < 1) {
+ continue;
+ }
+ return (buf[0] == '1');
+ }
+ setenv(CHECKED_KERNEL_CORE_ENV, "1", TRUE);
+ return -1;
+}
+
+/* Enable/disable core dumps for ourselves and our child processes */
+int
+cl_enable_coredumps(int doenable)
+{
+ int rc;
+ struct rlimit rlim;
+
+ if ((rc = getrlimit(RLIMIT_CORE, &rlim)) < 0) {
+ int errsave = errno;
+ cl_perror("Cannot get current core limit value.");
+ errno = errsave;
+ return rc;
+ }
+ if (rlim.rlim_max == 0 && geteuid() == 0) {
+ rlim.rlim_max = RLIM_INFINITY;
+ }
+
+ rlim.rlim_cur = (doenable ? rlim.rlim_max : 0);
+
+ if (doenable && rlim.rlim_max == 0) {
+ cl_log(LOG_WARNING
+ , "Not possible to enable core dumps (rlim_max is 0)");
+ }
+
+ if ((rc = setrlimit(RLIMIT_CORE, &rlim)) < 0) {
+ int errsave = errno;
+ cl_perror("Unable to %s core dumps"
+ , doenable ? "enable" : "disable");
+ errno = errsave;
+ return rc;
+ }
+ if (getenv(CHECKED_KERNEL_CORE_ENV) == NULL
+ && core_uses_pid() == 0) {
+ cl_log(LOG_WARNING
+ , "Core dumps could be lost if multiple dumps occur.");
+ cl_log(LOG_WARNING
+ , "Consider setting non-default value in %s"
+ " (or equivalent) for maximum supportability", PROC_SYS_KERNEL_CORE_PAT);
+ cl_log(LOG_WARNING
+ , "Consider setting %s (or equivalent) to"
+ " 1 for maximum supportability", PROC_SYS_KERNEL_CORE_PID);
+ }
+ return 0;
+}
+
+/*
+ * SIGQUIT 3 Core Quit from keyboard
+ * SIGILL 4 Core Illegal Instruction
+ * SIGABRT 6 Core Abort signal from abort(3)
+ * SIGFPE 8 Core Floating point exception
+ * SIGSEGV 11 Core Invalid memory reference
+ * SIGBUS 10,7,10 Core Bus error (bad memory access)
+ * SIGSYS 2,-,12 Core Bad argument to routine (SVID)
+ * SIGTRAP 5 Core Trace/breakpoint trap
+ * SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2 BSD)
+ * SIGXFSZ 25,25,31 Core File size limit exceeded (4.2 BSD)
+ */
+
+/*
+ * This function exists to allow security-sensitive programs
+ * to safely take core dumps. Such programs can't can't call
+ * cl_untaint_coredumps() alone - because it might cause a
+ * leak of confidential information - as information which should
+ * only be known by the "high-privilege" user id will be written
+ * into a core dump which is readable by the "low-privilege" user id.
+ * This is a bad thing.
+ *
+ * This function causes this program to call a special signal handler
+ * on receipt of any core dumping signal. This handler then does
+ * the following four things on receipt of a core dumping signal:
+ *
+ * 1) Set privileges to "maximum" on receipt of a signal
+ * 2) "untaint" themselves with regard to core dumping
+ * 3) set SIG_DFLT for the received signal
+ * 4) Kill themselves with the received core-dumping signal
+ *
+ * Any process *could* do this to get core dumps, but if your stack
+ * is screwed up, then the signal handler might not work.
+ * If you're core dumping because of a stack overflow, it certainly won't work.
+ *
+ * On the other hand, this function may work on some OSes that don't support
+ * prctl(2). This is an untested theory at this time...
+ */
+void
+cl_set_all_coredump_signal_handlers(void)
+{
+ static const int coresigs [] = {SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV
+#ifdef SIGBUS
+, SIGBUS
+#endif
+#ifdef SIGSYS
+, SIGSYS
+#endif
+#ifdef SIGTRAP
+, SIGTRAP
+#endif
+#ifdef SIGXCPU
+, SIGXCPU
+#endif
+#ifdef SIGXFSZ
+, SIGXFSZ
+#endif
+};
+ int j;
+
+ for (j=0; j < DIMOF(coresigs); ++j) {
+ cl_set_coredump_signal_handler(coresigs[j]);
+ }
+}
+
+/*
+ * See note above about why using this function directly is sometimes
+ * a bad idea, and you might need to use cl_set_all_coredump_signal_handlers()
+ * instead.
+ */
+void
+cl_untaint_coredumps(void)
+{
+#if defined(PR_SET_DUMPABLE)
+ prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL);
+#endif
+}
+static void
+cl_coredump_signal_handler(int nsig)
+{
+ return_to_orig_privs();
+ if (geteuid() == 0) {
+ /* Put ALL privileges back to root... */
+ if (setuid(0) < 0) {
+ cl_perror("cl_coredump_signal_handler: unable to setuid(0)");
+ }
+ }
+ cl_untaint_coredumps(); /* Do the best we know how to do... */
+ CL_SIGNAL(nsig, SIG_DFL);
+ kill(getpid(), nsig);
+}
+
+void
+cl_set_coredump_signal_handler(int nsig)
+{
+ CL_SIGNAL(nsig, cl_coredump_signal_handler);
+}
diff --git a/lib/clplumbing/cpulimits.c b/lib/clplumbing/cpulimits.c
new file mode 100644
index 0000000..4c03f23
--- /dev/null
+++ b/lib/clplumbing/cpulimits.c
@@ -0,0 +1,219 @@
+/*
+ * Functions to put dynamic limits on CPU consumption.
+ *
+ * Copyright (C) 2003 IBM Corporation
+ *
+ * Author: <alanr@unix.sh>
+ *
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ **************************************************************************
+ *
+ * This allows us to better catch runaway realtime processes that
+ * might otherwise hang the whole system (if they're POSIX realtime
+ * processes).
+ *
+ * We do this by getting a "lease" on CPU time, and then extending
+ * the lease every so often as real time elapses. Since we only
+ * extend the lease by a bounded amount computed on the basis of an
+ * upper bound of how much CPU the code is "expected" to consume during
+ * the lease interval, this means that if we go into an infinite
+ * loop, it is highly probable that this will be detected and our
+ * process will be terminated by the operating system with a SIGXCPU.
+ *
+ * If you want to handle this signal, then fine... Do so...
+ *
+ * If not, the default is to terminate the process and produce a core
+ * dump. This is a great default for debugging...
+ *
+ *
+ * The process is basically this:
+ * - Set the CPU percentage limit with cl_cpu_limit_setpercent()
+ * according to what you expect the CPU percentage to top out at
+ * measured over an interval at >= 60 seconds
+ * - Call cl_cpu_limit_ms_interval() to figure out how often to update
+ * the CPU limit (it returns milliseconds)
+ * - At least as often as indicated above, call cl_cpu_limit_update()
+ * to update our current CPU limit.
+ *
+ * These limits are approximate, so be a little conservative.
+ * If you've gone into an infinite loop, it'll likely get caught ;-)
+ *
+ * As of this writing, this code will never set the soft CPU limit less
+ * than four seconds, or greater than 60 seconds.
+ *
+ */
+#include <lha_internal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <clplumbing/longclock.h>
+#include <unistd.h>
+#include <clplumbing/cpulimits.h>
+#include <clplumbing/cl_log.h>
+
+static longclock_t nexttimetoupdate;
+
+/* How long between checking out CPU usage? */
+static int cpuinterval_ms = 0;
+
+/* How much cpu (in seconds) allowed at each check interval? */
+static int cpusecs;
+
+#define ROUND(foo) ((int)((foo)+0.5))
+
+
+/*
+ * Update our current CPU limit (via setrlimit) according to our
+ * current resource consumption, and our current cpu % limit
+ *
+ * We only set the soft CPU limit, and do not change the maximum
+ * (hard) CPU limit, but we respect it if it's already set.
+ *
+ * As a result, this code can be used by privileged and non-privileged
+ * processes.
+ */
+
+static int
+update_cpu_interval(void)
+{
+ struct rusage ru;
+ struct rlimit rlim;
+ unsigned long timesecs;
+ unsigned long microsec;
+
+ /* Compute how much CPU we've used so far... */
+
+ getrusage(RUSAGE_SELF, &ru);
+ timesecs = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+ microsec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+
+ /* Round up to the next higher second */
+ if (microsec > 1000000) {
+ timesecs += 2;
+ }else{
+ timesecs += 1;
+ }
+
+ /* Compute our next CPU limit */
+ timesecs += cpusecs;
+
+ /* Figure out when we next need to update our CPU limit */
+ nexttimetoupdate = add_longclock(time_longclock()
+ , msto_longclock(cpuinterval_ms));
+
+ getrlimit(RLIMIT_CPU, &rlim);
+
+ /* Make sure we don't exceed the hard CPU limit (if set) */
+ if (rlim.rlim_max != RLIM_INFINITY && timesecs > rlim.rlim_max) {
+ timesecs = rlim.rlim_max;
+ }
+#if 0
+ cl_log(LOG_DEBUG
+ , "Setting max CPU limit to %ld seconds", timesecs);
+#endif
+
+ /* Update the OS-level soft CPU limit */
+ rlim.rlim_cur = timesecs;
+ return setrlimit(RLIMIT_CPU, &rlim);
+}
+
+#define MININTERVAL 60 /* seconds */
+
+int
+cl_cpu_limit_setpercent(int ipercent)
+{
+ float percent;
+ int interval;
+
+ if (ipercent > 99) {
+ ipercent = 99;
+ }
+ if (ipercent < 1) {
+ ipercent = 1;
+ }
+ percent = ipercent;
+ percent /= (float)100;
+
+ interval= MININTERVAL;
+
+ /*
+ * Compute how much CPU we will allow to be used
+ * for each check interval.
+ *
+ * Rules:
+ * - we won't require checking more often than
+ * every 60 seconds
+ * - we won't limit ourselves to less than
+ * 4 seconds of CPU per checking interval
+ */
+ for (;;) {
+ cpusecs = ROUND((float)interval*percent);
+ if (cpusecs >= 4) {
+ break;
+ }
+ interval *= 2;
+ }
+
+ /*
+ * Now compute how long to go between updates to our CPU limit
+ * from the perspective of the OS (via setrlimit(2)).
+ *
+ * We do the computation this way because the CPU limit
+ * can only be set to the nearest second, but timers can
+ * generally be set more accurately.
+ */
+ cpuinterval_ms = (int)(((float)cpusecs / percent)*1000.0);
+
+ cl_log(LOG_DEBUG
+ , "Limiting CPU: %d CPU seconds every %d milliseconds"
+ , cpusecs, cpuinterval_ms);
+
+ return update_cpu_interval();
+}
+
+int
+cl_cpu_limit_ms_interval(void)
+{
+ return cpuinterval_ms;
+}
+
+int
+cl_cpu_limit_update(void)
+{
+ longclock_t now = time_longclock();
+ long msleft;
+
+ if (cpuinterval_ms <= 0) {
+ return 0;
+ }
+ if (cmp_longclock(now, nexttimetoupdate) > 0) {
+ return update_cpu_interval();
+ }
+ msleft = longclockto_ms(sub_longclock(nexttimetoupdate, now));
+ if (msleft < 500) {
+ return update_cpu_interval();
+ }
+ return 0;
+}
+int
+cl_cpu_limit_disable(void)
+{
+ struct rlimit rlim;
+ getrlimit(RLIMIT_CPU, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ return setrlimit(RLIMIT_CPU, &rlim);
+}
diff --git a/lib/clplumbing/ipcsocket.c b/lib/clplumbing/ipcsocket.c
new file mode 100644
index 0000000..14c3504
--- /dev/null
+++ b/lib/clplumbing/ipcsocket.c
@@ -0,0 +1,2767 @@
+/*
+ * ipcsocket unix domain socket implementation of IPC abstraction.
+ *
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>
+ *
+ * Stream support (c) 2004,2006 David Lee <t.d.lee@durham.ac.uk>
+ * Note: many of the variable/function names "*socket*" should be
+ * interpreted as having a more generic "ipc-channel-type" meaning.
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <clplumbing/ipc.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/cl_poll.h>
+
+#include <ha_msg.h>
+/* avoid including cib.h - used in gshi's "late message" code to avoid
+ * printing insanely large messages
+ */
+#define F_CIB_CALLDATA "cib_calldata"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_SYSLIMITS_H
+# include <sys/syslimits.h>
+#endif
+#ifdef HAVE_SYS_CRED_H
+# include <sys/cred.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+# include <sys/ucred.h>
+#endif
+
+/* For 'getpeerucred()' (Solaris 10 upwards) */
+#ifdef HAVE_UCRED_H
+# include <ucred.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+/*
+ * Normally use "socket" code. But on some OSes alternatives may be
+ * preferred (or necessary).
+ */
+#define HB_IPC_SOCKET 1
+#define HB_IPC_STREAM 2
+/* #define HB_IPC_ANOTHER 3 */
+
+#ifndef HB_IPC_METHOD
+# if defined(SO_PEERCRED) || defined(HAVE_GETPEEREID) \
+ || defined(SCM_CREDS) || defined(HAVE_GETPEERUCRED)
+# define HB_IPC_METHOD HB_IPC_SOCKET
+# elif defined(HAVE_STROPTS_H)
+# define HB_IPC_METHOD HB_IPC_STREAM
+# else
+# error. Surely we have sockets or streams...
+# endif
+#endif
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+# include <sys/poll.h>
+# include <netinet/in.h>
+# include <sys/un.h>
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+# include <stropts.h>
+#else
+# error "IPC type invalid"
+#endif
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108
+#endif
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+
+# define MAX_LISTEN_NUM 128
+
+# ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+# endif
+
+# ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+# endif
+
+#endif /* HB_IPC_METHOD */
+
+/***********************************************************************
+ *
+ * Determine the IPC authentication scheme... More machine dependent than
+ * we'd like, but don't know any better way...
+ *
+ ***********************************************************************/
+#ifdef SO_PEERCRED
+# define USE_SO_PEERCRED
+#elif HAVE_GETPEEREID
+# define USE_GETPEEREID
+#elif defined(SCM_CREDS)
+# define USE_SCM_CREDS
+#elif HAVE_GETPEERUCRED /* e.g. Solaris 10 upwards */
+# define USE_GETPEERUCRED
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+# define USE_STREAM_CREDS
+#else
+# define USE_DUMMY_CREDS
+/* This will make it compile, but attempts to authenticate
+ * will fail. This is a stopgap measure ;-)
+ */
+#endif
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+
+# ifdef USE_BINDSTAT_CREDS
+# ifndef SUN_LEN
+# define SUN_LEN(ptr) ((size_t) (offsetof (sockaddr_un, sun_path) + strlen ((ptr)->sun_path))
+# endif
+# endif
+
+#endif /* HB_IPC_METHOD */
+
+/* wait connection private data. */
+struct SOCKET_WAIT_CONN_PRIVATE{
+ /* the path name wich the connection will be built on. */
+ char path_name[UNIX_PATH_MAX];
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* the domain socket. */
+ int s;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ /* the streams pipe */
+ int pipefds[2];
+#endif
+};
+
+/* channel private data. */
+struct SOCKET_CH_PRIVATE{
+ /* the path name wich the connection will be built on. */
+ char path_name[UNIX_PATH_MAX];
+ /* the domain socket. */
+ int s;
+ /* the size of expecting data for below buffered message buf_msg */
+ int remaining_data;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* The address of our peer - used by USE_BINDSTAT_CREDS version of
+ * socket_verify_auth()
+ */
+ struct sockaddr_un *peer_addr;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ uid_t farside_uid;
+ gid_t farside_gid;
+#endif
+
+ /* the buf used to save unfinished message */
+ struct IPC_MESSAGE *buf_msg;
+};
+
+struct IPC_Stats {
+ long nsent;
+ long noutqueued;
+ long send_count;
+ long nreceived;
+ long ninqueued;
+ long recv_count;
+ int last_recv_errno;
+ int last_recv_rc;
+ int last_send_errno;
+ int last_send_rc;
+};
+
+static struct IPC_Stats SocketIPCStats = {0, 0, 0, 0};
+extern int debug_level;
+
+/* unix domain socket implementations of IPC functions. */
+
+static int socket_resume_io(struct IPC_CHANNEL *ch);
+
+static struct IPC_MESSAGE* socket_message_new(struct IPC_CHANNEL*ch
+, int msg_len);
+
+struct IPC_WAIT_CONNECTION *socket_wait_conn_new(GHashTable* ch_attrs);
+
+/* *** FIXME: This is also declared in 'ocf_ipc.c'. */
+struct IPC_CHANNEL* socket_client_channel_new(GHashTable *attrs);
+
+static struct IPC_CHANNEL* socket_server_channel_new(int sockfd);
+
+static struct IPC_CHANNEL * channel_new(int sockfd, int conntype, const char *pathname);
+static int client_channel_new_auth(int sockfd);
+static int verify_creds(struct IPC_AUTH *auth_info, uid_t uid, gid_t gid);
+
+typedef void (*DelProc)(IPC_Message*);
+
+static struct IPC_MESSAGE * ipcmsg_new(struct IPC_CHANNEL* ch,
+ const void* data, int len, void* private, DelProc d);
+
+static pid_t socket_get_farside_pid(int sockfd);
+
+extern int (*ipc_pollfunc_ptr)(struct pollfd *, nfds_t, int);
+
+static int socket_resume_io_read(struct IPC_CHANNEL *ch, int*, gboolean read1anyway);
+
+static struct IPC_OPS socket_ops;
+static gboolean ipc_time_debug_flag = TRUE;
+
+void
+set_ipc_time_debug_flag(gboolean flag)
+{
+ ipc_time_debug_flag = flag;
+}
+
+#ifdef IPC_TIME_DEBUG
+
+extern struct ha_msg* wirefmt2msg(const char* s, size_t length, int flag);
+void cl_log_message (int log_level, const struct ha_msg *m);
+int timediff(longclock_t t1, longclock_t t2);
+void ha_msg_del(struct ha_msg* msg);
+void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos);
+
+#define SET_ENQUEUE_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->enqueue_time, &t, sizeof(longclock_t))
+#define SET_SEND_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->send_time, &t, sizeof(longclock_t))
+#define SET_RECV_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->recv_time, &t, sizeof(longclock_t))
+#define SET_DEQUEUE_TIME(x,t) memcpy(&((struct SOCKET_MSG_HEAD*)x->msg_buf)->dequeue_time, &t, sizeof(longclock_t))
+
+static
+longclock_t
+get_enqueue_time(IPC_Message *ipcmsg)
+{
+ longclock_t t;
+
+ memcpy(&t,
+ &(((struct SOCKET_MSG_HEAD *)ipcmsg->msg_buf)->enqueue_time),
+ sizeof(longclock_t));
+
+ return t;
+}
+
+int
+timediff(longclock_t t1, longclock_t t2)
+{
+ longclock_t remain;
+
+ remain = sub_longclock(t1, t2);
+
+ return longclockto_ms(remain);
+}
+
+void
+ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos)
+{
+ int msdiff = 0;
+ longclock_t lnow = time_longclock();
+ char positions[4][16]={
+ "enqueue",
+ "send",
+ "recv",
+ "dequeue"};
+
+ if (ipc_time_debug_flag == FALSE) {
+ return ;
+ }
+
+ if (ipcmsg->msg_body == NULL
+ || ipcmsg->msg_buf == NULL) {
+ cl_log(LOG_ERR, "msg_body =%p, msg_bu=%p",
+ ipcmsg->msg_body, ipcmsg->msg_buf);
+ abort();
+ return;
+ }
+
+ switch(whichpos) {
+ case MSGPOS_ENQUEUE:
+ SET_ENQUEUE_TIME(ipcmsg, lnow);
+ break;
+ case MSGPOS_SEND:
+ SET_SEND_TIME(ipcmsg, lnow);
+ goto checktime;
+ case MSGPOS_RECV:
+ SET_RECV_TIME(ipcmsg, lnow);
+ goto checktime;
+ case MSGPOS_DEQUEUE:
+ SET_DEQUEUE_TIME(ipcmsg, lnow);
+
+ checktime:
+ msdiff = timediff(lnow, get_enqueue_time(ipcmsg));
+ if (msdiff > MAXIPCTIME) {
+ struct ha_msg* hamsg = NULL;
+ cl_log(LOG_WARNING,
+ " message delayed from enqueue to %s %d ms "
+ "(enqueue-time=%lu, peer pid=%d) ",
+ positions[whichpos],
+ msdiff,
+ longclockto_ms(get_enqueue_time(ipcmsg)),
+ ch->farside_pid);
+
+ (void)hamsg;
+#if 0
+ hamsg = wirefmt2msg(ipcmsg->msg_body, ipcmsg->msg_len, 0);
+ if (hamsg != NULL) {
+ struct ha_msg *crm_data = NULL;
+ crm_data = cl_get_struct(
+ hamsg, F_CRM_DATA);
+
+ if(crm_data == NULL) {
+ crm_data = cl_get_struct(
+ hamsg, F_CIB_CALLDATA);
+ }
+ if(crm_data != NULL) {
+ cl_msg_remove_value(
+ hamsg, crm_data);
+ }
+
+ cl_log_message(LOG_DEBUG, hamsg);
+ ha_msg_del(hamsg);
+ } else {
+ if (!ipcmsg) {
+ cl_log(LOG_ERR,
+ "IPC msg 0x%lx is unallocated"
+ , (gulong)ipcmsg);
+ return;
+ }
+ if (!ipcmsg->msg_body) {
+ cl_log(LOG_ERR,
+ "IPC msg body 0x%lx is unallocated"
+ , (gulong)ipcmsg->msg_body);
+ return;
+ }
+ }
+#endif
+
+ }
+ break;
+ default:
+ cl_log(LOG_ERR, "wrong position value in IPC:%d", whichpos);
+ return;
+ }
+}
+#endif
+
+void dump_ipc_info(const IPC_Channel* chan);
+
+#undef AUDIT_CHANNELS
+
+#ifndef AUDIT_CHANNELS
+# define CHANAUDIT(ch) /*NOTHING */
+#else
+# define CHANAUDIT(ch) socket_chan_audit(ch)
+# define MAXPID 65535
+
+static void
+socket_chan_audit(const struct IPC_CHANNEL* ch)
+{
+ int badch = FALSE;
+
+ struct SOCKET_CH_PRIVATE *chp;
+ struct stat b;
+
+ if ((chp = ch->ch_private) == NULL) {
+ cl_log(LOG_CRIT, "Bad ch_private");
+ badch = TRUE;
+ }
+ if (ch->ops != &socket_ops) {
+ cl_log(LOG_CRIT, "Bad socket_ops");
+ badch = TRUE;
+ }
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return;
+ }
+ if (!IPC_ISRCONN(ch)) {
+ cl_log(LOG_CRIT, "Bad ch_status [%d]", ch->ch_status);
+ badch = TRUE;
+ }
+ if (ch->farside_pid < 0 || ch->farside_pid > MAXPID) {
+ cl_log(LOG_CRIT, "Bad farside_pid");
+ badch = TRUE;
+ }
+ if (fstat(chp->s, &b) < 0) {
+ badch = TRUE;
+ } else if ((b.st_mode & S_IFMT) != S_IFSOCK) {
+ cl_log(LOG_CRIT, "channel @ 0x%lx: not a socket"
+ , (unsigned long)ch);
+ badch = TRUE;
+ }
+ if (chp->remaining_data < 0) {
+ cl_log(LOG_CRIT, "Negative remaining_data");
+ badch = TRUE;
+ }
+ if (chp->remaining_data < 0 || chp->remaining_data > MAXMSG) {
+ cl_log(LOG_CRIT, "Excessive/bad remaining_data");
+ badch = TRUE;
+ }
+ if (chp->remaining_data && chp->buf_msg == NULL) {
+ cl_log(LOG_CRIT
+ , "inconsistent remaining_data [%ld]/buf_msg[0x%lx]"
+ , (long)chp->remaining_data, (unsigned long)chp->buf_msg);
+ badch = TRUE;
+ }
+ if (chp->remaining_data == 0 && chp->buf_msg != NULL) {
+ cl_log(LOG_CRIT
+ , "inconsistent remaining_data [%ld]/buf_msg[0x%lx] (2)"
+ , (long)chp->remaining_data, (unsigned long)chp->buf_msg);
+ badch = TRUE;
+ }
+ if (ch->send_queue == NULL || ch->recv_queue == NULL) {
+ cl_log(LOG_CRIT, "bad send/recv queue");
+ badch = TRUE;
+ }
+ if (ch->recv_queue->current_qlen < 0
+ || ch->recv_queue->current_qlen > ch->recv_queue->max_qlen) {
+ cl_log(LOG_CRIT, "bad recv queue");
+ badch = TRUE;
+ }
+ if (ch->send_queue->current_qlen < 0
+ || ch->send_queue->current_qlen > ch->send_queue->max_qlen) {
+ cl_log(LOG_CRIT, "bad send_queue");
+ badch = TRUE;
+ }
+ if (badch) {
+ cl_log(LOG_CRIT, "Bad channel @ 0x%lx", (unsigned long)ch);
+ dump_ipc_info(ch);
+ abort();
+ }
+}
+#endif
+
+#ifdef CHEAT_CHECKS
+long SeqNums[32];
+
+static long
+cheat_get_sequence(IPC_Message* msg)
+{
+ const char header [] = "String-";
+ size_t header_len = sizeof(header)-1;
+ char * body;
+
+ if (msg == NULL || msg->msg_len < sizeof(header)
+ || msg->msg_len > sizeof(header) + 10
+ || strncmp(msg->msg_body, header, header_len) != 0) {
+ return -1L;
+ }
+ body = msg->msg_body;
+ return atol(body+header_len);
+}
+static char SavedReadBody[32];
+static char SavedReceivedBody[32];
+static char SavedQueuedBody[32];
+static char SavedSentBody[32];
+#ifndef MIN
+# define MIN(a,b) (a < b ? a : b)
+#endif
+
+static void
+save_body(struct IPC_MESSAGE *msg, char * savearea, size_t length)
+{
+ int mlen = strnlen(msg->msg_body, MIN(length, msg->msg_len));
+ memcpy(savearea, msg->msg_body, mlen);
+ savearea[mlen] = EOS;
+}
+
+static void
+audit_readmsgq_msg(gpointer msg, gpointer user_data)
+{
+ long cheatseq = cheat_get_sequence(msg);
+
+ if (cheatseq < SeqNums[1] || cheatseq > SeqNums[2]) {
+ cl_log(LOG_ERR
+ , "Read Q Message %ld not in range [%ld:%ld]"
+ , cheatseq, SeqNums[1], SeqNums[2]);
+ }
+}
+
+static void
+saveandcheck(struct IPC_CHANNEL * ch, struct IPC_MESSAGE* msg, char * savearea
+, size_t savesize, long* lastseq, const char * text)
+{
+ long cheatseq = cheat_get_sequence(msg);
+
+ save_body(msg, savearea, savesize);
+ if (*lastseq != 0 ) {
+ if (cheatseq != *lastseq +1) {
+ int j;
+ cl_log(LOG_ERR
+ , "%s packets out of sequence! %ld versus %ld [pid %d]"
+ , text, cheatseq, *lastseq, (int)getpid());
+ dump_ipc_info(ch);
+ for (j=0; j < 4; ++j) {
+ cl_log(LOG_DEBUG
+ , "SeqNums[%d] = %ld"
+ , j, SeqNums[j]);
+ }
+ cl_log(LOG_ERR
+ , "SocketIPCStats.nsent = %ld"
+ , SocketIPCStats.nsent);
+ cl_log(LOG_ERR
+ , "SocketIPCStats.noutqueued = %ld"
+ , SocketIPCStats.noutqueued);
+ cl_log(LOG_ERR
+ , "SocketIPCStats.nreceived = %ld"
+ , SocketIPCStats.nreceived);
+ cl_log(LOG_ERR
+ , "SocketIPCStats.ninqueued = %ld"
+ , SocketIPCStats.ninqueued);
+ }
+
+ }
+ g_list_foreach(ch->recv_queue->queue, audit_readmsgq_msg, NULL);
+ if (cheatseq > 0) {
+ *lastseq = cheatseq;
+ }
+}
+
+# define CHECKFOO(which, ch, msg, area, text) { \
+ saveandcheck(ch,msg,area,sizeof(area),SeqNums+which,text); \
+ }
+#else
+# define CHECKFOO(which, ch, msg, area, text) /* Nothing */
+#endif
+
+static void
+dump_msg(struct IPC_MESSAGE *msg, const char * label)
+{
+#ifdef CHEAT_CHECKS
+ cl_log(LOG_DEBUG, "%s packet (length %d) [%s] %ld pid %d"
+ , label, (int)msg->msg_len, (char*)msg->msg_body
+ , cheat_get_sequence(msg), (int)getpid());
+#else
+ cl_log(LOG_DEBUG, "%s length %d [%s] pid %d"
+ , label, (int)msg->msg_len, (char*)msg->msg_body
+ , (int)getpid());
+#endif
+}
+
+static void
+dump_msgq_msg(gpointer data, gpointer user_data)
+{
+ dump_msg(data, user_data);
+}
+
+void
+dump_ipc_info(const IPC_Channel* chan)
+{
+ char squeue[] = "Send queue";
+ char rqueue[] = "Receive queue";
+#ifdef CHEAT_CHECKS
+ cl_log(LOG_DEBUG, "Saved Last Body read[%s]", SavedReadBody);
+ cl_log(LOG_DEBUG, "Saved Last Body received[%s]", SavedReceivedBody);
+ cl_log(LOG_DEBUG, "Saved Last Body Queued[%s]", SavedQueuedBody);
+ cl_log(LOG_DEBUG, "Saved Last Body Sent[%s]", SavedSentBody);
+#endif
+ g_list_foreach(chan->send_queue->queue, dump_msgq_msg, squeue);
+ g_list_foreach(chan->recv_queue->queue, dump_msgq_msg, rqueue);
+ CHANAUDIT(chan);
+}
+
+/* destroy socket wait channel */
+static void
+socket_destroy_wait_conn(struct IPC_WAIT_CONNECTION * wait_conn)
+{
+ struct SOCKET_WAIT_CONN_PRIVATE * wc = wait_conn->ch_private;
+
+ if (wc != NULL) {
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ if (wc->s >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing socket %d"
+ , __FUNCTION__, wc->s);
+ }
+ close(wc->s);
+ cl_poll_ignore(wc->s);
+ unlink(wc->path_name);
+ wc->s = -1;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ cl_poll_ignore(wc->pipefds[0]);
+ if (wc->pipefds[0] >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing pipe[0] %d"
+ , __FUNCTION__, wc->pipefds[0]);
+ }
+ wc->pipefds[0] = -1;
+ }
+ if (wc->pipefds[1] >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing pipe[1] %d"
+ , __FUNCTION__, wc->pipefds[1]);
+ }
+ wc->pipefds[0] = -1;
+ }
+ unlink(wc->path_name);
+#endif
+ g_free(wc);
+ }
+ g_free((void*) wait_conn);
+}
+
+/* return a fd which can be listened on for new connections. */
+static int
+socket_wait_selectfd(struct IPC_WAIT_CONNECTION *wait_conn)
+{
+ struct SOCKET_WAIT_CONN_PRIVATE * wc = wait_conn->ch_private;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ return (wc == NULL ? -1 : wc->s);
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ return (wc == NULL ? -1 : wc->pipefds[0]);
+#endif
+}
+
+/* socket accept connection. */
+static struct IPC_CHANNEL*
+socket_accept_connection(struct IPC_WAIT_CONNECTION * wait_conn
+, struct IPC_AUTH *auth_info)
+{
+ struct IPC_CHANNEL * ch = NULL;
+ int s;
+ int new_sock;
+ struct SOCKET_WAIT_CONN_PRIVATE* conn_private;
+ struct SOCKET_CH_PRIVATE * ch_private ;
+ int auth_result = IPC_FAIL;
+ int saveerrno=errno;
+ gboolean was_error = FALSE;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* make peer_addr a pointer so it can be used by the
+ * USE_BINDSTAT_CREDS implementation of socket_verify_auth()
+ */
+ struct sockaddr_un * peer_addr = NULL;
+ socklen_t sin_size;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ struct strrecvfd strrecvfd;
+#endif
+
+ /* get select fd */
+
+ s = wait_conn->ops->get_select_fd(wait_conn);
+ if (s < 0) {
+ cl_log(LOG_ERR, "get_select_fd: invalid fd");
+ return NULL;
+ }
+
+ /* Get client connection. */
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ peer_addr = g_new(struct sockaddr_un, 1);
+ *peer_addr->sun_path = '\0';
+ sin_size = sizeof(struct sockaddr_un);
+ new_sock = accept(s, (struct sockaddr *)peer_addr, &sin_size);
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ if (ioctl(s, I_RECVFD, &strrecvfd) == -1) {
+ new_sock = -1;
+ }
+ else {
+ new_sock = strrecvfd.fd;
+ }
+#endif
+ saveerrno=errno;
+ if (new_sock == -1) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ cl_perror("socket_accept_connection: accept(sock=%d)"
+ , s);
+ }
+ was_error = TRUE;
+
+ } else {
+ if ((ch = socket_server_channel_new(new_sock)) == NULL) {
+ cl_log(LOG_ERR
+ , "socket_accept_connection:"
+ " Can't create new channel");
+ was_error = TRUE;
+ } else {
+ conn_private=(struct SOCKET_WAIT_CONN_PRIVATE*)
+ ( wait_conn->ch_private);
+ ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private);
+ strncpy(ch_private->path_name,conn_private->path_name
+ , sizeof(conn_private->path_name));
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ ch_private->peer_addr = peer_addr;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ ch_private->farside_uid = strrecvfd.uid;
+ ch_private->farside_gid = strrecvfd.gid;
+#endif
+ }
+ }
+
+ /* Verify the client authorization information. */
+ if(was_error == FALSE) {
+ auth_result = ch->ops->verify_auth(ch, auth_info);
+ if (auth_result == IPC_OK) {
+ ch->ch_status = IPC_CONNECT;
+ ch->farside_pid = socket_get_farside_pid(new_sock);
+ return ch;
+ }
+ saveerrno=errno;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ g_free(peer_addr);
+ peer_addr = NULL;
+#endif
+ errno=saveerrno;
+ return NULL;
+}
+
+/*
+ * Called by socket_destroy(). Disconnect the connection
+ * and set ch_status to IPC_DISCONNECT.
+ *
+ * parameters :
+ * ch (IN) the pointer to the channel.
+ *
+ * return values :
+ * IPC_OK the connection is disconnected successfully.
+ * IPC_FAIL operation fails.
+*/
+
+static int
+socket_disconnect(struct IPC_CHANNEL* ch)
+{
+ struct SOCKET_CH_PRIVATE* conn_info;
+
+ conn_info = (struct SOCKET_CH_PRIVATE*) ch->ch_private;
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s(sock=%d, ch=0x%lx){"
+ , __FUNCTION__
+ , conn_info->s, (unsigned long)ch);
+ }
+#if 0
+ if (ch->ch_status != IPC_DISCONNECT) {
+ cl_log(LOG_INFO, "forced disconnect for fd %d", conn_info->s);
+ }
+#endif
+ if (ch->ch_status == IPC_CONNECT) {
+ socket_resume_io(ch);
+ }
+
+ if (conn_info->s >= 0) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG
+ , "%s: closing socket %d"
+ , __FUNCTION__, conn_info->s);
+ }
+ close(conn_info->s);
+ cl_poll_ignore(conn_info->s);
+ conn_info->s = -1;
+ }
+ ch->ch_status = IPC_DISCONNECT;
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "}/*%s(sock=%d, ch=0x%lx)*/"
+ , __FUNCTION__, conn_info->s, (unsigned long)ch);
+ }
+ return IPC_OK;
+}
+
+/*
+ * destroy a ipc queue and clean all memory space assigned to this queue.
+ * parameters:
+ * q (IN) the pointer to the queue which should be destroied.
+ *
+ * FIXME: This function does not free up messages that might
+ * be in the queue.
+ */
+
+static void
+socket_destroy_queue(struct IPC_QUEUE * q)
+{
+ g_list_free(q->queue);
+
+ g_free((void *) q);
+}
+
+static void
+socket_destroy_channel(struct IPC_CHANNEL * ch)
+{
+ --ch->refcount;
+ if (ch->refcount > 0) {
+ return;
+ }
+ if (ch->ch_status == IPC_CONNECT) {
+ socket_resume_io(ch);
+ }
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "socket_destroy(ch=0x%lx){"
+ , (unsigned long)ch);
+ }
+ socket_disconnect(ch);
+ socket_destroy_queue(ch->send_queue);
+ socket_destroy_queue(ch->recv_queue);
+
+ if (ch->pool) {
+ ipc_bufpool_unref(ch->pool);
+ }
+
+ if (ch->ch_private != NULL) {
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ struct SOCKET_CH_PRIVATE *priv = (struct SOCKET_CH_PRIVATE *)
+ ch->ch_private;
+ if(priv->peer_addr != NULL) {
+ if (*priv->peer_addr->sun_path) {
+ unlink(priv->peer_addr->sun_path);
+ }
+ g_free((void*)(priv->peer_addr));
+ }
+#endif
+ g_free((void*)(ch->ch_private));
+ }
+ memset(ch, 0xff, sizeof(*ch));
+ g_free((void*)ch);
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "}/*socket_destroy(ch=0x%lx)*/"
+ , (unsigned long)ch);
+ }
+}
+
+static int
+socket_check_disc_pending(struct IPC_CHANNEL* ch)
+{
+ int rc;
+ struct pollfd sockpoll;
+
+ if (ch->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR, "check_disc_pending() already disconnected");
+ return IPC_BROKEN;
+ }
+ if (ch->recv_queue->current_qlen > 0) {
+ return IPC_OK;
+ }
+ sockpoll.fd = ch->ops->get_recv_select_fd(ch);
+ sockpoll.events = POLLIN;
+
+ rc = ipc_pollfunc_ptr(&sockpoll, 1, 0);
+
+ if (rc < 0) {
+ cl_log(LOG_INFO
+ , "socket_check_disc_pending() bad poll call");
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_BROKEN;
+ }
+
+ if (sockpoll.revents & POLLHUP) {
+ if (sockpoll.revents & POLLIN) {
+ ch->ch_status = IPC_DISC_PENDING;
+ } else {
+#if 1
+ cl_log(LOG_INFO, "HUP without input");
+#endif
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_BROKEN;
+ }
+ }
+ if (sockpoll.revents & POLLIN) {
+ int dummy;
+ socket_resume_io_read(ch, &dummy, FALSE);
+ }
+ return IPC_OK;
+}
+
+static int
+socket_initiate_connection(struct IPC_CHANNEL * ch)
+{
+ struct SOCKET_CH_PRIVATE* conn_info;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ struct sockaddr_un peer_addr; /* connector's address information */
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+#endif
+
+ conn_info = (struct SOCKET_CH_PRIVATE*) ch->ch_private;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* Prepare the socket */
+ memset(&peer_addr, 0, sizeof(peer_addr));
+ peer_addr.sun_family = AF_LOCAL; /* host byte order */
+
+ if (strlen(conn_info->path_name) >= sizeof(peer_addr.sun_path)) {
+ return IPC_FAIL;
+ }
+ strncpy(peer_addr.sun_path, conn_info->path_name, sizeof(peer_addr.sun_path));
+
+ /* Send connection request */
+ if (connect(conn_info->s, (struct sockaddr *)&peer_addr
+ , sizeof(struct sockaddr_un)) == -1) {
+ return IPC_FAIL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+
+#endif
+
+ ch->ch_status = IPC_CONNECT;
+ ch->farside_pid = socket_get_farside_pid(conn_info->s);
+ return IPC_OK;
+}
+
+static void
+socket_set_high_flow_callback(IPC_Channel* ch,
+ flow_callback_t callback,
+ void* userdata) {
+ ch->high_flow_callback = callback;
+ ch->high_flow_userdata = userdata;
+}
+
+static void
+socket_set_low_flow_callback(IPC_Channel* ch,
+ flow_callback_t callback,
+ void* userdata) {
+ ch->low_flow_callback = callback;
+ ch->low_flow_userdata = userdata;
+}
+
+static void
+socket_check_flow_control(struct IPC_CHANNEL* ch,
+ int orig_qlen,
+ int curr_qlen)
+{
+ if (!IPC_ISRCONN(ch)) {
+ return;
+ }
+
+ if (curr_qlen >= ch->high_flow_mark
+ && ch->high_flow_callback) {
+ ch->high_flow_callback(ch, ch->high_flow_userdata);
+ }
+
+ if (curr_qlen <= ch->low_flow_mark
+ && orig_qlen > ch->low_flow_mark
+ && ch->low_flow_callback) {
+ ch->low_flow_callback(ch, ch->low_flow_userdata);
+ }
+}
+
+static int
+socket_send(struct IPC_CHANNEL * ch, struct IPC_MESSAGE* msg)
+{
+ int orig_qlen;
+ int diff;
+ struct IPC_MESSAGE* newmsg;
+
+ if (msg->msg_len > MAXMSG) {
+ cl_log(LOG_ERR, "%s: sorry, cannot send messages "
+ "bigger than %d (requested %lu)",
+ __FUNCTION__, MAXMSG, (unsigned long)msg->msg_len);
+ return IPC_FAIL;
+ }
+ if (msg->msg_len < 0) {
+ cl_log(LOG_ERR, "socket_send: "
+ "invalid message");
+ return IPC_FAIL;
+ }
+
+ if (ch->ch_status != IPC_CONNECT) {
+ return IPC_FAIL;
+ }
+
+ ch->ops->resume_io(ch);
+
+ if (ch->send_queue->maxqlen_cnt &&
+ time(NULL) - ch->send_queue->last_maxqlen_warn >= 60) {
+ cl_log(LOG_ERR, "%u messages dropped on a non-blocking channel (send queue maximum length %d)",
+ ch->send_queue->maxqlen_cnt, (int)ch->send_queue->max_qlen);
+ ch->send_queue->maxqlen_cnt = 0;
+ }
+ if ( !ch->should_send_block &&
+ ch->send_queue->current_qlen >= ch->send_queue->max_qlen) {
+ if (!ch->send_queue->maxqlen_cnt) {
+ ch->send_queue->last_maxqlen_warn = time(NULL);
+ }
+ ch->send_queue->maxqlen_cnt++;
+
+ if (ch->should_block_fail) {
+ return IPC_FAIL;
+ } else {
+ return IPC_OK;
+ }
+ }
+
+ while (ch->send_queue->current_qlen >= ch->send_queue->max_qlen) {
+ if (ch->ch_status != IPC_CONNECT) {
+ cl_log(LOG_WARNING, "socket_send:"
+ " message queue exceeded and IPC not connected");
+ return IPC_FAIL;
+ }
+ cl_shortsleep();
+ ch->ops->resume_io(ch);
+ }
+
+ /* add the message into the send queue */
+ CHECKFOO(0,ch, msg, SavedQueuedBody, "queued message");
+ SocketIPCStats.noutqueued++;
+
+ diff = 0;
+ if (msg->msg_buf ) {
+ diff = (char*)msg->msg_body - (char*)msg->msg_buf;
+ }
+ if ( diff < (int)sizeof(struct SOCKET_MSG_HEAD) ) {
+ /* either we don't have msg->msg_buf set
+ * or we don't have enough bytes for socket head
+ * we delete this message and creates
+ * a new one and delete the old one
+ */
+
+ newmsg= socket_message_new(ch, msg->msg_len);
+ if (newmsg == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_write: "
+ "allocating memory for new ipc msg failed");
+ return IPC_FAIL;
+ }
+
+ memcpy(newmsg->msg_body, msg->msg_body, msg->msg_len);
+
+ if(msg->msg_done) {
+ msg->msg_done(msg);
+ };
+ msg = newmsg;
+ }
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch,msg, MSGPOS_ENQUEUE);
+#endif
+ ch->send_queue->queue = g_list_append(ch->send_queue->queue,
+ msg);
+ orig_qlen = ch->send_queue->current_qlen++;
+
+ socket_check_flow_control(ch, orig_qlen, orig_qlen +1 );
+
+ /* resume io */
+ ch->ops->resume_io(ch);
+ return IPC_OK;
+}
+
+static int
+socket_recv(struct IPC_CHANNEL * ch, struct IPC_MESSAGE** message)
+{
+ GList *element;
+
+ int nbytes;
+ int result;
+
+ socket_resume_io(ch);
+ result = socket_resume_io_read(ch, &nbytes, TRUE);
+
+ *message = NULL;
+
+ if (ch->recv_queue->current_qlen == 0) {
+ return result != IPC_OK ? result : IPC_FAIL;
+ /*return IPC_OK;*/
+ }
+ element = g_list_first(ch->recv_queue->queue);
+
+ if (element == NULL) {
+ /* Internal accounting error, but correctable */
+ cl_log(LOG_ERR
+ , "recv failure: qlen (%ld) > 0, but no message found."
+ , (long)ch->recv_queue->current_qlen);
+ ch->recv_queue->current_qlen = 0;
+ return IPC_FAIL;
+ }
+ *message = (struct IPC_MESSAGE *) (element->data);
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch, *message, MSGPOS_DEQUEUE);
+#endif
+
+ CHECKFOO(1,ch, *message, SavedReadBody, "read message");
+ SocketIPCStats.nreceived++;
+ ch->recv_queue->queue = g_list_remove(ch->recv_queue->queue
+ , element->data);
+ ch->recv_queue->current_qlen--;
+ return IPC_OK;
+}
+
+static int
+socket_check_poll(struct IPC_CHANNEL * ch
+, struct pollfd * sockpoll)
+{
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return IPC_OK;
+ }
+ if (sockpoll->revents & POLLHUP) {
+ /* If input present, or this is an output-only poll... */
+ if (sockpoll->revents & POLLIN
+ || (sockpoll-> events & POLLIN) == 0 ) {
+ ch->ch_status = IPC_DISC_PENDING;
+ return IPC_OK;
+ }
+#if 1
+ cl_log(LOG_INFO, "socket_check_poll(): HUP without input");
+#endif
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_BROKEN;
+
+ } else if (sockpoll->revents & (POLLNVAL|POLLERR)) {
+ /* Have we already closed the socket? */
+ if (fcntl(sockpoll->fd, F_GETFL) < 0) {
+ cl_perror("socket_check_poll(pid %d): bad fd [%d]"
+ , (int) getpid(), sockpoll->fd);
+ ch->ch_status = IPC_DISCONNECT;
+ return IPC_OK;
+ }
+ cl_log(LOG_ERR
+ , "revents failure: fd %d, flags 0x%x"
+ , sockpoll->fd, sockpoll->revents);
+ errno = EINVAL;
+ return IPC_FAIL;
+ }
+ return IPC_OK;
+}
+
+static int
+socket_waitfor(struct IPC_CHANNEL * ch
+, gboolean (*finished)(struct IPC_CHANNEL * ch))
+{
+ struct pollfd sockpoll;
+
+ CHANAUDIT(ch);
+ if (finished(ch)) {
+ return IPC_OK;
+ }
+
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return IPC_BROKEN;
+ }
+ sockpoll.fd = ch->ops->get_recv_select_fd(ch);
+
+ while (!finished(ch) && IPC_ISRCONN(ch)) {
+ int rc;
+
+ sockpoll.events = POLLIN;
+
+ /* Cannot call resume_io after the call to finished()
+ * and before the call to poll because we might
+ * change the state of the thing finished() is
+ * waiting for.
+ * This means that the poll call below would be
+ * not only pointless, but might
+ * make us hang forever waiting for this
+ * event which has already happened
+ */
+ if (ch->send_queue->current_qlen > 0) {
+ sockpoll.events |= POLLOUT;
+ }
+
+ rc = ipc_pollfunc_ptr(&sockpoll, 1, -1);
+
+ if (rc < 0) {
+ return (errno == EINTR ? IPC_INTR : IPC_FAIL);
+ }
+
+ rc = socket_check_poll(ch, &sockpoll);
+ if (sockpoll.revents & POLLIN) {
+ socket_resume_io(ch);
+ }
+ if (rc != IPC_OK) {
+ CHANAUDIT(ch);
+ return rc;
+ }
+ }
+
+ CHANAUDIT(ch);
+ return IPC_OK;
+}
+
+static int
+socket_waitin(struct IPC_CHANNEL * ch)
+{
+ return socket_waitfor(ch, ch->ops->is_message_pending);
+}
+static gboolean
+socket_is_output_flushed(struct IPC_CHANNEL * ch)
+{
+ return ! ch->ops->is_sending_blocked(ch);
+}
+
+static int
+socket_waitout(struct IPC_CHANNEL * ch)
+{
+ int rc;
+ CHANAUDIT(ch);
+ rc = socket_waitfor(ch, socket_is_output_flushed);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "socket_waitout failure: rc = %d", rc);
+ } else if (ch->ops->is_sending_blocked(ch)) {
+ cl_log(LOG_ERR, "socket_waitout output still blocked");
+ }
+ CHANAUDIT(ch);
+ return rc;
+}
+
+static gboolean
+socket_is_message_pending(struct IPC_CHANNEL * ch)
+{
+ int nbytes;
+
+ socket_resume_io_read(ch, &nbytes, TRUE);
+ ch->ops->resume_io(ch);
+ if (ch->recv_queue->current_qlen > 0) {
+ return TRUE;
+ }
+
+ return !IPC_ISRCONN(ch);
+}
+
+static gboolean
+socket_is_output_pending(struct IPC_CHANNEL * ch)
+{
+ socket_resume_io(ch);
+ return ch->ch_status == IPC_CONNECT
+ && ch->send_queue->current_qlen > 0;
+}
+
+static gboolean
+socket_is_sendq_full(struct IPC_CHANNEL * ch)
+{
+ ch->ops->resume_io(ch);
+ return(ch->send_queue->current_qlen == ch->send_queue->max_qlen);
+}
+
+static gboolean
+socket_is_recvq_full(struct IPC_CHANNEL * ch)
+{
+ ch->ops->resume_io(ch);
+ return(ch->recv_queue->current_qlen == ch->recv_queue->max_qlen);
+}
+
+static int
+socket_get_conntype(struct IPC_CHANNEL* ch)
+{
+ return ch->conntype;
+}
+
+static int
+socket_assert_auth(struct IPC_CHANNEL *ch, GHashTable *auth)
+{
+ cl_log(LOG_ERR
+ , "the assert_auth function for domain socket is not implemented");
+ return IPC_FAIL;
+}
+
+static int
+socket_resume_io_read(struct IPC_CHANNEL *ch, int* nbytes, gboolean read1anyway)
+{
+ struct SOCKET_CH_PRIVATE* conn_info;
+ int retcode = IPC_OK;
+ struct pollfd sockpoll;
+ int debug_loopcount = 0;
+ int debug_bytecount = 0;
+ size_t maxqlen = ch->recv_queue->max_qlen;
+ struct ipc_bufpool* pool = ch->pool;
+ int nmsgs = 0;
+ int spaceneeded;
+ *nbytes = 0;
+
+ CHANAUDIT(ch);
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ if (ch->ch_status == IPC_DISCONNECT) {
+ return IPC_BROKEN;
+ }
+
+ if (pool == NULL) {
+ ch->pool = pool = ipc_bufpool_new(0);
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_read: "
+ "memory allocation for ipc pool failed");
+ return IPC_FAIL;
+ }
+ }
+
+ if (ipc_bufpool_full(pool, ch, &spaceneeded)) {
+ struct ipc_bufpool* newpool;
+
+ newpool = ipc_bufpool_new(spaceneeded);
+ if (newpool == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_read: "
+ "memory allocation for a new ipc pool failed");
+ return IPC_FAIL;
+ }
+
+ ipc_bufpool_partial_copy(newpool, pool);
+ ipc_bufpool_unref(pool);
+ ch->pool = pool = newpool;
+ }
+ if (maxqlen <= 0 && read1anyway) {
+ maxqlen = 1;
+ }
+ if (ch->recv_queue->current_qlen < maxqlen && retcode == IPC_OK) {
+ void * msg_begin;
+ int msg_len;
+ int len;
+#if HB_IPC_METHOD == HB_IPC_STREAM
+ struct strbuf d;
+ int flags, rc;
+#endif
+
+ CHANAUDIT(ch);
+ ++debug_loopcount;
+
+ len = ipc_bufpool_spaceleft(pool);
+ msg_begin = pool->currpos;
+
+ CHANAUDIT(ch);
+
+ /* Now try to receive some data */
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ msg_len = recv(conn_info->s, msg_begin, len, MSG_DONTWAIT);
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ d.maxlen = len;
+ d.len = 0;
+ d.buf = msg_begin;
+ flags = 0;
+ rc = getmsg(conn_info->s, NULL, &d, &flags);
+ msg_len = (rc < 0) ? rc : d.len;
+#endif
+ SocketIPCStats.last_recv_rc = msg_len;
+ SocketIPCStats.last_recv_errno = errno;
+ ++SocketIPCStats.recv_count;
+
+ /* Did we get an error? */
+ if (msg_len < 0) {
+ switch (errno) {
+ case EAGAIN:
+ if (ch->ch_status==IPC_DISC_PENDING) {
+ ch->ch_status =IPC_DISCONNECT;
+ retcode = IPC_BROKEN;
+ }
+ break;
+
+ case ECONNREFUSED:
+ case ECONNRESET:
+ ch->ch_status = IPC_DISC_PENDING;
+ retcode= socket_check_disc_pending(ch);
+ break;
+
+ default:
+ cl_perror("socket_resume_io_read"
+ ": unknown recv error, peerpid=%d",
+ ch->farside_pid);
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ break;
+ }
+
+ } else if (msg_len == 0) {
+ ch->ch_status = IPC_DISC_PENDING;
+ if(ch->recv_queue->current_qlen <= 0) {
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ }
+ } else {
+ /* We read something! */
+ /* Note that all previous cases break out of the loop */
+ debug_bytecount += msg_len;
+ *nbytes = msg_len;
+ nmsgs = ipc_bufpool_update(pool, ch, msg_len, ch->recv_queue) ;
+
+ if (nmsgs < 0) {
+ /* we didn't like the other side */
+ cl_log(LOG_ERR, "socket_resume_io_read: "
+ "disconnecting the other side");
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ } else {
+ SocketIPCStats.ninqueued += nmsgs;
+ }
+ }
+ }
+
+ /* Check for errors uncaught by recv() */
+ /* NOTE: It doesn't seem right we have to do this every time */
+ /* FIXME?? */
+
+ memset(&sockpoll,0, sizeof(struct pollfd));
+ if ((retcode == IPC_OK)
+ && (sockpoll.fd = conn_info->s) >= 0) {
+ /* Just check for errors, not for data */
+ sockpoll.events = 0;
+ ipc_pollfunc_ptr(&sockpoll, 1, 0);
+ retcode = socket_check_poll(ch, &sockpoll);
+ }
+
+ CHANAUDIT(ch);
+ if (retcode != IPC_OK) {
+ return retcode;
+ }
+
+ return IPC_ISRCONN(ch) ? IPC_OK : IPC_BROKEN;
+}
+
+static int
+socket_resume_io_write(struct IPC_CHANNEL *ch, int* nmsg)
+{
+ int retcode = IPC_OK;
+ struct SOCKET_CH_PRIVATE* conn_info;
+
+ CHANAUDIT(ch);
+ *nmsg = 0;
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ while (ch->ch_status == IPC_CONNECT
+ && retcode == IPC_OK
+ && ch->send_queue->current_qlen > 0) {
+
+ GList * element;
+ struct IPC_MESSAGE * msg;
+ struct SOCKET_MSG_HEAD head;
+ struct IPC_MESSAGE* oldmsg = NULL;
+ int sendrc = 0;
+ struct IPC_MESSAGE* newmsg;
+ char* p;
+ unsigned int bytes_remaining;
+ int diff;
+
+ CHANAUDIT(ch);
+ element = g_list_first(ch->send_queue->queue);
+ if (element == NULL) {
+ /* OOPS! - correct consistency problem */
+ ch->send_queue->current_qlen = 0;
+ break;
+ }
+ msg = (struct IPC_MESSAGE *) (element->data);
+
+ diff = 0;
+ if (msg->msg_buf ) {
+ diff = (char*)msg->msg_body - (char*)msg->msg_buf;
+ }
+ if ( diff < (int)sizeof(struct SOCKET_MSG_HEAD) ) {
+ /* either we don't have msg->msg_buf set
+ * or we don't have enough bytes for socket head
+ * we delete this message and creates
+ * a new one and delete the old one
+ */
+
+ newmsg= socket_message_new(ch, msg->msg_len);
+ if (newmsg == NULL) {
+ cl_log(LOG_ERR, "socket_resume_io_write: "
+ "allocating memory for new ipc msg failed");
+ return IPC_FAIL;
+ }
+
+ memcpy(newmsg->msg_body, msg->msg_body, msg->msg_len);
+ oldmsg = msg;
+ msg = newmsg;
+ }
+
+ head.msg_len = msg->msg_len;
+ head.magic = HEADMAGIC;
+ memcpy(msg->msg_buf, &head, sizeof(struct SOCKET_MSG_HEAD));
+
+ if (ch->bytes_remaining == 0) {
+ /*we start to send a new message*/
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch, msg, MSGPOS_SEND);
+#endif
+ bytes_remaining = msg->msg_len + ch->msgpad;
+ p = msg->msg_buf;
+ } else {
+ bytes_remaining = ch->bytes_remaining;
+ p = ((char*)msg->msg_buf) + msg->msg_len + ch->msgpad
+ - bytes_remaining;
+
+ }
+
+ sendrc = 0;
+
+ do {
+#if HB_IPC_METHOD == HB_IPC_STREAM
+ struct strbuf d;
+ int msglen, putmsgrc;
+#endif
+
+ CHANAUDIT(ch);
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ sendrc = send(conn_info->s, p
+ , bytes_remaining, (MSG_DONTWAIT|MSG_NOSIGNAL));
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ d.maxlen = 0;
+ d.len = msglen = bytes_remaining;
+ d.buf = p;
+ putmsgrc = putmsg(conn_info->s, NULL, &d, 0);
+ sendrc = putmsgrc == 0 ? msglen : -1;
+#endif
+ SocketIPCStats.last_send_rc = sendrc;
+ SocketIPCStats.last_send_errno = errno;
+ ++SocketIPCStats.send_count;
+
+ if (sendrc <= 0) {
+ break;
+ } else {
+ p = p + sendrc;
+ bytes_remaining -= sendrc;
+ }
+
+ } while(bytes_remaining > 0 );
+
+ ch->bytes_remaining = bytes_remaining;
+
+ if (sendrc < 0) {
+ switch (errno) {
+ case EAGAIN:
+ retcode = IPC_OK;
+ break;
+ case EPIPE:
+ ch->ch_status = IPC_DISC_PENDING;
+ socket_check_disc_pending(ch);
+ retcode = IPC_BROKEN;
+ break;
+ default:
+ cl_perror("socket_resume_io_write"
+ ": send2 bad errno");
+ ch->ch_status = IPC_DISCONNECT;
+ retcode = IPC_FAIL;
+ break;
+ }
+ break;
+ } else {
+ int orig_qlen;
+
+ CHECKFOO(3,ch, msg, SavedSentBody, "sent message")
+
+ if (oldmsg) {
+ if (msg->msg_done != NULL) {
+ msg->msg_done(msg);
+ }
+ msg=oldmsg;
+ }
+
+ if(ch->bytes_remaining ==0) {
+ ch->send_queue->queue = g_list_remove(ch->send_queue->queue, msg);
+ if (msg->msg_done != NULL) {
+ msg->msg_done(msg);
+ }
+
+ SocketIPCStats.nsent++;
+ orig_qlen = ch->send_queue->current_qlen--;
+ socket_check_flow_control(ch, orig_qlen, orig_qlen -1 );
+ (*nmsg)++;
+ }
+ }
+ }
+ CHANAUDIT(ch);
+ if (retcode != IPC_OK) {
+ return retcode;
+ }
+ return IPC_ISRCONN(ch) ? IPC_OK : IPC_BROKEN;
+}
+
+static int
+socket_resume_io(struct IPC_CHANNEL *ch)
+{
+ int rc1 = IPC_OK;
+ int rc2 = IPC_OK;
+ int nwmsg = 1;
+ int nbytes_r = 1;
+ gboolean OKonce = FALSE;
+
+ CHANAUDIT(ch);
+ if (!IPC_ISRCONN(ch)) {
+ return IPC_BROKEN;
+ }
+
+ do {
+ if (nbytes_r > 0) {
+ rc1 = socket_resume_io_read(ch, &nbytes_r, FALSE);
+ }
+ if (nwmsg > 0) {
+ nwmsg = 0;
+ rc2 = socket_resume_io_write(ch, &nwmsg);
+ }
+ if (rc1 == IPC_OK || rc2 == IPC_OK) {
+ OKonce = TRUE;
+ }
+ } while ((nbytes_r > 0 || nwmsg > 0) && IPC_ISRCONN(ch));
+
+ if (IPC_ISRCONN(ch)) {
+ if (rc1 != IPC_OK) {
+ cl_log(LOG_ERR
+ , "socket_resume_io_read() failure");
+ }
+ if (rc2 != IPC_OK && IPC_CONNECT == ch->ch_status) {
+ cl_log(LOG_ERR
+ , "socket_resume_io_write() failure");
+ }
+ } else {
+ return (OKonce ? IPC_OK : IPC_BROKEN);
+ }
+
+ return (rc1 != IPC_OK ? rc1 : rc2);
+}
+
+static int
+socket_get_recv_fd(struct IPC_CHANNEL *ch)
+{
+ struct SOCKET_CH_PRIVATE* chp = ch ->ch_private;
+
+ return (chp == NULL ? -1 : chp->s);
+}
+
+static int
+socket_get_send_fd(struct IPC_CHANNEL *ch)
+{
+ return socket_get_recv_fd(ch);
+}
+
+static void
+socket_adjust_buf(struct IPC_CHANNEL *ch, int optname, unsigned q_len)
+{
+ const char *direction = optname == SO_SNDBUF ? "snd" : "rcv";
+ int fd = socket_get_send_fd(ch);
+ unsigned byte;
+
+ /* Arbitrary scaling.
+ * DEFAULT_MAX_QLEN is 64, default socket buf is often 64k to 128k,
+ * at least on those linux I checked.
+ * Keep that ratio, and allow for some overhead. */
+ if (q_len == 0)
+ /* client does not want anything,
+ * reduce system buffers as well */
+ byte = 4096;
+ else if (q_len < 512)
+ byte = (32 + q_len) * 1024;
+ else
+ byte = q_len * 1024;
+
+ if (0 == setsockopt(fd, SOL_SOCKET, optname, &byte, sizeof(byte))) {
+ if (debug_level > 1) {
+ cl_log(LOG_DEBUG, "adjusted %sbuf size to %u",
+ direction, byte);
+ }
+ } else {
+ /* If this fails, you may need to adjust net.core.rmem_max,
+ * ...wmem_max, or equivalent */
+ cl_log(LOG_WARNING, "adjust %sbuf size to %u failed: %s",
+ direction, byte, strerror(errno));
+ }
+}
+
+static int
+socket_set_send_qlen (struct IPC_CHANNEL* ch, int q_len)
+{
+ /* This seems more like an assertion failure than a normal error */
+ if (ch->send_queue == NULL) {
+ return IPC_FAIL;
+ }
+ socket_adjust_buf(ch, SO_SNDBUF, q_len);
+ ch->send_queue->max_qlen = q_len;
+ return IPC_OK;
+}
+
+static int
+socket_set_recv_qlen (struct IPC_CHANNEL* ch, int q_len)
+{
+ /* This seems more like an assertion failure than a normal error */
+ if (ch->recv_queue == NULL) {
+ return IPC_FAIL;
+ }
+ socket_adjust_buf(ch, SO_RCVBUF, q_len);
+ ch->recv_queue->max_qlen = q_len;
+ return IPC_OK;
+}
+
+static int ipcmsg_count_allocated = 0;
+static int ipcmsg_count_freed = 0;
+void socket_ipcmsg_dump_stats(void);
+void
+socket_ipcmsg_dump_stats(void) {
+ cl_log(LOG_INFO, "ipcsocket ipcmsg allocated=%d, freed=%d, diff=%d",
+ ipcmsg_count_allocated,
+ ipcmsg_count_freed,
+ ipcmsg_count_allocated - ipcmsg_count_freed);
+}
+
+static void
+socket_del_ipcmsg(IPC_Message* m)
+{
+ if (m == NULL) {
+ cl_log(LOG_ERR, "socket_del_ipcmsg:"
+ "msg is NULL");
+ return;
+ }
+
+ if (m->msg_body) {
+ memset(m->msg_body, 0, m->msg_len);
+ }
+ if (m->msg_buf) {
+ g_free(m->msg_buf);
+ }
+
+ memset(m, 0, sizeof(*m));
+ g_free(m);
+
+ ipcmsg_count_freed ++;
+}
+
+static IPC_Message*
+socket_new_ipcmsg(IPC_Channel* ch, const void* data, int len, void* private)
+{
+ IPC_Message* hdr;
+
+ if (ch == NULL || len < 0) {
+ cl_log(LOG_ERR, "socket_new_ipcmsg:"
+ " invalid parameter");
+ return NULL;
+ }
+
+ if (ch->msgpad > MAX_MSGPAD) {
+ cl_log(LOG_ERR, "socket_new_ipcmsg: too many pads "
+ "something is wrong");
+ return NULL;
+ }
+
+ hdr = ipcmsg_new(ch, data, len, private, socket_del_ipcmsg);
+
+ if (hdr) ipcmsg_count_allocated ++;
+
+ return hdr;
+}
+
+static
+struct IPC_MESSAGE *
+ipcmsg_new(struct IPC_CHANNEL * ch, const void* data, int len, void* private,
+ DelProc delproc)
+{
+ struct IPC_MESSAGE * hdr;
+ char* copy = NULL;
+ char* buf;
+ char* body;
+
+ if ((hdr = g_new(struct IPC_MESSAGE, 1)) == NULL) {
+ return NULL;
+ }
+ memset(hdr, 0, sizeof(*hdr));
+
+ if (len > 0) {
+ if ((copy = (char*)g_malloc(ch->msgpad + len)) == NULL) {
+ g_free(hdr);
+ return NULL;
+ }
+ if (data) {
+ memcpy(copy + ch->msgpad, data, len);
+ }
+ buf = copy;
+ body = copy + ch->msgpad;;
+ } else {
+ len = 0;
+ buf = body = NULL;
+ }
+ hdr->msg_len = len;
+ hdr->msg_buf = buf;
+ hdr->msg_body = body;
+ hdr->msg_ch = ch;
+ hdr->msg_done = delproc;
+ hdr->msg_private = private;
+
+ return hdr;
+}
+
+static int
+socket_get_chan_status(IPC_Channel* ch)
+{
+ socket_resume_io(ch);
+ return ch->ch_status;
+}
+
+/* socket object of the function table */
+static struct IPC_WAIT_OPS socket_wait_ops = {
+ socket_destroy_wait_conn,
+ socket_wait_selectfd,
+ socket_accept_connection,
+};
+
+/*
+ * create a new ipc queue whose length = 0 and inner queue = NULL.
+ * return the pointer to a new ipc queue or NULL is the queue can't be created.
+ */
+
+static struct IPC_QUEUE*
+socket_queue_new(void)
+{
+ struct IPC_QUEUE *temp_queue;
+
+ /* temp queue with length = 0 and inner queue = NULL. */
+ temp_queue = g_new(struct IPC_QUEUE, 1);
+ temp_queue->current_qlen = 0;
+ temp_queue->max_qlen = DEFAULT_MAX_QLEN;
+ temp_queue->queue = NULL;
+ temp_queue->last_maxqlen_warn = 0;
+ temp_queue->maxqlen_cnt = 0;
+ return temp_queue;
+}
+
+/*
+ * socket_wait_conn_new:
+ * Called by ipc_wait_conn_constructor to get a new socket
+ * waiting connection.
+ * (better explanation of this role might be nice)
+ *
+ * Parameters :
+ * ch_attrs (IN) the attributes used to create this connection.
+ *
+ * Return :
+ * the pointer to the new waiting connection or NULL if the connection
+ * can't be created.
+ *
+ * NOTE :
+ * for domain socket implementation, the only attribute needed is path name.
+ * so the user should
+ * create the hash table like this:
+ * GHashTable * attrs;
+ * attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ * g_hash_table_insert(attrs, PATH_ATTR, path_name);
+ * here PATH_ATTR is defined as "path".
+ *
+ * NOTE :
+ * The streams implementation uses "Streams Programming Guide", Solaris 8,
+ * as its guide (sample code near end of "Configuration" chapter 11).
+ */
+struct IPC_WAIT_CONNECTION *
+socket_wait_conn_new(GHashTable *ch_attrs)
+{
+ struct IPC_WAIT_CONNECTION * temp_ch;
+ char *path_name;
+ char *mode_attr;
+ int s, flags;
+ struct SOCKET_WAIT_CONN_PRIVATE *wait_private;
+ mode_t s_mode;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ struct sockaddr_un my_addr;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ int pipefds[2];
+#endif
+
+ path_name = (char *) g_hash_table_lookup(ch_attrs, IPC_PATH_ATTR);
+ mode_attr = (char *) g_hash_table_lookup(ch_attrs, IPC_MODE_ATTR);
+
+ if (mode_attr != NULL) {
+ s_mode = (mode_t)strtoul((const char *)mode_attr, NULL, 8);
+ } else {
+ s_mode = 0777;
+ }
+ if (path_name == NULL) {
+ return NULL;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* prepare the unix domain socket */
+ if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ cl_perror("socket_wait_conn_new: socket() failure");
+ return NULL;
+ }
+
+ if (unlink(path_name) < 0 && errno != ENOENT) {
+ cl_perror("socket_wait_conn_new: unlink failure(%s)",
+ path_name);
+ }
+ memset(&my_addr, 0, sizeof(my_addr));
+ my_addr.sun_family = AF_LOCAL; /* host byte order */
+
+ if (strlen(path_name) >= sizeof(my_addr.sun_path)) {
+ close(s);
+ return NULL;
+ }
+
+ strncpy(my_addr.sun_path, path_name, sizeof(my_addr.sun_path));
+
+ if (bind(s, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1) {
+ cl_perror("socket_wait_conn_new: trying to create in %s bind:"
+ , path_name);
+ close(s);
+ return NULL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ /* Set up the communication channel the clients will use to us (server) */
+ if (pipe(pipefds) == -1) {
+ cl_perror("pipe() failure");
+ return NULL;
+ }
+
+ /* Let clients have unique connections to us */
+ if (ioctl(pipefds[1], I_PUSH, "connld") == -1) {
+ cl_perror("ioctl(%d, I_PUSH, \"connld\") failure", pipefds[1]);
+ return NULL;
+ }
+
+ if (unlink(path_name) < 0 && errno != ENOENT) {
+ cl_perror("socket_wait_conn_new: unlink failure(%s)",
+ path_name);
+ }
+
+ if (mkfifo(path_name, s_mode) == -1) {
+ cl_perror("socket_wait_conn_new: mkfifo(%s, ...) failure", path_name);
+ return NULL;
+ }
+
+ if (fattach(pipefds[1], path_name) == -1) {
+ cl_perror("socket_wait_conn_new: fattach(..., %s) failure", path_name);
+ return NULL;
+ }
+
+ /* the pseudo-socket is the other part of the pipe */
+ s = pipefds[0];
+#endif
+
+ /* Change the permission of the socket */
+ if (chmod(path_name,s_mode) < 0) {
+ cl_perror("socket_wait_conn_new: failure trying to chmod %s"
+ , path_name);
+ close(s);
+ return NULL;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* listen to the socket */
+ if (listen(s, MAX_LISTEN_NUM) == -1) {
+ cl_perror("socket_wait_conn_new: listen(MAX_LISTEN_NUM)");
+ close(s);
+ return NULL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+
+#endif
+
+ flags = fcntl(s, F_GETFL);
+ if (flags == -1) {
+ cl_perror("socket_wait_conn_new: cannot read file descriptor flags");
+ close(s);
+ return NULL;
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl(s, F_SETFL, flags) < 0) {
+ cl_perror("socket_wait_conn_new: cannot set O_NONBLOCK");
+ close(s);
+ return NULL;
+ }
+
+ wait_private = g_new(struct SOCKET_WAIT_CONN_PRIVATE, 1);
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ wait_private->s = s;
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ wait_private->pipefds[0] = pipefds[0];
+ wait_private->pipefds[1] = pipefds[1];
+#endif
+ strncpy(wait_private->path_name, path_name, sizeof(wait_private->path_name));
+ temp_ch = g_new(struct IPC_WAIT_CONNECTION, 1);
+ temp_ch->ch_private = (void *) wait_private;
+ temp_ch->ch_status = IPC_WAIT;
+ temp_ch->ops = (struct IPC_WAIT_OPS *)&socket_wait_ops;
+
+ return temp_ch;
+}
+
+/*
+ * will be called by ipc_channel_constructor to create a new socket channel.
+ * parameters :
+ * attrs (IN) the hash table of the attributes used to create this channel.
+ *
+ * return:
+ * the pointer to the new waiting channel or NULL if the channel can't be created.
+*/
+
+struct IPC_CHANNEL *
+socket_client_channel_new(GHashTable *ch_attrs) {
+ char *path_name;
+ int sockfd;
+
+ /*
+ * I don't really understand why the client and the server use different
+ * parameter names...
+ *
+ * It's a really bad idea to store both integers and strings
+ * in the same table.
+ *
+ * Maybe we need an internal function with a different set of parameters?
+ */
+
+ /*
+ * if we want to seperate them. I suggest
+ * <client side>
+ * user call ipc_channel_constructor(ch_type,attrs) to create a new channel.
+ * ipc_channel_constructor() call socket_channel_new(GHashTable*)to
+ * create a new socket channel.
+ * <server side>
+ * wait_conn->accept_connection() will call another function to create a
+ * new channel. This function will take socketfd as the parameter to
+ * create a socket channel.
+ */
+
+ path_name = (char *) g_hash_table_lookup(ch_attrs, IPC_PATH_ATTR);
+ if (path_name == NULL) {
+ return NULL;
+ }
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ /* prepare the socket */
+ if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ cl_perror("socket_client_channel_new: socket");
+ return NULL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ sockfd = open(path_name, O_RDWR|O_NONBLOCK);
+ if (sockfd == -1) {
+ cl_perror("socket_client_channel_new: open(%s, ...) failure", path_name);
+ return NULL;
+ }
+#endif
+
+ if (client_channel_new_auth(sockfd) < 0) {
+ close(sockfd);
+ return NULL;
+ }
+ return channel_new(sockfd, IPC_CLIENT, path_name);
+}
+
+static
+int client_channel_new_auth(int sockfd) {
+#ifdef USE_BINDSTAT_CREDS
+ char rand_id[16];
+ char uuid_str_tmp[40];
+ struct sockaddr_un sock_addr;
+
+ /* Prepare the socket */
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sun_family = AF_UNIX;
+
+ /* make sure socket paths never clash */
+ uuid_generate(rand_id);
+ uuid_unparse(rand_id, uuid_str_tmp);
+
+ snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path),
+ "%s/%s", HA_VARLIBHBDIR, uuid_str_tmp);
+
+ unlink(sock_addr.sun_path);
+ if(bind(sockfd, (struct sockaddr*)&sock_addr, SUN_LEN(&sock_addr)) < 0) {
+ perror("Client bind() failure");
+ return 0;
+ }
+#endif
+
+ return 0;
+}
+
+static
+struct IPC_CHANNEL *
+socket_server_channel_new(int sockfd) {
+ return channel_new(sockfd, IPC_SERVER, "?");
+}
+
+static
+struct IPC_CHANNEL *
+channel_new(int sockfd, int conntype, const char *path_name) {
+ struct IPC_CHANNEL * temp_ch;
+ struct SOCKET_CH_PRIVATE* conn_info;
+ int flags;
+
+ if (path_name == NULL || strlen(path_name) >= sizeof(conn_info->path_name)) {
+ return NULL;
+ }
+
+ temp_ch = g_new(struct IPC_CHANNEL, 1);
+ if (temp_ch == NULL) {
+ cl_log(LOG_ERR, "channel_new: allocating memory for channel failed");
+ return NULL;
+ }
+ memset(temp_ch, 0, sizeof(struct IPC_CHANNEL));
+
+ conn_info = g_new(struct SOCKET_CH_PRIVATE, 1);
+
+ flags = fcntl(sockfd, F_GETFL);
+ if (flags == -1) {
+ cl_perror("channel_new: cannot read file descriptor flags");
+ g_free(conn_info); conn_info = NULL;
+ g_free(temp_ch);
+ if (conntype == IPC_CLIENT) close(sockfd);
+ return NULL;
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl(sockfd, F_SETFL, flags) < 0) {
+ cl_perror("channel_new: cannot set O_NONBLOCK");
+ g_free(conn_info); conn_info = NULL;
+ g_free(temp_ch);
+ if (conntype == IPC_CLIENT) close(sockfd);
+ return NULL;
+ }
+
+ conn_info->s = sockfd;
+ conn_info->remaining_data = 0;
+ conn_info->buf_msg = NULL;
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ conn_info->peer_addr = NULL;
+#endif
+ strncpy(conn_info->path_name, path_name, sizeof(conn_info->path_name));
+
+#ifdef DEBUG
+ cl_log(LOG_INFO, "Initializing socket %d to DISCONNECT", sockfd);
+#endif
+ temp_ch->ch_status = IPC_DISCONNECT;
+ temp_ch->ch_private = (void*) conn_info;
+ temp_ch->ops = (struct IPC_OPS *)&socket_ops;
+ temp_ch->msgpad = sizeof(struct SOCKET_MSG_HEAD);
+ temp_ch->bytes_remaining = 0;
+ temp_ch->should_send_block = FALSE;
+ temp_ch->should_block_fail = TRUE;
+ temp_ch->send_queue = socket_queue_new();
+ temp_ch->recv_queue = socket_queue_new();
+ temp_ch->pool = NULL;
+ temp_ch->high_flow_mark = temp_ch->send_queue->max_qlen;
+ temp_ch->low_flow_mark = -1;
+ temp_ch->conntype = conntype;
+ temp_ch->refcount = 0;
+ temp_ch->farside_uid = -1;
+ temp_ch->farside_gid = -1;
+
+ return temp_ch;
+}
+
+/*
+ * Create a new pair of pre-connected IPC channels similar to
+ * the result of pipe(2), or socketpair(2).
+ */
+
+int
+ipc_channel_pair(IPC_Channel* channels[2])
+{
+ int sockets[2];
+ int rc;
+ int j;
+ const char *pname;
+
+#if HB_IPC_METHOD == HB_IPC_SOCKET
+ pname = "[socketpair]";
+
+ if ((rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets)) < 0) {
+ return IPC_FAIL;
+ }
+#elif HB_IPC_METHOD == HB_IPC_STREAM
+ pname = "[pipe]";
+
+ if ((rc = pipe(sockets)) < 0) {
+ return IPC_FAIL;
+ }
+ rc = 0;
+ for (j=0; j < 2; ++j) {
+ if (fcntl(sockets[j], F_SETFL, O_NONBLOCK) < 0) {
+ cl_perror("ipc_channel_pair: cannot set O_NONBLOCK");
+ rc = -1;
+ }
+ }
+ if (rc < 0) {
+ close(sockets[0]);
+ close(sockets[1]);
+ return IPC_FAIL;
+ }
+#endif
+
+ if ((channels[0] = socket_server_channel_new(sockets[0])) == NULL) {
+ close(sockets[0]);
+ close(sockets[1]);
+ return IPC_FAIL;
+ }
+ if ((channels[1] = socket_server_channel_new(sockets[1])) == NULL) {
+ close(sockets[0]);
+ close(sockets[1]);
+ channels[0]->ops->destroy(channels[0]);
+ return IPC_FAIL;
+ }
+ for (j=0; j < 2; ++j) {
+ struct SOCKET_CH_PRIVATE* p = channels[j]->ch_private;
+ channels[j]->ch_status = IPC_CONNECT;
+ channels[j]->conntype = IPC_PEER;
+ /* Valid, but not terribly meaningful */
+ channels[j]->farside_pid = getpid();
+ strncpy(p->path_name, pname, sizeof(p->path_name));
+ }
+
+ return IPC_OK;
+}
+
+/* brief free the memory space allocated to msg and destroy msg. */
+
+static void
+socket_free_message(struct IPC_MESSAGE * msg) {
+#if 0
+ memset(msg->msg_body, 0xff, msg->msg_len);
+#endif
+ if (msg->msg_buf) {
+ g_free(msg->msg_buf);
+ } else {
+ g_free(msg->msg_body);
+ }
+#if 0
+ memset(msg, 0xff, sizeof(*msg));
+#endif
+ g_free((void *)msg);
+}
+
+/*
+ * create a new ipc message whose msg_body's length is msg_len.
+ *
+ * parameters :
+ * msg_len (IN) the length of this message body in this message.
+ *
+ * return :
+ * the pointer to the new message or NULL if the message can't be created.
+ */
+
+static struct IPC_MESSAGE*
+socket_message_new(struct IPC_CHANNEL *ch, int msg_len)
+{
+ return ipcmsg_new(ch, NULL, msg_len, NULL, socket_free_message);
+}
+
+/***********************************************************************
+ *
+ * IPC authentication schemes... More machine dependent than
+ * we'd like, but don't know any better way...
+ *
+ ***********************************************************************/
+
+static int
+verify_creds(struct IPC_AUTH *auth_info, uid_t uid, gid_t gid)
+{
+ int ret = IPC_FAIL;
+
+ if (!auth_info || (!auth_info->uid && !auth_info->gid)) {
+ return IPC_OK;
+ }
+ if ( auth_info->uid
+ && (g_hash_table_lookup(auth_info->uid
+ , GUINT_TO_POINTER((guint)uid)) != NULL)) {
+ ret = IPC_OK;
+ } else if (auth_info->gid
+ && (g_hash_table_lookup(auth_info->gid
+ , GUINT_TO_POINTER((guint)gid)) != NULL)) {
+ ret = IPC_OK;
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * SO_PEERCRED VERSION... (Linux)
+ ***********************************************************************/
+
+#ifdef USE_SO_PEERCRED
+/* verify the authentication information. */
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE * conn_info;
+ int ret = IPC_FAIL;
+ struct ucred cred;
+ socklen_t n = sizeof(cred);
+
+ if (ch == NULL || ch->ch_private == NULL) {
+ return IPC_FAIL;
+ }
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+ }
+
+ /* Get the credential information for our peer */
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+ if (getsockopt(conn_info->s, SOL_SOCKET, SO_PEERCRED, &cred, &n) != 0
+ || (size_t)n != sizeof(cred)) {
+ return ret;
+ }
+
+ ch->farside_uid = cred.uid;
+ ch->farside_gid = cred.gid;
+ if (ret == IPC_OK) {
+ return ret;
+ }
+#if 0
+ cl_log(LOG_DEBUG, "SO_PEERCRED returned [%d, (%ld:%ld)]"
+ , cred.pid, (long)cred.uid, (long)cred.uid);
+ cl_log(LOG_DEBUG, "Verifying authentication: cred.uid=%d cred.gid=%d"
+ , cred.uid, cred.gid);
+ cl_log(LOG_DEBUG, "Verifying authentication: uidptr=0x%lx gidptr=0x%lx"
+ , (unsigned long)auth_info->uid
+ , (unsigned long)auth_info->gid);
+#endif
+ /* verify the credential information. */
+ return verify_creds(auth_info, cred.uid, cred.gid);
+}
+
+/* get farside pid for our peer process */
+
+static
+pid_t
+socket_get_farside_pid(int sockfd)
+{
+ socklen_t n;
+ struct ucred *cred;
+ pid_t f_pid;
+
+ /* Get the credential information from peer */
+ n = sizeof(struct ucred);
+ cred = g_new(struct ucred, 1);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, cred, &n) != 0) {
+ g_free(cred);
+ return -1;
+ }
+
+ f_pid = cred->pid;
+ g_free(cred);
+ return f_pid;
+}
+#endif /* SO_PEERCRED version */
+
+#ifdef USE_GETPEEREID
+/*
+ * This is implemented in OpenBSD and FreeBSD.
+ *
+ * It's not a half-bad interface...
+ *
+ * This should probably be our standard way of doing it, and put it
+ * as a replacement library. That would simplify things...
+ */
+
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE *conn_info;
+ uid_t euid;
+ gid_t egid;
+ int ret = IPC_FAIL;
+
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+ }
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ if (getpeereid(conn_info->s, &euid, &egid) < 0) {
+ cl_perror("getpeereid() failure");
+ return ret;
+ }
+
+ ch->farside_uid = euid;
+ ch->farside_gid = egid;
+
+ /* verify the credential information. */
+ return verify_creds(auth_info, euid, egid);
+}
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif /* USE_GETPEEREID */
+
+/***********************************************************************
+ * SCM_CREDS VERSION... (*BSD systems)
+ ***********************************************************************/
+#ifdef USE_SCM_CREDS
+/* FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems
+ * This isn't an emergency, but should be done in the future...
+ * Hint: * Postgresql does both types of authentication...
+ * see src/backend/libpq/auth.c
+ * Not clear its SO_PEERCRED implementation works though ;-)
+ */
+
+/* Done.... Haven't tested yet. */
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct msghdr msg;
+ /* Credentials structure */
+
+#define EXTRASPACE 0
+
+#ifdef HAVE_STRUCT_CMSGCRED
+ /* FreeBSD */
+ typedef struct cmsgcred Cred;
+# define crRuid cmcred_uid
+# define crEuid cmcred_euid
+# define crRgid cmcred_gid
+# define crEgid cmcred_groups[0] /* Best guess */
+# define crpid cmcred_pid
+# define crngrp cmcred_ngroups
+# define crgrps cmcred_groups
+
+#elif HAVE_STRUCT_FCRED
+ /* Stevens' book */
+ typedef struct fcred Cred;
+# define crRuid fc_uid
+# define crRgid fc_rgid
+# define crEgid fc_gid
+# define crngrp fc_ngroups
+# define crgrps fc_groups
+
+#elif HAVE_STRUCT_SOCKCRED
+ /* NetBSD */
+ typedef struct sockcred Cred;
+# define crRuid sc_uid
+# define crEuid sc_euid
+# define crRgid sc_gid
+# define crEgid sc_egid
+# define crngrp sc_ngroups
+# define crgrps sc_groups
+# undef EXTRASPACE
+# define EXTRASPACE SOCKCREDSIZE(ngroups)
+
+#elif HAVE_STRUCT_CRED
+ typedef struct cred Cred;
+#define cruid c_uid
+
+#elif HAVE_STRUCT_UCRED
+ typedef struct ucred Cred;
+
+ /* reuse this define for the moment */
+# if HAVE_STRUCT_UCRED_DARWIN
+# define crEuid cr_uid
+# define crEgid cr_groups[0] /* Best guess */
+# define crgrps cr_groups
+# define crngrp cr_ngroups
+# else
+# define crEuid c_uid
+# define crEgid c_gid
+# endif
+#else
+# error "No credential type found!"
+#endif
+
+ struct SOCKET_CH_PRIVATE *conn_info;
+ int ret = IPC_FAIL;
+ char buf;
+
+ /* Compute size without padding */
+ #define CMSGSIZE (sizeof(struct cmsghdr)+(sizeof(Cred))+EXTRASPACE)
+
+ union {
+ char mem[CMSGSIZE];
+ struct cmsghdr hdr;
+ Cred credu;
+ }cmsgmem;
+ Cred cred;
+
+ /* Point to start of first structure */
+ struct cmsghdr *cmsg = &cmsgmem.hdr;
+
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+ }
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = g_new(struct iovec, 1);
+ msg.msg_iovlen = 1;
+ msg.msg_control = (char *) cmsg;
+ msg.msg_controllen = CMSGSIZE;
+ memset(cmsg, 0, sizeof(cmsgmem));
+
+ /*
+ * The one character which is received here is not meaningful; its
+ * purpose is only to make sure that recvmsg() blocks long enough for
+ * the other side to send its credentials.
+ */
+ msg.msg_iov->iov_base = &buf;
+ msg.msg_iov->iov_len = 1;
+
+ if (recvmsg(conn_info->s, &msg, 0) < 0
+ || cmsg->cmsg_len < CMSGSIZE
+ || cmsg->cmsg_type != SCM_CREDS) {
+ cl_perror("can't get credential information from peer");
+ return ret;
+ }
+
+ /* Avoid alignment issues - just copy it! */
+ memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred));
+
+ ch->farside_uid = cred.crEuid;
+ ch->farside_gid = cred.crEgid;
+ if (ret == IPC_OK) {
+ return ret;
+ }
+
+ /* verify the credential information. */
+ return verify_creds(auth_info, cred.crEuid, cred.crEgid);
+}
+
+/*
+ * FIXME! Need to implement SCM_CREDS mechanism for BSD-based systems
+ * this is similar to the SCM_CREDS mechanism for verify_auth() function.
+ * here we just want to get the pid of the other side from the credential
+ * information.
+ */
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ /* FIXME! */
+ return -1;
+}
+#endif /* SCM_CREDS version */
+
+/***********************************************************************
+ * Bind/Stat VERSION... (Supported on OSX/Darwin and 4.3+BSD at least...)
+ *
+ * This is for use on systems such as OSX-Darwin where
+ * none of the other options is available.
+ *
+ * This implementation has been adapted from "Advanced Programming
+ * in the Unix Environment", Section 15.5.2, by W. Richard Stevens.
+ *
+ */
+#ifdef USE_BINDSTAT_CREDS
+
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ int len = 0;
+ int ret = IPC_FAIL;
+ struct stat stat_buf;
+ struct sockaddr_un *peer_addr = NULL;
+ struct SOCKET_CH_PRIVATE *ch_private = NULL;
+
+ if(ch != NULL) {
+ ch_private = (struct SOCKET_CH_PRIVATE *)(ch->ch_private);
+ if(ch_private != NULL) {
+ peer_addr = ch_private->peer_addr;
+ }
+ }
+
+ if(ch == NULL) {
+ cl_log(LOG_ERR, "No channel to authenticate");
+ return IPC_FAIL;
+
+ } else if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ ret = IPC_OK; /* no restriction for authentication */
+
+ }
+
+ if(ch_private == NULL) {
+ cl_log(LOG_ERR, "No channel private data available");
+ return ret;
+
+ } else if(peer_addr == NULL) {
+ cl_log(LOG_ERR, "No peer information available");
+ return ret;
+ }
+
+ len = SUN_LEN(peer_addr);
+
+ if(len < 1) {
+ cl_log(LOG_ERR, "No peer information available");
+ return ret;
+ }
+ peer_addr->sun_path[len] = 0;
+ stat(peer_addr->sun_path, &stat_buf);
+
+ ch->farside_uid = stat_buf.st_uid;
+ ch->farside_gid = stat_buf.st_gid;
+ if (ret == IPC_OK) {
+ return ret;
+ }
+
+ if ((auth_info->uid == NULL || g_hash_table_size(auth_info->uid) == 0)
+ && auth_info->gid != NULL
+ && g_hash_table_size(auth_info->gid) != 0) {
+ cl_log(LOG_WARNING,
+ "GID-Only IPC security is not supported"
+ " on this platform.");
+ return IPC_BROKEN;
+ }
+
+ /* verify the credential information. */
+ return verify_creds(auth_info, stat_buf.st_uid, stat_buf.st_gid);
+}
+
+static pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif /* Bind/stat version */
+
+/***********************************************************************
+ * USE_STREAM_CREDS VERSION... (e.g. Solaris pre-10)
+ ***********************************************************************/
+#ifdef USE_STREAM_CREDS
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE *conn_info;
+
+ if (ch == NULL || ch->ch_private == NULL) {
+ return IPC_FAIL;
+ }
+
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ ch->farside_uid = conn_info->farside_uid;
+ ch->farside_gid = conn_info->farside_gid;
+
+ /* verify the credential information. */
+ return verify_creds(auth_info,
+ conn_info->farside_uid, conn_info->farside_gid);
+}
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif
+
+/***********************************************************************
+ * GETPEERUCRED VERSION... (e.g. Solaris 10 upwards)
+ ***********************************************************************/
+
+#ifdef USE_GETPEERUCRED
+/* verify the authentication information. */
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ struct SOCKET_CH_PRIVATE *conn_info;
+ ucred_t *ucred = NULL;
+ int rc = IPC_FAIL;
+
+ if (ch == NULL || ch->ch_private == NULL) {
+ return IPC_FAIL;
+ }
+
+ conn_info = (struct SOCKET_CH_PRIVATE *) ch->ch_private;
+
+ if (auth_info == NULL
+ || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+ rc = IPC_OK; /* no restriction for authentication */
+ }
+
+ if (getpeerucred(conn_info->s, &ucred) < 0) {
+ cl_perror("getpeereid() failure");
+ return rc;
+ }
+
+ ch->farside_uid = ucred_geteuid(ucred);
+ ch->farside_gid = ucred_getegid(ucred);
+ if (rc == IPC_OK) {
+ return rc;
+ }
+
+ /* verify the credential information. */
+ rc = verify_creds(auth_info,
+ ucred_geteuid(ucred), ucred_getegid(ucred));
+ ucred_free(ucred);
+ return rc;
+}
+
+static
+pid_t
+socket_get_farside_pid(int sockfd)
+{
+ ucred_t *ucred = NULL;
+ pid_t pid;
+
+ if (getpeerucred(sockfd, &ucred) < 0) {
+ cl_perror("getpeereid() failure");
+ return IPC_FAIL;
+ }
+
+ pid = ucred_getpid(ucred);
+
+ ucred_free(ucred);
+
+ return pid;
+}
+#endif
+
+/***********************************************************************
+ * DUMMY VERSION... (other systems...)
+ *
+ * Other options that seem to be out there include
+ * SCM_CREDENTIALS and LOCAL_CREDS
+ * There are some kludgy things you can do with SCM_RIGHTS
+ * to pass an fd which could only be opened by the user id to
+ * validate the user id, but I don't know of a similar kludge which
+ * would work for group ids. And, even the uid one will fail
+ * if normal users are allowed to give away (chown) files.
+ *
+ * Unfortunately, this set of authentication routines have become
+ * very important to this API and its users (like heartbeat).
+ *
+ ***********************************************************************/
+
+#ifdef USE_DUMMY_CREDS
+static int
+socket_verify_auth(struct IPC_CHANNEL* ch, struct IPC_AUTH * auth_info)
+{
+ return IPC_FAIL;
+}
+
+static
+pid_t
+socket_get_farside_pid(int sock)
+{
+ return -1;
+}
+#endif /* Dummy version */
+
+/* socket object of the function table */
+static struct IPC_OPS socket_ops = {
+ socket_destroy_channel,
+ socket_initiate_connection,
+ socket_verify_auth,
+ socket_assert_auth,
+ socket_send,
+ socket_recv,
+ socket_waitin,
+ socket_waitout,
+ socket_is_message_pending,
+ socket_is_output_pending,
+ socket_resume_io,
+ socket_get_send_fd,
+ socket_get_recv_fd,
+ socket_set_send_qlen,
+ socket_set_recv_qlen,
+ socket_set_high_flow_callback,
+ socket_set_low_flow_callback,
+ socket_new_ipcmsg,
+ socket_get_chan_status,
+ socket_is_sendq_full,
+ socket_is_recvq_full,
+ socket_get_conntype,
+ socket_disconnect,
+};
diff --git a/lib/clplumbing/ipctest.c b/lib/clplumbing/ipctest.c
new file mode 100644
index 0000000..333d3a0
--- /dev/null
+++ b/lib/clplumbing/ipctest.c
@@ -0,0 +1,1377 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#undef _GNU_SOURCE /* in case it was defined on the command line */
+#define _GNU_SOURCE
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+/* libgen.h: for 'basename()' on Solaris */
+#include <libgen.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_poll.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/ipc.h>
+
+#define MAXERRORS 1000
+#define MAXERRORS_RECV 10
+
+typedef int (*TestFunc_t)(IPC_Channel*chan, int count);
+
+static int channelpair(TestFunc_t client, TestFunc_t server, int count);
+#if 0
+static void clientserverpair(IPC_Channel* channels[2]);
+#endif
+
+static int echoserver(IPC_Channel*, int repcount);
+static int echoclient(IPC_Channel*, int repcount);
+static int asyn_echoserver(IPC_Channel*, int repcount);
+static int asyn_echoclient(IPC_Channel*, int repcount);
+static int mainloop_server(IPC_Channel* chan, int repcount);
+static int mainloop_client(IPC_Channel* chan, int repcount);
+
+static int checksock(IPC_Channel* channel);
+static void checkifblocked(IPC_Channel* channel);
+
+static int (*PollFunc)(struct pollfd * fds, unsigned int, int)
+= (int (*)(struct pollfd * fds, unsigned int, int)) poll;
+static gboolean checkmsg(IPC_Message* rmsg, const char * who, int rcount);
+
+static const char *procname;
+
+static const int iter_def = 10000; /* number of iterations */
+static int verbosity; /* verbosity level */
+
+/*
+ * The ipc interface can be invoked as either:
+ * 1. pair (pipe/socketpair);
+ * 2. separate connect/accept (like server with multiple independent clients).
+ *
+ * If number of clients is given as 0, the "pair" mechanism is used,
+ * otherwise the client/server mechanism.
+ */
+/* *** CLIENTS_MAX currently 1 while coding *** */
+#define CLIENTS_MAX 1 /* max. number of independent clients */
+static int clients_def; /* number of independent clients */
+
+static int
+channelpair(TestFunc_t clientfunc, TestFunc_t serverfunc, int count)
+{
+ IPC_Channel* channels[2];
+ int rc = 0;
+ int waitstat = 0;
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main process",
+ procname, (int)getpid(), __LINE__);
+ }
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+ default: /* Parent */
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main waiting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ while (wait(&waitstat) > 0) {
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ }
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main ended rc: %d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ if (rc > 127) {
+ rc = 127;
+ }
+ exit(rc);
+ break;
+ case 0: /* Child */
+ break;
+ }
+ /* Child continues here... */
+ if (ipc_channel_pair(channels) != IPC_OK) {
+ cl_perror("Can't create ipc channel pair");
+ exit(1);
+ }
+ checksock(channels[0]);
+ checksock(channels[1]);
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+
+ case 0: /* echo "client" Child */
+ channels[1]->ops->destroy(channels[1]);
+ channels[1] = NULL;
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client starting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ rc = clientfunc(channels[0], count);
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ exit (rc > 127 ? 127 : rc);
+ break;
+
+ default:
+ break;
+ }
+ channels[0]->ops->destroy(channels[0]);
+ channels[0] = NULL;
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server starting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ rc = serverfunc(channels[1], count);
+ wait(&waitstat);
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ return(rc);
+}
+
+/* server with many clients */
+static int
+clientserver(TestFunc_t clientfunc, TestFunc_t serverfunc, int count, int clients)
+{
+ IPC_Channel* channel;
+ int rc = 0;
+ int waitstat = 0;
+ struct IPC_WAIT_CONNECTION *wconn;
+ char path[] = IPC_PATH_ATTR;
+ char commpath[] = "/tmp/foobar"; /* *** CHECK/FIX: Is this OK? */
+ GHashTable * wattrs;
+ int i;
+ pid_t pid;
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main process",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+ default: /* Parent */
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main waiting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ while ((pid = wait(&waitstat)) > 0) {
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ }
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: main ended rc: %d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+ if (rc > 127) {
+ rc = 127;
+ }
+ exit(rc);
+ break;
+ case 0: /* Child */
+ break;
+ }
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d:",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ /* set up a server */
+ wattrs = g_hash_table_new(g_str_hash, g_str_equal);
+ if (! wattrs) {
+ cl_perror("g_hash_table_new() failed");
+ exit(1);
+ }
+ g_hash_table_insert(wattrs, path, commpath);
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d:",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ wconn = ipc_wait_conn_constructor(IPC_ANYTYPE, wattrs);
+ if (! wconn) {
+ cl_perror("could not establish server");
+ exit(1);
+ }
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d:",
+ procname, (int)getpid(), __LINE__);
+ }
+
+ /* spawn the clients */
+ for (i = 1; i <= clients; i++) {
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: fork client %d of %d",
+ procname, (int)getpid(), __LINE__, i, clients);
+ }
+ switch (fork()) {
+ case -1:
+ cl_perror("can't fork");
+ exit(1);
+ break;
+
+ case 0: /* echo "client" Child */
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client %d starting...",
+ procname, (int)getpid(), __LINE__, i);
+ }
+ channel = ipc_channel_constructor(IPC_ANYTYPE, wattrs);
+ if (channel == NULL) {
+ cl_perror("client: channel creation failed");
+ exit(1);
+ }
+
+ rc = channel->ops->initiate_connection(channel);
+ if (rc != IPC_OK) {
+ cl_perror("channel[1] failed to connect");
+ exit(1);
+ }
+ checksock(channel);
+ rc = clientfunc(channel, count);
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client %d ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc, i);
+ }
+ exit (rc > 127 ? 127 : rc);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server starting...",
+ procname, (int)getpid(), __LINE__);
+ }
+ /* accept on server */
+ /* ***
+ * Two problems (or more) here:
+ * 1. What to do if no incoming call pending?
+ * At present, fudge by sleeping a little so client gets started.
+ * 2. How to handle multiple clients?
+ * Would need to be able to await both new connections and
+ * data on existing connections.
+ * At present, fudge CLIENTS_MAX as 1.
+ * ***
+ */
+ sleep(1); /* *** */
+ channel = wconn->ops->accept_connection(wconn, NULL);
+ if (channel == NULL) {
+ cl_perror("server: acceptance failed");
+ }
+
+ checksock(channel);
+
+ rc = serverfunc(channel, count);
+
+ /* server finished: tidy up */
+ wconn->ops->destroy(wconn);
+
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: server ended rc:%d",
+ procname, (int)getpid(), __LINE__, rc);
+ }
+
+ /* reap the clients */
+ for (i = 1; i <= clients; i++) {
+ pid_t pid;
+
+ pid = wait(&waitstat);
+ if (verbosity >= 1) {
+ cl_log(LOG_DEBUG, "%s[%d]%d: client %d reaped:%d",
+ procname, (int)getpid(), __LINE__,
+ (int) pid, WIFEXITED(waitstat));
+ }
+ if (WIFEXITED(waitstat)) {
+ rc += WEXITSTATUS(waitstat);
+ }else{
+ rc += 1;
+ }
+ }
+
+ return(rc);
+}
+
+static void
+echomsgbody(void * body, int n, int niter, size_t * len)
+{
+ char *str = body;
+ int l;
+
+ l = snprintf(str, n-1, "String-%d", niter);
+ if (l < (n-1)) {
+ memset(&str[l], 'a', (n - (l+1)));
+ }
+ str[n-1] = '\0';
+ *len = n;
+}
+
+static void
+checkifblocked(IPC_Channel* chan)
+{
+ if (chan->ops->is_sending_blocked(chan)) {
+ cl_log(LOG_INFO, "Sending is blocked.");
+ chan->ops->resume_io(chan);
+ }
+}
+
+#ifdef CHEAT_CHECKS
+extern long SeqNums[32];
+#endif
+
+static int
+transport_tests(int iterations, int clients)
+{
+ int rc = 0;
+
+#ifdef CHEAT_CHECKS
+ memset(SeqNums, 0, sizeof(SeqNums));
+#endif
+ rc += (clients <= 0)
+ ? channelpair(echoclient, echoserver, iterations)
+ : clientserver(echoclient, echoserver, iterations, clients);
+
+#ifdef CHEAT_CHECKS
+ memset(SeqNums, 0, sizeof(SeqNums));
+#endif
+ rc += (clients <= 0)
+ ? channelpair(asyn_echoclient, asyn_echoserver, iterations)
+ : clientserver(asyn_echoclient, asyn_echoserver, iterations, clients);
+
+#ifdef CHEAT_CHECKS
+ memset(SeqNums, 0, sizeof(SeqNums));
+#endif
+ rc += (clients <= 0)
+ ? channelpair(mainloop_client, mainloop_server, iterations)
+ : clientserver(mainloop_client, mainloop_server, iterations, clients);
+
+ return rc;
+}
+
+static int data_size = 20;
+
+int
+main(int argc, char ** argv)
+{
+ int argflag, argerrs;
+ int iterations;
+ int clients;
+ int rc = 0;
+
+ /*
+ * Check and process arguments.
+ * -v: verbose
+ * -i: number of iterations
+ * -c: number of clients (invokes client/server mechanism)
+ * -s: data-size
+ */
+ procname = basename(argv[0]);
+
+ argerrs = 0;
+ iterations = iter_def;
+ clients = clients_def;
+ while ((argflag = getopt(argc, argv, "i:vuc:s:")) != EOF) {
+ switch (argflag) {
+ case 'i': /* iterations */
+ iterations = atoi(optarg);
+ break;
+ case 'v': /* verbosity */
+ verbosity++;
+ break;
+ case 'c': /* number of clients */
+ clients = atoi(optarg);
+ if (clients < 1 || clients > CLIENTS_MAX) {
+ fprintf(stderr, "number of clients out of range"
+ "(1 to %d)\n", CLIENTS_MAX);
+ argerrs++;
+ }
+ break;
+ case 's': /* data size */
+ data_size = atoi(optarg);
+ if (data_size < 0) {
+ fprintf(stderr, "data size must be >=0\n");
+ argerrs++;
+ }
+ if (data_size > MAXMSG) {
+ fprintf(stderr, "maximum data size is %d\n", MAXMSG);
+ argerrs++;
+ }
+ break;
+ default:
+ argerrs++;
+ break;
+ }
+ }
+ if (argerrs) {
+ fprintf(stderr,
+ "Usage: %s [-v] [-i iterations] [-c clients] [-s size]\n"
+ "\t-v : verbose\n"
+ "\t-i : iterations (default %d)\n"
+ "\t-c : number of clients (default %d; nonzero invokes client/server)\n"
+ "\t-s : data size (default 20 bytes)\n",
+ procname, iter_def, clients_def);
+ exit(1);
+ }
+
+ cl_log_set_entity(procname);
+ cl_log_enable_stderr(TRUE);
+
+
+
+ rc += transport_tests(iterations, clients);
+
+#if 0
+ /* Broken for the moment - need to fix it long term */
+ cl_log(LOG_INFO, "NOTE: Enabling poll(2) replacement code.");
+ PollFunc = cl_poll;
+ g_main_set_poll_func(cl_glibpoll);
+ ipc_set_pollfunc(cl_poll);
+
+ rc += transport_tests(5 * iterations, clients);
+#endif
+
+ cl_log(LOG_INFO, "TOTAL errors: %d", rc);
+
+ return (rc > 127 ? 127 : rc);
+}
+
+static int
+checksock(IPC_Channel* channel)
+{
+
+ if (!channel) {
+ cl_log(LOG_ERR, "Channel null");
+ return 1;
+ }
+ if (!IPC_ISRCONN(channel)) {
+ cl_log(LOG_ERR, "Channel status is %d"
+ ", not IPC_CONNECT", channel->ch_status);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+EOFcheck(IPC_Channel* chan)
+{
+ int fd = chan->ops->get_recv_select_fd(chan);
+ struct pollfd pf[1];
+ int rc;
+
+ cl_log(LOG_INFO, "channel state: %d", chan->ch_status);
+
+ if (chan->recv_queue->current_qlen > 0) {
+ cl_log(LOG_INFO, "EOF Receive queue has %ld messages in it"
+ , (long)chan->recv_queue->current_qlen);
+ }
+ if (fd <= 0) {
+ cl_log(LOG_INFO, "EOF receive fd: %d", fd);
+ }
+
+
+ pf[0].fd = fd;
+ pf[0].events = POLLIN|POLLHUP;
+ pf[0].revents = 0;
+
+ rc = poll(pf, 1, 0);
+
+ if (rc < 0) {
+ cl_perror("failed poll(2) call in EOFcheck");
+ return;
+ }
+
+ /* Got input? */
+ if (pf[0].revents & POLLIN) {
+ cl_log(LOG_INFO, "EOF socket %d (still) has input ready (real poll)"
+ , fd);
+ }
+ if ((pf[0].revents & ~(POLLIN|POLLHUP)) != 0) {
+ cl_log(LOG_INFO, "EOFcheck poll(2) bits: 0x%lx"
+ , (unsigned long)pf[0].revents);
+ }
+ pf[0].fd = fd;
+ pf[0].events = POLLIN|POLLHUP;
+ pf[0].revents = 0;
+ rc = PollFunc(pf, 1, 0);
+ if (rc < 0) {
+ cl_perror("failed PollFunc() call in EOFcheck");
+ return;
+ }
+
+ /* Got input? */
+ if (pf[0].revents & POLLIN) {
+ cl_log(LOG_INFO, "EOF socket %d (still) has input ready (PollFunc())"
+ , fd);
+ }
+ if ((pf[0].revents & ~(POLLIN|POLLHUP)) != 0) {
+ cl_log(LOG_INFO, "EOFcheck PollFunc() bits: 0x%lx"
+ , (unsigned long)pf[0].revents);
+ }
+}
+
+static int
+echoserver(IPC_Channel* wchan, int repcount)
+{
+ char *str;
+ int j;
+ int errcount = 0;
+ IPC_Message wmsg;
+ IPC_Message* rmsg = NULL;
+
+ if (!(str = malloc(data_size))) {
+ cl_log(LOG_ERR, "Out of memory");
+ exit(1);
+ }
+
+ memset(&wmsg, 0, sizeof(wmsg));
+ wmsg.msg_private = NULL;
+ wmsg.msg_done = NULL;
+ wmsg.msg_body = str;
+ wmsg.msg_buf = NULL;
+ wmsg.msg_ch = wchan;
+
+ cl_log(LOG_INFO, "Echo server: %d reps pid %d.", repcount, getpid());
+ for (j=1; j <= repcount
+ ;++j, rmsg != NULL && (rmsg->msg_done(rmsg),1)) {
+ int rc;
+
+ echomsgbody(str, data_size, j, &(wmsg.msg_len));
+ if ((rc = wchan->ops->send(wchan, &wmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest: send failed %d rc iter %d"
+ , rc, j);
+ ++errcount;
+ continue;
+ }
+
+ /*fprintf(stderr, "+"); */
+ wchan->ops->waitout(wchan);
+ checkifblocked(wchan);
+ /*fprintf(stderr, "S"); */
+
+ /* Try and induce a failure... */
+ if (j == repcount) {
+ sleep(1);
+ }
+
+ while ((rc = wchan->ops->waitin(wchan)) == IPC_INTR);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest server: waitin failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("waitin");
+ exit(1);
+ }
+
+ /*fprintf(stderr, "-"); */
+ if ((rc = wchan->ops->recv(wchan, &rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest server: recv failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("recv");
+ ++errcount;
+ rmsg=NULL;
+ continue;
+ }
+ /*fprintf(stderr, "s"); */
+ if (rmsg->msg_len != wmsg.msg_len) {
+ cl_log(LOG_ERR
+ , "echotest: length mismatch [%lu,%lu] iter %d"
+ , (unsigned long)rmsg->msg_len
+ , (unsigned long)wmsg.msg_len, j);
+ ++errcount;
+ continue;
+ }
+ if (strncmp(rmsg->msg_body, wmsg.msg_body, wmsg.msg_len)
+ != 0) {
+ cl_log(LOG_ERR
+ , "echotest: data mismatch. iteration %d"
+ , j);
+ ++errcount;
+ continue;
+ }
+
+ }
+ cl_log(LOG_INFO, "echoserver: %d errors", errcount);
+#if 0
+ cl_log(LOG_INFO, "destroying channel 0x%lx", (unsigned long)wchan);
+#endif
+ wchan->ops->destroy(wchan); wchan = NULL;
+
+ free(str);
+
+ return errcount;
+}
+static int
+echoclient(IPC_Channel* rchan, int repcount)
+{
+ int j;
+ int errcount = 0;
+ IPC_Message* rmsg;
+
+
+
+ cl_log(LOG_INFO, "Echo client: %d reps pid %d."
+ , repcount, (int)getpid());
+ for (j=1; j <= repcount ;++j) {
+
+ int rc;
+
+ while ((rc = rchan->ops->waitin(rchan)) == IPC_INTR);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echotest client: waitin failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("waitin");
+ exit(1);
+ }
+ /*fprintf(stderr, "/"); */
+
+ if ((rc = rchan->ops->recv(rchan, &rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echoclient: recv failed %d rc iter %d"
+ " errno=%d"
+ , rc, j, errno);
+ cl_perror("recv");
+ ++errcount;
+ if (errcount > MAXERRORS_RECV) {
+ cl_log(LOG_ERR,
+ "echoclient: errcount excessive: %d: abandoning",
+ errcount);
+ exit(1);
+ }
+ --j;
+ rmsg=NULL;
+ continue;
+ }
+ /*fprintf(stderr, "c"); */
+ if ((rc = rchan->ops->send(rchan, rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "echoclient: send failed %d rc iter %d"
+ , rc, j);
+ cl_log(LOG_INFO, "Message being sent: %s"
+ , (char*)rmsg->msg_body);
+ ++errcount;
+ continue;
+ }
+ /*fprintf(stderr, "%%"); */
+ rchan->ops->waitout(rchan);
+ checkifblocked(rchan);
+ /*fprintf(stderr, "C"); */
+ }
+ cl_log(LOG_INFO, "echoclient: %d errors", errcount);
+#if 0
+ cl_log(LOG_INFO, "destroying channel 0x%lx", (unsigned long)rchan);
+#endif
+ rchan->ops->destroy(rchan); rchan = NULL;
+ return errcount;
+}
+
+void dump_ipc_info(IPC_Channel* chan);
+
+static int
+checkinput(IPC_Channel* chan, const char * where, int* rdcount, int maxcount)
+{
+ IPC_Message* rmsg = NULL;
+ int errs = 0;
+ int rc;
+
+ while (chan->ops->is_message_pending(chan)
+ && errs < 10 && *rdcount < maxcount) {
+
+ if (chan->ch_status == IPC_DISCONNECT && *rdcount < maxcount){
+ cl_log(LOG_ERR
+ , "checkinput1[0x%lx %s]: EOF in iter %d"
+ , (unsigned long)chan, where, *rdcount);
+ EOFcheck(chan);
+ }
+
+ if (rmsg != NULL) {
+ rmsg->msg_done(rmsg);
+ rmsg = NULL;
+ }
+
+ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) {
+ if (chan->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR
+ , "checkinput2[0x%lx %s]: EOF in iter %d"
+ , (unsigned long)chan, where, *rdcount);
+ EOFcheck(chan);
+ return errs;
+ }
+ cl_log(LOG_ERR
+ , "checkinput[%s]: recv"
+ " failed: rc %d rdcount %d errno=%d"
+ , where, rc, *rdcount, errno);
+ cl_perror("recv");
+ rmsg=NULL;
+ ++errs;
+ continue;
+ }
+ *rdcount += 1;
+ if (!checkmsg(rmsg, where, *rdcount)) {
+ dump_ipc_info(chan);
+ ++errs;
+ }
+ if (*rdcount < maxcount && chan->ch_status == IPC_DISCONNECT){
+ cl_log(LOG_ERR
+ , "checkinput3[0x%lx %s]: EOF in iter %d"
+ , (unsigned long)chan, where, *rdcount);
+ EOFcheck(chan);
+ }
+
+ }
+ return errs;
+}
+
+static void
+async_high_flow_callback(IPC_Channel* ch, void* userdata)
+{
+ int* stopsending = userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ *stopsending = 1;
+
+}
+
+static void
+async_low_flow_callback(IPC_Channel* ch, void* userdata)
+{
+
+ int* stopsending = userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ *stopsending = 0;
+
+}
+
+
+static int
+asyn_echoserver(IPC_Channel* wchan, int repcount)
+{
+ int rdcount = 0;
+ int wrcount = 0;
+ int errcount = 0;
+ int blockedcount = 0;
+ IPC_Message* wmsg;
+ const char* w = "asyn_echoserver";
+ int stopsending = 0;
+
+ cl_log(LOG_INFO, "Asyn echo server: %d reps pid %d."
+ , repcount, (int)getpid());
+
+ (void)async_high_flow_callback;
+ (void)async_low_flow_callback;
+
+
+ wchan->ops->set_high_flow_callback(wchan, async_high_flow_callback, &stopsending);
+ wchan->ops->set_low_flow_callback(wchan, async_low_flow_callback, &stopsending);
+
+ wchan->low_flow_mark = 2;
+ wchan->high_flow_mark = 20;
+
+ while (rdcount < repcount) {
+ int rc;
+
+ while (wrcount < repcount && blockedcount < 10
+ && wchan->ch_status != IPC_DISCONNECT
+ ){
+
+ if (!stopsending){
+ ++wrcount;
+ if (wrcount > repcount) {
+ break;
+ }
+ wmsg = wchan->ops->new_ipcmsg(wchan, NULL, data_size, NULL);
+ echomsgbody(wmsg->msg_body, data_size, wrcount, &wmsg->msg_len);
+ if ((rc = wchan->ops->send(wchan, wmsg)) != IPC_OK){
+
+ cl_log(LOG_INFO, "channel sstatus in echo server is %d",
+ wchan->ch_status);
+ if (wchan->ch_status != IPC_CONNECT) {
+ cl_log(LOG_ERR
+ , "asyn_echoserver: send failed"
+ " %d rc iter %d"
+ , rc, wrcount);
+ ++errcount;
+ continue;
+ }else {/*send failed because of channel busy
+ * roll back
+ */
+ --wrcount;
+ }
+ }
+
+ if (wchan->ops->is_sending_blocked(wchan)) {
+ /* fprintf(stderr, "b"); */
+ ++blockedcount;
+ }else{
+ blockedcount = 0;
+ }
+ }
+
+
+ errcount += checkinput(wchan, w, &rdcount, repcount);
+ if (wrcount < repcount
+ && wchan->ch_status == IPC_DISCONNECT) {
+ ++errcount;
+ break;
+ }
+ }
+
+/* cl_log(LOG_INFO, "async_echoserver: wrcount =%d rdcount=%d B", wrcount, rdcount); */
+
+ wchan->ops->waitout(wchan);
+ errcount += checkinput(wchan, w, &rdcount, repcount);
+ if (wrcount >= repcount && rdcount < repcount) {
+ while ((rc = wchan->ops->waitin(wchan)) == IPC_INTR);
+
+ if (rc != IPC_OK) {
+ cl_log(LOG_ERR
+ , "asyn_echoserver: waitin()"
+ " failed %d rc rdcount %d errno=%d"
+ , rc, rdcount, errno);
+ cl_perror("waitin");
+ exit(1);
+ }
+ }
+ if (wchan->ch_status == IPC_DISCONNECT
+ && rdcount < repcount) {
+ cl_log(LOG_ERR,
+ "asyn_echoserver: EOF in iter %d (wrcount=%d)",
+ rdcount, wrcount);
+ EOFcheck(wchan);
+ ++errcount;
+ break;
+ }
+
+ blockedcount = 0;
+
+ }
+
+ cl_log(LOG_INFO, "asyn_echoserver: %d errors", errcount);
+#if 0
+ cl_log(LOG_INFO, "%d destroying channel 0x%lx", getpid(), (unsigned long)wchan);
+#endif
+ wchan->ops->destroy(wchan); wchan = NULL;
+ return errcount;
+}
+
+static int
+asyn_echoclient(IPC_Channel* chan, int repcount)
+{
+ int rdcount = 0;
+ int wrcount = 0;
+ int errcount = 0;
+ IPC_Message* rmsg;
+ int rfd = chan->ops->get_recv_select_fd(chan);
+ int wfd = chan->ops->get_send_select_fd(chan);
+ gboolean rdeqwr = (rfd == wfd);
+
+
+ cl_log(LOG_INFO, "Async Echo client: %d reps pid %d."
+ , repcount, (int)getpid());
+ ipc_set_pollfunc(PollFunc);
+
+ while (rdcount < repcount && errcount < repcount) {
+
+ int rc;
+ struct pollfd pf[2];
+ int nfd = 1;
+
+ pf[0].fd = rfd;
+ pf[0].events = POLLIN|POLLHUP;
+
+
+ if (chan->ops->is_sending_blocked(chan)) {
+ if (rdeqwr) {
+ pf[0].events |= POLLOUT;
+ }else{
+ nfd = 2;
+ pf[1].fd = wfd;
+ pf[1].events = POLLOUT|POLLHUP;
+ }
+ }
+
+ /* Have input? */
+ /* fprintf(stderr, "i"); */
+ while (chan->ops->is_message_pending(chan)
+ && rdcount < repcount) {
+ /*fprintf(stderr, "r"); */
+
+ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) {
+ if (!IPC_ISRCONN(chan)) {
+ cl_log(LOG_ERR
+ , "Async echoclient: disconnect"
+ " iter %d", rdcount+1);
+ ++errcount;
+ return errcount;
+ }
+ cl_log(LOG_ERR
+ , "Async echoclient: recv"
+ " failed %d rc iter %d errno=%d"
+ , rc, rdcount+1, errno);
+ cl_perror("recv");
+ rmsg=NULL;
+ ++errcount;
+ cl_log(LOG_INFO, "sleep(1)");
+ sleep(1);
+ continue;
+ }
+ /*fprintf(stderr, "c"); */
+ ++rdcount;
+
+
+ do {
+ rc = chan->ops->send(chan, rmsg);
+
+ }while (rc != IPC_OK && chan->ch_status == IPC_CONNECT);
+
+ if (chan->ch_status != IPC_CONNECT){
+ ++errcount;
+ cl_perror("send");
+ cl_log(LOG_ERR
+ , "Async echoclient: send failed"
+ " rc %d, iter %d", rc, rdcount);
+ cl_log(LOG_INFO, "Message being sent: %s"
+ , (char*)rmsg->msg_body);
+ if (!IPC_ISRCONN(chan)) {
+ cl_log(LOG_ERR
+ , "Async echoclient: EOF(2)"
+ " iter %d", rdcount+1);
+ EOFcheck(chan);
+ return errcount;
+ }
+ continue;
+
+ }
+
+
+ ++wrcount;
+ /*fprintf(stderr, "x"); */
+ }
+ if (rdcount >= repcount) {
+ break;
+ }
+ /*
+ * At this point it is possible that the POLLOUT bit
+ * being on is no longer necessary, but this will only
+ * cause an extra (false) output poll iteration at worst...
+ * This is because (IIRC) both is_sending_blocked(), and
+ * is_message_pending() both perform a resume_io().
+ * This might be confusing, but -- oh well...
+ */
+
+ /*
+ fprintf(stderr, "P");
+ cl_log(LOG_INFO, "poll[%d, 0x%x]"
+ , pf[0].fd, pf[0].events);
+ cl_log(LOG_DEBUG, "poll[%d, 0x%x]..."
+ , pf[0].fd, pf[0].events);
+ fprintf(stderr, "%%");
+ cl_log(LOG_DEBUG, "CallingPollFunc()");
+ */
+ rc = PollFunc(pf, nfd, -1);
+
+ /* Bad poll? */
+ if (rc <= 0) {
+ cl_perror("Async echoclient: bad poll rc."
+ " %d rc iter %d", rc, rdcount);
+ ++errcount;
+ continue;
+ }
+
+ /* Error indication? */
+ if ((pf[0].revents & (POLLERR|POLLNVAL)) != 0) {
+ cl_log(LOG_ERR
+ , "Async echoclient: bad poll revents."
+ " revents: 0x%x iter %d", pf[0].revents, rdcount);
+ ++errcount;
+ continue;
+ }
+
+ /* HUP without input... Premature EOF... */
+ if ((pf[0].revents & POLLHUP)
+ && ((pf[0].revents&POLLIN) == 0)) {
+ cl_log(LOG_ERR
+ , "Async echoclient: premature pollhup."
+ " revents: 0x%x iter %d", pf[0].revents, rdcount);
+ EOFcheck(chan);
+ ++errcount;
+ continue;
+ }
+
+ /* Error indication? */
+ if (nfd > 1
+ && (pf[1].revents & (POLLERR|POLLNVAL)) != 0) {
+ cl_log(LOG_ERR
+ , "Async echoclient: bad poll revents[1]."
+ " revents: 0x%x iter %d", pf[1].revents, rdcount);
+ ++errcount;
+ continue;
+ }
+
+ /* Output unblocked (only) ? */
+ if (pf[nfd-1].revents & POLLOUT) {
+ /*fprintf(stderr, "R");*/
+ chan->ops->resume_io(chan);
+ }else if ((pf[0].revents & POLLIN) == 0) {
+ /* Neither I nor O available... */
+ cl_log(LOG_ERR
+ , "Async echoclient: bad events."
+ " revents: 0x%x iter %d", pf[0].revents, rdcount);
+ ++errcount;
+ }
+ }
+ cl_poll_ignore(rfd);
+ cl_poll_ignore(wfd);
+ cl_log(LOG_INFO, "Async echoclient: %d errors, %d reads, %d writes",
+ errcount, rdcount, wrcount);
+#if 0
+ cl_log(LOG_INFO, "%d destroying channel 0x%lx",getpid(), (unsigned long)chan);
+#endif
+
+
+ chan->ops->waitout(chan);
+
+ chan->ops->destroy(chan); chan = NULL;
+ return errcount;
+}
+
+
+struct iterinfo {
+ int wcount;
+ int rcount;
+ int errcount;
+ IPC_Channel* chan;
+ int max;
+ gboolean sendingsuspended;
+};
+
+static GMainLoop* loop = NULL;
+
+
+
+
+static gboolean
+s_send_msg(gpointer data)
+{
+ struct iterinfo*i = data;
+ IPC_Message* wmsg;
+ int rc;
+
+ ++i->wcount;
+
+ wmsg = i->chan->ops->new_ipcmsg(i->chan, NULL, data_size, NULL);
+ echomsgbody(wmsg->msg_body, data_size, i->wcount, &wmsg->msg_len);
+
+ /*cl_log(LOG_INFO, "s_send_msg: sending out %d", i->wcount);*/
+
+ if ((rc = i->chan->ops->send(i->chan, wmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "s_send_msg: send failed"
+ " %d rc iter %d"
+ , rc, i->wcount);
+ cl_log(LOG_ERR
+ , "s_send_msg: channel status: %d qlen: %ld"
+ , i->chan->ch_status
+ , (long)i->chan->send_queue->current_qlen);
+ ++i->errcount;
+ if (i->chan->ch_status != IPC_CONNECT) {
+ cl_log(LOG_ERR, "s_send_msg: Exiting.");
+ return FALSE;
+ }
+ if (i->errcount >= MAXERRORS) {
+ g_main_quit(loop);
+ return FALSE;
+ }
+ }
+ return !i->sendingsuspended?i->wcount < i->max: FALSE;
+}
+
+
+
+
+static void
+mainloop_low_flow_callback(IPC_Channel* ch, void* userdata)
+{
+
+ struct iterinfo* i = (struct iterinfo*) userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ if (i->sendingsuspended){
+ i->sendingsuspended = FALSE;
+ g_idle_add(s_send_msg, i);
+ }
+
+ return;
+
+}
+
+static void
+mainloop_high_flow_callback(IPC_Channel* ch, void* userdata)
+{
+ struct iterinfo* i = (struct iterinfo*) userdata;
+
+ if (userdata == NULL){
+ cl_log(LOG_ERR, "userdata is NULL");
+ return;
+ }
+
+ i->sendingsuspended = TRUE;
+
+}
+
+
+static gboolean
+s_rcv_msg(IPC_Channel* chan, gpointer data)
+{
+ struct iterinfo*i = data;
+
+ i->errcount += checkinput(chan, "s_rcv_msg", &i->rcount, i->max);
+
+ if (chan->ch_status == IPC_DISCONNECT
+ || i->rcount >= i->max || i->errcount > MAXERRORS) {
+ if (i->rcount < i->max) {
+ ++i->errcount;
+ cl_log(LOG_INFO, "Early exit from s_rcv_msg");
+ }
+ g_main_quit(loop);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+checkmsg(IPC_Message* rmsg, const char * who, int rcount)
+{
+ char *str;
+ size_t len;
+
+ if (!(str = malloc(data_size))) {
+ cl_log(LOG_ERR, "Out of memory");
+ exit(1);
+ }
+
+ echomsgbody(str, data_size, rcount, &len);
+
+ if (rmsg->msg_len != len) {
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: length mismatch"
+ " [expected %u, got %lu] iteration %d"
+ , who, (unsigned)len
+ , (unsigned long)rmsg->msg_len
+ , rcount);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: expecting [%s]"
+ , who, str);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: got [%s] instead"
+ , who, (const char *)rmsg->msg_body);
+ return FALSE;
+ }
+ if (strncmp(rmsg->msg_body, str, len) != 0) {
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: data mismatch"
+ ". input iteration %d"
+ , who, rcount);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: expecting [%s]"
+ , who, str);
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: got [%s] instead"
+ , who, (const char *)rmsg->msg_body);
+ return FALSE;
+#if 0
+ }else if (strcmp(who, "s_rcv_msg") == 0) {
+#if 0
+
+ || strcmp(who, "s_echo_msg") == 0) {
+#endif
+ cl_log(LOG_ERR
+ , "checkmsg[%s]: data Good"
+ "! input iteration %d"
+ , who, rcount);
+#endif
+ }
+
+ free(str);
+
+ return TRUE;
+}
+
+static gboolean
+s_echo_msg(IPC_Channel* chan, gpointer data)
+{
+ struct iterinfo* i = data;
+ int rc;
+ IPC_Message* rmsg;
+
+ while (chan->ops->is_message_pending(chan)) {
+ if (chan->ch_status == IPC_DISCONNECT) {
+ break;
+ }
+
+ if ((rc = chan->ops->recv(chan, &rmsg)) != IPC_OK) {
+ cl_log(LOG_ERR
+ , "s_echo_msg: recv failed %d rc iter %d"
+ " errno=%d"
+ , rc, i->rcount+1, errno);
+ cl_perror("recv");
+ ++i->errcount;
+ goto retout;
+ }
+ i->rcount++;
+ if (!checkmsg(rmsg, "s_echo_msg", i->rcount)) {
+ ++i->errcount;
+ }
+
+
+
+ /*cl_log(LOG_INFO, "s_echo_msg: rcount= %d, wcount =%d", i->rcount, i->wcount);*/
+
+
+ do {
+ rc = chan->ops->send(chan, rmsg);
+
+ }while (rc != IPC_OK && chan->ch_status == IPC_CONNECT);
+
+ if (chan->ch_status != IPC_CONNECT){
+ cl_log(LOG_ERR,
+ "s_echo_msg: send failed %d rc iter %d qlen %ld",
+ rc, i->rcount, (long)chan->send_queue->current_qlen);
+ cl_perror("send");
+ i->errcount ++;
+
+ }
+
+ i->wcount+=1;
+ /*cl_log(LOG_INFO, "s_echo_msg: end of this ite");*/
+ }
+ retout:
+ /*fprintf(stderr, "%%");*/
+ if (i->rcount >= i->max || chan->ch_status == IPC_DISCONNECT
+ || i->errcount > MAXERRORS) {
+ chan->ops->waitout(chan);
+ g_main_quit(loop);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+init_iterinfo(struct iterinfo * i, IPC_Channel* chan, int max)
+{
+ memset(i, 0, sizeof(*i));
+ i->chan = chan;
+ i->max = max;
+ i->sendingsuspended = FALSE;
+}
+
+static int
+mainloop_server(IPC_Channel* chan, int repcount)
+{
+ struct iterinfo info;
+ guint sendmsgsrc;
+
+
+
+ loop = g_main_new(FALSE);
+ init_iterinfo(&info, chan, repcount);
+
+ chan->ops->set_high_flow_callback(chan, mainloop_high_flow_callback, &info);
+ chan->ops->set_low_flow_callback(chan, mainloop_low_flow_callback, &info);
+ chan->high_flow_mark = 20;
+ chan->low_flow_mark = 2;
+
+ sendmsgsrc = g_idle_add(s_send_msg, &info);
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan
+ , FALSE, s_rcv_msg, &info, NULL);
+ cl_log(LOG_INFO, "Mainloop echo server: %d reps pid %d.", repcount, (int)getpid());
+ g_main_run(loop);
+ g_main_destroy(loop);
+ g_source_remove(sendmsgsrc);
+ loop = NULL;
+ cl_log(LOG_INFO, "Mainloop echo server: %d errors", info.errcount);
+ return info.errcount;
+}
+static int
+mainloop_client(IPC_Channel* chan, int repcount)
+{
+ struct iterinfo info;
+ loop = g_main_new(FALSE);
+ init_iterinfo(&info, chan, repcount);
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan
+ , FALSE, s_echo_msg, &info, NULL);
+ cl_log(LOG_INFO, "Mainloop echo client: %d reps pid %d.", repcount, (int)getpid());
+ g_main_run(loop);
+ g_main_destroy(loop);
+ loop = NULL;
+ cl_log(LOG_INFO, "Mainloop echo client: %d errors, %d read %d written"
+ , info.errcount, info.rcount, info.wcount);
+ return info.errcount;
+}
diff --git a/lib/clplumbing/ipctransient.h b/lib/clplumbing/ipctransient.h
new file mode 100644
index 0000000..9c1746c
--- /dev/null
+++ b/lib/clplumbing/ipctransient.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+#undef _GNU_SOURCE /* in case it was defined on the command line */
+#define _GNU_SOURCE
+#include <lha_internal.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_poll.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <errno.h>
+
+#define MAXERRORS 1000
+#define MAX_IPC_FAIL 10
+#define FIFO_LEN 1024
+
+extern const char *procname;
+
+extern const char *commdir;
+
+void trans_getargs(int argc, char **argv);
+
+void default_ipctest_input_destroy(gpointer user_data);
+
+IPC_Message * create_simple_message(const char *text, IPC_Channel *ch);
diff --git a/lib/clplumbing/ipctransientclient.c b/lib/clplumbing/ipctransientclient.c
new file mode 100644
index 0000000..080acf2
--- /dev/null
+++ b/lib/clplumbing/ipctransientclient.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <ipctransient.h>
+
+#define MAX_MESSAGES 3
+static char *messages[MAX_MESSAGES];
+
+IPC_Message *create_simple_message(const char *text, IPC_Channel *ch);
+IPC_Channel *init_client_ipctest_comms(
+ const char *child, gboolean (*dispatch)(
+ IPC_Channel* source_data, gpointer user_data),
+ void *user_data);
+gboolean transient_client_callback(IPC_Channel* server, void* private_data);
+void client_send_message(
+ const char *message_text, IPC_Channel *server_channel, int iteration);
+
+#define MAXTSTMSG 1000
+
+int
+main(int argc, char ** argv)
+{
+ int lpc =0, iteration=0;
+ GMainLoop* client_main = NULL;
+ IPC_Channel *server_channel = NULL;
+
+ trans_getargs(argc, argv);
+
+ cl_log_set_entity(procname);
+ cl_log_enable_stderr(TRUE);
+
+ /* give the server a chance to start */
+ cl_log(LOG_INFO, "#--#--#--#--# Beginning test run %d against server %d...", lpc, iteration);
+ client_main = g_main_new(FALSE);
+
+ /* connect, send messages */
+ server_channel = init_client_ipctest_comms("echo", transient_client_callback, client_main);
+
+ if(server_channel == NULL) {
+ cl_log(LOG_ERR, "[Client %d] Could not connect to server", lpc);
+ return 1;
+ }
+
+ for(lpc = 0; lpc < MAX_MESSAGES; lpc++) {
+ messages[lpc] = (char *)malloc(sizeof(char)*MAXTSTMSG);
+ }
+ snprintf(messages[0], MAXTSTMSG
+ , "%s_%ld%c", "hello", (long)getpid(), '\0');
+ snprintf(messages[1], MAXTSTMSG
+ , "%s_%ld%c", "hello_world", (long)getpid(), '\0');
+ snprintf(messages[2], MAXTSTMSG
+ , "%s_%ld%c", "hello_world_again", (long)getpid(), '\0');
+
+ for(lpc = 0; lpc < MAX_MESSAGES; lpc++) {
+ client_send_message(messages[lpc], server_channel, lpc);
+ }
+
+ server_channel->ops->waitout(server_channel);
+
+ /* wait for the reply by creating a mainloop and running it until
+ * the callbacks are invoked...
+ */
+
+ cl_log(LOG_DEBUG, "Waiting for replies from the echo server");
+ g_main_run(client_main);
+ cl_log(LOG_INFO, "[Iteration %d] Client %d completed successfully", iteration, lpc);
+
+ return 0;
+}
+
+
+IPC_Channel *
+init_client_ipctest_comms(const char *child,
+ gboolean (*dispatch)(IPC_Channel* source_data
+ ,gpointer user_data),
+ void *user_data)
+{
+ IPC_Channel *ch;
+ GHashTable * attrs;
+ int local_sock_len = 2; /* 2 = '/' + '\0' */
+ char *commpath = NULL;
+ static char path[] = IPC_PATH_ATTR;
+
+ local_sock_len += strlen(child);
+ local_sock_len += strlen(commdir);
+
+ commpath = (char*)malloc(sizeof(char)*local_sock_len);
+ if (commpath == NULL){
+ cl_log(LOG_ERR, "%s: allocating memory failed", __FUNCTION__);
+ return NULL;
+ }
+ sprintf(commpath, "%s/%s", commdir, child);
+ commpath[local_sock_len - 1] = '\0';
+
+ cl_log(LOG_DEBUG, "[Client] Attempting to talk on: %s", commpath);
+
+ attrs = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(attrs, path, commpath);
+ ch = ipc_channel_constructor(IPC_ANYTYPE, attrs);
+ g_hash_table_destroy(attrs);
+
+ if (ch == NULL) {
+ cl_log(LOG_ERR, "[Client] Could not access channel on: %s", commpath);
+ return NULL;
+ } else if(ch->ops->initiate_connection(ch) != IPC_OK) {
+ cl_log(LOG_ERR, "[Client] Could not init comms on: %s", commpath);
+ return NULL;
+ }
+
+ G_main_add_IPC_Channel(G_PRIORITY_LOW,
+ ch, FALSE, dispatch, user_data,
+ default_ipctest_input_destroy);
+
+ return ch;
+}
+
+
+gboolean
+transient_client_callback(IPC_Channel* server, void* private_data)
+{
+ int lpc = 0;
+ IPC_Message *msg = NULL;
+ char *buffer = NULL;
+ static int received_responses = 0;
+
+ GMainLoop *mainloop = (GMainLoop*)private_data;
+
+ while(server->ops->is_message_pending(server) == TRUE) {
+ if (server->ch_status == IPC_DISCONNECT) {
+ /* The message which was pending for us is the
+ * new status of IPC_DISCONNECT */
+ break;
+ }
+ if(server->ops->recv(server, &msg) != IPC_OK) {
+ cl_log(LOG_ERR, "[Client] Error while invoking recv()");
+ perror("[Client] Receive failure:");
+ return FALSE;
+ }
+
+ if (msg != NULL) {
+ buffer = (char*)msg->msg_body;
+ cl_log(LOG_DEBUG, "[Client] Got text [text=%s]", buffer);
+ received_responses++;
+
+ if(lpc < MAX_MESSAGES && strcmp(messages[lpc], buffer) != 0)
+ {
+ cl_log(LOG_ERR, "[Client] Received someone else's message [%s] instead of [%s]", buffer, messages[lpc]);
+ }
+ else if(lpc >= MAX_MESSAGES)
+ {
+ cl_log(LOG_ERR, "[Client] Receivedan extra message [%s]", buffer);
+ }
+
+ lpc++;
+ msg->msg_done(msg);
+ } else {
+ cl_log(LOG_ERR, "[Client] No message this time");
+ }
+ }
+
+ if(server->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR, "[Client] Client received HUP");
+ return FALSE;
+ }
+
+ cl_log(LOG_DEBUG, "[Client] Processed %d IPC messages this time, %d total", lpc, received_responses);
+
+ if(received_responses > 2) {
+ cl_log(LOG_INFO, "[Client] Processed %d IPC messages, all done.", received_responses);
+ received_responses = 0;
+ g_main_quit(mainloop);
+ cl_log(LOG_INFO, "[Client] Exiting.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+client_send_message(const char *message_text,
+ IPC_Channel *server_channel,
+ int iteration)
+{
+ IPC_Message *a_message = NULL;
+ if(server_channel->ch_status != IPC_CONNECT) {
+ cl_log(LOG_WARNING, "[Client %d] Channel is in state %d before sending message [%s]",
+ iteration, server_channel->ch_status, message_text);
+ return;
+ }
+
+ a_message = create_simple_message(message_text, server_channel);
+ if(a_message == NULL) {
+ cl_log(LOG_ERR, "Could not create message to send");
+ } else {
+ cl_log(LOG_DEBUG, "[Client %d] Sending message: %s", iteration, (char*)a_message->msg_body);
+ while(server_channel->ops->send(server_channel, a_message) == IPC_FAIL) {
+ cl_log(LOG_ERR, "[Client %d] IPC channel is blocked", iteration);
+ cl_shortsleep();
+ }
+
+ if(server_channel->ch_status != IPC_CONNECT) {
+ cl_log(LOG_WARNING,
+ "[Client %d] Channel is in state %d after sending message [%s]",
+ iteration, server_channel->ch_status, message_text);
+ }
+ }
+}
diff --git a/lib/clplumbing/ipctransientlib.c b/lib/clplumbing/ipctransientlib.c
new file mode 100644
index 0000000..7a6721e
--- /dev/null
+++ b/lib/clplumbing/ipctransientlib.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <ipctransient.h>
+
+/* for basename() on some OSes (e.g. Solaris) */
+#include <libgen.h>
+
+#define WORKING_DIR HA_VARLIBHBDIR
+
+const char *procname = NULL;
+
+const char *commdir = WORKING_DIR;
+
+void
+trans_getargs(int argc, char **argv)
+{
+ int argflag, argerrs;
+
+ procname = basename(argv[0]);
+
+ argerrs = 0;
+ while ((argflag = getopt(argc, argv, "C:")) != EOF) {
+ switch (argflag) {
+ case 'C': /* directory to commpath */
+ commdir = optarg;
+ break;
+ default:
+ argerrs++;
+ break;
+ }
+ }
+ if (argerrs) {
+ fprintf(stderr,
+ "Usage: %s [-C commdir]\n"
+ "\t-C : directory to commpath (default %s)\n",
+ procname, WORKING_DIR);
+ exit(1);
+ }
+
+}
+
+void
+default_ipctest_input_destroy(gpointer user_data)
+{
+ cl_log(LOG_INFO, "default_ipctest_input_destroy:received HUP");
+}
+
+IPC_Message *
+create_simple_message(const char *text, IPC_Channel *ch)
+{
+ IPC_Message *ack_msg = NULL;
+ char *copy_text = NULL;
+
+ if(text == NULL) {
+ cl_log(LOG_ERR, "ERROR: can't create IPC_Message with no text");
+ return NULL;
+ } else if(ch == NULL) {
+ cl_log(LOG_ERR, "ERROR: can't create IPC_Message with no channel");
+ return NULL;
+ }
+
+ ack_msg = (IPC_Message *)malloc(sizeof(IPC_Message));
+ if (ack_msg == NULL){
+ cl_log(LOG_ERR, "create_simple_message:"
+ "allocating memory for IPC_Message failed");
+ return NULL;
+ }
+
+ memset(ack_msg, 0, sizeof(IPC_Message));
+
+ copy_text = strdup(text);
+
+ ack_msg->msg_private = NULL;
+ ack_msg->msg_done = NULL;
+ ack_msg->msg_body = copy_text;
+ ack_msg->msg_ch = ch;
+
+ ack_msg->msg_len = strlen(text)+1;
+
+ return ack_msg;
+}
diff --git a/lib/clplumbing/ipctransientserver.c b/lib/clplumbing/ipctransientserver.c
new file mode 100644
index 0000000..d7ee61d
--- /dev/null
+++ b/lib/clplumbing/ipctransientserver.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <ipctransient.h>
+
+gboolean transient_server_callback(IPC_Channel *client, gpointer user_data);
+gboolean transient_server_connect(IPC_Channel *client_channel, gpointer user_data);
+int init_server_ipc_comms(const char *child,
+ gboolean (*channel_client_connect)(IPC_Channel *newclient, gpointer user_data),
+ void (*channel_input_destroy)(gpointer user_data),
+ gboolean usenormalpoll);
+
+int
+main(int argc, char ** argv)
+{
+ int iteration = 0;
+ GMainLoop* mainloop = NULL;
+
+ trans_getargs(argc, argv);
+
+ cl_log_set_entity(procname);
+ cl_log_enable_stderr(TRUE);
+
+ init_server_ipc_comms("echo", transient_server_connect, default_ipctest_input_destroy, FALSE);
+
+ /* wait for the reply by creating a mainloop and running it until
+ * the callbacks are invoked...
+ */
+ mainloop = g_main_new(FALSE);
+
+ cl_log(LOG_INFO, "#--#--#--# Echo Server %d is active...", iteration);
+ g_main_run(mainloop);
+ cl_log(LOG_INFO, "#--#--#--# Echo Server %d is stopped...", iteration);
+
+ return 0;
+}
+
+
+int
+init_server_ipc_comms(const char *child,
+ gboolean (*channel_client_connect)(IPC_Channel *newclient, gpointer user_data),
+ void (*channel_input_destroy)(gpointer user_data),
+ gboolean usenormalpoll)
+{
+ /* the clients wait channel is the other source of events.
+ * This source delivers the clients connection events.
+ * listen to this source at a relatively lower priority.
+ */
+ mode_t mask;
+ IPC_WaitConnection *wait_ch;
+ GHashTable * attrs;
+ int local_sock_len = 2; /* 2 = '/' + '\0' */
+ char *commpath = NULL;
+ static char path[] = IPC_PATH_ATTR;
+
+ local_sock_len += strlen(child);
+ local_sock_len += strlen(commdir);
+
+ commpath = (char*)malloc(sizeof(char)*local_sock_len);
+ if (commpath == NULL){
+ cl_log(LOG_ERR, "%s: allocating memory failed", __FUNCTION__);
+ exit(1);
+ }
+ snprintf(commpath, local_sock_len, "%s/%s", commdir, child);
+ commpath[local_sock_len - 1] = '\0';
+
+ attrs = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(attrs, path, commpath);
+
+ mask = umask(0);
+ wait_ch = ipc_wait_conn_constructor(IPC_ANYTYPE, attrs);
+ if (wait_ch == NULL){
+ cl_perror("[Server] Can't create wait channel of type %s", IPC_ANYTYPE);
+ exit(1);
+ }
+ mask = umask(mask);
+ g_hash_table_destroy(attrs);
+
+ G_main_add_IPC_WaitConnection(G_PRIORITY_LOW,
+ wait_ch,
+ NULL,
+ FALSE,
+ channel_client_connect,
+ wait_ch, /* user data passed to ?? */
+ channel_input_destroy);
+
+ cl_log(LOG_INFO, "[Server] Listening on: %s", commpath);
+
+/* if (!usenormalpoll) { */
+/* g_main_set_poll_func(cl_glibpoll); */
+/* ipc_set_pollfunc(cl_poll); */
+/* } */
+ return 0;
+}
+
+gboolean
+transient_server_callback(IPC_Channel *client, gpointer user_data)
+{
+ int lpc = 0;
+ IPC_Message *msg = NULL;
+ char *buffer = NULL;
+ IPC_Message *reply = NULL;
+ int llpc = 0;
+
+ cl_log(LOG_DEBUG, "channel: %p", client);
+
+ cl_log(LOG_DEBUG, "Client status %d (disconnect=%d)", client->ch_status, IPC_DISCONNECT);
+
+ while(client->ops->is_message_pending(client)) {
+ if (client->ch_status == IPC_DISCONNECT) {
+ /* The message which was pending for us is that
+ * the IPC status is now IPC_DISCONNECT */
+ break;
+ }
+ if(client->ops->recv(client, &msg) != IPC_OK) {
+ cl_perror("[Server] Receive failure");
+ return FALSE;
+ }
+
+ if (msg != NULL) {
+ lpc++;
+ buffer = (char*)g_malloc(msg->msg_len+1);
+ memcpy(buffer,msg->msg_body, msg->msg_len);
+ buffer[msg->msg_len] = '\0';
+ cl_log(LOG_DEBUG, "[Server] Got xml [text=%s]", buffer);
+
+ reply = create_simple_message(strdup(buffer), client);
+ if (!reply) {
+ cl_log(LOG_ERR, "[Server] Could allocate reply msg.");
+ return FALSE;
+ }
+
+ llpc = 0;
+ while(llpc++ < MAX_IPC_FAIL && client->ops->send(client, reply) == IPC_FAIL) {
+ cl_log(LOG_WARNING, "[Server] ipc channel blocked");
+ cl_shortsleep();
+ }
+
+ if(lpc == MAX_IPC_FAIL) {
+ cl_log(LOG_ERR, "[Server] Could not send IPC, message. Channel is dead.");
+ free(reply);
+ return FALSE;
+ }
+
+ cl_log(LOG_DEBUG, "[Server] Sent reply");
+ msg->msg_done(msg);
+ } else {
+ cl_log(LOG_ERR, "[Server] No message this time");
+ continue;
+ }
+ }
+
+ cl_log(LOG_DEBUG, "[Server] Processed %d messages", lpc);
+
+ cl_log(LOG_DEBUG, "[Server] Client status %d", client->ch_status);
+ if(client->ch_status != IPC_CONNECT) {
+ cl_log(LOG_INFO, "[Server] Server received HUP from child");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+transient_server_connect(IPC_Channel *client_channel, gpointer user_data)
+{
+ /* assign the client to be something, or put in a hashtable */
+ cl_log(LOG_DEBUG, "A client tried to connect... and there was much rejoicing.");
+
+ if(client_channel == NULL) {
+ cl_log(LOG_ERR, "[Server] Channel was NULL");
+ } else if(client_channel->ch_status == IPC_DISCONNECT) {
+ cl_log(LOG_ERR, "[Server] Channel was disconnected");
+ } else {
+ cl_log(LOG_DEBUG, "[Server] Client is %s %p", client_channel == NULL?"NULL":"valid", client_channel);
+ cl_log(LOG_DEBUG, "[Server] Client status %d (disconnect=%d)", client_channel->ch_status, IPC_DISCONNECT);
+
+ cl_log(LOG_DEBUG, "[Server] Adding IPC Channel to main thread.");
+ G_main_add_IPC_Channel(G_PRIORITY_LOW,
+ client_channel,
+ FALSE,
+ transient_server_callback,
+ NULL,
+ default_ipctest_input_destroy);
+ }
+
+ return TRUE;
+}
diff --git a/lib/clplumbing/longclock.c b/lib/clplumbing/longclock.c
new file mode 100644
index 0000000..594c9c5
--- /dev/null
+++ b/lib/clplumbing/longclock.c
@@ -0,0 +1,275 @@
+/*
+ * Longclock operations
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <errno.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/cl_log.h>
+
+static unsigned Hz = 0;
+static longclock_t Lc_Hz;
+static double d_Hz;
+
+
+const longclock_t zero_longclock = 0UL;
+
+#ifndef CLOCK_T_IS_LONG_ENOUGH
+# undef time_longclock
+#endif
+
+#ifdef HAVE_LONGCLOCK_ARITHMETIC
+# undef msto_longclock
+# undef longclockto_ms
+# undef secsto_longclock
+# undef add_longclock
+# undef sub_longclock
+# undef cmp_longclock
+#endif
+
+
+unsigned
+hz_longclock(void)
+{
+ if (Hz == 0) {
+ /* Compute various hz-related constants */
+
+ Hz = sysconf(_SC_CLK_TCK);
+ Lc_Hz = (longclock_t)Hz;
+ d_Hz = (double) Hz;
+ }
+ return Hz;
+}
+
+#ifdef TIMES_ALLOWS_NULL_PARAM
+# define TIMES_PARAM NULL
+#else
+ static struct tms dummy_longclock_tms_struct;
+# define TIMES_PARAM &dummy_longclock_tms_struct
+#endif
+
+unsigned long
+cl_times(void) /* Make times(2) behave rationally on Linux */
+{
+ clock_t ret;
+#ifndef DISABLE_TIMES_KLUDGE
+ int save_errno = errno;
+
+ /*
+ * times(2) really returns an unsigned value ...
+ *
+ * We don't check to see if we got back the error value (-1), because
+ * the only possibility for an error would be if the address of
+ * longclock_dummy_tms_struct was invalid. Since it's a
+ * compiler-generated address, we assume that errors are impossible.
+ * And, unfortunately, it is quite possible for the correct return
+ * from times(2) to be exactly (clock_t)-1. Sigh...
+ *
+ */
+ errno = 0;
+#endif /* DISABLE_TIMES_KLUDGE */
+ ret = times(TIMES_PARAM);
+
+#ifndef DISABLE_TIMES_KLUDGE
+/*
+ * This is to work around a bug in the system call interface
+ * for times(2) found in glibc on Linux (and maybe elsewhere)
+ * It changes the return values from -1 to -4096 all into
+ * -1 and then dumps the -(return value) into errno.
+ *
+ * This totally bizarre behavior seems to be widespread in
+ * versions of Linux and glibc.
+ *
+ * Many thanks to Wolfgang Dumhs <wolfgang.dumhs (at) gmx.at>
+ * for finding and documenting this bizarre behavior.
+ */
+ if (errno != 0) {
+ ret = (clock_t) (-errno);
+ }
+ errno = save_errno;
+#endif /* DISABLE_TIMES_KLUDGE */
+
+ /* sizeof(long) may be larger than sizeof(clock_t).
+ * Don't jump from 0x7fffffff to 0xffffffff80000000
+ * because of sign extension.
+ * We do expect sizeof(clock_t) <= sizeof(long), however.
+ */
+ BUILD_BUG_ON(sizeof(clock_t) > sizeof(unsigned long));
+#define CLOCK_T_MAX (~0UL >> (8*(sizeof(unsigned long) - sizeof(clock_t))))
+ return (unsigned long)ret & CLOCK_T_MAX;
+}
+
+#ifdef CLOCK_T_IS_LONG_ENOUGH
+longclock_t
+time_longclock(void)
+{
+ /* See note below about deliberately ignoring errors... */
+ return (longclock_t)cl_times();
+}
+
+#else /* clock_t is shorter than 64 bits */
+
+#define BITSPERBYTE 8
+#define WRAPSHIFT (BITSPERBYTE*sizeof(clock_t))
+#define WRAPAMOUNT (((longclock_t) 1) << WRAPSHIFT)
+#define MINJUMP ((CLOCK_T_MAX/100UL)*99UL)
+
+longclock_t
+time_longclock(void)
+{
+ /* Internal note: This updates the static fields; care should be
+ * taken to not call a function like cl_log (which internally
+ * calls time_longclock() as well) just before this happens,
+ * because then this can recurse infinitely; that is why the
+ * cl_log call is where it is; found by Simon Graham. */
+ static gboolean calledbefore = FALSE;
+ static unsigned long lasttimes = 0L;
+ static unsigned long callcount = 0L;
+ static longclock_t lc_wrapcount = 0L;
+ unsigned long timesval;
+
+ ++callcount;
+
+ timesval = cl_times();
+
+ if (calledbefore && timesval < lasttimes) {
+ unsigned long jumpbackby = lasttimes - timesval;
+
+ if (jumpbackby < MINJUMP) {
+ /* Kernel weirdness */
+ cl_log(LOG_CRIT
+ , "%s: clock_t from times(2) appears to"
+ " have jumped backwards (in error)!"
+ , __FUNCTION__);
+ cl_log(LOG_CRIT
+ , "%s: old value was %lu"
+ ", new value is %lu, diff is %lu, callcount %lu"
+ , __FUNCTION__
+ , (unsigned long)lasttimes
+ , (unsigned long)timesval
+ , (unsigned long)jumpbackby
+ , callcount);
+ /* Assume jump back was the error and ignore it */
+ /* (i.e., hope it goes away) */
+ }else{
+ /* Normal looking wraparound */
+ /* update last time BEFORE loging as log call
+ can call this routine recursively leading
+ to double update of wrapcount! */
+
+ lasttimes = timesval;
+ lc_wrapcount += WRAPAMOUNT;
+
+ cl_log(LOG_INFO
+ , "%s: clock_t wrapped around (uptime)."
+ , __FUNCTION__);
+ }
+ }
+ else {
+ lasttimes = timesval;
+ calledbefore = TRUE;
+ }
+ return (lc_wrapcount | timesval);
+}
+#endif /* ! CLOCK_T_IS_LONG_ENOUGH */
+
+longclock_t
+msto_longclock(unsigned long ms)
+{
+ unsigned long secs = ms / 1000UL;
+ unsigned long msec = ms % 1000;
+ longclock_t result;
+
+ (void)(Hz == 0 && hz_longclock());
+
+ if (ms == 0) {
+ return (longclock_t)0UL;
+ }
+ result = secs * Lc_Hz + (msec * Lc_Hz)/1000;
+
+ if (result == 0) {
+ result = 1;
+ }
+ return result;
+}
+
+longclock_t
+secsto_longclock(unsigned long Secs)
+{
+ longclock_t secs = Secs;
+
+ (void)(Hz == 0 && hz_longclock());
+
+ return secs * Lc_Hz;
+}
+
+longclock_t
+dsecsto_longclock(double v)
+{
+ (void)(Hz == 0 && hz_longclock());
+
+ return (longclock_t) ((v * d_Hz)+0.5);
+
+}
+
+unsigned long
+longclockto_ms(longclock_t t)
+{
+ (void)(Hz == 0 && hz_longclock());
+
+ if (t == 0) {
+ return 0UL;
+ }
+ return (unsigned long) ((t*1000UL)/Lc_Hz);
+}
+#ifndef CLOCK_T_IS_LONG_ENOUGH
+long
+longclockto_long(longclock_t t)
+{
+ return ((long)(t));
+}
+
+longclock_t
+add_longclock(longclock_t l, longclock_t r)
+{
+ return l + r;
+}
+
+longclock_t
+sub_longclock(longclock_t l, longclock_t r)
+{
+ return l - r;
+}
+
+int
+cmp_longclock(longclock_t l, longclock_t r)
+{
+ if (l < r) {
+ return -1;
+ }
+ if (l > r) {
+ return 1;
+ }
+ return 0;
+}
+#endif /* CLOCK_T_IS_LONG_ENOUGH */
diff --git a/lib/clplumbing/md5.c b/lib/clplumbing/md5.c
new file mode 100644
index 0000000..b893483
--- /dev/null
+++ b/lib/clplumbing/md5.c
@@ -0,0 +1,335 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Cleaned up for heartbeat by
+ * Mitja Sarp <mitja@lysator.liu.se>
+ * Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Pan Jia Ming <jmltc@cn.ibm.com>
+ *
+ */
+
+#include <lha_internal.h>
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h> /* for sprintf() */
+#include <string.h> /* for memcpy() */
+#include <sys/types.h> /* for stupid systems */
+#include <netinet/in.h> /* for ntohl() */
+#include <clplumbing/cl_log.h>
+#include <clplumbing/md5.h>
+
+#define MD5_DIGESTSIZE 16
+#define MD5_BLOCKSIZE 64
+
+typedef struct MD5Context_st {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+}MD5Context;
+
+#define md5byte unsigned char
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+};
+
+void MD5Init(MD5Context *context);
+void MD5Update(MD5Context *context, md5byte const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+
+#ifdef CONFIG_BIG_ENDIAN
+static inline void byteSwap(uint32_t * buf, uint32_t len);
+
+static inline void
+byteSwap(uint32_t * buf, uint32_t len)
+{
+ int i;
+ for (i = 0; i < len; i ++) {
+ uint32_t tmp = buf[i];
+ buf[i] = ( (uint32_t) ((unsigned char *) &tmp)[0] ) |
+ (((uint32_t) ((unsigned char *) &tmp)[1]) << 8) |
+ (((uint32_t) ((unsigned char *) &tmp)[2]) << 16) |
+ (((uint32_t) ((unsigned char *) &tmp)[3]) << 24);
+ }
+}
+#elif defined(CONFIG_LITTLE_ENDIAN)
+ #define byteSwap(buf,words)
+#else
+ #error "Neither CONFIG_BIG_ENDIAN nor CONFIG_LITTLE_ENDIAN defined!"
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301ul;
+ ctx->buf[1] = 0xefcdab89ul;
+ ctx->buf[2] = 0x98badcfeul;
+ ctx->buf[3] = 0x10325476ul;
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+ if (t > len) {
+ memcpy((md5byte *)ctx->in + 64 - t, buf, len);
+ return;
+ }
+ /* First chunk is an odd size */
+ memcpy((md5byte *)ctx->in + 64 - t, buf, t);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(md5byte digest[16], MD5Context *ctx)
+{
+ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
+ md5byte *p = (md5byte *)ctx->in + count;
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0) { /* Padding forces an extra block */
+ memset(p, 0, count + 8);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ p = (md5byte *)ctx->in;
+ count = 56;
+ }
+ memset(p, 0, count);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ MD5Transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 16);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) ((x) ^ (y) ^ (z))
+#define F4(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+ (w += f(x,y,z) + (in), (w) = ((w)<<(s) | (w)>>(32-(s))) + (x))
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478ul, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756ul, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbul, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeul, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faful, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aul, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613ul, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501ul, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8ul, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7aful, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1ul, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beul, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122ul, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193ul, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eul, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821ul, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562ul, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340ul, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51ul, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaul, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dul, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453ul, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681ul, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8ul, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6ul, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6ul, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87ul, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edul, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905ul, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8ul, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9ul, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aul, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942ul, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681ul, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122ul, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cul, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44ul, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9ul, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60ul, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70ul, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6ul, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faul, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085ul, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05ul, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039ul, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5ul, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8ul, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665ul, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244ul, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97ul, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7ul, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039ul, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3ul, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92ul, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dul, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1ul, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4ful, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0ul, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314ul, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1ul, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82ul, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235ul, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbul, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391ul, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+int MD5( const unsigned char *data
+ , unsigned long len
+ , unsigned char *digest)
+{
+ MD5Context context;
+
+ MD5Init(&context);
+ MD5Update(&context, data, len);
+ MD5Final(digest, &context);
+
+ return 0;
+}
+
+int HMAC( const unsigned char * key
+ , unsigned int key_len
+ , const unsigned char * text
+ , unsigned long textlen
+ , unsigned char * digest)
+{
+ MD5Context context;
+ /* inner padding - key XORd with ipad */
+ unsigned char k_ipad[65];
+ /* outer padding - * key XORd with opad */
+ unsigned char k_opad[65];
+ unsigned char tk[MD5_DIGESTSIZE];
+ int i;
+
+ /* if key is longer than MD5_BLOCKSIZE bytes reset it to key=MD5(key) */
+ if (key_len > MD5_BLOCKSIZE) {
+ MD5Context tctx;
+ MD5Init(&tctx);
+ MD5Update(&tctx, (const unsigned char *)key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = (unsigned char *)tk;
+ key_len = MD5_DIGESTSIZE;
+ }
+ /* start out by storing key in pads */
+ memset(k_ipad, 0, sizeof k_ipad);
+ memset(k_opad, 0, sizeof k_opad);
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<MD5_BLOCKSIZE; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ /* perform inner MD5 */
+ MD5Init(&context); /* init context for 1st pass */
+ MD5Update(&context, k_ipad, MD5_BLOCKSIZE); /* start with inner pad */
+ MD5Update(&context, text, textlen); /* then text of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+
+ /* perform outer MD5 */
+ MD5Init(&context); /* init context for 2nd pass */
+ MD5Update(&context, k_opad, MD5_BLOCKSIZE); /* start with outer pad */
+ MD5Update(&context, digest, MD5_DIGESTSIZE); /* then results of 1st hash */
+
+ MD5Final(digest, &context); /* finish up 2nd pass */
+
+ return 0;
+}
diff --git a/lib/clplumbing/mkstemp_mode.c b/lib/clplumbing/mkstemp_mode.c
new file mode 100644
index 0000000..69c080b
--- /dev/null
+++ b/lib/clplumbing/mkstemp_mode.c
@@ -0,0 +1,56 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <clplumbing/mkstemp_mode.h>
+
+
+/*
+ * A slightly safer version of mkstemp(3)
+ *
+ * In this version, the file is initially created mode 0, and then chmod-ed
+ * to the requested permissions. This guarantees that the file is never
+ * open to others beyond the specified permissions at any time.
+ */
+int
+mkstemp_mode(char* template, mode_t filemode)
+{
+
+ mode_t maskval;
+ int fd;
+
+ maskval = umask(0777);
+
+ /* created file should now be mode 0000 */
+ fd = mkstemp(template);
+
+ umask(maskval); /* cannot fail :-) */
+
+ if (fd >= 0) {
+ if (chmod(template, filemode) < 0) {
+ int save = errno;
+ close(fd);
+ errno = save;
+ fd = -1;
+ }
+ }
+ return fd;
+}
diff --git a/lib/clplumbing/netstring_test.c b/lib/clplumbing/netstring_test.c
new file mode 100644
index 0000000..1f498ec
--- /dev/null
+++ b/lib/clplumbing/netstring_test.c
@@ -0,0 +1,255 @@
+/*
+ * netstring_test: Test program for testing the heartbeat binary/struct API
+ *
+ * Copyright (C) 2000 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_signal.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <hb_api_core.h>
+#include <hb_api.h>
+
+/*
+ * A heartbeat API test program...
+ */
+
+void NodeStatus(const char * node, const char * status, void * private);
+void LinkStatus(const char * node, const char *, const char *, void*);
+void gotsig(int nsig);
+
+void
+NodeStatus(const char * node, const char * status, void * private)
+{
+ cl_log(LOG_NOTICE, "Status update: Node %s now has status %s"
+ , node, status);
+}
+
+void
+LinkStatus(const char * node, const char * lnk, const char * status
+, void * private)
+{
+ cl_log(LOG_NOTICE, "Link Status update: Link %s/%s now has status %s"
+ , node, lnk, status);
+}
+
+int quitnow = 0;
+void gotsig(int nsig)
+{
+ (void)nsig;
+ quitnow = 1;
+}
+
+#define BUFSIZE 16
+extern int netstring_format;
+
+int
+main(int argc, char ** argv)
+{
+ struct ha_msg* reply;
+ struct ha_msg* pingreq = NULL;
+ unsigned fmask;
+ ll_cluster_t* hb;
+ int msgcount=0;
+ char databuf[BUFSIZE];
+ int i;
+#if 0
+ char * ctmp;
+ const char * cval;
+ int j;
+#endif
+
+ netstring_format = 0;
+
+ cl_log_set_entity(argv[0]);
+ cl_log_enable_stderr(TRUE);
+ cl_log_set_facility(LOG_USER);
+ hb = ll_cluster_new("heartbeat");
+ cl_log(LOG_INFO, "PID=%ld", (long)getpid());
+ cl_log(LOG_INFO, "Signing in with heartbeat");
+ if (hb->llc_ops->signon(hb, "ping")!= HA_OK) {
+ cl_log(LOG_ERR, "Cannot sign on with heartbeat");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(1);
+ }
+
+ if (hb->llc_ops->set_nstatus_callback(hb, NodeStatus, NULL) !=HA_OK){
+ cl_log(LOG_ERR, "Cannot set node status callback");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(2);
+ }
+
+ if (hb->llc_ops->set_ifstatus_callback(hb, LinkStatus, NULL)!=HA_OK){
+ cl_log(LOG_ERR, "Cannot set if status callback");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(3);
+ }
+
+#if 0
+ fmask = LLC_FILTER_RAW;
+#else
+ fmask = LLC_FILTER_DEFAULT;
+#endif
+ /* This isn't necessary -- you don't need this call - it's just for testing... */
+ cl_log(LOG_INFO, "Setting message filter mode");
+ if (hb->llc_ops->setfmode(hb, fmask) != HA_OK) {
+ cl_log(LOG_ERR, "Cannot set filter mode");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(4);
+ }
+
+ CL_SIGINTERRUPT(SIGINT, 1);
+ CL_SIGNAL(SIGINT, gotsig);
+
+ pingreq = ha_msg_new(0);
+ ha_msg_add(pingreq, F_TYPE, "ping");
+ {
+ struct ha_msg *childmsg;
+ struct ha_msg *grandchildmsg;
+
+ for(i = 0 ;i < BUFSIZE;i ++){
+ databuf[i] = 1 + i ;
+ }
+ databuf[4] = 0;
+
+ ha_msg_addbin(pingreq, "data",databuf , BUFSIZE);
+
+
+ childmsg = ha_msg_new(0);
+ ha_msg_add(childmsg, "name","testchild");
+ ha_msg_addbin(childmsg, "data",databuf , BUFSIZE);
+
+ grandchildmsg = ha_msg_new(0);
+ ha_msg_add(grandchildmsg, "name","grandchild");
+ ha_msg_addstruct(childmsg, "child",grandchildmsg);
+
+ if( ha_msg_addstruct(pingreq, "child", childmsg) != HA_OK){
+ cl_log(LOG_ERR, "adding a child message to the message failed");
+ exit(1);
+ }
+
+ }
+
+ cl_log(LOG_INFO, "printing out the pingreq message:");
+
+ ha_log_message(pingreq);
+ if (hb->llc_ops->sendclustermsg(hb, pingreq) == HA_OK) {
+ cl_log(LOG_INFO, "Sent ping request to cluster");
+ }else{
+ cl_log(LOG_ERR, "PING request FAIL to cluster");
+ }
+ errno = 0;
+ for(; !quitnow && (reply=hb->llc_ops->readmsg(hb, 1)) != NULL;) {
+ const char * type;
+ const char * orig;
+ ++msgcount;
+ if ((type = ha_msg_value(reply, F_TYPE)) == NULL) {
+ type = "?";
+ }
+ if ((orig = ha_msg_value(reply, F_ORIG)) == NULL) {
+ orig = "?";
+ }
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_NOTICE, "Got message %d of type [%s] from [%s]"
+ , msgcount, type, orig);
+
+ if (strcmp(type, "ping") ==0) {
+ int datalen = 0;
+ const char *data;
+ struct ha_msg *childmsg;
+
+ cl_log(LOG_INFO, "****************************************");
+ ha_log_message(reply);
+
+ data = cl_get_binary(reply, "data", &datalen);
+ if(data){
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "%d of data received,data=%s", datalen,data);
+ for(i = 0; i < datalen; i++){
+ if( databuf[i] != data[i]){
+ cl_log(LOG_ERR, "data does not match at %d",i);
+ break;
+ }
+ }
+ if(i == datalen){
+ cl_log(LOG_INFO,"data matches");
+ }
+ }else {
+ cl_log(LOG_WARNING, "cl_get_binary failed");
+ }
+
+ childmsg = cl_get_struct(reply,"child");
+ if(childmsg){
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "child message found");
+ ha_log_message(childmsg);
+ }else{
+ cl_log(LOG_WARNING, "cl_get_struct failed");
+ }
+
+ }
+
+#if 1
+ {
+ struct ha_msg *cpmsg;
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "****************************************************");
+ cl_log(LOG_INFO, "Testing ha_msg_copy():");
+ cpmsg = ha_msg_copy(reply);
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "orginal message is :");
+ cl_log(LOG_INFO, " ");
+ ha_log_message(reply);
+ cl_log(LOG_INFO, " ");
+ cl_log(LOG_INFO, "copied message is: ");
+ cl_log(LOG_INFO, " ");
+ ha_log_message(cpmsg);
+ ha_msg_del(cpmsg);
+ }
+
+ ha_msg_del(reply); reply=NULL;
+#endif
+ }
+
+ if (!quitnow) {
+ cl_log(LOG_ERR, "read_hb_msg returned NULL");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ }
+ if (hb->llc_ops->signoff(hb, TRUE) != HA_OK) {
+ cl_log(LOG_ERR, "Cannot sign off from heartbeat.");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(10);
+ }
+ if (hb->llc_ops->delete(hb) != HA_OK) {
+ cl_log(LOG_ERR, "Cannot delete API object.");
+ cl_log(LOG_ERR, "REASON: %s", hb->llc_ops->errmsg(hb));
+ exit(11);
+ }
+ return 0;
+}
diff --git a/lib/clplumbing/ocf_ipc.c b/lib/clplumbing/ocf_ipc.c
new file mode 100644
index 0000000..c243934
--- /dev/null
+++ b/lib/clplumbing/ocf_ipc.c
@@ -0,0 +1,594 @@
+/*
+ *
+ * ocf_ipc.c: IPC abstraction implementation.
+ *
+ *
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <clplumbing/ipc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <clplumbing/cl_log.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+
+static int num_pool_allocated = 0;
+static int num_pool_freed = 0;
+
+#ifdef IPC_TIME_DEBUG
+struct ha_msg;
+void cl_log_message (int log_level, const struct ha_msg *m);
+int timediff(longclock_t t1, longclock_t t2);
+void ha_msg_del(struct ha_msg* msg);
+void ipc_time_debug(IPC_Channel* ch, IPC_Message* ipcmsg, int whichpos);
+#endif
+
+struct IPC_WAIT_CONNECTION * socket_wait_conn_new(GHashTable* ch_attrs);
+struct IPC_CHANNEL * socket_client_channel_new(GHashTable* ch_attrs);
+
+int (*ipc_pollfunc_ptr)(struct pollfd*, unsigned int, int)
+= (int (*)(struct pollfd*, unsigned int, int)) poll;
+
+/* Set the IPC poll function to the given function */
+void
+ipc_set_pollfunc(int (*pf)(struct pollfd*, unsigned int, int))
+{
+ ipc_pollfunc_ptr = pf;
+}
+
+struct IPC_WAIT_CONNECTION *
+ipc_wait_conn_constructor(const char * ch_type, GHashTable* ch_attrs)
+{
+ if (strcmp(ch_type, "domain_socket") == 0
+ || strcmp(ch_type, IPC_UDS_CRED) == 0
+ || strcmp(ch_type, IPC_ANYTYPE) == 0
+ || strcmp(ch_type, IPC_DOMAIN_SOCKET) == 0) {
+ return socket_wait_conn_new(ch_attrs);
+ }
+ return NULL;
+}
+
+struct IPC_CHANNEL *
+ipc_channel_constructor(const char * ch_type, GHashTable* ch_attrs)
+{
+ if (strcmp(ch_type, "domain_socket") == 0
+ || strcmp(ch_type, IPC_UDS_CRED) == 0
+ || strcmp(ch_type, IPC_ANYTYPE) == 0
+ || strcmp(ch_type, IPC_DOMAIN_SOCKET) == 0) {
+
+ return socket_client_channel_new(ch_attrs);
+ }
+ return NULL;
+}
+
+static int
+gnametonum(const char * gname, int gnlen)
+{
+ char grpname[64];
+ struct group* grp;
+
+ if (isdigit((int) gname[0])) {
+ return atoi(gname);
+ }
+ if (gnlen >= (int)sizeof(grpname)) {
+ return -1;
+ }
+ strncpy(grpname, gname, gnlen);
+ grpname[gnlen] = EOS;
+ if ((grp = getgrnam(grpname)) == NULL) {
+ cl_log(LOG_ERR
+ , "Invalid group name [%s]", grpname);
+ return -1;
+ }
+ return (int)grp->gr_gid;
+}
+
+static int
+unametonum(const char * lname, int llen)
+{
+ char loginname[64];
+ struct passwd* pwd;
+
+ if (llen >= (int)sizeof(loginname)) {
+ cl_log(LOG_ERR
+ , "user id name [%s] is too long", loginname);
+ return -1;
+ }
+ strncpy(loginname, lname, llen);
+ loginname[llen] = EOS;
+
+ if (isdigit((int) loginname[0])) {
+ return atoi(loginname);
+ }
+ if ((pwd = getpwnam(loginname)) == NULL) {
+ cl_log(LOG_ERR
+ , "Invalid user id name [%s]", loginname);
+ return -1;
+ }
+ return (int)pwd->pw_uid;
+}
+
+static GHashTable*
+make_id_table(const char * list, int listlen, int (*map)(const char *, int))
+{
+ GHashTable* ret;
+ const char * id;
+ const char * lastid = list + listlen;
+ int idlen;
+ int idval;
+ static int one = 1;
+
+ ret = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ id = list;
+ while (id < lastid && *id != EOS) {
+ idlen = strcspn(id, ",");
+ if (id+idlen >= lastid) {
+ idlen = (lastid - id);
+ }
+ idval = map(id, idlen);
+ if (idval < 0) {
+ g_hash_table_destroy(ret);
+ return NULL;
+ }
+#if 0
+ cl_log(LOG_DEBUG
+ , "Adding [ug]id %*s [%d] to authorization g_hash_table"
+ , idlen, id, idval);
+#endif
+ g_hash_table_insert(ret, GUINT_TO_POINTER(idval), &one);
+ id += idlen;
+ if (id < lastid) {
+ id += strspn(id, ",");
+ }
+ }
+ return ret;
+}
+
+struct IPC_AUTH*
+ipc_str_to_auth(const char* uidlist, int uidlen, const char* gidlist, int gidlen)
+{
+ struct IPC_AUTH* auth;
+
+ auth = malloc(sizeof(struct IPC_AUTH));
+ if (auth == NULL) {
+ cl_log(LOG_ERR, "Out of memory for IPC_AUTH");
+ return NULL;
+ }
+
+ memset(auth, 0, sizeof(*auth));
+
+ if (uidlist) {
+ auth->uid = make_id_table(uidlist, uidlen, unametonum);
+ if (auth->uid == NULL) {
+ cl_log(LOG_ERR,
+ "Bad uid list [%*s]",
+ uidlen, uidlist);
+ goto errout;
+ }
+ }
+ if (gidlist) {
+ auth->gid = make_id_table(gidlist, gidlen, gnametonum);
+ if (auth->gid == NULL) {
+ cl_log(LOG_ERR ,
+ "Bad gid list [%*s]",
+ gidlen, gidlist);
+ goto errout;
+ }
+ }
+ return auth;
+
+ errout:
+ if (auth->uid) {
+ g_hash_table_destroy(auth->uid);
+ auth->uid = NULL;
+ }
+ if (auth->gid) {
+ g_hash_table_destroy(auth->gid);
+ auth->gid = NULL;
+ }
+ free(auth);
+ auth = NULL;
+ return NULL;
+}
+
+struct IPC_AUTH *
+ipc_set_auth(uid_t * a_uid, gid_t * a_gid, int num_uid, int num_gid)
+{
+ struct IPC_AUTH *temp_auth;
+ int i;
+ static int v = 1;
+
+ temp_auth = malloc(sizeof(struct IPC_AUTH));
+ if (temp_auth == NULL) {
+ cl_log(LOG_ERR, "%s: memory allocation failed",__FUNCTION__);
+ return NULL;
+ }
+ temp_auth->uid = g_hash_table_new(g_direct_hash, g_direct_equal);
+ temp_auth->gid = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ if (num_uid > 0) {
+ for (i=0; i<num_uid; i++) {
+ g_hash_table_insert(temp_auth->uid, GINT_TO_POINTER((gint)a_uid[i])
+ , &v);
+ }
+ }
+
+ if (num_gid > 0) {
+ for (i=0; i<num_gid; i++) {
+ g_hash_table_insert(temp_auth->gid, GINT_TO_POINTER((gint)a_gid[i])
+ , &v);
+ }
+ }
+
+ return temp_auth;
+}
+
+void
+ipc_destroy_auth(struct IPC_AUTH *auth)
+{
+ if (auth != NULL) {
+ if (auth->uid) {
+ g_hash_table_destroy(auth->uid);
+ }
+ if (auth->gid) {
+ g_hash_table_destroy(auth->gid);
+ }
+ free((void *)auth);
+ }
+}
+
+static void
+ipc_bufpool_display(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ return;
+ }
+ cl_log(LOG_INFO, "pool: refcount=%d, startpos=%p, currpos=%p,"
+ "consumepos=%p, endpos=%p, size=%d",
+ pool->refcount, pool->startpos,
+ pool->currpos, pool->consumepos,
+ pool->endpos, pool->size);
+}
+
+void
+ipc_bufpool_dump_stats(void)
+{
+ cl_log(LOG_INFO, "num_pool_allocated=%d, num_pool_freed=%d, diff=%d",
+ num_pool_allocated,
+ num_pool_freed,
+ num_pool_allocated - num_pool_freed);
+}
+
+#define POOLHDR_SIZE \
+ (sizeof(struct ipc_bufpool) + 2*sizeof(struct SOCKET_MSG_HEAD))
+
+struct ipc_bufpool*
+ipc_bufpool_new(int size)
+{
+ struct ipc_bufpool* pool;
+ int totalsize;
+
+ /* there are memories for two struct SOCKET_MSG_HEAD
+ * one for the big message, the other one for the next
+ * message. This code prevents allocating
+ * <big memory> <4k> <big memory><4k> ...
+ * from happening when a client sends big messages
+ * constantly*/
+
+ totalsize = size + POOLHDR_SIZE;
+
+ if (totalsize < POOL_SIZE) {
+ totalsize = POOL_SIZE;
+ }
+
+ if (totalsize > MAXMSG + POOLHDR_SIZE) {
+ cl_log(LOG_INFO, "ipc_bufpool_new: "
+ "asking for buffer with size %d; "
+ "corrupted data len???", totalsize);
+ return NULL;
+ }
+
+ pool = (struct ipc_bufpool*)malloc(totalsize+1);
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "%s: memory allocation failed", __FUNCTION__);
+ return NULL;
+ }
+ memset(pool, 0, totalsize);
+ pool->refcount = 1;
+ pool->startpos = pool->currpos = pool->consumepos =
+ ((char*)pool) + sizeof(struct ipc_bufpool);
+
+ pool->endpos = ((char*)pool) + totalsize;
+ pool->size = totalsize;
+
+ num_pool_allocated ++ ;
+
+ return pool;
+}
+
+void
+ipc_bufpool_del(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ return;
+ }
+
+ if (pool->refcount > 0) {
+ cl_log(LOG_ERR," ipc_bufpool_del:"
+ " IPC buffer pool reference count > 0");
+ return;
+ }
+
+ memset(pool, 0, pool->size);
+ free(pool);
+ num_pool_freed ++ ;
+}
+
+int
+ipc_bufpool_spaceleft(struct ipc_bufpool* pool)
+{
+ if( pool == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_spaceleft:"
+ " invalid input argument");
+ return 0;
+ }
+ return pool->endpos - pool->currpos;
+}
+
+/* brief free the memory space allocated to msg and destroy msg. */
+
+static void
+ipc_bufpool_msg_done(struct IPC_MESSAGE * msg)
+{
+ struct ipc_bufpool* pool;
+
+ if (msg == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_msg_done: invalid input");
+ return;
+ }
+
+ pool = (struct ipc_bufpool*)msg->msg_private;
+
+ ipc_bufpool_unref(pool);
+ free(msg);
+}
+
+static struct IPC_MESSAGE*
+ipc_bufpool_msg_new(void)
+{
+ struct IPC_MESSAGE * temp_msg;
+
+ temp_msg = malloc(sizeof(struct IPC_MESSAGE));
+ if (temp_msg == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_msg_new:"
+ "allocating new msg failed");
+ return NULL;
+ }
+
+ memset(temp_msg, 0, sizeof(struct IPC_MESSAGE));
+
+ return temp_msg;
+}
+
+static void
+ipcmsg_display(IPC_Message* ipcmsg)
+{
+ if (ipcmsg == NULL) {
+ cl_log(LOG_ERR, "ipcmsg is NULL");
+ return;
+ }
+ cl_log(LOG_INFO, "ipcmsg: msg_len=%lu, msg_buf=%p, msg_body=%p,"
+ "msg_done=%p, msg_private=%p, msg_ch=%p",
+ (unsigned long)ipcmsg->msg_len,
+ ipcmsg->msg_buf,
+ ipcmsg->msg_body,
+ ipcmsg->msg_done,
+ ipcmsg->msg_private,
+ ipcmsg->msg_ch);
+}
+
+/* after a recv call, we have new data
+ * in the pool buf, we need to update our
+ * pool struct to consume it
+ *
+ */
+
+int
+ipc_bufpool_update(struct ipc_bufpool* pool,
+ struct IPC_CHANNEL * ch,
+ int msg_len,
+ IPC_Queue* rqueue)
+{
+ IPC_Message* ipcmsg;
+ struct SOCKET_MSG_HEAD localhead;
+ struct SOCKET_MSG_HEAD* head = &localhead;
+ int nmsgs = 0 ;
+
+ if (rqueue == NULL) {
+ cl_log(LOG_ERR, "ipc_update_bufpool:"
+ "invalid input");
+ return 0;
+ }
+
+ pool->currpos += msg_len;
+
+ while(TRUE) {
+ /*not enough data for head*/
+ if ((int)(pool->currpos - pool->consumepos) < (int)ch->msgpad) {
+ break;
+ }
+
+ memcpy(head, pool->consumepos, sizeof(struct SOCKET_MSG_HEAD));
+
+ if (head->magic != HEADMAGIC) {
+ GList* last = g_list_last(rqueue->queue);
+ cl_log(LOG_ERR, "ipc_bufpool_update: "
+ "magic number in head does not match. "
+ "Something very bad happened, farside pid =%d",
+ ch->farside_pid);
+ cl_log(LOG_ERR, "magic=%x, expected value=%x", head->magic, HEADMAGIC);
+ ipc_bufpool_display(pool);
+ cl_log(LOG_INFO, "nmsgs=%d", nmsgs);
+ /*print out the last message in queue*/
+ if (last) {
+ IPC_Message* m = (IPC_Message*)last;
+ ipcmsg_display(m);
+ }
+ return -1;
+ }
+
+ if ( head->msg_len > MAXMSG) {
+ cl_log(LOG_ERR, "ipc_update_bufpool:"
+ "msg length is corruptted(%d)",
+ head->msg_len);
+ break;
+ }
+
+ if (pool->consumepos + ch->msgpad + head->msg_len
+ > pool->currpos) {
+ break;
+ }
+
+ ipcmsg = ipc_bufpool_msg_new();
+ if (ipcmsg == NULL) {
+ cl_log(LOG_ERR, "ipc_update_bufpool:"
+ "allocating memory for new ipcmsg failed");
+ break;
+
+ }
+ ipcmsg->msg_buf = pool->consumepos;
+ ipcmsg->msg_body = pool->consumepos + ch->msgpad;
+ ipcmsg->msg_len = head->msg_len;
+ ipcmsg->msg_private = pool;
+ ipcmsg->msg_done = ipc_bufpool_msg_done;
+#ifdef IPC_TIME_DEBUG
+ ipc_time_debug(ch,ipcmsg, MSGPOS_RECV);
+#endif
+ rqueue->queue = g_list_append(rqueue->queue, ipcmsg);
+ rqueue->current_qlen ++;
+ nmsgs++;
+
+ pool->consumepos += ch->msgpad + head->msg_len;
+ ipc_bufpool_ref(pool);
+ }
+ return nmsgs;
+}
+
+gboolean
+ipc_bufpool_full(struct ipc_bufpool* pool,
+ struct IPC_CHANNEL* ch,
+ int* dataspaceneeded)
+{
+ struct SOCKET_MSG_HEAD localhead;
+ struct SOCKET_MSG_HEAD* head = &localhead;
+
+ *dataspaceneeded = 0;
+ /* not enough space for head */
+ if ((int)(pool->endpos - pool->consumepos) < (int)ch->msgpad) {
+ return TRUE;
+ }
+
+ /*enough space for head*/
+ if ((int)(pool->currpos - pool->consumepos) >= (int)ch->msgpad) {
+ memcpy(head, pool->consumepos, sizeof(struct SOCKET_MSG_HEAD));
+
+ /* not enough space for data*/
+ if ( pool->consumepos + ch->msgpad + head->msg_len >= pool->endpos) {
+ *dataspaceneeded = head->msg_len;
+ return TRUE;
+ }
+ }
+
+ /* Either we are sure we have enough space
+ * or we cannot tell because we have not received
+ * head yet. But we are sure we have enough space
+ * for head
+ */
+ return FALSE;
+}
+
+int
+ipc_bufpool_partial_copy(struct ipc_bufpool* dstpool,
+ struct ipc_bufpool* srcpool)
+{
+ struct SOCKET_MSG_HEAD localhead;
+ struct SOCKET_MSG_HEAD *head = &localhead;
+ int space_needed;
+ int nbytes;
+
+ if (dstpool == NULL
+ || srcpool == NULL) {
+ cl_log(LOG_ERR, "ipc_bufpool_partial_ipcmsg_cp:"
+ "invalid input");
+ return IPC_FAIL;
+ }
+
+ if (srcpool->currpos - srcpool->consumepos >=
+ (ssize_t)sizeof(struct SOCKET_MSG_HEAD)) {
+
+ memcpy(head, srcpool->consumepos, sizeof(struct SOCKET_MSG_HEAD));
+ space_needed = head->msg_len + sizeof(*head);
+
+ if (space_needed > ipc_bufpool_spaceleft(dstpool)) {
+ cl_log(LOG_ERR, "ipc_bufpool_partial_ipcmsg_cp:"
+ " not enough space left in dst pool,spaced needed=%d",
+ space_needed);
+ return IPC_FAIL;
+ }
+ }
+
+ nbytes = srcpool->currpos - srcpool->consumepos;
+ memcpy(dstpool->consumepos, srcpool->consumepos,nbytes);
+
+ srcpool->currpos = srcpool->consumepos;
+ dstpool->currpos = dstpool->consumepos + nbytes;
+
+ return IPC_OK;
+}
+
+void
+ipc_bufpool_ref(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "ref_pool:"
+ " invalid input");
+ return;
+ }
+ pool->refcount ++;
+}
+
+void
+ipc_bufpool_unref(struct ipc_bufpool* pool)
+{
+ if (pool == NULL) {
+ cl_log(LOG_ERR, "unref_pool:"
+ " invalid input");
+ return;
+ }
+ pool->refcount --;
+ if (pool->refcount <= 0) {
+ ipc_bufpool_del(pool);
+ }
+}
diff --git a/lib/clplumbing/proctrack.c b/lib/clplumbing/proctrack.c
new file mode 100644
index 0000000..f6a9df2
--- /dev/null
+++ b/lib/clplumbing/proctrack.c
@@ -0,0 +1,515 @@
+/*
+ * Process tracking object.
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <time.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/Gmain_timeout.h>
+
+#define DEBUGPROCTRACK debugproctrack
+
+
+int debugproctrack = 0;
+static int LoggingIsEnabled = 1;
+static GHashTable* ProcessTable = NULL;
+static void InitProcTable(void);
+static void ForEachProcHelper(gpointer key, gpointer value
+, void * helper);
+static gboolean TrackedProcTimeoutFunction(gpointer p);
+
+static void
+InitProcTable()
+{
+ if (ProcessTable) {
+ return;
+ }
+
+ ProcessTable = g_hash_table_new(g_direct_hash, g_direct_equal);
+}
+
+/* Create/Log a new tracked process */
+void
+NewTrackedProc(pid_t pid, int isapgrp, ProcTrackLogType loglevel
+, void * privatedata, ProcTrack_ops* ops)
+{
+ ProcTrack* p = g_new(ProcTrack, 1);
+
+ InitProcTable();
+ p->pid = pid;
+ p->isapgrp = isapgrp;
+ p->loglevel = loglevel;
+ p->privatedata = privatedata;
+ p->ops = ops;
+ p->startticks = time_longclock();
+ p->starttime = time(NULL);
+ p->timerid = 0;
+ p->timeoutseq = -1;
+ p->killinfo = NULL;
+
+ g_hash_table_insert(ProcessTable, GINT_TO_POINTER(pid), p);
+
+ /* Tell them that someone registered a process */
+ if (p->ops->procregistered) {
+ p->ops->procregistered(p);
+ }
+}
+
+static struct signal_info_s {
+ int signo;
+ const char * sigdefine;
+ const char* sigwords;
+} signal_info [] = {
+
+#ifdef SIGHUP
+ {SIGHUP, "SIGHUP", "Hangup"},
+#endif
+#ifdef SIGINT
+ {SIGINT, "SIGINT", "Interrupt"},
+#endif
+#ifdef SIGQUIT
+ {SIGQUIT, "SIGQUIT", "Quit"},
+#endif
+#ifdef SIGILL
+ {SIGILL, "SIGILL", "Illegal instruction"},
+#endif
+#ifdef SIGTRAP
+ {SIGTRAP, "SIGTRAP", "Trace"},
+#endif
+#ifdef SIGABRT
+ {SIGABRT, "SIGABRT", "Abort"},
+#endif
+#ifdef SIGIOT
+ {SIGIOT, "SIGIOT", "IOT trap"},
+#endif
+#ifdef SIGBUS
+ {SIGBUS, "SIGBUS", "BUS error"},
+#endif
+#ifdef SIGFPE
+ {SIGFPE, "SIGFPE", "Floating-point exception"},
+#endif
+#ifdef SIGKILL
+ {SIGKILL, "SIGKILL", "Kill, unblockable"},
+#endif
+#ifdef SIGUSR1
+ {SIGUSR1, "SIGUSR1", "User-defined signal 1"},
+#endif
+#ifdef SIGSEGV
+ {SIGSEGV, "SIGSEGV", "Segmentation violation"},
+#endif
+#ifdef SIGUSR2
+ {SIGUSR2, "SIGUSR2", "User-defined signal 2"},
+#endif
+#ifdef SIGPIPE
+ {SIGPIPE, "SIGPIPE", "Broken pipe (POSIX)"},
+#endif
+#ifdef SIGALRM
+ {SIGALRM, "SIGALRM", "Alarm clock (POSIX)"},
+#endif
+#ifdef SIGTERM
+ {SIGTERM, "SIGTERM", "Termination (ANSI)"},
+#endif
+#ifdef SIGSTKFLT
+ {SIGSTKFLT, "SIGSTKFLT", "Stack fault"},
+#endif
+#ifdef SIGCHLD
+ {SIGCHLD, "SIGCHLD", "Child status has changed"},
+#endif
+#ifdef SIGCLD
+ {SIGCLD, "SIGCLD ", "Child status has changed"},
+#endif
+#ifdef SIGCONT
+ {SIGCONT, "SIGCONT", "Continue"},
+#endif
+#ifdef SIGSTOP
+ {SIGSTOP, "SIGSTOP", "Stop, unblockable"},
+#endif
+#ifdef SIGTSTP
+ {SIGTSTP, "SIGTSTP", "Keyboard stop"},
+#endif
+#ifdef SIGTTIN
+ {SIGTTIN, "SIGTTIN", "Background read from tty"},
+#endif
+#ifdef SIGTTOU
+ {SIGTTOU, "SIGTTOU", "Background write to tty"},
+#endif
+#ifdef SIGURG
+ {SIGURG, "SIGURG ", "Urgent condition on socket"},
+#endif
+#ifdef SIGXCPU
+ {SIGXCPU, "SIGXCPU", "CPU limit exceeded"},
+#endif
+#ifdef SIGXFSZ
+ {SIGXFSZ, "SIGXFSZ", "File size limit exceeded"},
+#endif
+#ifdef SIGVTALRM
+ {SIGVTALRM, "SIGVTALRM", "Virtual alarm clock"},
+#endif
+#ifdef SIGPROF
+ {SIGPROF, "SIGPROF", "Profiling alarm clock"},
+#endif
+#ifdef SIGWINCH
+ {SIGWINCH, "SIGWINCH", "Window size change"},
+#endif
+#ifdef SIGPOLL
+ {SIGPOLL, "SIGPOLL", "Pollable event occurred"},
+#endif
+#ifdef SIGIO
+ {SIGIO, "SIGIO", "I/O now possible"},
+#endif
+#ifdef SIGPWR
+ {SIGPWR, "SIGPWR", "Power failure restart"},
+#endif
+#ifdef SIGSYS
+ {SIGSYS, "SIGSYS", "Bad system call"},
+#endif
+};
+static const char *
+signal_name(int signo, const char ** sigdescription)
+{
+ int j;
+ for (j=0; j < DIMOF(signal_info); ++j) {
+ if (signal_info[j].signo == signo) {
+ if (sigdescription) {
+ *sigdescription = signal_info[j].sigwords;
+ }
+ return signal_info[j].sigdefine;
+ }
+ }
+ if (sigdescription) {
+ *sigdescription = NULL;
+ }
+ return NULL;
+}
+
+/* returns TRUE if 'pid' was registered */
+int
+ReportProcHasDied(int pid, int status)
+{
+ ProcTrack* p;
+ int signo=0;
+ int deathbyexit=0;
+ int deathbysig=0;
+ int exitcode=0;
+ int doreport = 0;
+ int debugreporting = 0;
+ const char * type;
+ ProcTrackLogType level;
+#ifdef WCOREDUMP
+ int didcoredump = 0;
+#endif
+ if ((p = GetProcInfo(pid)) == NULL) {
+ if (DEBUGPROCTRACK) {
+ cl_log(LOG_DEBUG
+ , "Process %d died (%d) but is not tracked."
+ , pid, status);
+ }
+ type = "untracked process";
+ level = PT_LOGNONE;
+ }else{
+ type = p->ops->proctype(p);
+ level = p->loglevel;
+ }
+
+ if (WIFEXITED(status)) {
+ deathbyexit=1;
+ exitcode = WEXITSTATUS(status);
+ }else if (WIFSIGNALED(status)) {
+ deathbysig=1;
+ signo = WTERMSIG(status);
+ doreport=1;
+ }
+ switch(level) {
+ case PT_LOGVERBOSE: doreport=1;
+ break;
+
+ case PT_LOGNONE: doreport = 0;
+ break;
+
+ case PT_LOGNORMAL: break;
+ }
+
+ if (!LoggingIsEnabled) {
+ doreport = 0;
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ /* Force a report on all core dumping processes */
+ didcoredump=1;
+ doreport=1;
+ }
+#endif
+ if (DEBUGPROCTRACK && !doreport) {
+ doreport = 1;
+ debugreporting = 1;
+ }
+
+ if (doreport) {
+ if (deathbyexit) {
+ cl_log((exitcode == 0 ? LOG_INFO : LOG_WARNING)
+ , "Managed %s process %d exited with return code %d."
+ , type, pid, exitcode);
+ }else if (deathbysig) {
+ const char * signame = NULL;
+ const char * sigwords = NULL;
+ int logtype;
+ signame = signal_name(signo, &sigwords);
+ logtype = (debugreporting ? LOG_INFO : LOG_WARNING);
+ /*
+ * Processes being killed isn't an error if
+ * we're only logging because of debugging.
+ */
+ if (signame && sigwords) {
+ cl_log(logtype
+ , "Managed %s process %d killed by"
+ " signal %d [%s - %s]."
+ , type, pid, signo
+ , signame, sigwords);
+ }else{
+ cl_log(logtype
+ , "Managed %s process %d killed by signal %d."
+ , type, pid, signo);
+ }
+ }else{
+ cl_log(LOG_ERR, "Managed %s process %d went away"
+ " strangely (!)"
+ , type, pid);
+ }
+ }
+#ifdef WCOREDUMP
+ if (didcoredump) {
+ /* We report ALL core dumps without exception */
+ cl_log(LOG_ERR, "Managed %s process %d dumped core"
+ , type, pid);
+ }
+#endif
+
+ if (p) {
+ RemoveTrackedProcTimeouts(pid);
+ /*
+ * From clplumbing/proctrack.h:
+ * (ProcTrack* p, int status, int signo, int exitcode
+ * , int waslogged);
+ */
+ p->ops->procdied(p, status, signo, exitcode, doreport);
+ if (p->privatedata) {
+ /* They may have forgotten to free something... */
+ cl_log(LOG_ERR, "Managed %s process %d did not"
+ " clean up private data!"
+ , type, pid);
+ }
+ g_hash_table_remove(ProcessTable, GINT_TO_POINTER(pid));
+ g_free(p);
+ }
+
+ return doreport;
+}
+
+/* Return information associated with the given PID (or NULL) */
+ProcTrack*
+GetProcInfo(pid_t pid)
+{
+ return (ProcessTable
+ ? g_hash_table_lookup(ProcessTable, GINT_TO_POINTER(pid))
+ : NULL);
+}
+
+/* "info" is 0-terminated (terminated by a 0 signal) */
+int
+SetTrackedProcTimeouts(pid_t pid, ProcTrackKillInfo* info)
+{
+ long mstimeout;
+ ProcTrack* pinfo;
+ pinfo = GetProcInfo(pid);
+
+ if (pinfo == NULL) {
+ return 0;
+ }
+
+ pinfo->timeoutseq = 0;
+ pinfo->killinfo = info;
+ mstimeout = pinfo->killinfo[0].mstimeout;
+ pinfo->timerid = Gmain_timeout_add(mstimeout
+ , TrackedProcTimeoutFunction
+ , GINT_TO_POINTER(pid));
+ return pinfo->timerid;
+}
+
+void
+RemoveTrackedProcTimeouts(pid_t pid)
+{
+ ProcTrack* pinfo;
+ pinfo = GetProcInfo(pid);
+
+ if (pinfo == NULL) {
+ return;
+ }
+
+ if (pinfo->killinfo && pinfo->timerid) {
+ Gmain_timeout_remove(pinfo->timerid);
+ }
+ pinfo->killinfo = NULL;
+ pinfo->timerid = 0;
+}
+
+static gboolean
+TrackedProcTimeoutFunction(gpointer p)
+{
+ /* This is safe - Pids are relatively small ints */
+ pid_t pid = POINTER_TO_SIZE_T(p); /*pointer cast as int*/
+ ProcTrack* pinfo;
+ int nsig;
+ long mstimeout;
+ int hadprivs;
+
+ pinfo = GetProcInfo(pid);
+
+ if (pinfo == NULL) {
+ cl_log(LOG_ERR, "%s: bad pinfo in call (pid %d)", __FUNCTION__, pid);
+ return FALSE;
+ }
+ if (pinfo->timeoutseq < 0 || pinfo->killinfo == NULL) {
+ cl_log(LOG_ERR
+ , "%s: bad call (pid %d): killinfo (%d, 0x%lx)"
+ , __FUNCTION__, pid
+ , pinfo->timeoutseq
+ , (unsigned long)POINTER_TO_SIZE_T(pinfo->killinfo));
+ return FALSE;
+ }
+
+ pinfo->timerid = 0;
+ nsig = pinfo->killinfo[pinfo->timeoutseq].signalno;
+
+ if (nsig == 0) {
+ if (CL_PID_EXISTS(pid)) {
+ cl_log(LOG_ERR
+ , "%s: %s process (PID %d) will not die!"
+ , __FUNCTION__
+ , pinfo->ops->proctype(pinfo)
+ , (int)pid);
+ }
+ return FALSE;
+ }
+ pinfo->timeoutseq++;
+ cl_log(LOG_WARNING, "%s process (PID %d) timed out (try %d)"
+ ". Killing with signal %s (%d)."
+ , pinfo->ops->proctype(pinfo), (int)pid
+ , pinfo->timeoutseq
+ , signal_name(nsig, NULL)
+ , nsig);
+
+ if (pinfo->isapgrp && nsig > 0) {
+ pid = -pid;
+ }
+
+ if (!(hadprivs = cl_have_full_privs())) {
+ return_to_orig_privs();
+ }
+ if (kill(pid, nsig) < 0) {
+ if (errno == ESRCH) {
+ /* Mission accomplished! */
+ cl_log(LOG_INFO, "%s process (PID %d) died before killing (try %d)"
+ , pinfo->ops->proctype(pinfo), (int)pid
+ , pinfo->timeoutseq);
+ return FALSE;
+ }else{
+ cl_perror("%s: kill(%d,%d) failed"
+ , __FUNCTION__, pid, nsig);
+ }
+ }
+ if (!hadprivs) {
+ return_to_dropped_privs();
+ }
+ mstimeout = pinfo->killinfo[pinfo->timeoutseq].mstimeout;
+ pinfo->timerid = Gmain_timeout_add(mstimeout
+ , TrackedProcTimeoutFunction
+ , p);
+ if (pinfo->timerid <= 0) {
+ cl_log(LOG_ERR, "%s: Could not add new kill timer [%u]"
+ , __FUNCTION__, pinfo->timerid);
+ kill(pid, SIGKILL);
+ }
+ if (debugproctrack) {
+ cl_log(LOG_DEBUG, "%s process (PID %d) scheduled to be killed again"
+ " (try %d) in %ld ms [timerid %u]"
+ , pinfo->ops->proctype(pinfo), (int)pid
+ , pinfo->timeoutseq
+ , mstimeout
+ , pinfo->timerid);
+ }
+ return FALSE;
+}
+
+/* Helper struct to allow us to stuff 3 args into one ;-) */
+struct prochelper {
+ ProcTrack_ops* type;
+ ProcTrackFun fun;
+ void* data;
+};
+
+/* Helper function to call user's function with right args... */
+static void
+ForEachProcHelper(gpointer key, gpointer value, void * helper)
+{
+ ProcTrack* p = value;
+ struct prochelper* ph = helper;
+
+ if (ph->type != NULL && ph->type != p->ops) {
+ return;
+ }
+
+ ph->fun(p, ph->data);
+}
+/*
+ * Iterate over the set of tracked processes.
+ * If proctype is NULL, then walk through them all, otherwise only those
+ * of the given type.
+ */
+void
+ForEachProc(ProcTrack_ops* proctype, ProcTrackFun f, void * data)
+{
+ struct prochelper ph;
+
+ InitProcTable();
+ ph.fun = f;
+ ph.type = proctype;
+ ph.data = data;
+ g_hash_table_foreach(ProcessTable, ForEachProcHelper, &ph);
+}
+
+void
+DisableProcLogging()
+{
+ LoggingIsEnabled = 0;
+}
+
+void
+EnableProcLogging()
+{
+ LoggingIsEnabled = 1;
+}
diff --git a/lib/clplumbing/realtime.c b/lib/clplumbing/realtime.c
new file mode 100644
index 0000000..9271204
--- /dev/null
+++ b/lib/clplumbing/realtime.c
@@ -0,0 +1,354 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stddef.h>
+/* The BSD's do not use malloc.h directly. */
+/* They use stdlib.h instead */
+#ifndef BSD
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif
+#endif
+#include <unistd.h>
+#ifdef _POSIX_MEMLOCK
+# include <sys/mman.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+#include <string.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/uids.h>
+#include <time.h>
+#include <errno.h>
+
+static gboolean cl_realtimepermitted = TRUE;
+static void cl_rtmalloc_setup(void);
+
+#define HOGRET 0xff
+/*
+ * Slightly wacko recursive function to touch requested amount
+ * of stack so we have it pre-allocated inside our realtime code
+ * as per suggestion from mlockall(2)
+ */
+#ifdef _POSIX_MEMLOCK
+static unsigned char
+cl_stack_hogger(unsigned char * inbuf, int kbytes)
+{
+ unsigned char buf[1024];
+
+ if (inbuf == NULL) {
+ memset(buf, HOGRET, sizeof(buf));
+ }else{
+ memcpy(buf, inbuf, sizeof(buf));
+ }
+
+ if (kbytes > 0) {
+ return cl_stack_hogger(buf, kbytes-1);
+ }else{
+ return buf[sizeof(buf)-1];
+ }
+/* #else
+ return HOGRET;
+*/
+}
+#endif
+/*
+ * We do things this way to hopefully defeat "smart" malloc code which
+ * handles large mallocs as special cases using mmap().
+ */
+static void
+cl_malloc_hogger(int kbytes)
+{
+ long size = kbytes * 1024;
+ int chunksize = 1024;
+ long nchunks = (int)(size / chunksize);
+ int chunkbytes = nchunks * sizeof(void *);
+ void** chunks;
+ int j;
+
+#ifdef HAVE_MALLOPT
+# ifdef M_MMAP_MAX
+ /* Keep malloc from using mmap */
+ mallopt(M_MMAP_MAX, 0);
+#endif
+# ifdef M_TRIM_THRESHOLD
+ /* Keep malloc from giving memory back to the system */
+ mallopt(M_TRIM_THRESHOLD, -1);
+# endif
+#endif
+ chunks=malloc(chunkbytes);
+ if (chunks == NULL) {
+ cl_log(LOG_INFO, "Could not preallocate (%d) bytes"
+ , chunkbytes);
+ return;
+ }
+ memset(chunks, 0, chunkbytes);
+
+ for (j=0; j < nchunks; ++j) {
+ chunks[j] = malloc(chunksize);
+ if (chunks[j] == NULL) {
+ cl_log(LOG_INFO, "Could not preallocate (%d) bytes"
+ , chunksize);
+ }else{
+ memset(chunks[j], 0, chunksize);
+ }
+ }
+ for (j=0; j < nchunks; ++j) {
+ if (chunks[j]) {
+ free(chunks[j]);
+ chunks[j] = NULL;
+ }
+ }
+ free(chunks);
+ chunks = NULL;
+}
+
+/*
+ * Make us behave like a soft real-time process.
+ * We need scheduling priority and being locked in memory.
+ * If you ask us nicely, we'll even grow the stack and heap
+ * for you before locking you into memory ;-).
+ */
+void
+cl_make_realtime(int spolicy, int priority, int stackgrowK, int heapgrowK)
+{
+#ifdef DEFAULT_REALTIME_POLICY
+ struct sched_param sp;
+ int staticp;
+#endif
+
+ if (heapgrowK > 0) {
+ cl_malloc_hogger(heapgrowK);
+ }
+
+#ifdef _POSIX_MEMLOCK
+ if (stackgrowK > 0) {
+ unsigned char ret;
+ if ((ret=cl_stack_hogger(NULL, stackgrowK)) != HOGRET) {
+ cl_log(LOG_INFO, "Stack hogger failed 0x%x"
+ , ret);
+ }
+ }
+#endif
+ cl_rtmalloc_setup();
+
+ if (!cl_realtimepermitted) {
+ cl_log(LOG_INFO
+ , "Request to set pid %ld to realtime ignored."
+ , (long)getpid());
+ return;
+ }
+
+#ifdef DEFAULT_REALTIME_POLICY
+ if (spolicy < 0) {
+ spolicy = DEFAULT_REALTIME_POLICY;
+ }
+
+ if (priority <= 0) {
+ priority = sched_get_priority_min(spolicy);
+ }
+
+ if (priority > sched_get_priority_max(spolicy)) {
+ priority = sched_get_priority_max(spolicy);
+ }
+
+
+ if ((staticp=sched_getscheduler(0)) < 0) {
+ cl_perror("unable to get scheduler parameters.");
+ }else{
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = priority;
+
+ if (sched_setscheduler(0, spolicy, &sp) < 0) {
+ cl_perror("Unable to set scheduler parameters.");
+ }
+ }
+#endif
+
+#if defined _POSIX_MEMLOCK
+# ifdef RLIMIT_MEMLOCK
+# define THRESHOLD(lim) (((lim))/2)
+ {
+ unsigned long growsize = ((stackgrowK+heapgrowK)*1024);
+ struct rlimit memlocklim;
+
+ getrlimit(RLIMIT_MEMLOCK, &memlocklim); /* Allow for future added fields */
+ memlocklim.rlim_max = RLIM_INFINITY;
+ memlocklim.rlim_cur = RLIM_INFINITY;
+ /* Try and remove memory locking limits -- if we can */
+ if (setrlimit(RLIMIT_MEMLOCK, &memlocklim) < 0) {
+ /* Didn't work - get what we can */
+ getrlimit(RLIMIT_MEMLOCK, &memlocklim);
+ memlocklim.rlim_cur = memlocklim.rlim_max;
+ setrlimit(RLIMIT_MEMLOCK, &memlocklim);
+ }
+
+ /* Could we get 'enough' ? */
+ /* (this is a guess - might not be right if we're not root) */
+ if (getrlimit(RLIMIT_MEMLOCK, &memlocklim) >= 0
+ && memlocklim.rlim_cur != RLIM_INFINITY
+ && (growsize >= THRESHOLD(memlocklim.rlim_cur))) {
+ cl_log(LOG_ERR
+ , "Cannot lock ourselves into memory: System limits"
+ " on locked-in memory are too small.");
+ return;
+ }
+ }
+# endif /*RLIMIT_MEMLOCK*/
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) >= 0) {
+ if (ANYDEBUG) {
+ cl_log(LOG_DEBUG, "pid %d locked in memory.", (int) getpid());
+ }
+
+ } else if(errno == ENOSYS) {
+ const char *err = strerror(errno);
+ cl_log(LOG_WARNING, "Unable to lock pid %d in memory: %s",
+ (int) getpid(), err);
+
+ } else {
+ cl_perror("Unable to lock pid %d in memory", (int) getpid());
+ }
+#endif
+}
+
+void
+cl_make_normaltime(void)
+{
+#ifdef DEFAULT_REALTIME_POLICY
+ struct sched_param sp;
+
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = sched_get_priority_min(SCHED_OTHER);
+ if (sched_setscheduler(0, SCHED_OTHER, &sp) < 0) {
+ cl_perror("unable to (re)set scheduler parameters.");
+ }
+#endif
+#ifdef _POSIX_MEMLOCK
+ /* Not strictly necessary. */
+ munlockall();
+#endif
+}
+
+void
+cl_disable_realtime(void)
+{
+ cl_realtimepermitted = FALSE;
+}
+
+void
+cl_enable_realtime(void)
+{
+ cl_realtimepermitted = TRUE;
+}
+
+/* Give up the CPU for a little bit */
+/* This is similar to sched_yield() but allows lower prio processes to run */
+int
+cl_shortsleep(void)
+{
+ static const struct timespec req = {0,2000001L};
+
+ return nanosleep(&req, NULL);
+}
+
+
+static int post_rt_morecore_count = 0;
+static unsigned long init_malloc_arena = 0L;
+
+#ifdef HAVE_MALLINFO
+# define MALLOC_TOTALSIZE() (((unsigned long)mallinfo().arena)+((unsigned long)mallinfo().hblkhd))
+#else
+# define MALLOC_TOTALSIZE() (0L)
+#endif
+
+
+
+/* Return the number of times we went after more core */
+int
+cl_nonrealtime_malloc_count(void)
+{
+ return post_rt_morecore_count;
+}
+unsigned long
+cl_nonrealtime_malloc_size(void)
+{
+ return (MALLOC_TOTALSIZE() - init_malloc_arena);
+}
+/* Log the number of times we went after more core */
+void
+cl_realtime_malloc_check(void)
+{
+ static int lastcount = 0;
+ static unsigned long oldarena = 0UL;
+
+ if (oldarena == 0UL) {
+ oldarena = init_malloc_arena;
+ }
+
+ if (post_rt_morecore_count > lastcount) {
+
+ if (MALLOC_TOTALSIZE() > oldarena) {
+
+ cl_log(LOG_WARNING,
+ "Performed %d more non-realtime malloc calls.",
+ post_rt_morecore_count - lastcount);
+
+ cl_log(LOG_INFO,
+ "Total non-realtime malloc bytes: %ld",
+ MALLOC_TOTALSIZE() - init_malloc_arena);
+ oldarena = MALLOC_TOTALSIZE();
+
+ }
+
+ lastcount = post_rt_morecore_count;
+ }
+}
+
+#ifdef HAVE___DEFAULT_MORECORE
+
+static void (*our_save_morecore_hook)(void) = NULL;
+static void cl_rtmalloc_morecore_fun(void);
+
+static void
+cl_rtmalloc_morecore_fun(void)
+{
+ post_rt_morecore_count++;
+ if (our_save_morecore_hook) {
+ our_save_morecore_hook();
+ }
+}
+#endif
+
+static void
+cl_rtmalloc_setup(void)
+{
+ static gboolean inityet = FALSE;
+ if (!inityet) {
+ init_malloc_arena = MALLOC_TOTALSIZE();
+#ifdef HAVE___DEFAULT_MORECORE
+ our_save_morecore_hook = __after_morecore_hook;
+ __after_morecore_hook = cl_rtmalloc_morecore_fun;
+ inityet = TRUE;
+#endif
+ }
+ }
diff --git a/lib/clplumbing/replytrack.c b/lib/clplumbing/replytrack.c
new file mode 100644
index 0000000..8c7c38e
--- /dev/null
+++ b/lib/clplumbing/replytrack.c
@@ -0,0 +1,643 @@
+
+/*
+ * Reply tracking library.
+ *
+ * Copyright (c) 2007 Alan Robertson
+ * Author: Alan Robertson <alanr@unix.sh>
+ *
+ ******************************************************************
+ * This library is useful for tracking replies to multicast messages
+ * sent to cluster members. It tracks incremental membership changes
+ * according to any desired criteria, and then keeps track of when
+ * the last expected reply is received according to the dynamically
+ * updated membership as of when the message was sent out.
+ ******************************************************************
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <memory.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/replytrack.h>
+#include <clplumbing/Gmain_timeout.h>
+
+/*
+ * These are the only data items that go in our GHashTables
+ */
+struct rt_node_info {
+ char * nodename;
+ cl_uuid_t nodeid;
+};
+
+struct node_tables {
+
+ GHashTable* uuidmap; /* Keyed by uuid */
+ int uuidcount;
+ GHashTable* namemap; /* Keyed by nodename*/
+ int namecount;
+};
+struct _nodetrack {
+ struct node_tables nt;
+ int refcount;
+ nodetrack_callback_t callback;
+ gpointer user_data;
+ nodetrack_callback_t extra_callback;
+ gpointer ext_data;
+};
+
+/*
+ * Things we use to track outstanding replies
+ * This is the structure used by the replytrack_t typedef
+ */
+struct _replytrack {
+ replytrack_callback_t callback;
+ gpointer user_data;
+ unsigned timerid;
+ struct node_tables tables;
+ gboolean expectingmore;
+ nodetrack_t* membership;
+};
+
+struct _nodetrack_intersection {
+ nodetrack_t** tables;
+ int ntables;
+ nodetrack_callback_t callback;
+ gpointer user_data;
+ nodetrack_t* intersection;
+};
+
+static cl_uuid_t nulluuid;
+static int nodetrack_t_count = 0;
+static int replytrack_t_count = 0;
+static int replytrack_intersection_t_count = 0;
+
+static struct rt_node_info *
+rt_node_info_new(const char * nodename, cl_uuid_t nodeid)
+{
+ struct rt_node_info* ret;
+
+ if (!nodename) {
+ return NULL;
+ }
+ ret = MALLOCT(struct rt_node_info);
+
+ if (!ret) {
+ return ret;
+ }
+ ret->nodename = strdup(nodename);
+ if (!ret->nodename) {
+ free(ret);
+ ret = NULL;
+ return ret;
+ }
+ ret->nodeid = nodeid;
+ return ret;
+}
+
+static void
+rt_node_info_del(struct rt_node_info * ni)
+{
+ if (ni != NULL) {
+ if (ni->nodename != NULL) {
+ free(ni->nodename);
+ }
+ memset(ni, 0, sizeof(*ni));
+ free(ni);
+ }
+}
+
+/*
+ * namehash cannot be NULL, idhash cannot be NULL, and nodename cannot be NULL
+ *
+ * 'id' can be a NULL uuid, in which case it goes into only the name table
+ * 'nodename' can change over time - in which case we update our tables.
+ * It is possible for one nodename to have more than one uuid.
+ * We allow for that.
+ *
+ * Changing the uuid but keeping the nodename the same is considered to be
+ * adding a new node with the same nodename.
+ * Exception: A node with a null uuid is presumed to have acquired a proper
+ * uuid if it is later seen with a non-null UUID
+ */
+
+static gboolean
+del_node_from_hashtables(struct node_tables *t
+, const char * nodename, cl_uuid_t id)
+{
+ struct rt_node_info * entry;
+ if (cl_uuid_is_null(&id)) {
+ if ((entry = g_hash_table_lookup(t->namemap,nodename))!=NULL){
+ g_hash_table_remove(t->namemap, nodename);
+ rt_node_info_del(entry);
+ t->namecount--;
+ }
+ return TRUE;
+ }
+ if ((entry=g_hash_table_lookup(t->uuidmap, &id)) != NULL) {
+ g_hash_table_remove(t->uuidmap, &id);
+ rt_node_info_del(entry);
+ t->uuidcount--;
+ }
+ return TRUE;
+}
+
+
+static gboolean
+add_node_to_hashtables(struct node_tables * t
+, const char * nodename, cl_uuid_t id)
+{
+ struct rt_node_info* idinfo = NULL;
+
+ if (cl_uuid_is_null(&id)) {
+ /* Supplied uuid is the null UUID - insert in name table */
+ struct rt_node_info* ninfo;
+ if (g_hash_table_lookup(t->namemap, nodename) == NULL) {
+ if (NULL == (ninfo = rt_node_info_new(nodename, id))){
+ goto outofmem;
+ }
+ g_hash_table_insert(t->namemap,ninfo->nodename,ninfo);
+ t->namecount++;
+ }
+ return TRUE;
+ }
+
+ /* Supplied uuid is not the null UUID */
+
+ if (g_hash_table_lookup(t->uuidmap,&id) == NULL) {
+ /* See if a corresponding name is in name map */
+ /* If so, delete it - assume uuid was missing before */
+
+ if (g_hash_table_lookup(t->namemap, nodename) != NULL) {
+ del_node_from_hashtables(t, nodename, nulluuid);
+ }
+ /* Not yet in our uuid hash table */
+ idinfo = rt_node_info_new(nodename, id);
+ if (idinfo == NULL) {
+ goto outofmem;
+ }
+ g_hash_table_insert(t->uuidmap, &idinfo->nodeid, idinfo);
+ t->uuidcount++;
+ }
+ return TRUE;
+outofmem:
+ cl_log(LOG_ERR, "%s: out of memory", __FUNCTION__);
+ return FALSE;
+}
+
+static gboolean
+create_new_hashtables(struct node_tables*t)
+{
+ t->namemap = g_hash_table_new(g_str_hash, g_str_equal);
+ if (t->namemap == NULL) {
+ return FALSE;
+ }
+ t->uuidmap = g_hash_table_new(cl_uuid_g_hash, cl_uuid_g_equal);
+ if (t->uuidmap == NULL) {
+ g_hash_table_destroy(t->namemap);
+ t->namemap = NULL;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+hashtable_destroy_rt_node_info(gpointer key, gpointer rti, gpointer unused)
+{
+ rt_node_info_del(rti);
+ rti = key = NULL;
+ return TRUE;
+}
+
+static void
+destroy_map_hashtable(GHashTable*t)
+{
+ g_hash_table_foreach_remove(t, hashtable_destroy_rt_node_info,NULL);
+ g_hash_table_destroy(t);
+ t = NULL;
+}
+
+struct tablehelp {
+ struct node_tables* t;
+ gboolean ret;
+};
+
+static void
+copy_hashtables_helper(gpointer key_unused, gpointer value
+, gpointer user_data)
+{
+ struct tablehelp * th = user_data;
+ struct rt_node_info* ni = value;
+ if (!add_node_to_hashtables(th->t, ni->nodename, ni->nodeid)) {
+ th->ret = FALSE;
+ }
+}
+
+static gboolean
+copy_hashtables(struct node_tables* tin, struct node_tables* tout)
+{
+ struct tablehelp newtables;
+ if (!create_new_hashtables(tout)){
+ return FALSE;
+ }
+ newtables.t = tout;
+ newtables.ret = TRUE;
+
+ g_hash_table_foreach(tout->namemap,copy_hashtables_helper,&newtables);
+ if (!newtables.ret) {
+ return FALSE;
+ }
+ g_hash_table_foreach(tout->uuidmap,copy_hashtables_helper,&newtables);
+ return newtables.ret;
+}
+
+static gboolean mbr_inityet = FALSE;
+static void
+init_global_membership(void)
+{
+ if (mbr_inityet) {
+ return;
+ }
+ mbr_inityet = TRUE;
+ memset(&nulluuid, 0, sizeof(nulluuid));
+}
+
+gboolean /* Call us when an expected replier joins / comes up */
+nodetrack_nodeup(nodetrack_t * mbr, const char * node, cl_uuid_t uuid)
+{
+ gboolean ret;
+ ret = add_node_to_hashtables(&mbr->nt, node, uuid);
+ if (ret && mbr->callback) {
+ mbr->callback(mbr, node, uuid, NODET_UP, mbr->user_data);
+ }
+ if (mbr->extra_callback) {
+ mbr->extra_callback(mbr, node, uuid, NODET_UP,mbr->ext_data);
+ }
+ return ret;
+}
+
+gboolean /* Call us when an expected replier goes down / away */
+nodetrack_nodedown(nodetrack_t* mbr, const char* node, cl_uuid_t uuid)
+{
+ if (mbr->callback) {
+ mbr->callback(mbr, node, uuid, NODET_DOWN, mbr->user_data);
+ }
+ if (mbr->extra_callback) {
+ mbr->extra_callback(mbr, node,uuid,NODET_DOWN,mbr->ext_data);
+ }
+ return del_node_from_hashtables(&mbr->nt, node, uuid);
+}
+
+/* This function calls the user's timeout callback */
+static gboolean
+replytrack_timeout_helper(gpointer rldata)
+{
+ replytrack_t* rl = rldata;
+ rl->expectingmore = FALSE;
+ rl->timerid = 0;
+ if (rl->callback) {
+ rl->callback(rl, rl->user_data, REPLYT_TIMEOUT);
+ }
+ return FALSE;
+}
+
+replytrack_t* /* replytrack_t constructor */
+replytrack_new(nodetrack_t * membership
+, replytrack_callback_t callback
+, unsigned long timeout_ms
+, gpointer user_data)
+{
+ replytrack_t* ret = MALLOCT(replytrack_t);
+ if (!ret) {
+ return ret;
+ }
+ if (!copy_hashtables(&membership->nt, &ret->tables)) {
+ free(ret);
+ ret = NULL;
+ return ret;
+ }
+ replytrack_t_count++;
+ ret->membership = membership;
+ ret->membership->refcount++;
+ ret->callback = callback;
+ ret->user_data = user_data;
+ ret->expectingmore = TRUE;
+ ret->timerid = 0;
+ if (timeout_ms != 0 && callback != NULL) {
+ ret->timerid = Gmain_timeout_add(timeout_ms
+ , replytrack_timeout_helper, ret);
+ }
+ return ret;
+}
+
+void /* replytrack_t destructor */
+replytrack_del(replytrack_t * rl)
+{
+ rl->membership->refcount--;
+ replytrack_t_count++;
+ if (rl->expectingmore && rl->timerid > 0) {
+ cl_log(LOG_INFO
+ , "%s: destroying replytrack while still expecting"
+ " %d replies"
+ , __FUNCTION__
+ , (rl->tables.namecount + rl->tables.uuidcount));
+ }
+ if (rl->timerid > 0) {
+ g_source_remove(rl->timerid);
+ rl->timerid = 0;
+ }
+ destroy_map_hashtable(rl->tables.namemap);
+ rl->tables.namemap=NULL;
+ destroy_map_hashtable(rl->tables.uuidmap);
+ rl->tables.uuidmap=NULL;
+ memset(&rl, 0, sizeof(rl));
+ free(rl);
+ rl=NULL;
+}
+
+gboolean /* Call replytrack_gotreply when you receive an expected reply */
+replytrack_gotreply(replytrack_t*rl, const char * node, cl_uuid_t uuid)
+{
+ gboolean lastone;
+ del_node_from_hashtables(&rl->tables, node, uuid);
+ lastone = (rl->tables.namecount + rl->tables.uuidcount) == 0;
+ if (lastone) {
+ rl->expectingmore = FALSE;
+ if (rl->timerid > 0) {
+ g_source_remove(rl->timerid);
+ rl->timerid = 0;
+ }
+ if (rl->callback){
+ rl->callback(rl, rl->user_data, REPLYT_ALLRCVD);
+ }
+ }
+ return lastone;
+}
+
+struct replytrack_iterator_data {
+ replytrack_t* rlist;
+ replytrack_iterator_t f;
+ int count;
+ gpointer user_data;
+};
+
+
+static void /* g_hash_table user-level iteration helper */
+replytrack_iterator_helper(gpointer key_unused, gpointer entry
+, gpointer user_data)
+{
+ struct replytrack_iterator_data* ri = user_data;
+ struct rt_node_info* ni = entry;
+ if (ri && ri->rlist) {
+ ++ri->count;
+ if (ri->f) {
+ ri->f(ri->rlist, ri->user_data
+ , ni->nodename, ni->nodeid);
+ }
+ }
+}
+
+
+
+int /* iterate through the outstanding expected replies */
+replytrack_outstanding_iterate(replytrack_t* rl
+, replytrack_iterator_t i, gpointer user_data)
+{
+ struct replytrack_iterator_data id;
+ id.rlist = rl;
+ id.f = i;
+ id.count = 0;
+ id.user_data = user_data;
+ g_hash_table_foreach(rl->tables.namemap, replytrack_iterator_helper
+ , &id);
+ g_hash_table_foreach(rl->tables.uuidmap, replytrack_iterator_helper
+ , &id);
+ if (id.count != (rl->tables.namecount + rl->tables.uuidcount)) {
+ cl_log(LOG_ERR
+ , "%s: iteration count %d disagrees with"
+ " (namecount %d+uuidcount %d)"
+ , __FUNCTION__, id.count
+ , rl->tables.namecount,rl->tables.uuidcount);
+ }
+ return id.count;
+}
+int /* return count of outstanding expected replies */
+replytrack_outstanding_count(replytrack_t* rl)
+{
+ return (rl->tables.namecount + rl->tables.uuidcount);
+}
+
+nodetrack_t*
+nodetrack_new(nodetrack_callback_t callback, gpointer user_data)
+{
+ nodetrack_t* ret = MALLOCT(nodetrack_t);
+ if (!mbr_inityet) {
+ init_global_membership();
+ }
+ if (!ret) {
+ return ret;
+ }
+ nodetrack_t_count++;
+ ret->refcount = 0;
+ if (!create_new_hashtables(&ret->nt)) {
+ free(ret);
+ ret = NULL;
+ }
+ ret->user_data = user_data;
+ ret->callback = callback;
+ ret->extra_callback = NULL;
+ ret->ext_data = NULL;
+ return ret;
+}
+void
+nodetrack_del(nodetrack_t * np)
+{
+ if (np->refcount) {
+ cl_log(LOG_ERR
+ , "%s: reply tracking reference count is %d"
+ , __FUNCTION__, np->refcount);
+ }
+ nodetrack_t_count--;
+ destroy_map_hashtable(np->nt.namemap);
+ np->nt.namemap=NULL;
+ destroy_map_hashtable(np->nt.uuidmap);
+ np->nt.uuidmap=NULL;
+ memset(np, 0, sizeof(*np));
+ free(np);
+}
+
+gboolean
+nodetrack_ismember(nodetrack_t* mbr, const char * name, cl_uuid_t u)
+{
+ if (cl_uuid_is_null(&u)) {
+ return(g_hash_table_lookup(mbr->nt.namemap, name) != NULL);
+ }
+ return (g_hash_table_lookup(mbr->nt.uuidmap, &u) != NULL);
+}
+
+struct nodetrack_iterator_data {
+ nodetrack_t* rlist;
+ nodetrack_iterator_t f;
+ int count;
+ gpointer user_data;
+};
+static void /* g_hash_table user-level iteration helper */
+nodetrack_iterator_helper(gpointer key_unused, gpointer entry
+, gpointer user_data)
+{
+ struct nodetrack_iterator_data* ri = user_data;
+ struct rt_node_info* ni = entry;
+ if (ri && ri->rlist) {
+ ++ri->count;
+ if (ri->f) {
+ ri->f(ri->rlist, ri->user_data
+ , ni->nodename, ni->nodeid);
+ }
+ }
+}
+
+int /* iterate through the outstanding expected replies */
+nodetrack_iterate(nodetrack_t* rl
+, nodetrack_iterator_t i, gpointer user_data)
+{
+ struct nodetrack_iterator_data id;
+ id.rlist = rl;
+ id.f = i;
+ id.count = 0;
+ id.user_data = user_data;
+ g_hash_table_foreach(rl->nt.namemap, nodetrack_iterator_helper
+ , &id);
+ g_hash_table_foreach(rl->nt.uuidmap, nodetrack_iterator_helper
+ , &id);
+ if (id.count != (rl->nt.namecount + rl->nt.uuidcount)) {
+ cl_log(LOG_ERR
+ , "%s: iteration count %d disagrees with"
+ " (namecount %d+uuidcount %d)"
+ , __FUNCTION__, id.count
+ , rl->nt.namecount,rl->nt.uuidcount);
+ }
+ return id.count;
+}
+static void
+intersection_callback
+( nodetrack_t * mbr
+, const char * node
+, cl_uuid_t u
+, nodetrack_change_t reason
+, gpointer user_data)
+{
+ nodetrack_intersection_t* it = user_data;
+ int j;
+ gboolean allfound = TRUE;
+
+ if (reason == NODET_DOWN) {
+ if (nodetrack_ismember(it->intersection, node, u)) {
+ nodetrack_nodedown(it->intersection,node,u);
+ }
+ return;
+ }
+ for (j=0; j < it->ntables && allfound; ++j) {
+ if (nodetrack_ismember(it->tables[j], node, u)) {
+ allfound = FALSE;
+ }
+ }
+ if (allfound) {
+ nodetrack_nodeup(it->intersection, node, u);
+ }
+}
+
+struct li_helper {
+ nodetrack_intersection_t* i;
+ gboolean result;
+};
+
+static void
+intersection_init_iterator(nodetrack_t* nt
+, gpointer ghelp
+, const char* node
+, cl_uuid_t uuid)
+{
+ struct li_helper* help = ghelp;
+ gboolean allfound = TRUE;
+ int j;
+
+ for (j=1; allfound && j < help->i->ntables; ++j) {
+ if (!nodetrack_ismember(help->i->tables[j]
+ , node, uuid)) {
+ allfound = FALSE;
+ }
+ }
+ if (allfound) {
+ nodetrack_nodeup(help->i->intersection, node, uuid);
+ }
+}
+
+nodetrack_intersection_t*
+nodetrack_intersection_new(nodetrack_t** tables, int ntables
+, nodetrack_callback_t callback, gpointer user_data)
+{
+ nodetrack_intersection_t* ret;
+ int j;
+ ret = MALLOCT(nodetrack_intersection_t);
+ if (!ret) {
+ return ret;
+ }
+ ret->intersection = nodetrack_new(callback, user_data);
+ if (!ret->intersection) {
+ free(ret);
+ ret = NULL;
+ return ret;
+ }
+ ret->tables = tables;
+ ret->ntables = ntables;
+ ret->callback = callback;
+ ret->user_data = user_data;
+ for (j=0; j < ntables; ++j) {
+ tables[j]->refcount ++;
+ tables[j]->ext_data = ret;
+ tables[j]->extra_callback = intersection_callback;
+ }
+ /* Initialize the intersection membership list */
+ nodetrack_iterate(tables[0], intersection_init_iterator, ret);
+ replytrack_intersection_t_count++;
+ return ret;
+}
+void
+nodetrack_intersection_del(nodetrack_intersection_t* p)
+{
+ int j;
+
+ for (j=0; j < p->ntables; ++j) {
+ p->tables[j]->refcount ++;
+ }
+ nodetrack_del(p->intersection);
+ p->intersection = NULL;
+ memset(p, 0, sizeof(*p));
+ free(p);
+ p = NULL;
+ replytrack_intersection_t_count--;
+}
+
+nodetrack_t*
+nodetrack_intersection_table(nodetrack_intersection_t*p)
+{
+ return p->intersection;
+}
diff --git a/lib/clplumbing/setproctitle.c b/lib/clplumbing/setproctitle.c
new file mode 100644
index 0000000..ffc5481
--- /dev/null
+++ b/lib/clplumbing/setproctitle.c
@@ -0,0 +1,235 @@
+/*
+ * setproctitle.c
+ *
+ * The code in this file, setproctitle.c is heavily based on code from
+ * proftpd, please see the licening information below.
+ *
+ * This file added to the heartbeat tree by Horms <horms@vergenet.net>
+ *
+ * Code to portably change the title of a programme as displayed
+ * by ps(1).
+ *
+ * heartbeat: Linux-HA heartbeat code
+ *
+ * Copyright (C) 1999,2000,2001 Alan Robertson <alanr@unix.sh>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * ProFTPD - FTP server daemon
+ * Copyright (c) 1997, 1998 Public Flood Software
+ * Copyright (C) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
+ * and other respective copyright holders give permission to link this program
+ * with OpenSSL, and distribute the resulting executable, without including
+ * the source code for OpenSSL in the source distribution.
+ */
+
+#include <lha_internal.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#define PF_ARGV_NONE 0
+#define PF_ARGV_NEW 1
+#define PF_ARGV_WRITEABLE 2
+#define PF_ARGV_PSTAT 3
+#define PF_ARGV_PSSTRINGS 4
+
+#if PF_ARGV_TYPE == PF_ARGV_PSTAT
+# include <pstat.h>
+#endif
+
+#include <clplumbing/setproctitle.h>
+
+#if PF_ARGV_TYPE != PF_ARGV_NONE
+static char **Argv = NULL;
+static char *LastArgv = NULL;
+#endif /* PF_ARGV_TYPE != PF_ARGV_NONE */
+
+extern char **environ;
+
+#ifdef HAVE___PROGNAME
+extern char *__progname;
+extern char *__progname_full;
+#endif /* HAVE___PROGNAME */
+
+int
+init_set_proc_title(int argc, char *argv[], char *envp[])
+{
+#if PF_ARGV_TYPE == PF_ARGV_NONE
+ return 0;
+#else
+ int i;
+ int envpsize;
+ char **p;
+
+ /* Move the environment so setproctitle can use the space.
+ */
+ for(i = envpsize = 0; envp[i] != NULL; i++) {
+ envpsize += strlen(envp[i]) + 1;
+ }
+
+ p = (char **) malloc((i + 1) * sizeof(char *));
+ if (p == NULL) {
+ return -1;
+ }
+
+ environ = p;
+
+ for(i = 0; envp[i] != NULL; i++) {
+ environ[i] = strdup(envp[i]);
+ if(environ[i] == NULL) {
+ goto error_environ;
+ }
+ }
+ environ[i] = NULL;
+
+ Argv = argv;
+
+ for(i = 0; i < argc; i++) {
+ if(!i || (LastArgv + 1 == argv[i]))
+ LastArgv = argv[i] + strlen(argv[i]);
+ }
+
+ for(i = 0; envp[i] != NULL; i++) {
+ if((LastArgv + 1) == envp[i]) {
+ LastArgv = envp[i] + strlen(envp[i]);
+ }
+ }
+
+#ifdef HAVE___PROGNAME
+ /* Set the __progname and __progname_full variables so glibc and
+ * company don't go nuts. - MacGyver
+ */
+
+ __progname = strdup("heartbeat");
+ if (__progname == NULL) {
+ goto error_environ;
+ }
+ __progname_full = strdup(argv[0]);
+ if (__progname_full == NULL) {
+ goto error_environ;
+ }
+#endif /* HAVE___PROGNAME */
+
+ return 0;
+
+error_environ:
+ for(i = 0; environ[i] != NULL; i++) {
+ free(environ[i]);
+ }
+ free(environ);
+ return -1;
+#endif /* PF_ARGV_TYPE == PF_ARGV_NONE */
+}
+
+void set_proc_title(const char *fmt,...)
+{
+#if PF_ARGV_TYPE != PF_ARGV_NONE
+ va_list msg;
+ static char statbuf[BUFSIZ];
+
+#ifndef HAVE_SETPROCTITLE
+#if PF_ARGV_TYPE == PF_ARGV_PSTAT
+ union pstun pst;
+#endif /* PF_ARGV_PSTAT */
+ int i,maxlen = (LastArgv - Argv[0]) - 2;
+ char *p;
+#endif /* HAVE_SETPROCTITLE */
+
+ va_start(msg,fmt);
+
+ memset(statbuf, 0, sizeof(statbuf));
+
+
+#ifdef HAVE_SETPROCTITLE
+# if __FreeBSD__ >= 4 && !defined(FREEBSD4_0) && !defined(FREEBSD4_1)
+ /* FreeBSD's setproctitle() automatically prepends the process name. */
+ vsnprintf(statbuf, sizeof(statbuf), fmt, msg);
+
+# else /* FREEBSD4 */
+ /* Manually append the process name for non-FreeBSD platforms. */
+ vsnprintf(statbuf + strlen(statbuf), sizeof(statbuf) - strlen(statbuf),
+ fmt, msg);
+
+# endif /* FREEBSD4 */
+ setproctitle("%s", statbuf);
+
+#else /* HAVE_SETPROCTITLE */
+ /* Manually append the process name for non-setproctitle() platforms. */
+ vsnprintf(statbuf + strlen(statbuf), sizeof(statbuf) - strlen(statbuf),
+ fmt, msg);
+
+#endif /* HAVE_SETPROCTITLE */
+
+ va_end(msg);
+
+#ifdef HAVE_SETPROCTITLE
+ return;
+#else
+ i = strlen(statbuf);
+
+#if PF_ARGV_TYPE == PF_ARGV_NEW
+ /* We can just replace argv[] arguments. Nice and easy.
+ */
+ Argv[0] = statbuf;
+ Argv[1] = NULL;
+#endif /* PF_ARGV_NEW */
+
+#if PF_ARGV_TYPE == PF_ARGV_WRITEABLE
+ /* We can overwrite individual argv[] arguments. Semi-nice.
+ */
+ snprintf(Argv[0], maxlen, "%s", statbuf);
+ p = &Argv[0][i];
+
+ while(p < LastArgv)
+ *p++ = '\0';
+ Argv[1] = NULL;
+#endif /* PF_ARGV_WRITEABLE */
+
+#if PF_ARGV_TYPE == PF_ARGV_PSTAT
+ pst.pst_command = statbuf;
+ pstat(PSTAT_SETCMD, pst, i, 0, 0);
+#endif /* PF_ARGV_PSTAT */
+
+#if PF_ARGV_TYPE == PF_ARGV_PSSTRINGS
+ PS_STRINGS->ps_nargvstr = 1;
+ PS_STRINGS->ps_argvstr = statbuf;
+#endif /* PF_ARGV_PSSTRINGS */
+
+#endif /* HAVE_SETPROCTITLE */
+
+#endif /* PF_ARGV_TYPE != PF_ARGV_NONE */
+}
diff --git a/lib/clplumbing/timers.c b/lib/clplumbing/timers.c
new file mode 100644
index 0000000..c3e99da
--- /dev/null
+++ b/lib/clplumbing/timers.c
@@ -0,0 +1,119 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <clplumbing/timers.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/longclock.h>
+
+int
+setmsrepeattimer(long ms)
+{
+ long secs = ms / 1000L;
+ long usecs = (ms % 1000L)*1000L;
+ struct itimerval nexttime =
+ { {secs, usecs} /* Repeat Interval */
+ , {secs, usecs} /* Timer Value */
+ };
+
+#if 0
+ cl_log(LOG_DEBUG, "Setting repeating timer for %ld ms"
+ , ms);
+#endif
+
+
+ /* Is this right??? */
+ CL_IGNORE_SIG(SIGALRM);
+ return setitimer(ITIMER_REAL, &nexttime, NULL);
+}
+
+int
+setmsalarm(long ms)
+{
+ long secs = ms / 1000L;
+ long usecs = (ms % 1000L)*1000L;
+ struct itimerval nexttime =
+ { {0L, 0L} /* Repeat Interval */
+ , {secs, usecs} /* Timer Value */
+ };
+
+ return setitimer(ITIMER_REAL, &nexttime, NULL);
+}
+
+int
+cancelmstimer(void)
+{
+ struct itimerval nexttime =
+ { {0L, 0L} /* Repeat Interval */
+ , {0L, 0L} /* Timer Value */
+ };
+ return setitimer(ITIMER_REAL, &nexttime, NULL);
+}
+
+
+static int alarmpopped = 0;
+
+static void
+st_timer_handler(int nsig)
+{
+ ++alarmpopped;
+}
+
+/*
+ * Pretty simple:
+ * 1) Set up SIGALRM signal handler
+ * 2) set alarmpopped to FALSE;
+ * 2) Record current time
+ * 3) Call setmsalarm(ms)
+ * 4) Call pause(2)
+ * 5) Call cancelmstimer()
+ * 6) Reset signal handler
+ * 7) See if SIGALRM happened
+ * if so: return zero
+ * if not: get current time, and compute milliseconds left 'til signal
+ * should arrive, and return that...
+ */
+long
+mssleep(long ms)
+{
+ struct sigaction saveaction;
+ longclock_t start;
+ longclock_t finish;
+ unsigned long elapsedms;
+
+ memset(&saveaction, 0, sizeof(saveaction));
+
+ cl_signal_set_simple_handler(SIGALRM, st_timer_handler, &saveaction);
+ alarmpopped = 0;
+ start = time_longclock();
+ setmsalarm(ms);
+ pause();
+ cancelmstimer();
+ cl_signal_set_simple_handler(SIGALRM, saveaction.sa_handler, &saveaction);
+ if (alarmpopped) {
+ return 0;
+ }
+
+ finish = time_longclock();
+ elapsedms = longclockto_ms(sub_longclock(finish, start));
+ return ms - elapsedms;
+}
diff --git a/lib/clplumbing/transient-test.sh b/lib/clplumbing/transient-test.sh
new file mode 100755
index 0000000..7da88bf
--- /dev/null
+++ b/lib/clplumbing/transient-test.sh
@@ -0,0 +1,120 @@
+#!/bin/sh
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+#
+####FIXME
+# Known problems within this testing:
+# 1. Doesn't reflect "someone else's message" problems into error count.
+# 2. Path to "ipctransient{client,server}" not flexible enough.
+
+no_logs=0
+exit_on_error=1
+num_servers=10
+num_clients=10
+client_time=2
+commpath_args=""
+
+cmd=`basename $0`
+USAGE="Usage: $cmd [-c clients] [-s servers] [-t timetowait] [-C commpath]"
+
+while getopts c:s:C:t: c
+do
+ case $c in
+ c)
+ num_clients=$OPTARG
+ ;;
+ s)
+ num_servers=$OPTARG
+ ;;
+ t)
+ client_time=$OPTARG
+ ;;
+ C)
+ commpath_args="-$c $OPTARG"
+ ;;
+ \?)
+ echo $USAGE
+ exit 2
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+total_failed=0
+
+server_failed=0
+server_loop_cnt=0
+while [ $server_loop_cnt != $num_servers ]; do
+ echo "############ DEBUG: Starting server iter $server_loop_cnt"
+ if [ $no_logs = 1 ]; then
+ ./ipctransientserver $commpath_args > /dev/null 2>&1 &
+ else
+ ./ipctransientserver $commpath_args &
+ fi
+ server_pid=$!
+
+ sleep 5
+
+ client_failed=0
+ client_loop_cnt=0
+ while [ $client_loop_cnt != $num_clients ]; do
+ sleep 5
+ echo "############ DEBUG: Starting client iter $client_loop_cnt"
+ if [ $no_logs = 1 ]; then
+ ./ipctransientclient $commpath_args > /dev/null 2>&1 &
+ else
+ ./ipctransientclient $commpath_args &
+ fi
+ client_pid=$!
+ sleep $client_time
+ if [ $exit_on_error = 1 ];then
+ kill -0 $client_pid > /dev/null 2>&1
+ else
+ kill -9 $client_pid > /dev/null 2>&1
+ fi
+ rc=$?
+ if [ $rc = 0 ]; then
+ echo "############ ERROR: Iter $client_loop_cnt failed to receive all messages"
+ client_failed=`expr $client_failed + 1`
+ if [ $exit_on_error = 1 ];then
+ echo "terminating after first error..."
+ exit 0
+ fi
+ else
+ echo "############ INFO: Iter $client_loop_cnt passed"
+ fi
+
+ client_loop_cnt=`expr $client_loop_cnt + 1`;
+ done
+ server_loop_cnt=`expr $server_loop_cnt + 1`;
+ total_failed=`expr $total_failed + $client_failed`
+ kill -9 $server_pid > /dev/null 2>&1
+ rc=$?
+ if [ $rc = 0 ]; then
+ echo "############ ERROR: Server was already dead"
+ server_failed=`expr $server_failed + 1`
+ fi
+done
+
+total_failed=`expr $total_failed + $server_failed`
+
+if [ $total_failed = 0 ]; then
+ echo "INFO: All tests passed"
+else
+ echo "ERROR: $total_failed tests failed"
+fi
+
+exit $total_failed
diff --git a/lib/clplumbing/uids.c b/lib/clplumbing/uids.c
new file mode 100644
index 0000000..0727e1d
--- /dev/null
+++ b/lib/clplumbing/uids.c
@@ -0,0 +1,140 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/coredumps.h>
+#define NOBODY "nobody"
+
+#if defined(HAVE_SETEUID) && defined(HAVE_SETEGID) && \
+ defined(_POSIX_SAVED_IDS)
+# define CAN_DROP_PRIVS 1
+
+#endif
+
+
+
+
+#ifndef CAN_DROP_PRIVS
+ int drop_privs(uid_t uid, gid_t gid) { return 0; }
+ int return_to_orig_privs(void) { return 0; }
+ int return_to_dropped_privs(void) { return 0; }
+ int cl_have_full_privs(void) { return 0; }
+#else
+
+static int anysaveduid = 0;
+static uid_t nobodyuid=-1;
+static gid_t nobodygid=-1;
+static uid_t poweruid=-1;
+static gid_t powergid=-1;
+static int privileged_state = 1;
+
+/* WARNING: uids are unsigned! */
+#define WANT_NOBODY(uid) ((uid) == 0)
+
+int /* Become nobody - and remember our original privileges */
+drop_privs(uid_t uid, gid_t gid)
+{
+ int rc;
+ gid_t curgid = getgid();
+
+ if (!anysaveduid) {
+ poweruid=getuid();
+ powergid=curgid;
+ }
+
+ if (WANT_NOBODY(uid)) {
+ struct passwd* p;
+
+ p = getpwnam(NOBODY);
+
+ if (p == NULL) {
+ return -1;
+ }
+ uid = p->pw_uid;
+ gid = p->pw_gid;
+ }
+ if (setegid(gid) < 0) {
+ return -1;
+ }
+ rc = seteuid(uid);
+
+ if (rc >= 0) {
+ anysaveduid = 1;
+ nobodyuid=uid;
+ nobodygid=gid;
+ privileged_state = 0;
+ }else{
+ /* Attempt to recover original privileges */
+ int err = errno;
+ setegid(curgid);
+ errno = err;
+ }
+ cl_untaint_coredumps();
+ return rc;
+}
+
+int /* Return to our original privileges (if any) */
+return_to_orig_privs(void)
+{
+ int rc;
+ if (!anysaveduid) {
+ return 0;
+ }
+ if (seteuid(poweruid) < 0) {
+ return -1;
+ }
+ privileged_state = 1;
+ rc = setegid(powergid);
+ /*
+ * Sad but true, for security reasons we can't call
+ * cl_untaint_coredumps() here - because it might cause an
+ * leak of confidential information for some applications.
+ * So, the applications need to use either cl_untaint_coredumps()
+ * when they change privileges, or they need to call
+ * cl_set_all_coredump_signal_handlers() to handle core dump
+ * signals and set their privileges to maximum before core
+ * dumping. See the comments in coredumps.c for more details.
+ */
+ return rc;
+}
+
+int /* Return to "nobody" level of privs (if any) */
+return_to_dropped_privs(void)
+{
+ int rc;
+
+ if (!anysaveduid) {
+ return 0;
+ }
+ setegid(nobodygid);
+ privileged_state = 0;
+ rc = seteuid(nobodyuid);
+ /* See note above about dumping core */
+ return rc;
+}
+
+/* Return TRUE if we have full privileges at the moment */
+int
+cl_have_full_privs(void)
+{
+ return privileged_state != 0;
+}
+#endif
diff --git a/lib/lrm/Makefile.am b/lib/lrm/Makefile.am
new file mode 100644
index 0000000..815f92f
--- /dev/null
+++ b/lib/lrm/Makefile.am
@@ -0,0 +1,36 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+lrmdir = $(localstatedir)/lib/heartbeat/lrm
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(GLIBLIB)
+
+lib_LTLIBRARIES = liblrm.la
+liblrm_la_SOURCES = lrm_msg.c clientlib.c racommon.c
+liblrm_la_LDFLAGS = -version-info 2:0:0 $(COMMONLIBS)
+liblrm_la_CFLAGS = $(INCLUDES)
+
+install-exec-local:
+ $(mkinstalldirs) $(DESTDIR)$(lrmdir)
+ -chgrp $(GLUE_DAEMON_GROUP) $(DESTDIR)/$(lrmdir)
+ chmod 770 $(DESTDIR)/$(lrmdir)
diff --git a/lib/lrm/clientlib.c b/lib/lrm/clientlib.c
new file mode 100644
index 0000000..78dcdc8
--- /dev/null
+++ b/lib/lrm/clientlib.c
@@ -0,0 +1,1612 @@
+/*
+ * Client Library for Local Resource Manager API.
+ *
+ * Author: Huang Zhen <zhenh@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <glib.h>
+#include <clplumbing/ipc.h>
+#include <ha_msg.h>
+#include <lrm/lrm_api.h>
+
+#include <lrm/lrm_msg.h>
+
+/* FIXME: Notice: this define should be replaced when merge to the whole pkg*/
+#define LRM_MAXPIDLEN 256
+#define LRM_ID "lrm"
+
+#define LOG_FAIL_create_lrm_msg(msg_type) \
+ cl_log(LOG_ERR, "%s(%d): failed to create a %s message with " \
+ "function create_lrm_msg." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_FAIL_create_lrm_rsc_msg(msg_type) \
+ cl_log(LOG_ERR, "%s(%d): failed to create a %s message with " \
+ "function create_lrm_rsc_msg." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_FAIL_receive_reply(msg_type) \
+ cl_log(LOG_ERR, "%s(%d): failed to receive a reply message of %s." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_FAIL_SEND_MSG(msg_type, chan_name) \
+ cl_log(LOG_ERR, "%s(%d): failed to send a %s message to lrmd " \
+ "via %s channel." \
+ , __FUNCTION__, __LINE__, msg_type, chan_name)
+
+#define LOG_GOT_FAIL_RET(priority, msg_type) \
+ cl_log(priority, "%s(%d): got a return code HA_FAIL from " \
+ "a reply message of %s with function get_ret_from_msg." \
+ , __FUNCTION__, __LINE__, msg_type)
+
+#define LOG_BASIC_ERROR(apiname) \
+ cl_log(LOG_ERR, "%s(%d): %s failed." \
+ , __FUNCTION__, __LINE__, apiname)
+
+#define LOG_FAIL_GET_MSG_FIELD(priority, field_name, msg) \
+ {cl_log(priority, "%s(%d): failed to get the value " \
+ "of field %s from a ha_msg" \
+ , __FUNCTION__, __LINE__, field_name); \
+ cl_log(LOG_INFO, "%s: Message follows:", __FUNCTION__); \
+ cl_log_message(LOG_INFO, (msg)); \
+ }
+
+/* declare the functions used by the lrm_ops structure*/
+static int lrm_signon (ll_lrm_t* lrm, const char * app_name);
+static int lrm_signoff (ll_lrm_t*);
+static int lrm_delete (ll_lrm_t*);
+static int lrm_set_lrm_callback (ll_lrm_t* lrm,
+ lrm_op_done_callback_t op_done_callback_func);
+static GList* lrm_get_rsc_class_supported (ll_lrm_t* lrm);
+static GList* lrm_get_rsc_type_supported (ll_lrm_t* lrm, const char* class);
+static GList* lrm_get_rsc_provider_supported (ll_lrm_t* lrm
+ ,const char* class, const char* type);
+static char* lrm_get_rsc_type_metadata(ll_lrm_t* lrm, const char* class
+ ,const char* type, const char* provider);
+static GHashTable* lrm_get_all_type_metadata(ll_lrm_t*, const char* class);
+static GList* lrm_get_all_rscs (ll_lrm_t* lrm);
+static lrm_rsc_t* lrm_get_rsc (ll_lrm_t* lrm, const char* rsc_id);
+static int lrm_add_rsc (ll_lrm_t*, const char* id, const char* class
+ ,const char* type, const char* provider
+ ,GHashTable* parameter);
+static int lrm_delete_rsc (ll_lrm_t*, const char* id);
+static int lrm_fail_rsc (ll_lrm_t* lrm, const char* rsc_id, const int fail_rc
+ ,const char* fail_reason);
+static int lrm_set_lrmd_param (ll_lrm_t* lrm, const char* name, const char *value);
+static char* lrm_get_lrmd_param (ll_lrm_t* lrm, const char* name);
+static IPC_Channel* lrm_ipcchan (ll_lrm_t*);
+static int lrm_msgready (ll_lrm_t*);
+static int lrm_rcvmsg (ll_lrm_t*, int blocking);
+static struct lrm_ops lrm_ops_instance =
+{
+ lrm_signon,
+ lrm_signoff,
+ lrm_delete,
+ lrm_set_lrm_callback,
+ lrm_set_lrmd_param,
+ lrm_get_lrmd_param,
+ lrm_get_rsc_class_supported,
+ lrm_get_rsc_type_supported,
+ lrm_get_rsc_provider_supported,
+ lrm_get_rsc_type_metadata,
+ lrm_get_all_type_metadata,
+ lrm_get_all_rscs,
+ lrm_get_rsc,
+ lrm_add_rsc,
+ lrm_delete_rsc,
+ lrm_fail_rsc,
+ lrm_ipcchan,
+ lrm_msgready,
+ lrm_rcvmsg
+};
+/* declare the functions used by the lrm_rsc_ops structure*/
+static int rsc_perform_op (lrm_rsc_t*, lrm_op_t* op);
+static int rsc_cancel_op (lrm_rsc_t*, int call_id);
+static int rsc_flush_ops (lrm_rsc_t*);
+static GList* rsc_get_cur_state (lrm_rsc_t*, state_flag_t* cur_state);
+static lrm_op_t* rsc_get_last_result (lrm_rsc_t*, const char* op_type);
+static gint compare_call_id(gconstpointer a, gconstpointer b);
+
+static struct rsc_ops rsc_ops_instance =
+{
+ rsc_perform_op,
+ rsc_cancel_op,
+ rsc_flush_ops,
+ rsc_get_cur_state,
+ rsc_get_last_result
+};
+
+
+/* define the internal data used by the client library*/
+static int is_signed_on = FALSE;
+static IPC_Channel* ch_cmd = NULL;
+static IPC_Channel* ch_cbk = NULL;
+static lrm_op_done_callback_t op_done_callback = NULL;
+
+/* define some utility functions*/
+static int get_ret_from_ch(IPC_Channel* ch);
+static int get_ret_from_msg(struct ha_msg* msg);
+static struct ha_msg* op_to_msg (lrm_op_t* op);
+static lrm_op_t* msg_to_op(struct ha_msg* msg);
+static void free_op (lrm_op_t* op);
+
+/* define of the api functions*/
+ll_lrm_t*
+ll_lrm_new (const char * llctype)
+{
+ ll_lrm_t* lrm;
+
+ /* check the parameter*/
+ if (0 != STRNCMP_CONST(llctype, LRM_ID)) {
+ cl_log(LOG_ERR, "ll_lrm_new: wrong parameter");
+ return NULL;
+ }
+
+ /* alloc memory for lrm*/
+ if (NULL == (lrm = (ll_lrm_t*) g_new(ll_lrm_t,1))) {
+ cl_log(LOG_ERR, "ll_lrm_new: can not allocate memory");
+ return NULL;
+ }
+ /* assign the ops*/
+ lrm->lrm_ops = &lrm_ops_instance;
+
+ return lrm;
+}
+
+static int
+lrm_signon (ll_lrm_t* lrm, const char * app_name)
+{
+
+ GHashTable* ch_cmd_attrs;
+ GHashTable* ch_cbk_attrs;
+
+ struct ha_msg* msg;
+
+ char path[] = IPC_PATH_ATTR;
+ char cmd_path[] = LRM_CMDPATH;
+ char callback_path[] = LRM_CALLBACKPATH;
+
+ /* check parameters*/
+ if (NULL == lrm || NULL == app_name) {
+ cl_log(LOG_ERR, "lrm_signon: wrong parameter");
+ return HA_FAIL;
+ }
+
+ /* if already signed on, sign off first*/
+ if (is_signed_on) {
+ cl_log(LOG_WARNING,
+ "lrm_signon: the client is alreay signed on, re-sign");
+ lrm_signoff(lrm);
+ }
+
+ /* create the command ipc channel to lrmd*/
+ ch_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(ch_cmd_attrs, path, cmd_path);
+ ch_cmd = ipc_channel_constructor(IPC_ANYTYPE, ch_cmd_attrs);
+ g_hash_table_destroy(ch_cmd_attrs);
+
+ if (NULL == ch_cmd){
+ lrm_signoff(lrm);
+ cl_log(LOG_WARNING,
+ "lrm_signon: can not connect to lrmd for cmd channel");
+ return HA_FAIL;
+ }
+
+ if (IPC_OK != ch_cmd->ops->initiate_connection(ch_cmd)) {
+ lrm_signoff(lrm);
+ cl_log(LOG_WARNING,
+ "lrm_signon: can not initiate connection");
+ return HA_FAIL;
+ }
+
+ /* construct the reg msg*/
+ if (NULL == (msg = create_lrm_reg_msg(app_name))) {
+ lrm_signoff(lrm);
+ cl_log(LOG_ERR,"lrm_signon: failed to create a register message");
+ return HA_FAIL;
+ }
+
+ /* send the msg*/
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ lrm_signoff(lrm);
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(REGISTER, "ch_cmd");
+ return HA_FAIL;
+ }
+ /* parse the return msg*/
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ ha_msg_del(msg);
+ lrm_signoff(lrm);
+ LOG_FAIL_receive_reply(REGISTER);
+ return HA_FAIL;
+ }
+
+ /* create the callback ipc channel to lrmd*/
+ ch_cbk_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(ch_cbk_attrs, path, callback_path);
+ ch_cbk = ipc_channel_constructor(IPC_ANYTYPE,ch_cbk_attrs);
+ g_hash_table_destroy(ch_cbk_attrs);
+
+ if (NULL == ch_cbk) {
+ ha_msg_del(msg);
+ lrm_signoff(lrm);
+ cl_log(LOG_ERR, "lrm_signon: failed to construct a callback "
+ "channel to lrmd");
+ return HA_FAIL;
+ }
+
+ if (IPC_OK != ch_cbk->ops->initiate_connection(ch_cbk)) {
+ ha_msg_del(msg);
+ lrm_signoff(lrm);
+ cl_log(LOG_ERR,
+ "lrm_signon: failed to initiate the callback channel.");
+ return HA_FAIL;
+ }
+ /* send the msg*/
+ if (HA_OK != msg2ipcchan(msg,ch_cbk)) {
+ lrm_signoff(lrm);
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(REGISTER, "ch_cbk");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* parse the return msg*/
+ if (HA_OK != get_ret_from_ch(ch_cbk)) {
+ lrm_signoff(lrm);
+ LOG_FAIL_receive_reply(REGISTER);
+ return HA_FAIL;
+ }
+ /* ok, we sign on sucessfully now*/
+ is_signed_on = TRUE;
+ return HA_OK;
+}
+
+static int
+lrm_signoff (ll_lrm_t* lrm)
+{
+ /* close channels */
+ if (NULL != ch_cmd) {
+ if (IPC_ISWCONN(ch_cmd)) {
+ ch_cmd->ops->destroy(ch_cmd);
+ }
+ ch_cmd = NULL;
+ }
+ if (NULL != ch_cbk) {
+ if (IPC_ISWCONN(ch_cbk)) {
+ ch_cbk->ops->destroy(ch_cbk);
+ }
+ ch_cbk = NULL;
+ }
+ is_signed_on = FALSE;
+
+ return HA_OK;
+}
+
+static int
+lrm_delete (ll_lrm_t* lrm)
+{
+ /* check the parameter */
+ if (NULL == lrm) {
+ cl_log(LOG_ERR,"lrm_delete: the parameter is a null pointer.");
+ return HA_FAIL;
+ }
+ g_free(lrm);
+
+ return HA_OK;
+}
+
+static int
+lrm_set_lrm_callback (ll_lrm_t* lrm,
+ lrm_op_done_callback_t op_done_callback_func)
+
+{
+ op_done_callback = op_done_callback_func;
+
+ return HA_OK;
+}
+
+static GList*
+lrm_get_rsc_class_supported (ll_lrm_t* lrm)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ GList* class_list = NULL;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR,
+ "lrm_get_rsc_class_supported: ch_cmd is a null pointer.");
+ return NULL;
+ }
+ /* create the get ra type message */
+ msg = create_lrm_msg(GETRSCCLASSES);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETRSCCLASSES);
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCCLASSES, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCCLASSES);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_WARNING, GETRSCCLASSES);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the ra type list from message */
+ class_list = ha_msg_value_str_list(ret,F_LRM_RCLASS);
+
+ ha_msg_del(ret);
+
+ return class_list;
+}
+static GList*
+lrm_get_rsc_type_supported (ll_lrm_t* lrm, const char* rclass)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ GList* type_list = NULL;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR, "%s(%d): ch_cmd is null."
+ , __FUNCTION__, __LINE__);
+
+ return NULL;
+ }
+ /* create the get ra type message */
+ msg = create_lrm_msg(GETRSCTYPES);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETRSCTYPES);
+ return NULL;
+ }
+ if ( HA_OK != ha_msg_add(msg, F_LRM_RCLASS, rclass)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCTYPES, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCTYPES);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETRSCTYPES);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the ra type list from message */
+ type_list = ha_msg_value_str_list(ret,F_LRM_RTYPES);
+
+ ha_msg_del(ret);
+
+ return type_list;
+}
+static GList*
+lrm_get_rsc_provider_supported (ll_lrm_t* lrm, const char* class, const char* type)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ GList* provider_list = NULL;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR,
+ "lrm_get_rsc_provider_supported: ch_mod is null.");
+ return NULL;
+ }
+ /* create the get ra providers message */
+ msg = create_lrm_msg(GETPROVIDERS);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETPROVIDERS);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg, F_LRM_RCLASS, class)
+ || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, type)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETPROVIDERS, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETPROVIDERS);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETPROVIDERS);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the ra provider list from message */
+ provider_list = ha_msg_value_str_list(ret,F_LRM_RPROVIDERS);
+
+ ha_msg_del(ret);
+
+ return provider_list;
+}
+
+/*
+ * lrm_get_all_type_metadatas():
+ * The key of the hash table is in the format "type:provider"
+ * The value of the hash table is the metadata.
+ */
+static GHashTable*
+lrm_get_all_type_metadata (ll_lrm_t* lrm, const char* rclass)
+{
+ GHashTable* metas = g_hash_table_new_full(g_str_hash, g_str_equal
+ , g_free, g_free);
+ GList* types = lrm_get_rsc_type_supported (lrm, rclass);
+ GList* providers = NULL;
+ GList* cur_type = NULL;
+ GList* cur_provider = NULL;
+
+ cur_type = g_list_first(types);
+ while (cur_type != NULL)
+ {
+ const char* type;
+ char key[MAXLENGTH];
+ type = (const char*) cur_type->data;
+ providers = lrm_get_rsc_provider_supported(lrm, rclass, type);
+ cur_provider = g_list_first(providers);
+ while (cur_provider != NULL) {
+ const char* meta;
+ const char* provider;
+ provider = (const char*) cur_provider->data;
+ meta = lrm_get_rsc_type_metadata(lrm,rclass,type,provider);
+ if (NULL == meta) {
+ cur_provider = g_list_next(cur_provider);
+ continue;
+ }
+ snprintf(key,MAXLENGTH, "%s:%s",type,provider);
+ key[MAXLENGTH-1]='\0';
+ g_hash_table_insert(metas,g_strdup(key),g_strdup(meta));
+ cur_provider = g_list_next(cur_provider);
+ }
+ lrm_free_str_list(providers);
+ cur_type=g_list_next(cur_type);
+ }
+ lrm_free_str_list(types);
+ return metas;
+}
+
+static char*
+lrm_get_rsc_type_metadata (ll_lrm_t* lrm, const char* rclass, const char* rtype,
+ const char* provider)
+{
+ struct ha_msg* msg;
+ struct ha_msg* ret;
+ const char* tmp = NULL;
+ char* metadata = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd)
+ {
+ cl_log(LOG_ERR,
+ "lrm_get_rsc_type_metadata: ch_mod is null.");
+ return NULL;
+ }
+ /* create the get ra type message */
+ msg = create_lrm_msg(GETRSCMETA);
+ if (NULL == msg ) {
+ LOG_FAIL_create_lrm_msg(GETRSCMETA);
+ return NULL;
+ }
+
+ if (HA_OK != ha_msg_add(msg, F_LRM_RCLASS, rclass)
+ || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, rtype)){
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ if( provider ) {
+ if (HA_OK != ha_msg_add(msg, F_LRM_RPROVIDER, provider)) {
+ LOG_BASIC_ERROR("ha_msg_add");
+ ha_msg_del(msg);
+ return NULL;
+ }
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCMETA, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return message */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCMETA);
+ return NULL;
+ }
+ /* get the return code of the message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETRSCMETA);
+ ha_msg_del(ret);
+ return NULL;
+ }
+
+ /* get the metadata from message */
+ tmp = cl_get_string(ret, F_LRM_METADATA);
+ if (NULL!=tmp) {
+ metadata = g_strdup(tmp);
+ }
+ ha_msg_del(ret);
+
+ return metadata;
+}
+
+static GList*
+lrm_get_all_rscs (ll_lrm_t* lrm)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ GList* rid_list = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_get_all_rscs: ch_mod is null.");
+ return NULL;
+ }
+ /* create the msg of get all resource */
+ msg = create_lrm_msg(GETALLRCSES);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETALLRCSES);
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETALLRCSES, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return msg */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETALLRCSES);
+ return NULL;
+ }
+ /* get the return code of msg */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, GETALLRCSES);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* get the rsc_id list from msg */
+ rid_list = ha_msg_value_str_list(ret,F_LRM_RID);
+
+ ha_msg_del(ret);
+ /* return the id list */
+ return rid_list;
+
+}
+
+static lrm_rsc_t*
+lrm_get_rsc (ll_lrm_t* lrm, const char* rsc_id)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ lrm_rsc_t* rsc = NULL;
+
+ /* check whether the rsc_id is available */
+ if (strlen(rsc_id) >= RID_LEN) {
+ cl_log(LOG_ERR, "lrm_get_rsc: rsc_id is too long.");
+ return NULL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_get_rsc: ch_mod is null.");
+ return NULL;
+ }
+ /* create the msg of get resource */
+ msg = create_lrm_rsc_msg(rsc_id, GETRSC);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(GETRSC);
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSC, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return msg from lrmd */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSC);
+ return NULL;
+ }
+ /* get the return code of return message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ ha_msg_del(ret);
+ return NULL;
+ }
+ /* create a new resource structure */
+ rsc = g_new(lrm_rsc_t, 1);
+
+ /* fill the field of resource with the data from msg */
+ rsc->id = g_strdup(ha_msg_value(ret, F_LRM_RID));
+ rsc->type = g_strdup(ha_msg_value(ret, F_LRM_RTYPE));
+ rsc->class = g_strdup(ha_msg_value(ret, F_LRM_RCLASS));
+ rsc->provider = g_strdup(ha_msg_value(ret, F_LRM_RPROVIDER));
+ rsc->params = ha_msg_value_str_table(ret,F_LRM_PARAM);
+
+ rsc->ops = &rsc_ops_instance;
+ ha_msg_del(ret);
+ /* return the new resource */
+ return rsc;
+}
+
+static int
+lrm_fail_rsc (ll_lrm_t* lrm, const char* rsc_id, const int fail_rc
+, const char* fail_reason)
+{
+ struct ha_msg* msg;
+
+ /* check whether the rsc_id is available */
+ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) {
+ cl_log(LOG_ERR, "%s: wrong parameter rsc_id.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "%s: ch_mod is null.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* create the message */
+ msg = create_lrm_rsc_msg(rsc_id,FAILRSC);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(FAILRSC);
+ return HA_FAIL;
+ }
+ if ((fail_reason && HA_OK != ha_msg_add(msg,F_LRM_FAIL_REASON,fail_reason))
+ || HA_OK != ha_msg_add_int(msg, F_LRM_ASYNCMON_RC, fail_rc)
+ ) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return HA_FAIL;
+ }
+ /* send to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(FAILRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the result */
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, FAILRSC);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+static int
+lrm_set_lrmd_param(ll_lrm_t* lrm, const char* name, const char *value)
+{
+ struct ha_msg* msg;
+
+ if (!name || !value) {
+ cl_log(LOG_ERR, "%s: no parameter name or value", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "%s: ch_mod is null.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ /* create the message */
+ msg = create_lrm_msg(SETLRMDPARAM);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(SETLRMDPARAM);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_NAME,name)
+ || HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_VAL,value)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return HA_FAIL;
+ }
+ /* send to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(FAILRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the result */
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, FAILRSC);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+static char*
+lrm_get_lrmd_param (ll_lrm_t* lrm, const char *name)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ const char* value = NULL;
+ char* v2;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_get_rsc: ch_mod is null.");
+ return NULL;
+ }
+ /* create the msg of get resource */
+ msg = create_lrm_msg(GETLRMDPARAM);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_msg(GETLRMDPARAM);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg,F_LRM_LRMD_PARAM_NAME,name)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETLRMDPARAM, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+ /* get the return msg from lrmd */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETLRMDPARAM);
+ return NULL;
+ }
+ /* get the return code of return message */
+ if (HA_OK != get_ret_from_msg(ret)) {
+ ha_msg_del(ret);
+ return NULL;
+ }
+ value = ha_msg_value(ret,F_LRM_LRMD_PARAM_VAL);
+ if (!value) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_LRMD_PARAM_VAL, ret);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ v2 = g_strdup(value);
+ ha_msg_del(ret);
+ return v2;
+}
+
+static int
+lrm_add_rsc (ll_lrm_t* lrm, const char* rsc_id, const char* class
+, const char* type, const char* provider, GHashTable* parameter)
+{
+ struct ha_msg* msg;
+
+ /* check whether the rsc_id is available */
+ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) {
+ cl_log(LOG_ERR, "lrm_add_rsc: wrong parameter rsc_id.");
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_add_rsc: ch_mod is null.");
+ return HA_FAIL;
+ }
+
+ /* create the message of add resource */
+ msg = create_lrm_addrsc_msg(rsc_id, class, type, provider, parameter);
+ if ( NULL == msg) {
+ cl_log(LOG_ERR, "%s(%d): failed to create a ADDSRC message "
+ "with function create_lrm_addrsc_msg"
+ , __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+ /* send to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(ADDRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the result */
+ if (HA_OK != get_ret_from_ch(ch_cmd)) {
+ LOG_GOT_FAIL_RET(LOG_ERR, ADDRSC);
+ return HA_FAIL;
+ }
+
+ return HA_OK;
+}
+
+static int
+lrm_delete_rsc (ll_lrm_t* lrm, const char* rsc_id)
+{
+ struct ha_msg* msg = NULL;
+ int rc;
+
+ /* check whether the rsc_id is available */
+ if (NULL == rsc_id || RID_LEN <= strlen(rsc_id)) {
+ cl_log(LOG_ERR, "lrm_delete_rsc: wrong parameter rsc_id.");
+ return HA_FAIL;
+ }
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "lrm_delete_rsc: ch_mod is null.");
+ return HA_FAIL;
+ }
+
+ /* create the msg of del resource */
+ msg = create_lrm_rsc_msg(rsc_id, DELRSC);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(DELRSC);
+ return HA_FAIL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(DELRSC, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ /* check the response of the msg */
+ rc = get_ret_from_ch(ch_cmd);
+ if (rc != HA_OK && rc != HA_RSCBUSY) {
+ LOG_GOT_FAIL_RET(LOG_ERR, DELRSC);
+ return HA_FAIL;
+ }
+
+ return rc;
+}
+
+static IPC_Channel*
+lrm_ipcchan (ll_lrm_t* lrm)
+{
+ if (NULL == ch_cbk) {
+ cl_log(LOG_ERR,
+ "lrm_inputfd: callback channel is null.");
+ return NULL;
+ }
+
+ return ch_cbk;
+}
+
+static gboolean
+lrm_msgready (ll_lrm_t* lrm)
+{
+ if (NULL == ch_cbk) {
+ cl_log(LOG_ERR,
+ "lrm_msgready: callback channel is null.");
+ return FALSE;
+ }
+ return ch_cbk->ops->is_message_pending(ch_cbk);
+}
+
+static int
+lrm_rcvmsg (ll_lrm_t* lrm, int blocking)
+{
+ struct ha_msg* msg = NULL;
+ lrm_op_t* op = NULL;
+ int msg_count = 0;
+
+ /* if it is not blocking mode and no message in the channel, return */
+ if ((!lrm_msgready(lrm)) && (!blocking)) {
+ cl_log(LOG_DEBUG,
+ "lrm_rcvmsg: no message and non-block.");
+ return msg_count;
+ }
+ /* wait until message ready */
+ if (!lrm_msgready(lrm)) {
+ ch_cbk->ops->waitin(ch_cbk);
+ }
+ while (lrm_msgready(lrm)) {
+ if (ch_cbk->ch_status == IPC_DISCONNECT) {
+ return msg_count;
+ }
+ /* get the message */
+ msg = msgfromIPC(ch_cbk, MSG_ALLOWINTR);
+ if (msg == NULL) {
+ cl_log(LOG_WARNING,
+ "%s(%d): receive a null message with msgfromIPC."
+ , __FUNCTION__, __LINE__);
+ return msg_count;
+ }
+ msg_count++;
+
+ op = msg_to_op(msg);
+ if (NULL!=op && NULL!=op_done_callback) {
+ (*op_done_callback)(op);
+ }
+ free_op(op);
+ ha_msg_del(msg);
+ }
+
+ return msg_count;
+}
+
+/* following are the functions for rsc_ops */
+static int
+rsc_perform_op (lrm_rsc_t* rsc, lrm_op_t* op)
+{
+ int rc = 0;
+ struct ha_msg* msg = NULL;
+ char* rsc_id;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd
+ || NULL == rsc
+ || NULL == rsc->id
+ || NULL == op
+ || NULL == op->op_type) {
+ cl_log(LOG_ERR,
+ "rsc_perform_op: wrong parameters.");
+ return HA_FAIL;
+ }
+ /* create the msg of perform op */
+ rsc_id = op->rsc_id;
+ op->rsc_id = rsc->id;
+ msg = op_to_msg(op);
+ op->rsc_id = rsc_id;
+ if ( NULL == msg) {
+ cl_log(LOG_ERR, "rsc_perform_op: failed to create a message "
+ "with function op_to_msg");
+ return HA_FAIL;
+ }
+ /* send it to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(PERFORMOP, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+
+ /* check return code, the return code is the call_id of the op */
+ rc = get_ret_from_ch(ch_cmd);
+ return rc;
+}
+
+static int
+rsc_cancel_op (lrm_rsc_t* rsc, int call_id)
+{
+ int rc;
+ struct ha_msg* msg = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_cancel_op: ch_mod is null.");
+ return HA_FAIL;
+ }
+ /* check parameter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_cancel_op: parameter rsc is null.");
+ return HA_FAIL;
+ }
+ /* create the msg of flush ops */
+ msg = create_lrm_rsc_msg(rsc->id,CANCELOP);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(CANCELOP);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_add_int(msg, F_LRM_CALLID, call_id)) {
+ LOG_BASIC_ERROR("ha_msg_add_int");
+ ha_msg_del(msg);
+ return HA_FAIL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(CANCELOP, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+
+ rc = get_ret_from_ch(ch_cmd);
+
+ return rc;
+}
+
+static int
+rsc_flush_ops (lrm_rsc_t* rsc)
+{
+ int rc;
+ struct ha_msg* msg = NULL;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_flush_ops: ch_mod is null.");
+ return HA_FAIL;
+ }
+ /* check parameter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_flush_ops: parameter rsc is null.");
+ return HA_FAIL;
+ }
+ /* create the msg of flush ops */
+ msg = create_lrm_rsc_msg(rsc->id,FLUSHOPS);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(CANCELOP);
+ return HA_FAIL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(FLUSHOPS, "ch_cmd");
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+
+ rc = get_ret_from_ch(ch_cmd);
+
+ return rc>0?rc:HA_FAIL;
+}
+static gint
+compare_call_id(gconstpointer a, gconstpointer b)
+{
+ const lrm_op_t* opa = (const lrm_op_t*)a;
+ const lrm_op_t* opb = (const lrm_op_t*)b;
+ return opa->call_id - opb->call_id;
+}
+static GList*
+rsc_get_cur_state (lrm_rsc_t* rsc, state_flag_t* cur_state)
+{
+ GList* op_list = NULL, * tmplist = NULL;
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ struct ha_msg* op_msg = NULL;
+ lrm_op_t* op = NULL;
+ int state;
+ int op_count, i;
+
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_get_cur_state: ch_mod is null.");
+ return NULL;
+ }
+ /* check paramter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_get_cur_state: parameter rsc is null.");
+ return NULL;
+ }
+ /* create the msg of get current state of resource */
+ msg = create_lrm_rsc_msg(rsc->id,GETRSCSTATE);
+ if ( NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(GETRSCSTATE);
+ return NULL;
+ }
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETRSCSTATE, "ch_cmd");
+ return NULL;
+ }
+ ha_msg_del(msg);
+
+ /* get the return msg */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETRSCSTATE);
+ return NULL;
+ }
+
+ /* get the state of the resource from the message */
+ if (HA_OK != ha_msg_value_int(ret, F_LRM_STATE, &state)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_STATE, ret);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ *cur_state = (state_flag_t)state;
+ /* the first msg includes the count of pending ops. */
+ if (HA_OK != ha_msg_value_int(ret, F_LRM_OPCNT, &op_count)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_WARNING, F_LRM_OPCNT, ret);
+ ha_msg_del(ret);
+ return NULL;
+ }
+ ha_msg_del(ret);
+ for (i = 0; i < op_count; i++) {
+ /* one msg for one op */
+ op_msg = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+
+ if (NULL == op_msg) {
+ cl_log(LOG_WARNING, "%s(%d): failed to receive a "
+ "(pending operation) message from lrmd."
+ , __FUNCTION__, __LINE__);
+ continue;
+ }
+ op = msg_to_op(op_msg);
+ /* add msg to the return list */
+
+ if (NULL != op) {
+ op_list = g_list_append(op_list, op);
+ }
+ else {
+ cl_log(LOG_WARNING, "%s(%d): failed to make a operation "
+ "from a message with function msg_to_op"
+ , __FUNCTION__, __LINE__);
+ }
+ ha_msg_del(op_msg);
+ }
+ op_list = g_list_sort(op_list, compare_call_id);
+
+ /* Delete the duplicate op for call_id */
+#if 0
+ cl_log(LOG_WARNING, "Before uniquing");
+ tmplist = g_list_first(op_list);
+ while (tmplist != NULL) {
+ cl_log(LOG_WARNING, "call_id=%d", ((lrm_op_t*)(tmplist->data))->call_id);
+ tmplist = g_list_next(tmplist);
+ }
+#endif
+
+ tmplist = g_list_first(op_list);
+ while (tmplist != NULL) {
+ if (NULL != g_list_previous(tmplist)) {
+ if (((lrm_op_t*)(g_list_previous(tmplist)->data))->call_id
+ == ((lrm_op_t*)(tmplist->data))->call_id) {
+ op_list = g_list_remove_link (op_list, tmplist);
+ free_op((lrm_op_t *)tmplist->data);
+ g_list_free_1(tmplist);
+ tmplist = g_list_first(op_list);
+ }
+ }
+ tmplist = g_list_next(tmplist);
+ }
+
+#if 0
+ cl_log(LOG_WARNING, "After uniquing");
+ while (tmplist != NULL) {
+ cl_log(LOG_WARNING, "call_id=%d", ((lrm_op_t*)(tmplist->data))->call_id);
+ tmplist = g_list_next(tmplist);
+ }
+#endif
+
+ return op_list;
+}
+
+static lrm_op_t*
+rsc_get_last_result (lrm_rsc_t* rsc, const char* op_type)
+{
+ struct ha_msg* msg = NULL;
+ struct ha_msg* ret = NULL;
+ lrm_op_t* op = NULL;
+ int opcount = 0;
+ /* check whether the channel to lrmd is available */
+ if (NULL == ch_cmd) {
+ cl_log(LOG_ERR, "rsc_get_last_result: ch_mod is null.");
+ return NULL;
+ }
+ /* check parameter */
+ if (NULL == rsc) {
+ cl_log(LOG_ERR, "rsc_get_last_result: parameter rsc is null.");
+ return NULL;
+ }
+ /* create the msg of get last op */
+ msg = create_lrm_rsc_msg(rsc->id,GETLASTOP);
+ if (NULL == msg) {
+ LOG_FAIL_create_lrm_rsc_msg(GETLASTOP);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg, F_LRM_RID, rsc->id)) {
+ LOG_BASIC_ERROR("ha_msg_add");
+ ha_msg_del(msg);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_add(msg, F_LRM_OP, op_type)) {
+ LOG_BASIC_ERROR("ha_msg_add");
+ ha_msg_del(msg);
+ return NULL;
+ }
+
+ /* send the msg to lrmd */
+ if (HA_OK != msg2ipcchan(msg,ch_cmd)) {
+ ha_msg_del(msg);
+ LOG_FAIL_SEND_MSG(GETLASTOP, "ch_cmd");
+ return NULL;
+ }
+
+ /* get the return msg */
+ ret = msgfromIPC(ch_cmd, MSG_ALLOWINTR);
+ if (NULL == ret) {
+ LOG_FAIL_receive_reply(GETLASTOP);
+ ha_msg_del(msg);
+ return NULL;
+ }
+ if (HA_OK != ha_msg_value_int(ret,F_LRM_OPCNT, &opcount)) {
+ op = NULL;
+ }
+ else if ( 1 == opcount ) {
+ op = msg_to_op(ret);
+ }
+ ha_msg_del(msg);
+ ha_msg_del(ret);
+ return op;
+}
+/*
+ * following are the implements of the utility functions
+ */
+lrm_op_t*
+lrm_op_new(void)
+{
+ lrm_op_t* op;
+
+ op = g_new0(lrm_op_t, 1);
+ op->op_status = LRM_OP_PENDING;
+ return op;
+}
+
+static lrm_op_t*
+msg_to_op(struct ha_msg* msg)
+{
+ lrm_op_t* op;
+ const char* op_type;
+ const char* app_name;
+ const char* rsc_id;
+ const char* fail_reason;
+ const char* output;
+ const void* user_data;
+
+ op = lrm_op_new();
+
+ /* op->timeout, op->interval, op->target_rc, op->call_id*/
+ if (HA_OK != ha_msg_value_int(msg,F_LRM_TIMEOUT, &op->timeout)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_INTERVAL, &op->interval)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_TARGETRC, &op->target_rc)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_DELAY, &op->start_delay)
+ || HA_OK != ha_msg_value_int(msg,F_LRM_CALLID, &op->call_id)) {
+ LOG_BASIC_ERROR("ha_msg_value_int");
+ free_op(op);
+ return NULL;
+ }
+
+ /* op->op_status */
+ if (HA_OK !=
+ ha_msg_value_int(msg, F_LRM_OPSTATUS, (int*)&op->op_status)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_WARNING, F_LRM_OPSTATUS, msg);
+ op->op_status = LRM_OP_PENDING;
+ }
+
+ /* if it finished successfully */
+ if (LRM_OP_DONE == op->op_status ) {
+ /* op->rc */
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_RC, &op->rc)) {
+ free_op(op);
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RC, msg);
+ return NULL;
+ }
+ /* op->output */
+ output = cl_get_string(msg, F_LRM_DATA);
+ if (NULL != output){
+ op->output = g_strdup(output);
+ }
+ else {
+ op->output = NULL;
+ }
+ } else if(op->op_status == LRM_OP_PENDING) {
+ op->rc = EXECRA_STATUS_UNKNOWN;
+
+ } else {
+ op->rc = EXECRA_EXEC_UNKNOWN_ERROR;
+ }
+
+
+ /* op->app_name */
+ app_name = ha_msg_value(msg, F_LRM_APP);
+ if (NULL == app_name) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_APP, msg);
+ free_op(op);
+ return NULL;
+ }
+ op->app_name = g_strdup(app_name);
+
+
+ /* op->op_type */
+ op_type = ha_msg_value(msg, F_LRM_OP);
+ if (NULL == op_type) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_OP, msg);
+ free_op(op);
+ return NULL;
+ }
+ op->op_type = g_strdup(op_type);
+
+ /* op->rsc_id */
+ rsc_id = ha_msg_value(msg, F_LRM_RID);
+ if (NULL == rsc_id) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RID, msg);
+ free_op(op);
+ return NULL;
+ }
+ op->rsc_id = g_strdup(rsc_id);
+
+ /* op->fail_reason present only on async failures */
+ fail_reason = ha_msg_value(msg, F_LRM_FAIL_REASON);
+ if (fail_reason) {
+ op->fail_reason = g_strdup(fail_reason);
+ }
+
+ /* op->user_data */
+ user_data = cl_get_string(msg, F_LRM_USERDATA);
+
+ if (NULL != user_data) {
+ op->user_data = g_strdup(user_data);
+ }
+
+ /* time_stamps */
+ if (ha_msg_value_ul(msg, F_LRM_T_RUN, &op->t_run) != HA_OK
+ || ha_msg_value_ul(msg, F_LRM_T_RCCHANGE, &op->t_rcchange) != HA_OK
+ || ha_msg_value_ul(msg, F_LRM_EXEC_TIME, &op->exec_time) != HA_OK
+ || ha_msg_value_ul(msg, F_LRM_QUEUE_TIME, &op->queue_time) != HA_OK) {
+ /* cl_log(LOG_WARNING
+ , "%s:%d: failed to get the timing information"
+ , __FUNCTION__, __LINE__);
+ */
+ }
+
+ /* op->params */
+ op->params = ha_msg_value_str_table(msg, F_LRM_PARAM);
+
+ ha_msg_value_int(msg, F_LRM_RSCDELETED, &op->rsc_deleted);
+
+ return op;
+}
+
+static struct ha_msg*
+op_to_msg (lrm_op_t* op)
+{
+ struct ha_msg* msg = ha_msg_new(15);
+ if (!msg) {
+ LOG_BASIC_ERROR("ha_msg_new");
+ return NULL;
+ }
+
+ if (HA_OK != ha_msg_add(msg, F_LRM_TYPE, PERFORMOP)
+ || HA_OK != ha_msg_add(msg, F_LRM_RID, op->rsc_id)
+ || HA_OK != ha_msg_add(msg, F_LRM_OP, op->op_type)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_TIMEOUT, op->timeout)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_INTERVAL, op->interval)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_DELAY, op->start_delay)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_COPYPARAMS, op->copyparams)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_T_RUN,op->t_run)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_T_RCCHANGE, op->t_rcchange)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_EXEC_TIME, op->exec_time)
+ || HA_OK != ha_msg_add_ul(msg, F_LRM_QUEUE_TIME, op->queue_time)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_TARGETRC, op->target_rc)
+ || ( op->app_name && (HA_OK != ha_msg_add(msg, F_LRM_APP, op->app_name)))
+ || ( op->user_data && (HA_OK != ha_msg_add(msg,F_LRM_USERDATA,op->user_data)))
+ || ( op->params && (HA_OK != ha_msg_add_str_table(msg,F_LRM_PARAM,op->params)))) {
+ LOG_BASIC_ERROR("op_to_msg conversion failed");
+ ha_msg_del(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+static int
+get_ret_from_ch(IPC_Channel* ch)
+{
+ int ret;
+ struct ha_msg* msg;
+
+ msg = msgfromIPC(ch, MSG_ALLOWINTR);
+
+ if (NULL == msg) {
+ cl_log(LOG_ERR
+ , "%s(%d): failed to receive message with function msgfromIPC"
+ , __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_RET, &ret)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RET, msg);
+ ha_msg_del(msg);
+ return HA_FAIL;
+ }
+ ha_msg_del(msg);
+ return ret;
+}
+
+static int
+get_ret_from_msg(struct ha_msg* msg)
+{
+ int ret;
+
+ if (NULL == msg) {
+ cl_log(LOG_ERR, "%s(%d): the parameter is a NULL pointer."
+ , __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_RET, &ret)) {
+ LOG_FAIL_GET_MSG_FIELD(LOG_ERR, F_LRM_RET, msg);
+ return HA_FAIL;
+ }
+ return ret;
+}
+static void
+free_op (lrm_op_t* op)
+{
+ if (NULL == op) {
+ return;
+ }
+ if (NULL != op->op_type) {
+ g_free(op->op_type);
+ }
+ if (NULL != op->output) {
+ g_free(op->output);
+ }
+ if (NULL != op->rsc_id) {
+ g_free(op->rsc_id);
+ }
+ if (NULL != op->app_name) {
+ g_free(op->app_name);
+ }
+ if (NULL != op->user_data) {
+ g_free(op->user_data);
+ }
+ if (NULL != op->params) {
+ free_str_table(op->params);
+ }
+ g_free(op);
+}
+
+void lrm_free_op(lrm_op_t* op) {
+ free_op(op);
+}
+void lrm_free_rsc(lrm_rsc_t* rsc) {
+ if (NULL == rsc) {
+ return;
+ }
+ if (NULL != rsc->id) {
+ g_free(rsc->id);
+ }
+ if (NULL != rsc->type) {
+ g_free(rsc->type);
+ }
+ if (NULL != rsc->class) {
+ g_free(rsc->class);
+ }
+ if (NULL != rsc->provider) {
+ g_free(rsc->provider);
+ }
+ if (NULL != rsc->params) {
+ free_str_table(rsc->params);
+ }
+ g_free(rsc);
+}
+void lrm_free_str_list(GList* list) {
+ GList* item;
+ if (NULL == list) {
+ return;
+ }
+ item = g_list_first(list);
+ while (NULL != item) {
+ if (NULL != item->data) {
+ g_free(item->data);
+ }
+ list = g_list_delete_link(list, item);
+ item = g_list_first(list);
+ }
+}
+void lrm_free_op_list(GList* list) {
+ GList* item;
+ if (NULL == list) {
+ return;
+ }
+ item = g_list_first(list);
+ while (NULL != item) {
+ if (NULL != item->data) {
+ free_op((lrm_op_t*)item->data);
+ }
+ list = g_list_delete_link(list, item);
+ item = g_list_first(list);
+ }
+}
+void lrm_free_str_table(GHashTable* table) {
+ if (NULL != table) {
+ free_str_table(table);
+ }
+}
+
+const char *
+execra_code2string(uniform_ret_execra_t code)
+{
+ switch(code) {
+ case EXECRA_EXEC_UNKNOWN_ERROR:
+ return "unknown exec error";
+ case EXECRA_NO_RA:
+ return "no RA";
+ case EXECRA_OK:
+ return "ok";
+ case EXECRA_UNKNOWN_ERROR:
+ return "unknown error";
+ case EXECRA_INVALID_PARAM:
+ return "invalid parameter";
+ case EXECRA_UNIMPLEMENT_FEATURE:
+ return "unimplemented feature";
+ case EXECRA_INSUFFICIENT_PRIV:
+ return "insufficient privileges";
+ case EXECRA_NOT_INSTALLED:
+ return "not installed";
+ case EXECRA_NOT_CONFIGURED:
+ return "not configured";
+ case EXECRA_NOT_RUNNING:
+ return "not running";
+ /* For status command only */
+ case EXECRA_RUNNING_MASTER:
+ return "master";
+ case EXECRA_FAILED_MASTER:
+ return "master (failed)";
+ case EXECRA_RA_DEAMON_DEAD1:
+ return "status: daemon dead";
+ case EXECRA_RA_DEAMON_DEAD2:
+ return "status: daemon dead";
+ case EXECRA_RA_DEAMON_STOPPED:
+ return "status: daemon stopped";
+ case EXECRA_STATUS_UNKNOWN:
+ return "status: unknown";
+ default:
+ break;
+ }
+
+ return "<unknown>";
+}
diff --git a/lib/lrm/lrm_msg.c b/lib/lrm/lrm_msg.c
new file mode 100644
index 0000000..fdd3b3f
--- /dev/null
+++ b/lib/lrm/lrm_msg.c
@@ -0,0 +1,212 @@
+/*
+ * Message Functions For Local Resource Manager
+ *
+ * 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
+ *
+ */
+
+/*
+ * By Huang Zhen <zhenh@cn.ibm.com> 2004/2/13
+ *
+ */
+#include <lha_internal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <clplumbing/cl_log.h>
+#include <ha_msg.h>
+#include <lrm/lrm_api.h>
+#include <lrm/lrm_msg.h>
+#define LOG_BASIC_ERROR(apiname) \
+ cl_log(LOG_ERR, "%s(%d): %s failed.", __FUNCTION__, __LINE__, apiname)
+
+const lrm_op_t lrm_zero_op; /* Default initialized to zeros */
+
+static void
+copy_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ GHashTable* taget_table = (GHashTable*)user_data;
+ g_hash_table_insert(taget_table, g_strdup(key), g_strdup(value));
+}
+
+GHashTable*
+copy_str_table(GHashTable* src_table)
+{
+ GHashTable* target_table = NULL;
+
+ if ( NULL == src_table) {
+ return NULL;
+ }
+ target_table = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_foreach(src_table, copy_pair, target_table);
+ return target_table;
+}
+
+static void
+merge_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ GHashTable *merged = (GHashTable*)user_data;
+
+ if (g_hash_table_lookup(merged, key)) {
+ return;
+ }
+
+ g_hash_table_insert(merged, g_strdup(key), g_strdup(value));
+}
+
+GHashTable*
+merge_str_tables(GHashTable* old, GHashTable* new)
+{
+ GHashTable* merged = NULL;
+ if ( NULL == old ) {
+ return copy_str_table(new);
+ }
+ if ( NULL == new ) {
+ return copy_str_table(old);
+ }
+ merged = copy_str_table(new);
+ g_hash_table_foreach(old, merge_pair, merged);
+ return merged;
+}
+
+static gboolean
+free_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ g_free(key);
+ g_free(value);
+ return TRUE;
+}
+
+void
+free_str_table(GHashTable* hash_table)
+{
+ g_hash_table_foreach_remove(hash_table, free_pair, NULL);
+ g_hash_table_destroy(hash_table);
+}
+
+
+
+struct ha_msg*
+create_lrm_msg (const char* msg)
+{
+ struct ha_msg* ret;
+ if ((NULL == msg) || (0 == strlen(msg))) {
+ return NULL;
+ }
+
+ ret = ha_msg_new(1);
+ if (HA_OK != ha_msg_add(ret, F_LRM_TYPE, msg)) {
+ ha_msg_del(ret);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ return ret;
+}
+
+struct ha_msg*
+create_lrm_reg_msg(const char* app_name)
+{
+ struct ha_msg* ret;
+ if ((NULL == app_name) || (0 == strlen(app_name))) {
+ return NULL;
+ }
+
+ ret = ha_msg_new(5);
+
+ if(HA_OK != ha_msg_add(ret, F_LRM_TYPE, REGISTER)
+ || HA_OK != ha_msg_add(ret, F_LRM_APP, app_name)
+ || HA_OK != ha_msg_add_int(ret, F_LRM_PID, getpid())
+ || HA_OK != ha_msg_add_int(ret, F_LRM_GID, getegid())
+ || HA_OK != ha_msg_add_int(ret, F_LRM_UID, getuid())) {
+ ha_msg_del(ret);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ return ret;
+}
+
+struct ha_msg*
+create_lrm_addrsc_msg(const char* rid, const char* class, const char* type,
+ const char* provider, GHashTable* params)
+{
+ struct ha_msg* msg;
+ if (NULL==rid||NULL==class||NULL==type) {
+ return NULL;
+ }
+
+ msg = ha_msg_new(5);
+ if(HA_OK != ha_msg_add(msg, F_LRM_TYPE, ADDRSC)
+ || HA_OK != ha_msg_add(msg, F_LRM_RID, rid)
+ || HA_OK != ha_msg_add(msg, F_LRM_RCLASS, class)
+ || HA_OK != ha_msg_add(msg, F_LRM_RTYPE, type)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+
+ if( provider ) {
+ if (HA_OK != ha_msg_add(msg, F_LRM_RPROVIDER, provider)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ }
+
+ if ( params ) {
+ if (HA_OK != ha_msg_add_str_table(msg,F_LRM_PARAM,params)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ }
+ return msg;
+}
+
+
+struct ha_msg*
+create_lrm_rsc_msg(const char* rid, const char* msg)
+{
+ struct ha_msg* ret;
+ if ((NULL == rid) ||(NULL == msg) || (0 == strlen(msg))) {
+ return NULL;
+ }
+
+ ret = ha_msg_new(2);
+ if(HA_OK != ha_msg_add(ret, F_LRM_TYPE, msg)
+ || HA_OK != ha_msg_add(ret, F_LRM_RID, rid)) {
+ ha_msg_del(ret);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ return ret;
+}
+
+
+
+struct ha_msg*
+create_lrm_ret(int ret, int fields)
+{
+ struct ha_msg* msg = ha_msg_new(fields);
+ if(HA_OK != ha_msg_add(msg, F_LRM_TYPE, RETURN)
+ || HA_OK != ha_msg_add_int(msg, F_LRM_RET, ret)) {
+ ha_msg_del(msg);
+ LOG_BASIC_ERROR("ha_msg_add");
+ return NULL;
+ }
+ return msg;
+}
+
diff --git a/lib/lrm/racommon.c b/lib/lrm/racommon.c
new file mode 100644
index 0000000..2670f05
--- /dev/null
+++ b/lib/lrm/racommon.c
@@ -0,0 +1,178 @@
+/*
+ * Common functions for LRM interface to resource agents
+ *
+ * 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
+ *
+ * File: racommon.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ */
+
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <sys/stat.h>
+#include <clplumbing/cl_log.h>
+#include <lrm/raexec.h>
+#include <lrm/racommon.h>
+
+void
+get_ra_pathname(const char* class_path, const char* type, const char* provider,
+ char pathname[])
+{
+ char* type_dup;
+ char* base_name;
+
+ type_dup = g_strndup(type, RA_MAX_NAME_LENGTH);
+ if (type_dup == NULL) {
+ cl_log(LOG_ERR, "No enough memory to allocate.");
+ pathname[0] = '\0';
+ return;
+ }
+
+ base_name = basename(type_dup);
+
+ if ( strncmp(type, base_name, RA_MAX_NAME_LENGTH) == 0 ) {
+ /*the type does not include path*/
+ if (provider) {
+ snprintf(pathname, RA_MAX_NAME_LENGTH, "%s/%s/%s",
+ class_path, provider, type);
+ }else{
+ snprintf(pathname, RA_MAX_NAME_LENGTH, "%s/%s",
+ class_path,type);
+ }
+ }else{
+ /*the type includes path, just copy it to pathname*/
+ if ( *type == '/' ) {
+ g_strlcpy(pathname, type, RA_MAX_NAME_LENGTH);
+ } else {
+ *pathname = '\0';
+ cl_log(LOG_ERR, "%s: relative paths not allowed: %s",
+ __FUNCTION__, type);
+ }
+ }
+
+ g_free(type_dup);
+}
+
+/*
+ * Description: Filter a file.
+ * Return Value:
+ * TRUE: the file is qualified.
+ * FALSE: the file is unqualified.
+ * Notes: A qualifed file is a regular file with execute bits
+ * which does not start with '.'
+ */
+gboolean
+filtered(char * file_name)
+{
+ struct stat buf;
+ char *s;
+
+ if ( stat(file_name, &buf) != 0 ) {
+ return FALSE;
+ }
+ if ( ((s = strrchr(file_name,'/')) && *(s+1) == '.')
+ || *file_name == '.' ) {
+ return FALSE;
+ }
+
+ if ( S_ISREG(buf.st_mode)
+ && ( ( buf.st_mode & S_IXUSR ) || ( buf.st_mode & S_IXGRP )
+ || ( buf.st_mode & S_IXOTH ) ) ) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+get_runnable_list(const char* class_path, GList ** rsc_info)
+{
+ struct dirent **namelist;
+ int file_num;
+
+ if ( rsc_info == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list");
+ return -2;
+ }
+
+ if ( *rsc_info != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list."\
+ "will cause memory leak.");
+ *rsc_info = NULL;
+ }
+
+ file_num = scandir(class_path, &namelist, NULL, alphasort);
+ if (file_num < 0) {
+ cl_log(LOG_ERR, "scandir failed in RA plugin");
+ return -2;
+ } else{
+ while (file_num--) {
+ char tmp_buffer[FILENAME_MAX+1];
+
+ tmp_buffer[0] = '\0';
+ tmp_buffer[FILENAME_MAX] = '\0';
+ snprintf(tmp_buffer, FILENAME_MAX, "%s/%s",
+ class_path, namelist[file_num]->d_name );
+ if ( filtered(tmp_buffer) == TRUE ) {
+ *rsc_info = g_list_append(*rsc_info,
+ g_strdup(namelist[file_num]->d_name));
+ }
+ free(namelist[file_num]);
+ }
+ free(namelist);
+ }
+ return g_list_length(*rsc_info);
+}
+
+int
+get_failed_exec_rc(void)
+{
+ int rc;
+
+ switch (errno) { /* see execve(2) */
+ case ENOENT: /* No such file or directory */
+ case EISDIR: /* Is a directory */
+ rc = EXECRA_NOT_INSTALLED;
+ break;
+ case EACCES: /* permission denied (various errors) */
+ rc = EXECRA_INSUFFICIENT_PRIV;
+ break;
+ default:
+ rc = EXECRA_EXEC_UNKNOWN_ERROR;
+ break;
+ }
+ return rc;
+}
+
+void
+closefiles(void)
+{
+ int fd;
+
+ /* close all descriptors except stdin/out/err and channels to logd */
+ for (fd = getdtablesize() - 1; fd > STDERR_FILENO; fd--) {
+ /*if (!cl_log_is_logd_fd(fd))*/
+ close(fd);
+ }
+}
diff --git a/lib/pils/Makefile.am b/lib/pils/Makefile.am
new file mode 100644
index 0000000..d47c6c7
--- /dev/null
+++ b/lib/pils/Makefile.am
@@ -0,0 +1,57 @@
+#
+# pils: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+
+AM_CFLAGS = @CFLAGS@
+
+## include files
+#pkginclude_HEADERS = $(top_srcdir)/include/pils/plugin.h \
+# $(top_srcdir)/include/pils/interface.h
+
+## binaries
+#sbin_PROGRAMS = main
+
+
+#main_SOURCES = main.c
+
+#main_LDADD = libpils.la @LIBLTDL@ \
+# $(GLIBLIB) \
+# $(top_builddir)/replace/libreplace.la
+#main_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@
+
+
+## libraries
+
+lib_LTLIBRARIES = libpils.la
+
+plugindir = $(libdir)/@HB_PKG@/plugins/test
+plugin_LTLIBRARIES = test.la
+
+libpils_la_SOURCES = pils.c
+libpils_la_LDFLAGS = -version-info 2:0:0
+libpils_la_LIBADD = $(top_builddir)/replace/libreplace.la \
+ @LIBLTDL@ $(GLIBLIB)
+test_la_SOURCES = test.c
+test_la_LDFLAGS = -export-dynamic -module -avoid-version
diff --git a/lib/pils/main.c b/lib/pils/main.c
new file mode 100644
index 0000000..32faceb
--- /dev/null
+++ b/lib/pils/main.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <stdio.h>
+#include <pils/generic.h>
+
+#define MOD "/home/alanr/modules"
+
+GHashTable* test1functions = NULL;
+
+long one = 1;
+long two = 2;
+long three = 3;
+long four = 4;
+
+static int TestCallBack
+( GenericPILCallbackType t
+, PILPluginUniv* univ
+, const char * iftype
+, const char * ifname
+, void* userptr
+);
+
+static PILGenericIfMgmtRqst RegRqsts [] =
+ { {"test", &test1functions, &one, TestCallBack, &two},
+ {NULL, NULL, NULL, NULL, NULL}
+};
+
+int
+main(int argc, char ** argv)
+{
+ PILPluginUniv * u;
+ PIL_rc rc;
+ int j;
+
+
+ u = NewPILPluginUniv(MOD);
+ /* PILSetDebugLevel(u, NULL, NULL, 0); */
+ PILLogMemStats();
+
+
+ if ((rc = PILLoadPlugin(u, "InterfaceMgr", "generic", &RegRqsts))
+ != PIL_OK) {
+ fprintf(stderr, "generic plugin load Error = [%s]\n"
+ , lt_dlerror());
+ /*exit(1);*/
+ }
+ /* PILSetDebugLevel(u, NULL, NULL, 0); */
+
+ for (j=0; j < 10; ++j) {
+ PILLogMemStats();
+ fprintf(stderr, "****Loading plugin test/test\n");
+ if ((rc = PILLoadPlugin(u, "test", "test", NULL)) != PIL_OK) {
+ printf("ERROR: test plugin load error = [%d/%s]\n"
+ , rc, lt_dlerror());
+ }
+ PILLogMemStats();
+ fprintf(stderr, "****UN-loading plugin test/test\n");
+ if ((rc = PILIncrIFRefCount(u, "test", "test", -1))!= PIL_OK){
+ printf("ERROR: test plugin UNload error = [%d/%s]\n"
+ , rc, lt_dlerror());
+ }
+ }
+ PILLogMemStats();
+ DelPILPluginUniv(u); u = NULL;
+ PILLogMemStats();
+
+ return 0;
+}
+
+
+static int
+TestCallBack
+( GenericPILCallbackType t
+, PILPluginUniv* univ
+, const char * iftype
+, const char * ifname
+, void* userptr)
+{
+ char cbbuf[32];
+
+ switch(t) {
+ case PIL_REGISTER:
+ snprintf(cbbuf, sizeof(cbbuf), "PIL_REGISTER");
+ break;
+
+ case PIL_UNREGISTER:
+ snprintf(cbbuf, sizeof(cbbuf), "PIL_UNREGISTER");
+ break;
+
+ default:
+ snprintf(cbbuf, sizeof(cbbuf), "type [%d?]", t);
+ break;
+ }
+
+ fprintf(stderr, "Callback: (%s, univ: 0x%lx, module: %s/%s, user ptr: 0x%lx (%ld))\n"
+ , cbbuf
+ , (unsigned long) univ
+ , iftype, ifname
+ , (unsigned long)userptr
+ , (*((long *)userptr)));
+ return PIL_OK;
+}
+
diff --git a/lib/pils/pils.c b/lib/pils/pils.c
new file mode 100644
index 0000000..4243b22
--- /dev/null
+++ b/lib/pils/pils.c
@@ -0,0 +1,2152 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+
+/* Dumbness... */
+#define time FooTimeParameter
+#define index FooIndexParameter
+# include <glib.h>
+#undef time
+#undef index
+
+
+#define ENABLE_PIL_DEFS_PRIVATE
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+
+#ifndef STRLEN_CONST
+# define STRLEN_CONST(con) (sizeof(con)-1)
+#endif
+
+#include <pils/interface.h>
+
+#define NEW(type) (g_new(type,1))
+#define ZAP(obj) memset(obj, 0, sizeof(*obj))
+#define DELETE(obj) {g_free(obj); obj = NULL;}
+
+#ifdef LTDL_SHLIB_EXT
+# define PLUGINSUFFIX LTDL_SHLIB_EXT
+#else
+# define PLUGINSUFFIX ".so"
+#endif
+
+static int PluginDebugLevel = 0;
+
+#define DEBUGPLUGIN (PluginDebugLevel > 0)
+
+
+
+static PIL_rc InterfaceManager_plugin_init(PILPluginUniv* univ);
+
+static char** PILPluginTypeListPlugins(PILPluginType* pitype, int* picount);
+static PILInterface* FindIF(PILPluginUniv* universe, const char *iftype
+, const char * ifname);
+static PIL_rc PluginExists(const char * PluginPath);
+static char * PILPluginPath(PILPluginUniv* universe, const char * plugintype
+, const char * pluginname);
+
+
+void DelPILPluginUniv(PILPluginUniv*);
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * functions, so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ *
+ * They all follow the same calling sequence though. It is:
+ * String name"*" type object
+ * "*" type object with the name given by 1st argument
+ * NULL
+ *
+ * For example:
+ * RmAPILPluginType takes
+ * string name
+ * PILPluginType* object with the given name.
+ */
+static gboolean RmAPILPluginType
+( gpointer pitname /* Name of this plugin type */
+, gpointer pitype /* PILPluginType* */
+, gpointer notused
+);
+static void RemoveAPILPluginType(PILPluginType*);
+
+static PILPluginType* NewPILPluginType
+( PILPluginUniv* pluginuniv
+, const char * plugintype
+);
+static void DelPILPluginType(PILPluginType*);
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ */
+static gboolean RmAPILPlugin
+( gpointer piname /* Name of this plugin */
+, gpointer plugin /* PILPlugin* */
+, gpointer notused
+);
+static void RemoveAPILPlugin(PILPlugin*);
+
+
+static PILPlugin* NewPILPlugin(PILPluginType* pitype
+ , const char * plugin_name
+ , lt_dlhandle dlhand
+ , PILPluginInitFun PluginSym);
+static void DelPILPlugin(PILPlugin*);
+
+struct MemStat {
+ unsigned long news;
+ unsigned long frees;
+};
+
+static struct PluginStats {
+ struct MemStat plugin;
+ struct MemStat pitype;
+ struct MemStat piuniv;
+ struct MemStat interface;
+ struct MemStat interfacetype;
+ struct MemStat interfaceuniv;
+}PILstats;
+
+#define STATNEW(t) {PILstats.t.news ++; }
+#define STATFREE(t) {PILstats.t.frees ++; }
+
+
+
+static PILInterfaceUniv* NewPILInterfaceUniv(PILPluginUniv*);
+static void DelPILInterfaceUniv(PILInterfaceUniv*);
+/*
+ * These RmA* functions primarily called from hash_table_foreach, but
+ * not necessarily, so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ */
+static gboolean RmAPILInterfaceType
+( gpointer iftypename /* Name of this interface type */
+, gpointer iftype /* PILInterfaceType* */
+, gpointer notused
+);
+static void RemoveAPILInterfaceType(PILInterfaceType*, PILInterfaceType*);
+
+static PILInterfaceType* NewPILInterfaceType
+( PILInterfaceUniv*
+, const char * typename
+, void* ifexports, void* user_data
+);
+static void DelPILInterfaceType(PILInterfaceType*);
+/*
+ * These RmA* functions are designed to be called from
+ * hash_table_foreach, so they have gpointer arguments. When calling
+ * them by hand, take special care to pass the right argument types.
+ * They can be called from other places safely also.
+ */
+static gboolean RmAPILInterface
+( gpointer ifname /* Name of this interface */
+, gpointer plugin /* PILInterface* */
+, gpointer notused
+);
+static PIL_rc RemoveAPILInterface(PILInterface*);
+static void DelPILPluginType(PILPluginType*);
+
+static PILInterface* NewPILInterface
+( PILInterfaceType* interfacetype
+, const char* interfacename
+, void * exports
+, PILInterfaceFun closefun
+, void* ud_interface
+, PILPlugin* loading_plugin /* The plugin that loaded us */
+);
+static void DelPILInterface(PILInterface*);
+static PIL_rc close_ifmgr_interface(PILInterface*, void*);
+
+
+
+
+/*
+ * For consistency, we show up as a plugin in our our system.
+ *
+ * Here are our exports as a plugin.
+ *
+ */
+static const char * PIL_PILPluginVersion(void);
+static void PIL_PILPluginClose (PILPlugin*);
+void PILpisysSetDebugLevel (int level);
+int PILpisysGetDebugLevel(void);
+static const char * PIL_PILPluginLicense (void);
+static const char * PIL_PILPluginLicenseUrl (void);
+
+static const PILPluginOps PluginExports =
+{ PIL_PILPluginVersion
+, PILpisysGetDebugLevel
+, PILpisysSetDebugLevel
+, PIL_PILPluginLicense
+, PIL_PILPluginLicenseUrl
+, PIL_PILPluginClose
+};
+
+/* Prototypes for the functions that we export to every plugin */
+static PIL_rc PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* mops);
+static PIL_rc PILunregister_plugin(PILPlugin* piinfo);
+static PIL_rc
+PILRegisterInterface
+( PILPlugin* piinfo
+, const char * interfacetype /* Type of interface */
+, const char * interfacename /* Name of interface */
+, void* Ops /* Ops exported by this interface */
+, PILInterfaceFun closefunc /* Ops exported by this interface */
+, PILInterface** interfaceid /* Interface id (OP) */
+, void** Imports /* Functions imported by */
+ /* this interface (OP) */
+, void* ud_interface /* interface user data */
+);
+static PIL_rc PILunregister_interface(PILInterface* interfaceid);
+static void PILLog(PILLogLevel priority, const char * fmt, ...)
+ G_GNUC_PRINTF(2,3);
+
+
+/*
+ * This is the set of functions that we export to every plugin
+ *
+ * That also makes it the set of functions that every plugin imports.
+ *
+ */
+
+static PILPluginImports PILPluginImportSet =
+{ PILregister_plugin /* register_plugin */
+, PILunregister_plugin /* unregister_plugin */
+, PILRegisterInterface /* register_interface */
+, RemoveAPILInterface /* unregister_interface */
+, PILLoadPlugin /* load_plugin */
+, PILLog /* Logging function */
+, g_malloc /* Malloc function */
+, g_realloc /* realloc function */
+, g_free /* Free function */
+, g_strdup /* Strdup function */
+};
+
+static PIL_rc ifmgr_register_interface(PILInterface* newif
+ , void** imports);
+static PIL_rc ifmgr_unregister_interface(PILInterface* interface);
+
+/*
+ * For consistency, the master interface manager is a interface in the
+ * system. Below is our set of exported Interface functions.
+ *
+ * Food for thought: This is the interface manager whose name is
+ * interface. This makes it the Interface Interface interface ;-)
+ * (or the Interface/Interface interface if you prefer)
+ */
+
+static PILInterfaceOps IfExports =
+{ ifmgr_register_interface
+, ifmgr_unregister_interface
+};
+
+
+
+/*
+ * Below is the set of functions we export to every interface manager.
+ */
+
+static int IfRefCount(PILInterface * ifh);
+static int IfIncrRefCount(PILInterface*eifinfo,int plusminus);
+static int PluginIncrRefCount(PILPlugin*eifinfo,int plusminus);
+#if 0
+static int PluginRefCount(PILPlugin * ifh);
+#endif
+static void IfForceUnregister(PILInterface *eifinfo);
+static void IfForEachClientRemove(PILInterface* manangerif
+ , gboolean(*f)(PILInterface* clientif, void * other)
+ , void* other);
+
+static PILInterfaceImports IFManagerImports =
+{ IfRefCount
+, IfIncrRefCount
+, IfForceUnregister
+, IfForEachClientRemove
+};
+static void PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype);
+static void PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv);
+static void PILValidatePluginUniv(gpointer key, gpointer pitype, gpointer);
+static void PILValidateInterface(gpointer key, gpointer interface, gpointer iftype);
+static void PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv);
+static void PILValidateInterfaceUniv(gpointer key, gpointer puniv, gpointer);
+
+/*****************************************************************************
+ *
+ * This code is for managing plugins, and interacting with them...
+ *
+ ****************************************************************************/
+
+static PILPlugin*
+NewPILPlugin( PILPluginType* pitype
+ , const char * plugin_name
+ , lt_dlhandle dlhand
+ , PILPluginInitFun PluginSym)
+{
+ PILPlugin* ret = NEW(PILPlugin);
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILPlugin(0x%lx)", (unsigned long)ret);
+ }
+
+ STATNEW(plugin);
+ ret->MagicNum = PIL_MAGIC_PLUGIN;
+ ret->plugin_name = g_strdup(plugin_name);
+ ret->plugintype = pitype;
+ ret->refcnt = 0;
+ ret->dlhandle = dlhand;
+ ret->dlinitfun = PluginSym;
+ PILValidatePlugin(ret->plugin_name, ret, pitype);
+ return ret;
+}
+static void
+DelPILPlugin(PILPlugin*pi)
+{
+
+ if (pi->refcnt > 0) {
+ PILLog(PIL_INFO, "DelPILPlugin: Non-zero refcnt");
+ }
+
+ if (pi->dlhandle) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Closing dlhandle for (%s/%s)"
+ , pi->plugintype->plugintype, pi->plugin_name);
+ }
+ lt_dlclose(pi->dlhandle);
+ }else if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NO dlhandle for (%s/%s)!"
+ , pi->plugintype->plugintype, pi->plugin_name);
+ }
+ DELETE(pi->plugin_name);
+ ZAP(pi);
+ DELETE(pi);
+ STATFREE(plugin);
+}
+
+
+static PILPluginType dummymlpitype =
+{ PIL_MAGIC_PLUGINTYPE
+, NULL /*plugintype*/
+, NULL /*piuniv*/
+, NULL /*Plugins*/
+, PILPluginTypeListPlugins /* listplugins */
+};
+
+static PILPluginType*
+NewPILPluginType(PILPluginUniv* pluginuniv
+ , const char * plugintype
+)
+{
+ PILPluginType* ret = NEW(PILPluginType);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILPlugintype(0x%lx)", (unsigned long)ret);
+ }
+ STATNEW(pitype);
+
+ *ret = dummymlpitype;
+
+ ret->plugintype = g_strdup(plugintype);
+ ret->piuniv = pluginuniv;
+ ret->Plugins = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(pluginuniv->PluginTypes
+ , g_strdup(ret->plugintype), ret);
+ PILValidatePluginType(ret->plugintype, ret, pluginuniv);
+ return ret;
+}
+static void
+DelPILPluginType(PILPluginType*pitype)
+{
+ PILValidatePluginType(NULL, pitype, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILPluginType(%s)", pitype->plugintype);
+ }
+
+ STATFREE(pitype);
+ g_hash_table_foreach_remove(pitype->Plugins, RmAPILPlugin, NULL);
+ g_hash_table_destroy(pitype->Plugins);
+ DELETE(pitype->plugintype);
+ ZAP(pitype);
+ DELETE(pitype);
+}
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key *
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean
+RmAPILPlugin /* IsA GHFunc: required for g_hash_table_foreach_remove() */
+( gpointer piname /* Name of this plugin */
+, gpointer plugin /* PILPlugin* */
+, gpointer notused
+)
+{
+ PILPlugin* Plugin = plugin;
+ PILPluginType* Pitype = Plugin->plugintype;
+
+ PILValidatePlugin(piname, plugin, NULL);
+ PILValidatePluginType(NULL, Pitype, NULL);
+ g_assert(IS_PILPLUGIN(Plugin));
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILPlugin(%s/%s)", Pitype->plugintype
+ , Plugin->plugin_name);
+ }
+ /* Normally called from g_hash_table_foreachremove or equivalent */
+
+ DelPILPlugin(plugin);
+ DELETE(piname);
+ return TRUE;
+}
+
+static void
+RemoveAPILPlugin(PILPlugin*Plugin)
+{
+ PILPluginType* Pitype = Plugin->plugintype;
+ gpointer key;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RemoveAPILPlugin(%s/%s)"
+ , Pitype->plugintype
+ , Plugin->plugin_name);
+ }
+ if (g_hash_table_lookup_extended(Pitype->Plugins
+ , Plugin->plugin_name, &key, (void*)&Plugin)) {
+
+ g_hash_table_remove(Pitype->Plugins, key);
+ RmAPILPlugin(key, Plugin, NULL);
+ key = NULL;
+ Plugin = NULL;
+
+ }else{
+ g_assert_not_reached();
+ }
+ if (g_hash_table_size(Pitype->Plugins) == 0) {
+ RemoveAPILPluginType(Pitype);
+ /* Pitype is now invalid */
+ Pitype = NULL;
+ }
+}
+
+PILPluginUniv*
+NewPILPluginUniv(const char * basepluginpath)
+{
+ PILPluginUniv* ret = NEW(PILPluginUniv);
+
+ /* The delimiter separating search path components */
+ const char* path_delim = G_SEARCHPATH_SEPARATOR_S;
+ char * fullpath;
+
+ STATNEW(piuniv);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILPluginUniv(0x%lx)"
+ , (unsigned long)ret);
+ }
+ if (!g_path_is_absolute(basepluginpath)) {
+ DELETE(ret);
+ return(ret);
+ }
+ ret->MagicNum = PIL_MAGIC_PLUGINUNIV;
+ fullpath = g_strdup_printf("%s%s%s", basepluginpath
+ , path_delim, PILS_BASE_PLUGINDIR);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "PILS: Plugin path = %s", fullpath);
+ }
+
+ /* Separate the root directory PATH into components */
+ ret->rootdirlist = g_strsplit(fullpath, path_delim, 100);
+ g_free(fullpath);
+
+ ret->PluginTypes = g_hash_table_new(g_str_hash, g_str_equal);
+ ret->imports = &PILPluginImportSet;
+ ret->ifuniv = NewPILInterfaceUniv(ret);
+ PILValidatePluginUniv(NULL, ret, NULL);
+ return ret;
+}
+
+/* Change memory allocation functions immediately after creating universe */
+void
+PilPluginUnivSetMemalloc(PILPluginUniv* u
+, gpointer (*allocfun)(glib_size_t size)
+, gpointer (*reallocfun)(gpointer ptr, glib_size_t size)
+, void (*freefun)(void* space)
+, char* (*strdupfun)(const char *s))
+{
+ u->imports->alloc = allocfun;
+ u->imports->mrealloc = reallocfun;
+ u->imports->mfree = freefun;
+ u->imports->mstrdup = strdupfun;
+}
+
+
+/* Change logging functions - preferably right after creating universe */
+void
+PilPluginUnivSetLog(PILPluginUniv* u
+, void (*logfun) (PILLogLevel priority, const char * fmt, ...))
+{
+ u->imports->log = logfun;
+}
+
+void
+DelPILPluginUniv(PILPluginUniv* piuniv)
+{
+
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILPluginUniv(0x%lx)"
+ , (unsigned long)piuniv);
+ }
+ STATFREE(piuniv);
+ PILValidatePluginUniv(NULL, piuniv, NULL);
+ DelPILInterfaceUniv(piuniv->ifuniv);
+ piuniv->ifuniv = NULL;
+ g_hash_table_foreach_remove(piuniv->PluginTypes
+ , RmAPILPluginType, NULL);
+ g_hash_table_destroy(piuniv->PluginTypes);
+ g_strfreev(piuniv->rootdirlist);
+ ZAP(piuniv);
+ DELETE(piuniv);
+}
+
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key *
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */
+RmAPILPluginType
+( gpointer pitname /* Name of this plugin type "real" key */
+, gpointer pitype /* PILPluginType* */
+, gpointer notused
+)
+{
+ PILPluginType* Plugintype = pitype;
+
+ g_assert(IS_PILPLUGINTYPE(Plugintype));
+ PILValidatePluginType(pitname, pitype, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILPluginType(%s)"
+ , Plugintype->plugintype);
+ }
+ /*
+ * This function is usually but not always called by
+ * g_hash_table_foreach_remove()
+ */
+
+ DelPILPluginType(pitype);
+ DELETE(pitname);
+ return TRUE;
+}
+static void
+RemoveAPILPluginType(PILPluginType*Plugintype)
+{
+ PILPluginUniv* Pluginuniv = Plugintype->piuniv;
+ gpointer key;
+ if (g_hash_table_lookup_extended(Pluginuniv->PluginTypes
+ , Plugintype->plugintype, &key, (void*)&Plugintype)) {
+
+ g_hash_table_remove(Pluginuniv->PluginTypes, key);
+ RmAPILPluginType(key, Plugintype, NULL);
+ }else{
+ g_assert_not_reached();
+ }
+}
+
+/*
+ * InterfaceManager_plugin_init: Initialize the handling of
+ * "Interface Manager" interfaces.
+ *
+ * There are a few potential bootstrapping problems here ;-)
+ *
+ */
+static PIL_rc
+InterfaceManager_plugin_init(PILPluginUniv* univ)
+{
+ PILPluginImports* imports = univ->imports;
+ PILPluginType* pitype;
+ PILInterface* ifinfo;
+ PILInterfaceType* iftype;
+ void* dontcare;
+ PILPlugin* ifmgr_plugin;
+ PIL_rc rc;
+
+
+ iftype = NewPILInterfaceType(univ->ifuniv, PI_IFMANAGER, &IfExports
+ , NULL);
+
+ g_hash_table_insert(univ->ifuniv->iftypes
+ , g_strdup(PI_IFMANAGER), iftype);
+
+ pitype = NewPILPluginType(univ, PI_IFMANAGER);
+
+ g_hash_table_insert(univ->PluginTypes
+ , g_strdup(PI_IFMANAGER), pitype);
+
+ ifmgr_plugin= NewPILPlugin(pitype, PI_IFMANAGER, NULL, NULL);
+
+ g_hash_table_insert(pitype->Plugins
+ , g_strdup(PI_IFMANAGER), ifmgr_plugin);
+
+ /* We can call register_plugin, since it doesn't depend on us... */
+ rc = imports->register_plugin(ifmgr_plugin, &PluginExports);
+ if (rc != PIL_OK) {
+ PILLog(PIL_CRIT, "register_plugin() failed in init: %s"
+ , PIL_strerror(rc));
+ return(rc);
+ }
+ /*
+ * Now, we're registering interfaces, and are into some deep
+ * Catch-22 if do it the "easy" way, since our code is
+ * needed in order to support interface loading for the type of
+ * interface we are (a Interface interface).
+ *
+ * So, instead of calling imports->register_interface(), we have to
+ * do the work ourselves here...
+ *
+ * Since no one should yet be registered to handle Interface
+ * interfaces, we need to bypass the hash table handler lookup
+ * that register_interface would do and call the function that
+ * register_interface would call...
+ *
+ */
+
+ /* The first argument is the PILInterfaceType* */
+ ifinfo = NewPILInterface(iftype, PI_IFMANAGER, &IfExports
+ , close_ifmgr_interface, NULL, NULL);
+ ifinfo->ifmanager = iftype->ifmgr_ref = ifinfo;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "InterfaceManager_plugin_init(0x%lx/%s)"
+ , (unsigned long)ifinfo, ifinfo->interfacename);
+ }
+ PILValidatePluginUniv(NULL, univ, NULL);
+ ifmgr_register_interface(ifinfo, &dontcare);
+ PILValidatePluginUniv(NULL, univ, NULL);
+
+ return(PIL_OK);
+}/*InterfaceManager_plugin_init*/
+
+
+/* Return current IfIf "plugin" version (not very interesting for us) */
+static const char *
+PIL_PILPluginVersion(void)
+{
+ return("1.0");
+}
+
+/* Return current IfIf debug level */
+int
+PILpisysGetDebugLevel(void)
+{
+ return(PluginDebugLevel);
+}
+
+/* Set current IfIf debug level */
+void
+PILpisysSetDebugLevel (int level)
+{
+ PluginDebugLevel = level;
+}
+struct set_debug_helper {
+ const char * pitype;
+ const char * piname;
+ int level;
+};
+
+static void
+PILSetDebugLeveltoPlugin(gpointer key, gpointer plugin, gpointer Helper)
+{
+ PILPlugin* p = plugin;
+ struct set_debug_helper* helper = Helper;
+
+ p->pluginops->setdebuglevel(helper->level);
+}
+
+static void
+PILSetDebugLevelbyType(const void * key, gpointer plugintype, gpointer Helper)
+{
+ struct set_debug_helper* helper = Helper;
+
+
+ PILPluginType* t = plugintype;
+
+ if (helper->piname == NULL) {
+ g_hash_table_foreach(t->Plugins, PILSetDebugLeveltoPlugin
+ , helper);
+ }else{
+ PILPlugin* p = g_hash_table_lookup(t->Plugins
+ , helper->piname);
+ if (p != NULL) {
+ p->pluginops->setdebuglevel(helper->level);
+ }
+ }
+}
+
+void
+PILSetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname
+, int level)
+{
+ struct set_debug_helper helper = {pitype, piname, level};
+
+ if (u == NULL) {
+ return;
+ }
+
+ if (pitype == NULL) {
+ g_hash_table_foreach(u->PluginTypes
+ /*
+ * Reason for this next cast:
+ * SetDebugLevelbyType takes const gpointer
+ * arguments, unlike a GHFunc which doesn't.
+ */
+ , (GHFunc)PILSetDebugLevelbyType
+ , &helper);
+ }else{
+ PILPluginType* t = g_hash_table_lookup(u->PluginTypes
+ , pitype);
+ if (t != NULL) {
+ PILSetDebugLevelbyType(pitype, t, &helper);
+ }
+ }
+}
+
+
+int
+PILGetDebugLevel(PILPluginUniv* u, const char * pitype, const char * piname)
+{
+ PILPluginType* t;
+ PILPlugin* p;
+ if ( u == NULL
+ || pitype == NULL
+ || (t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL
+ || (p = g_hash_table_lookup(t->Plugins, piname)) == NULL) {
+ return -1;
+ }
+ return p->pluginops->getdebuglevel();
+}
+
+/* Close/shutdown our PILPlugin (the interface manager interface plugin) */
+/* All our interfaces will have already been shut down and unregistered */
+static void
+PIL_PILPluginClose (PILPlugin* plugin)
+{
+}
+static const char *
+PIL_PILPluginLicense (void)
+{
+ return LICENSE_LGPL;
+}
+static const char *
+PIL_PILPluginLicenseUrl (void)
+{
+ return URL_LGPL;
+}
+
+/*****************************************************************************
+ *
+ * This code is for managing interfaces, and interacting with them...
+ *
+ ****************************************************************************/
+
+
+static PILInterface*
+NewPILInterface(PILInterfaceType* interfacetype
+ , const char* interfacename
+ , void * exports
+ , PILInterfaceFun closefun
+ , void* ud_interface
+ , PILPlugin* loading_plugin)
+{
+ PILInterface* ret = NULL;
+ PILInterface* look = NULL;
+
+
+ if ((look = g_hash_table_lookup(interfacetype->interfaces
+ , interfacename)) != NULL) {
+ PILLog(PIL_DEBUG, "Deleting PILInterface!");
+ DelPILInterface(look);
+ }
+ ret = NEW(PILInterface);
+ STATNEW(interface);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILInterface(0x%lx)", (unsigned long)ret);
+ }
+
+ if (ret) {
+ ret->MagicNum = PIL_MAGIC_INTERFACE;
+ ret->interfacetype = interfacetype;
+ ret->exports = exports;
+ ret->ud_interface = ud_interface;
+ ret->interfacename = g_strdup(interfacename);
+ ret->ifmanager = interfacetype->ifmgr_ref;
+ ret->loadingpi = loading_plugin;
+ g_hash_table_insert(interfacetype->interfaces
+ , g_strdup(ret->interfacename), ret);
+
+ ret->if_close = closefun;
+ ret->refcnt = 1;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILInterface(0x%lx:%s/%s)*** user_data: 0x%p *******"
+ , (unsigned long)ret
+ , interfacetype->typename
+ , ret->interfacename
+ , ud_interface);
+ }
+ }
+ return ret;
+}
+static void
+DelPILInterface(PILInterface* intf)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterface(0x%lx/%s)"
+ , (unsigned long)intf, intf->interfacename);
+ }
+ STATFREE(interface);
+ DELETE(intf->interfacename);
+ ZAP(intf);
+ DELETE(intf);
+}
+
+static PILInterfaceType*
+NewPILInterfaceType(PILInterfaceUniv*univ, const char * typename
+, void* ifeports, void* user_data)
+{
+ PILInterfaceType* ifmgr_types;
+ PILInterface* ifmgr_ref;
+ PILInterfaceType* ret = NEW(PILInterfaceType);
+
+
+ STATNEW(interfacetype);
+ ret->MagicNum = PIL_MAGIC_INTERFACETYPE;
+ ret->typename = g_strdup(typename);
+ ret->interfaces = g_hash_table_new(g_str_hash, g_str_equal);
+ ret->ud_if_type = user_data;
+ ret->universe = univ;
+ ret->ifmgr_ref = NULL;
+
+ /* Now find the pointer to our if type in the Interface Universe */
+ if ((ifmgr_types = g_hash_table_lookup(univ->iftypes, PI_IFMANAGER))
+ != NULL) {
+ if ((ifmgr_ref=g_hash_table_lookup(ifmgr_types->interfaces
+ , typename)) != NULL) {
+ ret->ifmgr_ref = ifmgr_ref;
+ }else {
+ g_assert(strcmp(typename, PI_IFMANAGER) == 0);
+ }
+ }else {
+ g_assert(strcmp(typename, PI_IFMANAGER) == 0);
+ }
+
+ /* Insert ourselves into our parent's table */
+ g_hash_table_insert(univ->iftypes, g_strdup(typename), ret);
+ return ret;
+}
+static void
+DelPILInterfaceType(PILInterfaceType*ift)
+{
+ PILInterfaceUniv* u = ift->universe;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterfaceType(%s)"
+ , ift->typename);
+ }
+ STATFREE(interfacetype);
+
+ PILValidateInterfaceUniv(NULL, u, NULL);
+
+ /*
+ * RmAPILInterface refuses to remove the interface for the
+ * Interface manager, because it must be removed last.
+ *
+ * Otherwise we won't be able to unregister interfaces
+ * for other types of objects, and we'll be very confused.
+ */
+
+ g_hash_table_foreach_remove(ift->interfaces, RmAPILInterface, NULL);
+
+ PILValidateInterfaceUniv(NULL, u, NULL);
+
+ if (g_hash_table_size(ift->interfaces) > 0) {
+ gpointer key, iftype;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "DelPILInterfaceType(%s): table size (%d)"
+ , ift->typename, g_hash_table_size(ift->interfaces));
+ }
+ if (g_hash_table_lookup_extended(ift->interfaces
+ , PI_IFMANAGER, &key, &iftype)) {
+ DelPILInterface((PILInterface*)iftype);
+ DELETE(key);
+ }
+ }
+ DELETE(ift->typename);
+ g_hash_table_destroy(ift->interfaces);
+ ZAP(ift);
+ DELETE(ift);
+}
+
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key *
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean /* IsAGHFunc: required for g_hash_table_foreach_remove() */
+RmAPILInterface
+( gpointer ifname /* Name of this interface */
+, gpointer intf /* PILInterface* */
+, gpointer notused
+)
+{
+ PILInterface* If = intf;
+ PILInterfaceType* Iftype = If->interfacetype;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterface(0x%lx/%s)"
+ , (unsigned long)If, If->interfacename);
+ }
+ g_assert(IS_PILINTERFACE(If));
+
+ /*
+ * Don't remove the master interface manager this way, or
+ * Somebody will have a cow...
+ */
+ if (If == If->ifmanager) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterface: skipping (%s)"
+ , If->interfacename);
+ }
+ return FALSE;
+ }
+ PILValidateInterface(ifname, If, Iftype);
+ PILValidateInterfaceType(NULL, Iftype, NULL);
+
+ /*
+ * This function is usually but not always called by
+ * g_hash_table_foreach_remove()
+ */
+
+ PILunregister_interface(If);
+ PILValidateInterface(ifname, If, Iftype);
+ PILValidateInterfaceType(NULL, Iftype, NULL);
+ DELETE(ifname);
+ DelPILInterface(If);
+ return TRUE;
+}
+static PIL_rc
+RemoveAPILInterface(PILInterface* pif)
+{
+ PILInterfaceType* Iftype = pif->interfacetype;
+ gpointer key;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RemoveAPILInterface(0x%lx/%s)"
+ , (unsigned long)pif, pif->interfacename);
+ }
+ if (g_hash_table_lookup_extended(Iftype->interfaces
+ , pif->interfacename, &key, (void*)&pif)) {
+ g_assert(IS_PILINTERFACE(pif));
+ g_hash_table_remove(Iftype->interfaces, key);
+ RmAPILInterface(key, pif, NULL);
+ }else{
+ g_assert_not_reached();
+ }
+
+ if (g_hash_table_size(Iftype->interfaces) == 0) {
+ /* The generic plugin handler doesn't want us to
+ * delete it's types...
+ */
+ if (Iftype->ifmgr_ref->refcnt <= 1) {
+ RemoveAPILInterfaceType(Iftype, NULL);
+ }
+ }
+ return PIL_OK;
+}
+
+
+/* Register a Interface Interface (Interface manager) */
+static PIL_rc
+ifmgr_register_interface(PILInterface* intf
+, void** imports)
+{
+ PILInterfaceType* ift = intf->interfacetype;
+ PILInterfaceUniv* ifuniv = ift->universe;
+ PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "Registering Implementation manager for"
+ " Interface type '%s'"
+ , intf->interfacename);
+ }
+
+ ifops = intf->exports;
+ if (ifops->RegisterInterface == NULL
+ || ifops->UnRegisterInterface == NULL) {
+ PILLog(PIL_DEBUG, "ifmgr_register_interface(%s)"
+ ": NULL exported function pointer"
+ , intf->interfacename);
+ return PIL_INVAL;
+ }
+
+ *imports = &IFManagerImports;
+
+ if(g_hash_table_lookup(ifuniv->iftypes, intf->interfacename) == NULL){
+ /* It registers itself into ifuniv automatically */
+ NewPILInterfaceType(ifuniv,intf->interfacename, &IfExports
+ , NULL);
+ }
+ return PIL_OK;
+}
+
+static gboolean
+RemoveAllClients(PILInterface*interface, void * managerif)
+{
+ /*
+ * Careful! We can't remove ourselves this way...
+ * This gets taken care of as a special case in DelPILInterfaceUniv...
+ */
+ if (managerif == interface) {
+ return FALSE;
+ }
+ PILunregister_interface(interface);
+ return TRUE;
+}
+
+/* Unconditionally unregister a interface manager (InterfaceMgr Interface) */
+static PIL_rc
+ifmgr_unregister_interface(PILInterface* interface)
+{
+ /*
+ * We need to unregister every interface we manage
+ */
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "ifmgr_unregister_interface(%s)"
+ , interface->interfacename);
+ }
+
+ IfForEachClientRemove(interface, RemoveAllClients, interface);
+ return PIL_OK;
+}
+
+/* Called to close the Interface manager for type Interface */
+static PIL_rc
+close_ifmgr_interface(PILInterface* us, void* ud_interface)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "close_ifmgr_interface(%s)"
+ , us->interfacename);
+ }
+ /* Nothing much to do */
+ return PIL_OK;
+}
+
+/* Return the reference count for this interface */
+static int
+IfRefCount(PILInterface * eifinfo)
+{
+ return eifinfo->refcnt;
+}
+
+/* Modify the reference count for this interface */
+static int
+IfIncrRefCount(PILInterface*eifinfo, int plusminus)
+{
+ if(DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfIncrRefCount(%d + %d )"
+ , eifinfo->refcnt, plusminus);
+ }
+ eifinfo->refcnt += plusminus;
+ if (eifinfo->refcnt <= 0) {
+ eifinfo->refcnt = 0;
+ /* Unregister this interface. */
+ RemoveAPILInterface(eifinfo);
+ return 0;
+ }
+ return eifinfo->refcnt;
+}
+
+#if 0
+static int
+PluginRefCount(PILPlugin * pi)
+{
+ return pi->refcnt;
+}
+#endif
+
+static int
+PluginIncrRefCount(PILPlugin*pi, int plusminus)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PluginIncrRefCount(%d + %d )"
+ , pi->refcnt, plusminus);
+ }
+ pi->refcnt += plusminus;
+ if (pi->refcnt <= 0) {
+ pi->refcnt = 0;
+ RemoveAPILPlugin(pi);
+ return 0;
+ }
+ return pi->refcnt;
+}
+
+static PILInterface*
+FindIF(PILPluginUniv* universe, const char *iftype, const char * ifname)
+{
+ PILInterfaceUniv* puniv;
+ PILInterfaceType* ptype;
+
+ if (universe == NULL || (puniv = universe->ifuniv) == NULL
+ || (ptype=g_hash_table_lookup(puniv->iftypes, iftype))==NULL){
+ return NULL;
+ }
+ return g_hash_table_lookup(ptype->interfaces, ifname);
+}
+
+PIL_rc
+PILIncrIFRefCount(PILPluginUniv* mu
+, const char * interfacetype
+, const char * interfacename
+, int plusminus)
+{
+ PILInterface* intf = FindIF(mu, interfacetype, interfacename);
+
+ if (intf) {
+ g_assert(IS_PILINTERFACE(intf));
+ IfIncrRefCount(intf, plusminus);
+ return PIL_OK;
+ }
+ return PIL_NOPLUGIN;
+}
+
+int
+PILGetIFRefCount(PILPluginUniv* mu
+, const char * interfacetype
+, const char * interfacename)
+{
+ PILInterface* intf = FindIF(mu, interfacetype, interfacename);
+
+ return IfRefCount(intf);
+}
+
+static void
+IfForceUnregister(PILInterface *id)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfForceUnRegister(%s)"
+ , id->interfacename);
+ }
+ RemoveAPILInterface(id);
+}
+
+struct f_e_c_helper {
+ gboolean(*fun)(PILInterface* clientif, void * passalong);
+ void* passalong;
+};
+
+static gboolean IfForEachClientHelper(gpointer key
+, gpointer iftype, gpointer helper_v);
+
+static gboolean
+IfForEachClientHelper(gpointer unused, gpointer iftype, gpointer v)
+{
+ struct f_e_c_helper* s = (struct f_e_c_helper*)v;
+
+ g_assert(IS_PILINTERFACE((PILInterface*)iftype));
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfForEachClientHelper(%s)"
+ , ((PILInterface*)iftype)->interfacename);
+ }
+
+ return s->fun((PILInterface*)iftype, s->passalong);
+}
+
+
+static void
+IfForEachClientRemove
+( PILInterface* mgrif
+, gboolean(*f)(PILInterface* clientif, void * passalong)
+, void* passalong /* usually PILInterface* */
+)
+{
+ PILInterfaceType* mgrt;
+ PILInterfaceUniv* u;
+ const char * ifname;
+ PILInterfaceType* clientt;
+
+ struct f_e_c_helper h = {f, passalong};
+
+
+ if (mgrif == NULL || (mgrt = mgrif->interfacetype) == NULL
+ || (u = mgrt->universe) == NULL
+ || (ifname = mgrif->interfacename) == NULL) {
+ PILLog(PIL_WARN, "bad parameters to IfForEachClientRemove");
+ return;
+ }
+
+ if ((clientt = g_hash_table_lookup(u->iftypes, ifname)) == NULL) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "Interface manager [%s/%s] has no clients"
+ , PI_IFMANAGER, ifname);
+ }
+ return;
+ };
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "IfForEachClientRemove(%s:%s)"
+ , mgrt->typename, clientt->typename);
+ }
+ if (clientt->ifmgr_ref != mgrif) {
+ PILLog(PIL_WARN, "Bad ifmgr_ref ptr in PILInterfaceType");
+ return;
+ }
+
+ g_hash_table_foreach_remove(clientt->interfaces, IfForEachClientHelper
+ , &h);
+}
+
+static PIL_rc
+PILregister_plugin(PILPlugin* piinfo, const PILPluginOps* commonops)
+{
+ piinfo->pluginops = commonops;
+
+ return PIL_OK;
+}
+
+static PIL_rc
+PILunregister_plugin(PILPlugin* piinfo)
+{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILunregister_plugin(%s)"
+ , piinfo->plugin_name);
+ }
+ RemoveAPILPlugin(piinfo);
+ return PIL_OK;
+}
+
+/* General logging function (not really UPPILS-specific) */
+static void
+PILLog(PILLogLevel priority, const char * format, ...)
+{
+ va_list args;
+ GLogLevelFlags flags;
+
+ switch(priority) {
+ case PIL_FATAL: flags = G_LOG_LEVEL_ERROR;
+ break;
+ case PIL_CRIT: flags = G_LOG_LEVEL_CRITICAL;
+ break;
+
+ default: /* FALL THROUGH... */
+ case PIL_WARN: flags = G_LOG_LEVEL_WARNING;
+ break;
+
+ case PIL_INFO: flags = G_LOG_LEVEL_INFO;
+ break;
+ case PIL_DEBUG: flags = G_LOG_LEVEL_DEBUG;
+ break;
+ };
+ va_start (args, format);
+ g_logv (G_LOG_DOMAIN, flags, format, args);
+ va_end (args);
+}
+
+static const char * PIL_strerrmsgs [] =
+{ "Success"
+, "Invalid Parameters"
+, "Bad plugin/interface type"
+, "Duplicate entry (plugin/interface name/type)"
+, "Oops happens"
+, "No such plugin/interface/interface type"
+};
+
+const char *
+PIL_strerror(PIL_rc rc)
+{
+ int irc = (int) rc;
+ static char buf[128];
+
+ if (irc < 0 || irc >= DIMOF(PIL_strerrmsgs)) {
+ snprintf(buf, sizeof(buf), "return code %d (?)", irc);
+ return buf;
+ }
+ return PIL_strerrmsgs[irc];
+}
+
+/*
+ * Returns the PATHname of the file containing the requested plugin
+ * This file handles PATH-like semantics from the rootdirlist.
+ * It is also might be the right place to put alias handing in the future...
+ */
+static char *
+PILPluginPath(PILPluginUniv* universe, const char * plugintype
+, const char * pluginname)
+{
+ char * PluginPath = NULL;
+ char ** spath_component;
+
+ for (spath_component = universe->rootdirlist; *spath_component
+ ; ++ spath_component) {
+
+ if (PluginPath) {
+ g_free(PluginPath); PluginPath=NULL;
+ }
+
+ PluginPath = g_strdup_printf("%s%s%s%s%s%s"
+ , *spath_component
+ , G_DIR_SEPARATOR_S
+ , plugintype
+ , G_DIR_SEPARATOR_S
+ , pluginname
+ , PLUGINSUFFIX);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "PILS: Looking for %s/%s => [%s]"
+ , plugintype, pluginname, PluginPath);
+ }
+
+ if (PluginExists(PluginPath) == PIL_OK) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "Plugin path for %s/%s => [%s]"
+ , plugintype, pluginname, PluginPath);
+ }
+ return PluginPath;
+ }
+ /* FIXME: Put alias file processing here... */
+ }
+
+ /* Can't find 'em all... */
+ return PluginPath;
+}
+
+static PIL_rc
+PluginExists(const char * PluginPath)
+{
+ /* Make sure we can read and execute the plugin file */
+ /* This test is nice, because dlopen reasons aren't return codes */
+
+ if (access(PluginPath, R_OK) != 0) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin file %s does not exist"
+ , PluginPath);
+ }
+ return PIL_NOPLUGIN;
+ }
+ return PIL_OK;
+}
+
+/* Return PIL_OK if the given plugin exists */
+PIL_rc
+PILPluginExists(PILPluginUniv* piuniv
+, const char * plugintype
+, const char * pluginname)
+{
+ PIL_rc rc;
+ char * path = PILPluginPath(piuniv, plugintype, pluginname);
+
+ if (path == NULL) {
+ return PIL_INVAL;
+ }
+ rc = PluginExists(path);
+ DELETE(path);
+ return rc;
+}
+
+/*
+ * PILLoadPlugin() - loads a plugin into memory and calls the
+ * initial() entry point in the plugin.
+ *
+ *
+ * Method:
+ *
+ * Construct file name of plugin.
+ * See if plugin exists. If not, fail with PIL_NOPLUGIN.
+ *
+ * Search Universe for plugin type
+ * If found, search plugin type for pluginname
+ * if found, fail with PIL_EXIST.
+ * Otherwise,
+ * Create new Plugin type structure
+ * Use lt_dlopen() on plugin to get lt_dlhandle for it.
+ *
+ * Construct the symbol name of the initialization function.
+ *
+ * Use lt_dlsym() to find the pointer to the init function.
+ *
+ * Call the initialization function.
+ */
+PIL_rc
+PILLoadPlugin(PILPluginUniv* universe, const char * plugintype
+, const char * pluginname
+, void* plugin_user_data)
+{
+ PIL_rc rc;
+ char * PluginPath;
+ char * PluginSym;
+ PILPluginType* pitype;
+ PILPlugin* piinfo;
+ lt_dlhandle dlhand;
+ PILPluginInitFun initfun;
+
+ PluginPath = PILPluginPath(universe, plugintype, pluginname);
+
+ if ((rc=PluginExists(PluginPath)) != PIL_OK) {
+ DELETE(PluginPath);
+ return rc;
+ }
+
+ if((pitype=g_hash_table_lookup(universe->PluginTypes, plugintype))
+ != NULL) {
+ if ((piinfo = g_hash_table_lookup
+ ( pitype->Plugins, pluginname)) != NULL) {
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin %s already loaded"
+ , PluginPath);
+ }
+ DELETE(PluginPath);
+ return PIL_EXIST;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PluginType %s already present"
+ , plugintype);
+ }
+ }else{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Creating PluginType for %s"
+ , plugintype);
+ }
+ /* Create a new PILPluginType object */
+ pitype = NewPILPluginType(universe, plugintype);
+ }
+
+ g_assert(pitype != NULL);
+
+ /*
+ * At this point, we have a PILPluginType object and our
+ * plugin name is not listed in it.
+ */
+
+ dlhand = lt_dlopen(PluginPath);
+
+ if (!dlhand) {
+ PILLog(PIL_WARN
+ , "lt_dlopen() failure on plugin %s/%s [%s]."
+ " Reason: [%s]"
+ , plugintype, pluginname
+ , PluginPath
+ , lt_dlerror());
+ DELETE(PluginPath);
+ return PIL_NOPLUGIN;
+ }
+ DELETE(PluginPath);
+ /* Construct the magic init function symbol name */
+ PluginSym = g_strdup_printf(PIL_FUNC_FMT
+ , plugintype, pluginname);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin %s/%s init function: %s"
+ , plugintype, pluginname
+ , PluginSym);
+ }
+
+ initfun = lt_dlsym(dlhand, PluginSym);
+
+ if (initfun == NULL) {
+ PILLog(PIL_WARN
+ , "Plugin %s/%s init function (%s) not found"
+ , plugintype, pluginname, PluginSym);
+ DELETE(PluginSym);
+ lt_dlclose(dlhand); dlhand=NULL;
+ DelPILPluginType(pitype);
+ return PIL_NOPLUGIN;
+ }
+ DELETE(PluginSym);
+ /*
+ * Construct the new PILPlugin object
+ */
+ piinfo = NewPILPlugin(pitype, pluginname, dlhand, initfun);
+ g_assert(piinfo != NULL);
+ g_hash_table_insert(pitype->Plugins, g_strdup(piinfo->plugin_name), piinfo);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Plugin %s/%s loaded and constructed."
+ , plugintype, pluginname);
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Calling init function in plugin %s/%s."
+ , plugintype, pluginname);
+ }
+ /* Save away the user_data for later */
+ piinfo->ud_plugin = plugin_user_data;
+ /* initfun is allowed to change ud_plugin if they want */
+ initfun(piinfo, universe->imports, plugin_user_data);
+
+ return PIL_OK;
+}/*PILLoadPlugin*/
+
+
+
+#define REPORTERR(msg) PILLog(PIL_CRIT, "%s", msg)
+
+/*
+ * Register an interface.
+ *
+ * This function is exported to plugins for their use.
+ */
+static PIL_rc
+PILRegisterInterface(PILPlugin* piinfo
+, const char * interfacetype /* Type of interface */
+, const char * interfacename /* Name of interface */
+, void* Ops /* Info (functions) exported
+ by this interface */
+, PILInterfaceFun close_func /* Close function for interface */
+, PILInterface** interfaceid /* Interface id (OP) */
+, void** Imports /* Functions imported by
+ this interface (OP) */
+, void* ud_interface /* Optional user_data */
+)
+{
+ PILPluginUniv* piuniv; /* Universe this plugin is in */
+ PILPluginType* pitype; /* Type of this plugin */
+ PILInterfaceUniv* ifuniv; /* Universe this interface is in */
+ PILInterfaceType*iftype; /* Type of this interface */
+ PILInterface* ifinfo; /* Info about this Interface */
+
+ PILInterfaceType*ifmgrtype; /* PILInterfaceType for PI_IFMANAGER */
+ PILInterface* ifmgrinfo; /* Interf info for "interfacetype" */
+ const PILInterfaceOps* ifops; /* Ops vector for InterfaceManager */
+ /* of type "interfacetype" */
+ PIL_rc rc;
+
+ if ( piinfo == NULL
+ || (pitype = piinfo->plugintype) == NULL
+ || (piuniv = pitype->piuniv) == NULL
+ || (ifuniv = piuniv->ifuniv) == NULL
+ || ifuniv->iftypes == NULL
+ ) {
+ REPORTERR("bad parameters to PILRegisterInterface");
+ return PIL_INVAL;
+ }
+
+ /* Now we have lots of info, but not quite enough... */
+
+ if ((iftype = g_hash_table_lookup(ifuniv->iftypes, interfacetype))
+ == NULL) {
+
+ /* Try to autoload the needed interface handler */
+ rc = PILLoadPlugin(piuniv, PI_IFMANAGER, interfacetype, NULL);
+
+ /* See if the interface handler loaded like we expect */
+ if ((iftype = g_hash_table_lookup(ifuniv->iftypes
+ , interfacetype)) == NULL) {
+ return PIL_BADTYPE;
+ }
+ }
+ if ((ifinfo = g_hash_table_lookup(iftype->interfaces, interfacename))
+ != NULL) {
+ g_warning("Attempt to register duplicate interface: %s/%s"
+ , interfacetype, interfacename);
+ return PIL_EXIST;
+ }
+ /*
+ * OK... Now we know it is valid, and isn't registered...
+ * Let's locate the InterfaceManager registrar for this type
+ */
+ if ((ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER))
+ == NULL) {
+ REPORTERR("No " PI_IFMANAGER " type!");
+ return PIL_OOPS;
+ }
+ if ((ifmgrinfo = g_hash_table_lookup(ifmgrtype->interfaces
+ , interfacetype)) == NULL) {
+ PILLog(PIL_CRIT
+ , "No interface manager for given type (%s) !"
+ , interfacetype);
+ return PIL_BADTYPE;
+ }
+
+ ifops = ifmgrinfo->exports;
+
+ /* Now we have all the information anyone could possibly want ;-) */
+
+ ifinfo = NewPILInterface(iftype, interfacename, Ops
+ , close_func, ud_interface, piinfo);
+
+ g_assert(ifmgrinfo == ifinfo->ifmanager);
+ *interfaceid = ifinfo;
+
+ /* Call the registration function for our interface type */
+ rc = ifops->RegisterInterface(ifinfo, Imports);
+
+
+ /* Increment reference count of interface manager */
+ IfIncrRefCount(ifmgrinfo, 1);
+
+ /* Increment the ref count of the plugin that loaded us */
+ PluginIncrRefCount(piinfo, 1);
+
+ if (rc != PIL_OK) {
+ RemoveAPILInterface(ifinfo);
+ }
+ return rc;
+}
+
+/*
+ * Method:
+ *
+ * Verify interface is valid.
+ *
+ * Call interface close function.
+ *
+ * Call interface manager unregister function
+ *
+ * Call RmAPILInterface to remove from InterfaceType table, and
+ * free interface object.
+ *
+ */
+
+static PIL_rc
+PILunregister_interface(PILInterface* id)
+{
+ PILInterfaceType* t;
+ PILInterfaceUniv* u;
+ PIL_rc rc;
+ PILInterface* ifmgr_info; /* Pointer to our interface handler */
+ const PILInterfaceOps* exports; /* InterfaceManager operations for
+ * the type of interface we are
+ */
+
+ if ( id == NULL
+ || (t = id->interfacetype) == NULL
+ || (u = t->universe) == NULL
+ || id->interfacename == NULL) {
+ PILLog(PIL_WARN, "PILunregister_interface: bad interfaceid");
+ return PIL_INVAL;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILunregister_interface(%s/%s)"
+ , t->typename, id->interfacename);
+ }
+ PILValidateInterface(NULL, id, t);
+ PILValidateInterfaceType(NULL, t, u);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "Calling InterfaceClose on %s/%s"
+ , t->typename, id->interfacename);
+ }
+
+ /* Call the close function supplied by the interface */
+
+ if ((id->if_close != NULL)
+ && ((rc=id->if_close(id, id->ud_interface)) != PIL_OK)) {
+ PILLog(PIL_WARN, "InterfaceClose on %s/%s returned %s"
+ , t->typename, id->interfacename
+ , PIL_strerror(rc));
+ } else {
+ rc = PIL_OK;
+ }
+
+ /* Find the InterfaceManager that manages us */
+ ifmgr_info = t->ifmgr_ref;
+
+ g_assert(ifmgr_info != NULL);
+
+ /* Find the exported functions from that IFIF */
+ exports = ifmgr_info->exports;
+
+ g_assert(exports != NULL && exports->UnRegisterInterface != NULL);
+
+ /* Call the interface manager unregister function */
+ exports->UnRegisterInterface(id);
+
+ /* Decrement reference count of interface manager */
+ IfIncrRefCount(ifmgr_info, -1);
+ /* This may make ifmgr_info invalid */
+ ifmgr_info = NULL;
+
+ /* Decrement the reference count of the plugin that loaded us */
+ PluginIncrRefCount(id->loadingpi, -1);
+
+ return rc;
+}
+
+static PILInterfaceUniv*
+NewPILInterfaceUniv(PILPluginUniv* piuniv)
+{
+ PILInterfaceUniv* ret = NEW(PILInterfaceUniv);
+ static int ltinityet = 0;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "NewPILInterfaceUniv(0x%lx)"
+ , (unsigned long)ret);
+ }
+ if (!ltinityet) {
+ ltinityet=1;
+ lt_dlinit();
+ }
+ STATNEW(interfaceuniv);
+ ret->MagicNum = PIL_MAGIC_INTERFACEUNIV;
+ /* Make the two universes point at each other */
+ ret->piuniv = piuniv;
+ piuniv->ifuniv = ret;
+
+ ret->iftypes = g_hash_table_new(g_str_hash, g_str_equal);
+
+ InterfaceManager_plugin_init(piuniv);
+ return ret;
+}
+
+static void
+DelPILInterfaceUniv(PILInterfaceUniv* ifuniv)
+{
+ PILInterfaceType* ifmgrtype;
+ g_assert(ifuniv!= NULL && ifuniv->iftypes != NULL);
+ PILValidateInterfaceUniv(NULL, ifuniv, NULL);
+
+ STATFREE(interfaceuniv);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterfaceUniv(0x%lx)"
+ , (unsigned long) ifuniv);
+ }
+ g_hash_table_foreach_remove(ifuniv->iftypes, RmAPILInterfaceType, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "DelPILInterfaceUniv: final cleanup");
+ }
+ ifmgrtype = g_hash_table_lookup(ifuniv->iftypes, PI_IFMANAGER);
+ RemoveAPILInterfaceType(ifmgrtype, ifmgrtype);
+ /*
+ * FIXME! need to delete the interface for PI_IFMANAGER last
+ * Right now, it seems to happen last, but I think that's
+ * coincidence...
+ */
+ g_hash_table_destroy(ifuniv->iftypes);
+ ZAP(ifuniv);
+ DELETE(ifuniv);
+}
+
+/*
+ * These RmA* functions primarily called from hash_table_foreach,
+ * so they have gpointer arguments. This *not necessarily* clause
+ * is why they do the g_hash_table_lookup_extended call instead of
+ * just deleting the key. When called from outside, the key
+ * may not be pointing at the key to actually free, but a copy
+ * of the key.
+ */
+static gboolean /* IsA GHFunc: required for g_hash_table_foreach_remove() */
+RmAPILInterfaceType
+( gpointer typename /* Name of this interface type */
+, gpointer iftype /* PILInterfaceType* */
+, gpointer notused
+)
+{
+ PILInterfaceType* Iftype = iftype;
+ PILInterfaceUniv* Ifuniv = Iftype->universe;
+
+ /*
+ * We are not always called by g_hash_table_foreach_remove()
+ */
+
+ g_assert(IS_PILINTERFACETYPE(Iftype));
+ PILValidateInterfaceUniv(NULL, Ifuniv, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterfaceType(%s)"
+ , (char*)typename);
+ }
+ if (iftype != notused
+ && strcmp(Iftype->typename, PI_IFMANAGER) == 0) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RmAPILInterfaceType: skipping (%s)"
+ , (char*)typename);
+ }
+ return FALSE;
+ }
+
+ DelPILInterfaceType(iftype);
+ DELETE(typename);
+
+ return TRUE;
+}
+
+static void
+RemoveAPILInterfaceType(PILInterfaceType*Iftype, PILInterfaceType* t2)
+{
+ PILInterfaceUniv* Ifuniv = Iftype->universe;
+ gpointer key;
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "RemoveAPILInterfaceType(%s)"
+ , Iftype->typename);
+ }
+ if (t2 != Iftype
+ && strcmp(Iftype->typename, PI_IFMANAGER) == 0) {
+ PILLog(PIL_DEBUG, "RemoveAPILInterfaceType: skipping (%s)"
+ , Iftype->typename);
+ return;
+ }
+ if (g_hash_table_lookup_extended(Ifuniv->iftypes
+ , Iftype->typename, &key, (gpointer)&Iftype)) {
+
+ g_hash_table_remove(Ifuniv->iftypes, key);
+ RmAPILInterfaceType(key, Iftype, t2);
+ }else{
+ g_assert_not_reached();
+ }
+}
+
+/*
+ * We need to write more functions: These include...
+ *
+ * Plugin functions:
+ *
+ * PILPluginPath() - returns path name for a given plugin
+ *
+ * PILPluginTypeList() - returns list of plugins of a given type
+ *
+ */
+static void free_dirlist(struct dirent** dlist, int n);
+
+static int qsort_string_cmp(const void *a, const void *b);
+
+
+static void
+free_dirlist(struct dirent** dlist, int n)
+{
+ int j;
+ for (j=0; j < n; ++j) {
+ if (dlist[j]) {
+ free(dlist[j]);
+ dlist[j] = NULL;
+ }
+ }
+ free(dlist);
+}
+
+static int
+qsort_string_cmp(const void *a, const void *b)
+{
+ return(strcmp(*(const char * const *)a, *(const char * const *)b));
+}
+
+#define FREE_DIRLIST(dlist, n) {free_dirlist(dlist, n); dlist = NULL;}
+
+static int
+so_select (const struct dirent *dire)
+{
+
+ const char obj_end [] = PLUGINSUFFIX;
+ const char *end = &dire->d_name[strlen(dire->d_name)
+ - (STRLEN_CONST(obj_end))];
+
+
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "In so_select: %s.", dire->d_name);
+ }
+ if (end < dire->d_name) {
+ return 0;
+ }
+ if (strcmp(end, obj_end) == 0) {
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "FILE %s looks like a plugin name."
+ , dire->d_name);
+ }
+ return 1;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "FILE %s Doesn't look like a plugin name [%s] "
+ "%zd %zd %s."
+ , dire->d_name, end
+ , sizeof(obj_end), strlen(dire->d_name)
+ , &dire->d_name[strlen(dire->d_name)
+ - (STRLEN_CONST(obj_end))]);
+ }
+
+ return 0;
+}
+
+/* Return (sorted) list of available plugin names */
+static char**
+PILPluginTypeListPlugins(PILPluginType* pitype
+, int * picount /* Can be NULL ... */)
+{
+ const char * piclass = pitype->plugintype;
+ unsigned plugincount = 0;
+ char ** result = NULL;
+ int initoff = 0;
+ char ** pelem;
+
+ /* Return all the plugins in all the directories in the PATH */
+
+ for (pelem=pitype->piuniv->rootdirlist; *pelem; ++pelem) {
+ int j;
+ GString* path;
+ int dircount;
+ struct dirent** files;
+
+
+ path = g_string_new(*pelem);
+ g_assert(piclass != NULL);
+ if (piclass) {
+ if (g_string_append_c(path, G_DIR_SEPARATOR) == NULL
+ || g_string_append(path, piclass) == NULL) {
+ g_string_free(path, 1); path = NULL;
+ return(NULL);
+ }
+ }
+
+ files = NULL;
+ errno = 0;
+ dircount = scandir(path->str, &files
+ , SCANSEL_CAST so_select, NULL);
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILS: Examining directory [%s]"
+ ": [%d] files matching [%s] suffix found."
+ , path->str, dircount, PLUGINSUFFIX);
+ }
+ g_string_free(path, 1); path=NULL;
+
+ if (dircount <= 0) {
+ if (files != NULL) {
+ FREE_DIRLIST(files, dircount);
+ files = NULL;
+ }
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG
+ , "PILS: skipping empty directory"
+ " in PILPluginTypeListPlugins()");
+ }
+ continue;
+ }
+
+ initoff = plugincount;
+ plugincount += dircount;
+ if (result == NULL) {
+ result = (char **) g_malloc((plugincount+1)*sizeof(char *));
+ }else{
+ result = (char **) g_realloc(result
+ , (plugincount+1)*sizeof(char *));
+ }
+
+ for (j=0; j < dircount; ++j) {
+ char* s;
+ unsigned slen = strlen(files[j]->d_name)
+ - STRLEN_CONST(PLUGINSUFFIX);
+
+ s = g_malloc(slen+1);
+ strncpy(s, files[j]->d_name, slen);
+ s[slen] = EOS;
+ result[initoff+j] = s;
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILS: plugin [%s] found"
+ , s);
+ }
+ }
+ FREE_DIRLIST(files, dircount);
+ files = NULL;
+ }
+
+ if (picount != NULL) {
+ *picount = plugincount;
+ }
+ if (result) {
+ result[plugincount] = NULL;
+ /* Return them in sorted order... */
+ qsort(result, plugincount, sizeof(char *), qsort_string_cmp);
+ }else{
+ if (DEBUGPLUGIN) {
+ PILLog(PIL_DEBUG, "PILS: NULL return"
+ " from PILPluginTypeListPlugins()");
+ }
+ }
+
+
+ return result;
+}
+/* Return (sorted) list of available plugin names */
+char**
+PILListPlugins(PILPluginUniv* u, const char * pitype
+, int * picount /* Can be NULL ... */)
+{
+ PILPluginType* t;
+ if ((t = g_hash_table_lookup(u->PluginTypes, pitype)) == NULL) {
+ if (picount) {
+ *picount = 0;
+ }
+ t = NewPILPluginType(u, pitype);
+ if (!t) {
+ return NULL;
+ }
+ }
+ return PILPluginTypeListPlugins(t, picount);
+}
+
+void
+PILFreePluginList(char ** pluginlist)
+{
+ char ** ml = pluginlist;
+
+ if (!ml) {
+ return;
+ }
+
+ while (*ml != NULL) {
+ DELETE(*ml);
+ }
+ DELETE(pluginlist);
+}
+
+
+static void
+PILValidatePlugin(gpointer key, gpointer plugin, gpointer pitype)
+{
+ const char * Key = key;
+ const PILPlugin * Plugin = plugin;
+
+ g_assert(IS_PILPLUGIN(Plugin));
+
+ g_assert(Key == NULL || strcmp(Key, Plugin->plugin_name) == 0);
+
+ g_assert (Plugin->refcnt >= 0 );
+
+ /* g_assert (Plugin->pluginops != NULL ); */
+ g_assert (strcmp(Key, PI_IFMANAGER) == 0 || Plugin->dlinitfun != NULL );
+ g_assert (strcmp(Plugin->plugin_name, PI_IFMANAGER) == 0
+ || Plugin->dlhandle != NULL);
+ g_assert(Plugin->plugintype != NULL);
+ g_assert(IS_PILPLUGINTYPE(Plugin->plugintype));
+ g_assert(pitype == NULL || pitype == Plugin->plugintype);
+}
+
+static void
+PILValidatePluginType(gpointer key, gpointer pitype, gpointer piuniv)
+{
+ char * Key = key;
+ PILPluginType * Pitype = pitype;
+ PILPluginUniv * Muniv = piuniv;
+
+ g_assert(IS_PILPLUGINTYPE(Pitype));
+ g_assert(Muniv == NULL || IS_PILPLUGINUNIV(Muniv));
+ g_assert(Key == NULL || strcmp(Key, Pitype->plugintype) == 0);
+ g_assert(IS_PILPLUGINUNIV(Pitype->piuniv));
+ g_assert(piuniv == NULL || piuniv == Pitype->piuniv);
+ g_assert(Pitype->Plugins != NULL);
+ g_hash_table_foreach(Pitype->Plugins, PILValidatePlugin, Pitype);
+}
+static void
+PILValidatePluginUniv(gpointer key, gpointer piuniv, gpointer dummy)
+{
+ PILPluginUniv * Muniv = piuniv;
+
+ g_assert(IS_PILPLUGINUNIV(Muniv));
+ g_assert(Muniv->rootdirlist != NULL);
+ g_assert(Muniv->imports != NULL);
+ g_hash_table_foreach(Muniv->PluginTypes, PILValidatePluginType, piuniv);
+ PILValidateInterfaceUniv(NULL, Muniv->ifuniv, piuniv);
+}
+static void
+PILValidateInterface(gpointer key, gpointer interface, gpointer iftype)
+{
+ char * Key = key;
+ PILInterface* Interface = interface;
+ g_assert(IS_PILINTERFACE(Interface));
+ g_assert(Key == NULL || strcmp(Key, Interface->interfacename) == 0);
+ g_assert(IS_PILINTERFACETYPE(Interface->interfacetype));
+ g_assert(iftype == NULL || iftype == Interface->interfacetype);
+ g_assert(Interface->ifmanager!= NULL);
+ g_assert(IS_PILINTERFACE(Interface->ifmanager));
+ g_assert(strcmp(Interface->interfacetype->typename
+ , Interface->ifmanager->interfacename)== 0);
+ g_assert(Interface->exports != NULL);
+}
+static void
+PILValidateInterfaceType(gpointer key, gpointer iftype, gpointer ifuniv)
+{
+ char * Key = key;
+ PILInterfaceType* Iftype = iftype;
+ g_assert(IS_PILINTERFACETYPE(Iftype));
+ g_assert(Key == NULL || strcmp(Key, Iftype->typename) == 0);
+ g_assert(ifuniv == NULL || Iftype->universe == ifuniv);
+ g_assert(Iftype->interfaces != NULL);
+ g_assert(Iftype->ifmgr_ref != NULL);
+ g_assert(IS_PILINTERFACE(Iftype->ifmgr_ref));
+ g_assert(Key == NULL || strcmp(Key, Iftype->ifmgr_ref->interfacename) == 0);
+
+ g_hash_table_foreach(Iftype->interfaces, PILValidateInterface, iftype);
+}
+static void
+PILValidateInterfaceUniv(gpointer key, gpointer ifuniv, gpointer piuniv)
+{
+ PILInterfaceUniv* Ifuniv = ifuniv;
+ PILPluginUniv* Pluginuniv = piuniv;
+ g_assert(IS_PILINTERFACEUNIV(Ifuniv));
+ g_assert(Pluginuniv == NULL || IS_PILPLUGINUNIV(Pluginuniv));
+ g_assert(piuniv == NULL || piuniv == Ifuniv->piuniv);
+ g_hash_table_foreach(Ifuniv->iftypes, PILValidateInterfaceType, ifuniv);
+}
+
+#define PRSTAT(type) { \
+ PILLog(PIL_INFO, "Plugin system objects (" #type "): " \
+ "\tnew %ld free \%ld current %ld" \
+ , PILstats.type.news \
+ , PILstats.type.frees \
+ , PILstats.type.news - PILstats.type.frees); \
+}
+void
+PILLogMemStats(void)
+{
+ PRSTAT(plugin);
+ PRSTAT(pitype);
+ PRSTAT(piuniv);
+ PRSTAT(interface);
+ PRSTAT(interfacetype);
+ PRSTAT(interfaceuniv);
+}
+
+/*
+ * Function for logging with the given logging function
+ * The reason why it's here is so we can get printf arg checking
+ * You can't get that when you call a function pointer directly.
+ */
+void
+PILCallLog(PILLogFun logfun, PILLogLevel priority, const char * fmt, ...)
+{
+ va_list args;
+ char * str;
+ int err = errno;
+
+ va_start (args, fmt);
+ str = g_strdup_vprintf(fmt, args);
+ va_end (args);
+ logfun(priority, "%s", str);
+ g_free(str);
+ errno = err;
+}
diff --git a/lib/pils/test.c b/lib/pils/test.c
new file mode 100644
index 0000000..c2cdb26
--- /dev/null
+++ b/lib/pils/test.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+/*
+ * Sample Interface manager.
+ */
+#define PIL_PLUGINTYPE test
+#define PIL_PLUGINTYPENAME "test"
+#define PIL_PLUGIN test
+#define PIL_PLUGINNAME "test"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+/* We are a interface manager... */
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+
+#include <pils/interface.h>
+
+PIL_PLUGIN_BOILERPLATE("1.0", DebugFlag, Ourclose)
+
+/*
+ * Places to store information gotten during registration.
+ */
+static const PILPluginImports* OurPIImports; /* Imported plugin funs */
+static PILPlugin* OurPlugin; /* Our plugin info */
+static PILInterfaceImports* OurIfImports; /* Interface imported funs */
+static PILInterface* OurIf; /* Pointer to interface info */
+
+static void
+Ourclose (PILPlugin* us)
+{
+}
+
+/*
+ * Our Interface Manager interfaces - exported to the universe!
+ *
+ * (or at least the interface management universe ;-).
+ *
+ */
+static PILInterfaceOps OurIfOps = {
+ /* FIXME -- put some in here !! */
+};
+
+PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*);
+
+static PIL_rc
+IfClose(PILInterface*intf, void* ud_interface)
+{
+ OurPIImports->log(PIL_INFO, "In Ifclose (test plugin)");
+ return PIL_OK;
+}
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr)
+{
+ PIL_rc ret;
+ /*
+ * Force compiler to check our parameters...
+ */
+ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun;
+
+
+ OurPIImports = imports;
+ OurPlugin = us;
+
+ imports->log(PIL_INFO, "Plugin %s: user_ptr = %lx"
+ , PIL_PLUGINNAME, (unsigned long)user_ptr);
+
+ imports->log(PIL_INFO, "Registering ourselves as a plugin");
+
+ /* Register as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ imports->log(PIL_INFO, "Registering our interfaces");
+
+ /* Register our interfaces */
+ ret = imports->register_interface
+ ( us
+ , PIL_PLUGINTYPENAME
+ , PIL_PLUGINNAME
+ , &OurIfOps /* Exported interface operations */
+ , IfClose /* Interface Close function */
+ , &OurIf
+ , (void*)&OurIfImports
+ , NULL);
+ imports->log(PIL_INFO, "test init function: returning %d"
+ , ret);
+
+ return ret;
+}
diff --git a/lib/plugins/InterfaceMgr/HBauth.c b/lib/plugins/InterfaceMgr/HBauth.c
new file mode 100644
index 0000000..eae22cf
--- /dev/null
+++ b/lib/plugins/InterfaceMgr/HBauth.c
@@ -0,0 +1,171 @@
+/*
+ * Heartbeat authentication interface manager
+ *
+ * Copyright 2001 Alan Robertson <alanr@unix.sh>
+ * Licensed under the GNU Lesser General Public License
+ *
+ * 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
+ *
+ */
+ */
+#define PIL_PLUGINTYPE InterfaceMgr
+#define PIL_PLUGIN HBauth
+
+#define PIN(f) #f
+#define PIN2(f) PIN(f)
+#define PIN3 PIN2(PIL_PLUGIN)
+#define PIT PIN2(PIL_PLUGINTYPE)
+
+/* We are a interface manager... */
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+
+#include <lha_internal.h>
+#include <pils/interface.h>
+#include <stdio.h>
+
+PIL_PLUGIN_BOILERPLATE2("1.0", AuthDebugFlag)
+
+
+/*
+ * Places to store information gotten during registration.
+ */
+static const PILPluginImports* AuthPIImports; /* Imported plugin fcns */
+static PILPlugin* AuthPlugin; /* Our plugin info */
+static PILInterfaceImports* AuthIfImports; /* Interface imported fcns */
+static PILInterface* AuthIf; /* Our Auth Interface info */
+
+/* Our exported auth interface management functions */
+static PIL_rc RegisterAuthIF(PILInterface* ifenv, void** imports);
+
+static PIL_rc UnregisterAuthIF(PILInterface*iifinfo);
+
+/*
+ * Our Interface Manager interfaces - exported to the universe!
+ *
+ * (or at least to the interface management universe ;-).
+ *
+ * These are the interfaces which are used to manage our
+ * client authentication interfaces
+ *
+ */
+static PILInterfaceOps AuthIfOps =
+{ RegisterAuthIF
+, UnregisterAuthIF
+};
+
+
+PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*);
+
+/*
+ * Our user_ptr is presumed to point at a GHashTable for us
+ * to put plugin into when they show up, and drop from when
+ * they disappear.
+ *
+ * We need to think more carefully about the way for us to get
+ * the user_ptr from the global environment.
+ *
+ * We need to think more carefully about how interface registration
+ * etc. interact with plugin loading, reference counts, etc. and how
+ * the application that uses us (i.e., heartbeat) interacts with us.
+ *
+ * Issues include:
+ * - freeing all memory,
+ * - making sure things are all cleaned up correctly
+ * - Thread-safety?
+ *
+ * I think the global system should handle thread-safety.
+ */
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr)
+{
+ PIL_rc ret;
+ /*
+ * Force compiler to check our parameters...
+ */
+ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun;
+
+
+ if (user_ptr == NULL) {
+ imports->log(PIL_CRIT
+ , "Interface Manager %s requires non-NULL "
+ " user pointer (to GHashTable) at initialization"
+ , PIN3);
+ return PIL_INVAL;
+ }
+
+ AuthPIImports = imports;
+ AuthPlugin = us;
+
+ /* Register as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+
+ /* Register our interfaces */
+ ret = imports->register_interface(us
+ , PIT
+ , PIN3
+ , &AuthIfOps
+ , NULL
+ , &AuthIf /* Our interface object pointer */
+ , (void**)&AuthIfImports /* Interface-imported functions */
+ , user_ptr);
+ return ret;
+}
+
+/*
+ * We get called for every authentication interface that gets registered.
+ *
+ * It's our job to make the authentication interface that's
+ * registering with us available to the system.
+ *
+ * We do that by adding it to a g_hash_table of authentication
+ * plugin. The rest of the system takes it from there...
+ * The key is the authentication method, and the data
+ * is a pointer to the functions the method exports.
+ * It's a piece of cake ;-)
+ */
+static PIL_rc
+RegisterAuthIF(PILInterface* intf, void** imports)
+{
+ GHashTable* authtbl = intf->ifmanager->ud_interface;
+
+ g_assert(authtbl != NULL);
+
+ /* Reference count should now be one */
+ g_assert(intf->refcnt == 1);
+ g_hash_table_insert(authtbl, intf->interfacename, intf->exports);
+
+ return PIL_OK;
+}
+
+/* Unregister a client authentication interface -
+ * We get called from the interface mgmt sys when someone requests that
+ * a interface be unregistered.
+ */
+static PIL_rc
+UnregisterAuthIF(PILInterface*intf)
+{
+ GHashTable* authtbl = intf->ifmanager->ud_interface;
+ g_assert(authtbl != NULL);
+
+ intf->refcnt--;
+ g_assert(intf->refcnt >= 0);
+ if (intf->refcnt <= 0) {
+ g_hash_table_remove(authtbl, intf->interfacetype);
+ }
+ return PIL_OK;
+}
+
diff --git a/lib/plugins/InterfaceMgr/Makefile.am b/lib/plugins/InterfaceMgr/Makefile.am
new file mode 100644
index 0000000..86b88d1
--- /dev/null
+++ b/lib/plugins/InterfaceMgr/Makefile.am
@@ -0,0 +1,33 @@
+#
+# InterfaceMgr: Interface manager plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \
+ -I$(top_builddir)/lib/upmls -I$(top_srcdir)/lib/upmls
+
+## libraries
+
+plugindir = $(libdir)/@HB_PKG@/plugins/InterfaceMgr
+plugin_LTLIBRARIES = generic.la
+
+generic_la_SOURCES = generic.c
+generic_la_LDFLAGS = -export-dynamic -module -avoid-version
diff --git a/lib/plugins/InterfaceMgr/generic.c b/lib/plugins/InterfaceMgr/generic.c
new file mode 100644
index 0000000..6ddad3b
--- /dev/null
+++ b/lib/plugins/InterfaceMgr/generic.c
@@ -0,0 +1,452 @@
+/*
+ *
+ * Generic interface (implementation) manager
+ *
+ * Copyright 2001 Alan Robertson <alanr@unix.sh>
+ * Licensed under the GNU Lesser General Public License
+ *
+ * This manager will manage any number of types of interfaces.
+ *
+ * This means that when any implementations of our client interfaces register
+ * or unregister, it is us that makes their interfaces show up in the outside
+ * world.
+ *
+ * And, of course, we have to do this in a very generic way, since we have
+ * no idea about the client programs or interface types, or anything else.
+ *
+ * We do that by getting a parameter passed to us which tell us the names
+ * of the interface types we want to manage, and the address of a GHashTable
+ * for each type that we put the implementation in when they register
+ * themselves.
+ *
+ * So, each type of interface that we manage gets its own private
+ * GHashTable of the implementations of that type that are currently
+ * registered.
+ *
+ * For example, if we manage communication modules, their exported
+ * interfaces will be registered in a hash table. If we manage
+ * authentication modules, they'll have their (separate) hash table that
+ * their exported interfaces are registered in.
+ *
+ *
+ * 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
+ *
+ */
+
+#define PIL_PLUGINTYPE InterfaceMgr
+#define PIL_PLUGINTYPE_S "InterfaceMgr"
+#define PIL_PLUGIN generic
+#define PIL_PLUGIN_S "generic"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+/* We are an interface manager... */
+#define ENABLE_PLUGIN_MANAGER_PRIVATE
+#define ENABLE_PIL_DEFS_PRIVATE
+
+#include <lha_internal.h>
+#include <pils/generic.h>
+
+#include <stdio.h>
+
+PIL_PLUGIN_BOILERPLATE("1.0", GenDebugFlag, CloseGeneralPluginManager)
+
+/*
+ * Key is interface type, value is a PILGenericIfMgmtRqst.
+ * The key is g_strdup()ed, but the struct is not copied.
+ */
+
+static gboolean FreeAKey(gpointer key, gpointer value, gpointer data);
+
+/*
+ * Places to store information gotten during registration.
+ */
+static const PILPluginImports* GenPIImports; /* Imported plugin fcns */
+static PILPlugin* GenPlugin; /* Our plugin info */
+static PILInterfaceImports* GenIfImports; /* Interface imported fcns */
+
+/* Our exported generic interface management functions */
+static PIL_rc RegisterGenIF(PILInterface* ifenv, void** imports);
+
+static PIL_rc UnregisterGenIF(PILInterface*iifinfo);
+
+static PIL_rc CloseGenInterfaceManager(PILInterface*, void* info);
+
+/*
+ * Our Interface Manager interfaces - exported to the universe!
+ *
+ * (or at least to the interface management universe ;-).
+ *
+ * These are the interfaces which are used to manage our
+ * client implementations
+ */
+static PILInterfaceOps GenIfOps =
+{ RegisterGenIF
+, UnregisterGenIF
+};
+
+
+PIL_rc PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void*);
+
+/*
+ * Our user_ptr is presumed to point to NULL-terminated array of
+ * PILGenericIfMgmtRqst structs.
+ *
+ * These requests have pointers to GHashTables for us
+ * to put plugins into when they show up, and drop from when
+ * they disappear.
+ *
+ * Issues include:
+ * - freeing all memory,
+ * - making sure things are all cleaned up correctly
+ * - Thread-safety?
+ *
+ * IMHO the global system should handle thread-safety.
+ */
+static PIL_rc AddAnInterfaceType(PILPlugin*us, GHashTable* MasterTable, PILGenericIfMgmtRqst* req);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, PILPluginImports* imports, void *user_ptr)
+{
+ PIL_rc ret;
+ PILGenericIfMgmtRqst* user_req;
+ PILGenericIfMgmtRqst* curreq;
+ GHashTable* MasterTable = NULL;
+ /*
+ * Force the compiler to check our parameters...
+ */
+ PILPluginInitFun fun = &PIL_PLUGIN_INIT; (void)fun;
+
+
+ GenPIImports = imports;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "IF manager %s: initializing.", PIL_PLUGIN_S);
+ }
+
+ if (user_ptr == NULL) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "%s Interface Manager requires non-NULL "
+ " PILGenericIfMgmtRqst user pointer at initialization."
+ , PIL_PLUGIN_S);
+ return PIL_INVAL;
+ }
+
+ GenPlugin = us;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "IF manager %s: registering as a plugin."
+ , PIL_PLUGIN_S);
+ }
+
+ user_req = user_ptr;
+ MasterTable = g_hash_table_new(g_str_hash, g_str_equal);
+ us->ud_plugin = MasterTable; /* Override passed value */
+
+ /* Register ourselves as a plugin */
+
+ if ((ret = imports->register_plugin(us, &OurPIExports)) != PIL_OK) {
+ PILCallLog(imports->log, PIL_CRIT
+ , "IF manager %s unable to register as plugin (%s)"
+ , PIL_PLUGIN_S, PIL_strerror(ret));
+
+ return ret;
+ }
+
+ /*
+ * Register to manage implementations
+ * for all the interface types we've been asked to manage.
+ */
+
+ for(curreq = user_req; curreq->iftype != NULL; ++curreq) {
+ PIL_rc newret;
+
+ newret = AddAnInterfaceType(us, MasterTable, curreq);
+
+ if (newret != PIL_OK) {
+ ret = newret;
+ }
+ }
+
+ /*
+ * Our plugin and all our registered plugin types
+ * have ud_plugin pointing at MasterTable.
+ */
+
+ return ret;
+}
+
+static PIL_rc
+AddAnInterfaceType(PILPlugin*us, GHashTable* MasterTable, PILGenericIfMgmtRqst* req)
+{
+ PIL_rc rc;
+ PILInterface* GenIf; /* Our Generic Interface info*/
+
+ g_assert(MasterTable != NULL);
+ g_hash_table_insert(MasterTable, g_strdup(req->iftype), req);
+
+ if (req->ifmap == NULL) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "IF manager %s: iftype %s has NULL"
+ " ifmap pointer address."
+ , PIL_PLUGIN_S, req->iftype);
+ return PIL_INVAL;
+ }
+ if ((*req->ifmap) != NULL) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "IF manager %s: iftype %s GHashTable pointer"
+ " was not initialized to NULL"
+ , PIL_PLUGIN_S, req->iftype);
+ return PIL_INVAL;
+ }
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "IF manager %s: registering ourselves"
+ " to manage interface type %s"
+ , PIL_PLUGIN_S, req->iftype);
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: ifmap: 0x%lx callback: 0x%lx"
+ " imports: 0x%lx"
+ , PIL_PLUGIN_S
+ , (unsigned long)req->ifmap
+ , (unsigned long)req->callback
+ , (unsigned long)req->importfuns);
+ }
+
+ /* Create the hash table to communicate with this client */
+ *(req->ifmap) = g_hash_table_new(g_str_hash, g_str_equal);
+
+ rc = GenPIImports->register_interface(us
+ , PIL_PLUGINTYPE_S
+ , req->iftype /* the iftype we're managing here */
+ , &GenIfOps
+ , CloseGenInterfaceManager
+ , &GenIf
+ , (void*)&GenIfImports
+ , MasterTable); /* Point ud_interface to MasterTable */
+
+ /* We don't ever want to be unloaded... */
+ GenIfImports->ModRefCount(GenIf, +100);
+
+ if (rc != PIL_OK) {
+ PILCallLog(GenPIImports->log, PIL_CRIT
+ , "Generic interface manager %s: unable to register"
+ " to manage interface type %s: %s"
+ , PIL_PLUGIN_S, req->iftype
+ , PIL_strerror(rc));
+ }
+ return rc;
+}
+
+static void
+CloseGeneralPluginManager(PILPlugin* us)
+{
+
+ GHashTable* MasterTable = us->ud_plugin;
+ int count;
+
+ g_assert(MasterTable != NULL);
+
+ /*
+ * All our clients have already been shut down automatically
+ * This is the final shutdown for us...
+ */
+
+
+ /* There *shouldn't* be any keys in there ;-) */
+
+ if ((count=g_hash_table_size(MasterTable)) > 0) {
+
+ /* But just in case there are... */
+ g_hash_table_foreach_remove(MasterTable, FreeAKey, NULL);
+ }
+ g_hash_table_destroy(MasterTable);
+ us->ud_plugin = NULL;
+ return;
+}
+
+/*
+ * We get called for every time an implementation registers itself as
+ * implementing one of the kinds of interfaces we manage.
+ *
+ * It's our job to make the implementation that's
+ * registering with us available to the system.
+ *
+ * We do that by adding it to a GHashTable for its interface type
+ * Our users in the rest of the system takes it from there...
+ *
+ * The key to the GHashTable is the implementation name, and the data is
+ * a pointer to the information the implementation exports.
+ *
+ * It's a piece of cake ;-)
+ */
+static PIL_rc
+RegisterGenIF(PILInterface* intf, void** imports)
+{
+ PILGenericIfMgmtRqst* ifinfo;
+ GHashTable* MasterTable = intf->ifmanager->ud_interface;
+
+ g_assert(MasterTable != NULL);
+
+ /* Reference count should now be one */
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: interface %s/%s registering."
+ , PIL_PLUGIN_S, intf->interfacetype->typename
+ , intf->interfacename);
+ }
+ g_assert(intf->refcnt == 1);
+ /*
+ * We need to add it to the table that goes with this particular
+ * type of interface.
+ */
+ if ((ifinfo = g_hash_table_lookup(MasterTable
+ , intf->interfacetype->typename)) != NULL) {
+ GHashTable* ifmap = *(ifinfo->ifmap);
+
+ g_hash_table_insert(ifmap, intf->interfacename,intf->exports);
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: Inserted interface [%s] in hash"
+ " table @ 0x%08lx"
+ , PIL_PLUGIN_S, intf->interfacename
+ , (unsigned long)ifmap);
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: Exports are here: 0x%08x"
+ , PIL_PLUGIN_S
+ , GPOINTER_TO_UINT(intf->exports));
+ }
+
+ if (ifinfo->callback != NULL) {
+ PILInterfaceType* t = intf->interfacetype;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: callback 0x%lx"
+ , PIL_PLUGIN_S
+ , (unsigned long)ifinfo->callback);
+ }
+ ifinfo->callback(PIL_REGISTER
+ , t->universe->piuniv, intf->interfacename
+ , t->typename, ifinfo->userptr);
+ }
+
+ *imports = ifinfo->importfuns;
+
+ return PIL_OK;
+
+ }else{
+ PILCallLog(GenPIImports->log, PIL_WARN
+ , "RegisterGenIF: interface type %s not found"
+ , intf->interfacename);
+ }
+ return PIL_INVAL;
+}
+
+/* Unregister an implementation -
+ * We get called from the interface management system when someone
+ * has requested that an implementation of a client interface be
+ * unregistered.
+ */
+static PIL_rc
+UnregisterGenIF(PILInterface*intf)
+{
+ GHashTable* MasterTable = intf->ifmanager->ud_interface;
+ PILGenericIfMgmtRqst* ifinfo;
+
+ g_assert(MasterTable != NULL);
+ g_assert(intf->refcnt >= 0);
+ /*
+ * Go through the "master table" and find client table,
+ * notify client we're about to remove this entry, then
+ * then remove this entry from it.
+ */
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: unregistering interface %s/%s."
+ , PIL_PLUGIN_S, intf->interfacetype->typename
+ , intf->interfacename);
+ }
+ if ((ifinfo = g_hash_table_lookup(MasterTable
+ , intf->interfacetype->typename)) != NULL) {
+
+ GHashTable* ifmap = *(ifinfo->ifmap);
+
+ if (ifinfo->callback != NULL) {
+ PILInterfaceType* t = intf->interfacetype;
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_DEBUG
+ , "%s IF manager: callback 0x%lx"
+ , PIL_PLUGIN_S
+ , (unsigned long)ifinfo->callback);
+ }
+ ifinfo->callback(PIL_UNREGISTER
+ , t->universe->piuniv, intf->interfacename
+ , t->typename, ifinfo->userptr);
+ }
+
+ /* Remove the client entry from master table */
+ g_hash_table_remove(ifmap, intf->interfacename);
+
+ }else{
+ PILCallLog(GenPIImports->log, PIL_WARN
+ , "UnregisterGenIF: interface type %s not found"
+ , intf->interfacename);
+ return PIL_INVAL;
+ }
+ return PIL_OK;
+}
+
+/*
+ * Close down the generic interface manager.
+ */
+static PIL_rc
+CloseGenInterfaceManager(PILInterface*intf, void* info)
+{
+ void* key;
+ void* data;
+ GHashTable* MasterTable = intf->ud_interface;
+
+ if (GenDebugFlag) {
+ PILCallLog(GenPIImports->log, PIL_INFO
+ , "In CloseGenInterFaceManager on %s/%s (MasterTable: 0x%08lx)"
+ , intf->interfacetype->typename, intf->interfacename
+ , (unsigned long)MasterTable);
+ }
+
+
+ g_assert(MasterTable != NULL);
+ if (g_hash_table_lookup_extended(MasterTable
+ , intf->interfacename, &key, &data)) {
+ PILGenericIfMgmtRqst* ifinfo = data;
+ g_hash_table_destroy(*(ifinfo->ifmap));
+ *(ifinfo->ifmap) = NULL;
+ g_hash_table_remove(MasterTable, key);
+ g_free(key);
+ }else{
+ g_assert_not_reached();
+ }
+ return PIL_OK;
+}
+
+static gboolean
+FreeAKey(gpointer key, gpointer value, gpointer data)
+{
+ g_free(key);
+ return TRUE;
+}
diff --git a/lib/plugins/Makefile.am b/lib/plugins/Makefile.am
new file mode 100644
index 0000000..21827cd
--- /dev/null
+++ b/lib/plugins/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2008 Andrew Beekhof
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+SUBDIRS = InterfaceMgr stonith lrm compress
diff --git a/lib/plugins/compress/Makefile.am b/lib/plugins/compress/Makefile.am
new file mode 100644
index 0000000..3a3193a
--- /dev/null
+++ b/lib/plugins/compress/Makefile.am
@@ -0,0 +1,52 @@
+#
+# InterfaceMgr: Interface manager plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+if BUILD_ZLIB_COMPRESS_MODULE
+zlibmodule = zlib.la
+endif
+
+if BUILD_BZ2_COMPRESS_MODULE
+bz2module = bz2.la
+endif
+
+SUBDIRS =
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \
+ -I$(top_builddir)/lib/upmls -I$(top_srcdir)/lib/upmls
+
+AM_CFLAGS = @CFLAGS@
+
+## libraries
+
+halibdir = $(libdir)/@HB_PKG@
+plugindir = $(halibdir)/plugins/compress
+plugin_LTLIBRARIES = $(zlibmodule) $(bz2module)
+
+zlib_la_SOURCES = zlib.c
+zlib_la_LDFLAGS = -export-dynamic -module -avoid-version -lz
+zlib_la_LIBADD = $(top_builddir)/replace/libreplace.la
+
+bz2_la_SOURCES = bz2.c
+bz2_la_LDFLAGS = -export-dynamic -module -avoid-version -lbz2
+bz2_la_LIBADD = $(top_builddir)/replace/libreplace.la
+
diff --git a/lib/plugins/compress/bz2.c b/lib/plugins/compress/bz2.c
new file mode 100644
index 0000000..2eab116
--- /dev/null
+++ b/lib/plugins/compress/bz2.c
@@ -0,0 +1,142 @@
+ /* bz2.c: compression module using bz2 for heartbeat.
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * SECURITY NOTE: It would be very easy for someone to masquerade as the
+ * device that you're pinging. If they don't know the password, all they can
+ * do is echo back the packets that you're sending out, or send out old ones.
+ * This does mean that if you're using such an approach, that someone could
+ * make you think you have quorum when you don't during a cluster partition.
+ * The danger in that seems small, but you never know ;-)
+ *
+ * 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
+ *
+ */
+
+
+
+#define PIL_PLUGINTYPE HB_COMPRESS_TYPE
+#define PIL_PLUGINTYPE_S HB_COMPRESS_TYPE_S
+#define PIL_PLUGIN bz2
+#define PIL_PLUGIN_S "bz2"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <lha_internal.h>
+#include <stdio.h>
+#include <pils/plugin.h>
+#include <compress.h>
+#include <bzlib.h>
+#include <clplumbing/cl_log.h>
+#include <string.h>
+
+
+static struct hb_compress_fns bz2Ops;
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static struct hb_media_imports* OurImports;
+static void* interfprivate;
+
+#define LOG PluginImports->log
+#define MALLOC PluginImports->alloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &bz2Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , interfprivate);
+}
+
+static int
+bz2_compress(char* dest, size_t* destlen,
+ const char* _src, size_t srclen)
+{
+ int ret;
+ char* src;
+ unsigned int tmpdestlen;
+
+ memcpy(&src, &_src, sizeof(char*));
+
+ tmpdestlen = *destlen;
+ ret = BZ2_bzBuffToBuffCompress(dest, &tmpdestlen, src, srclen, 1, 0, 30);
+ if (ret != BZ_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *destlen = tmpdestlen;
+
+ return HA_OK;
+}
+
+static int
+bz2_decompress(char* dest, size_t* destlen,
+ const char* _src, size_t srclen)
+{
+
+ int ret;
+ char* src;
+ unsigned int tmpdestlen;
+
+ memcpy(&src, &_src, sizeof(char*));
+
+ tmpdestlen = *destlen;
+ ret = BZ2_bzBuffToBuffDecompress(dest, &tmpdestlen, src, srclen, 1, 0);
+ if (ret != BZ_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *destlen = tmpdestlen;
+
+ return HA_OK;
+}
+
+static const char*
+bz2_getname(void)
+{
+ return "bz2";
+}
+
+static struct hb_compress_fns bz2Ops ={
+ bz2_compress,
+ bz2_decompress,
+ bz2_getname,
+};
diff --git a/lib/plugins/compress/zlib.c b/lib/plugins/compress/zlib.c
new file mode 100644
index 0000000..5958966
--- /dev/null
+++ b/lib/plugins/compress/zlib.c
@@ -0,0 +1,135 @@
+ /* zlib.c: compression module using zlib for heartbeat.
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * SECURITY NOTE: It would be very easy for someone to masquerade as the
+ * device that you're pinging. If they don't know the password, all they can
+ * do is echo back the packets that you're sending out, or send out old ones.
+ * This does mean that if you're using such an approach, that someone could
+ * make you think you have quorum when you don't during a cluster partition.
+ * The danger in that seems small, but you never know ;-)
+ *
+ * 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
+ *
+ */
+
+
+
+#define PIL_PLUGINTYPE HB_COMPRESS_TYPE
+#define PIL_PLUGINTYPE_S HB_COMPRESS_TYPE_S
+#define PIL_PLUGIN zlib
+#define PIL_PLUGIN_S "zlib"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <lha_internal.h>
+#include <pils/plugin.h>
+#include <compress.h>
+#include <zlib.h>
+#include <clplumbing/cl_log.h>
+#include <string.h>
+
+
+static struct hb_compress_fns zlibOps;
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static struct hb_media_imports* OurImports;
+static void* interfprivate;
+
+#define LOG PluginImports->log
+#define MALLOC PluginImports->alloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &zlibOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , interfprivate);
+}
+
+static int
+zlib_compress(char* dest, size_t* _destlen,
+ const char* src, size_t _srclen)
+{
+ int ret;
+ uLongf destlen = *_destlen;
+ uLongf srclen = _srclen;
+
+ ret = compress((Bytef *)dest, &destlen, (const Bytef *)src, srclen);
+ if (ret != Z_OK){
+ cl_log(LOG_ERR, "%s: compression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *_destlen = destlen;
+ return HA_OK;
+
+}
+
+static int
+zlib_decompress(char* dest, size_t* _destlen,
+ const char* src, size_t _srclen)
+{
+
+ int ret;
+ uLongf destlen = *_destlen;
+ uLongf srclen = _srclen;
+
+ ret = uncompress((Bytef *)dest, &destlen, (const Bytef *)src, srclen);
+ if (ret != Z_OK){
+ cl_log(LOG_ERR, "%s: decompression failed",
+ __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ *_destlen = destlen;
+
+ return HA_OK;
+}
+
+static const char*
+zlib_getname(void)
+{
+ return "zlib";
+}
+
+static struct hb_compress_fns zlibOps ={
+ zlib_compress,
+ zlib_decompress,
+ zlib_getname,
+};
diff --git a/lib/plugins/lrm/Makefile.am b/lib/plugins/lrm/Makefile.am
new file mode 100644
index 0000000..fd24579
--- /dev/null
+++ b/lib/plugins/lrm/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+if UPSTART
+SUBDIRS = dbus
+endif
+
+LRM_DIR = lrm
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+if UPSTART
+INCLUDES += $(DBUS_CFLAGS)
+endif
+
+halibdir = $(libdir)/@HB_PKG@
+havarlibdir = $(localstatedir)/lib/@HB_PKG@
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/lrm/liblrm.la \
+ $(GLIBLIB)
+
+plugindir = $(halibdir)/plugins/RAExec
+
+plugin_LTLIBRARIES = lsb.la ocf.la heartbeat.la
+if UPSTART
+plugin_LTLIBRARIES += upstart.la
+endif
+
+lsb_la_SOURCES = raexeclsb.c
+lsb_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+ocf_la_SOURCES = raexecocf.c
+ocf_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+heartbeat_la_SOURCES = raexechb.c
+heartbeat_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
+
+if UPSTART
+upstart_la_SOURCES = raexecupstart.c upstart-dbus.c
+upstart_la_LDFLAGS = -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version \
+ $(DBUS_LIBS)
+endif
diff --git a/lib/plugins/lrm/dbus/Makefile.am b/lib/plugins/lrm/dbus/Makefile.am
new file mode 100644
index 0000000..ec93436
--- /dev/null
+++ b/lib/plugins/lrm/dbus/Makefile.am
@@ -0,0 +1,16 @@
+if UPSTART
+BINDINGS=Upstart_Instance.h \
+ Upstart_Job.h \
+ Upstart.h
+
+all-local:
+ for header in $(BINDINGS); do \
+ input=com.ubuntu.`echo $$header | sed 's/\.h//' | tr _ .`.xml; \
+ $(DBUS_BINDING_TOOL) --mode=glib-client $$input > $$header; \
+ done
+
+clean-local:
+ rm -f $(BINDINGS)
+
+EXTRA_DIST = *.xml
+endif
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml
new file mode 100644
index 0000000..d4f7ab2
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Instance.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.Instance.xml - interface definition for interface
+ objects
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="com.ubuntu.Upstart0_6.Instance">
+ <!-- Methods to directly control instances. Unlike the equivalent methods
+ for a Job, these are not permitted to pass or set environment. -->
+ <method name="Start">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Stop">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Restart">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+
+ <!-- Basic information about an Instance -->
+ <property name="name" type="s" access="read" />
+ <property name="goal" type="s" access="read" />
+ <property name="state" type="s" access="read" />
+ <property name="processes" type="a(si)" access="read" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml
new file mode 100644
index 0000000..27f47a1
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.Job.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.Job.xml - interface definition for job objects
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node>
+ <interface name="com.ubuntu.Upstart0_6.Job">
+ <!-- Get object paths for instances, while you can figure these out too,
+ it's still better form to use these -->
+ <method name="GetInstance">
+ <arg name="env" type="as" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="GetInstanceByName">
+ <arg name="name" type="s" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="GetAllInstances">
+ <arg name="instances" type="ao" direction="out" />
+ </method>
+
+ <!-- Signals for changes to the instance list for a job -->
+ <signal name="InstanceAdded">
+ <arg name="instance" type="o" />
+ </signal>
+ <signal name="InstanceRemoved">
+ <arg name="instance" type="o" />
+ </signal>
+
+ <!-- Job control; the environment arguments are used for both instance
+ selection and for passing environment to the processes of the job. -->
+ <method name="Start">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+ <method name="Stop">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+ <method name="Restart">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ <arg name="instance" type="o" direction="out" />
+ </method>
+
+ <!-- Basic information about a Job -->
+ <property name="name" type="s" access="read" />
+ <property name="description" type="s" access="read" />
+ <property name="author" type="s" access="read" />
+ <property name="version" type="s" access="read" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml
new file mode 100644
index 0000000..a4331cd
--- /dev/null
+++ b/lib/plugins/lrm/dbus/com.ubuntu.Upstart.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- upstart
+
+ com.ubuntu.Upstart.xml - interface definition for manager object
+
+ Copyright © 2009 Canonical Ltd.
+ Author: Scott James Remnant <scott@netsplit.com>.
+
+ This file is free software; Canonical Ltd gives unlimited permission
+ to copy and/or distribute it, with or without modifications, as long
+ as this notice is preserved.
+
+ Communication and interaction with Upstart through this interface is
+ permitted without restriction.
+ -->
+
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<node name="/com/ubuntu/Upstart">
+ <interface name="com.ubuntu.Upstart0_6">
+ <!-- Reload all configuration sources -->
+ <method name="ReloadConfiguration">
+ </method>
+
+ <!-- Get object paths for jobs, while you can figure them out, it's
+ better form to use these -->
+ <method name="GetJobByName">
+ <arg name="name" type="s" direction="in" />
+ <arg name="job" type="o" direction="out" />
+ </method>
+ <method name="GetAllJobs">
+ <arg name="jobs" type="ao" direction="out" />
+ </method>
+
+ <!-- Signals for changes to the job list -->
+ <signal name="JobAdded">
+ <arg name="job" type="o" />
+ </signal>
+ <signal name="JobRemoved">
+ <arg name="job" type="o" />
+ </signal>
+
+ <!-- Event emission -->
+ <method name="EmitEvent">
+ <annotation name="com.netsplit.Nih.Method.Async" value="true" />
+ <arg name="name" type="s" direction="in" />
+ <arg name="env" type="as" direction="in" />
+ <arg name="wait" type="b" direction="in" />
+ </method>
+
+ <!-- Basic information about Upstart -->
+ <property name="version" type="s" access="read" />
+ <property name="log_priority" type="s" access="readwrite" />
+ </interface>
+</node>
diff --git a/lib/plugins/lrm/raexechb.c b/lib/plugins/lrm/raexechb.c
new file mode 100644
index 0000000..f9f1eb9
--- /dev/null
+++ b/lib/plugins/lrm/raexechb.c
@@ -0,0 +1,416 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ *
+ * File: raexechb.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN heartbeat
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "heartbeat"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+static const char * RA_PATH = HB_RA_DIR;
+
+static const char meta_data_template[] =
+"<?xml version=\"1.0\"?>\n"
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
+"<resource-agent name=\"%s\">\n"
+"<version>1.0</version>\n"
+"<longdesc lang=\"en\">\n"
+"%s"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">%s</shortdesc>\n"
+"<parameters>\n"
+"<parameter name=\"1\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the first argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[1]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"2\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the second argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[2]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"3\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the third argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[3]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"4\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the fourth argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[4]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"<parameter name=\"5\" unique=\"1\" required=\"0\">\n"
+"<longdesc lang=\"en\">\n"
+"This argument will be passed as the fifth argument to the "
+"heartbeat resource agent (assuming it supports one)\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">argv[5]</shortdesc>\n"
+"<content type=\"string\" default=\" \" />\n"
+"</parameter>\n"
+"</parameters>\n"
+"<actions>\n"
+"<action name=\"start\" timeout=\"15\" />\n"
+"<action name=\"stop\" timeout=\"15\" />\n"
+"<action name=\"status\" timeout=\"15\" />\n"
+"<action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
+"<action name=\"meta-data\" timeout=\"5\" />\n"
+"</actions>\n"
+"<special tag=\"heartbeart\">\n"
+"</special>\n"
+"</resource-agent>\n";
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static int get_resource_list(GList ** rsc_info);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+static const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params, RA_ARGV params_argv);
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+static int idebuglevel = 0;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ if (getenv(HADEBUGVAL) != NULL && atoi(getenv(HADEBUGVAL)) > 0 ) {
+ idebuglevel = atoi(getenv(HADEBUGVAL));
+ cl_log(LOG_DEBUG, "LRM debug level set to %d", idebuglevel);
+ }
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * Real work starts here ;-)
+ */
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ RA_ARGV params_argv;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ uniform_ret_execra_t exit_value;
+ GString * debug_info;
+ char * optype_tmp = NULL;
+ int index_tmp = 0;
+
+ /* How to generate the meta-data? There is nearly no value
+ * information in meta-data build up in current way.
+ * Should directly add meta-data to the script itself?
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "meta-data") ) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(0);
+ }
+
+ /* To simulate the 'monitor' operation with 'status'.
+ * Now suppose there is no 'monitor' operation for heartbeat scripts.
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "monitor") ) {
+ optype_tmp = g_strdup("status");
+ } else {
+ optype_tmp = g_strdup(op_type);
+ }
+
+ /* Prepare the call parameter */
+ if (0 > prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)) {
+ cl_log(LOG_ERR, "HB RA: Error of preparing parameters");
+ g_free(optype_tmp);
+ return -1;
+ }
+ g_free(optype_tmp);
+
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+
+ /* let this log show only high loglevel. */
+ if (idebuglevel > 1) {
+ debug_info = g_string_new("");
+ do {
+ g_string_append(debug_info, params_argv[index_tmp]);
+ g_string_append(debug_info, " ");
+ } while (params_argv[++index_tmp] != NULL);
+ debug_info->str[debug_info->len-1] = '\0';
+
+ cl_log(LOG_DEBUG, "RA instance %s executing: heartbeat::%s"
+ , rsc_id, debug_info->str);
+
+ g_string_free(debug_info, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ execv(ra_pathname, params_argv);
+ cl_perror("(%s:%s:%d) execv failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+
+ switch (errno) {
+ case ENOENT: /* No such file or directory */
+ case EISDIR: /* Is a directory */
+ exit_value = EXECRA_NOT_INSTALLED;
+ break;
+ default:
+ exit_value = EXECRA_EXEC_UNKNOWN_ERROR;
+ }
+ exit(exit_value);
+}
+
+static int
+prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params_ht, RA_ARGV params_argv)
+{
+ int tmp_len, index;
+ int ht_size = 0;
+ int param_num = 0;
+ char buf_tmp[20];
+ void * value_tmp;
+
+ if (params_ht) {
+ ht_size = g_hash_table_size(params_ht);
+ }
+ if ( ht_size+3 > MAX_PARAMETER_NUM ) {
+ cl_log(LOG_ERR, "Too many parameters");
+ return -1;
+ }
+
+ /* Now suppose the parameter format stored in Hashtabe is as like as
+ * key="1", value="-Wl,soname=test"
+ * Moreover, the key is supposed as a string transfered from an integer.
+ * It may be changed in the future.
+ */
+ /* Notice: if ht_size==0, no actual arguments except op_type */
+ for (index = 1; index <= ht_size; index++ ) {
+ snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
+ value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
+ /* suppose the key is consecutive */
+ if ( value_tmp == NULL ) {
+/* cl_log(LOG_WARNING, "Parameter ordering error in"\
+ "prepare_cmd_parameters, raexeclsb.c");
+ cl_log(LOG_WARNING, "search key=%s.", buf_tmp);
+*/ continue;
+ }
+ param_num ++;
+ params_argv[param_num] = g_strdup((char *)value_tmp);
+ }
+
+ tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
+ params_argv[0] = g_strndup(rsc_type, tmp_len);
+ /* Add operation code as the last argument */
+ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
+ params_argv[param_num+1] = g_strndup(op_type, tmp_len);
+ /* Add the teminating NULL pointer */
+ params_argv[param_num+2] = NULL;
+ return 0;
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+
+ /* Now there is no formal related specification for Heartbeat RA
+ * scripts. Temporarily deal as LSB init script.
+ */
+ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
+ with LSB standard.
+ */
+ const char * stop_pattern1 = "*stopped*",
+ * stop_pattern2 = "*not*running*",
+ * running_pattern1 = "*running*",
+ * running_pattern2 = "*OK*";
+ char * lower_std_output = NULL;
+
+ if(ret_execra == EXECRA_NOT_INSTALLED) {
+ return ret_execra;
+ }
+
+ if ( 0 == STRNCMP_CONST(op_type, "status")
+ || 0 == STRNCMP_CONST(op_type, "monitor")) {
+ if (std_output == NULL ) {
+ cl_log(LOG_WARNING, "No status output from the (hb) resource agent.");
+ return EXECRA_NOT_RUNNING;
+ }
+
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG, "RA output was: [%s]", std_output);
+ }
+
+ lower_std_output = g_ascii_strdown(std_output, -1);
+
+ if ( TRUE == g_pattern_match_simple(stop_pattern1
+ , lower_std_output) || TRUE ==
+ g_pattern_match_simple(stop_pattern2
+ , lower_std_output) ) {
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG
+ , "RA output [%s] matched stopped pattern"
+ " [%s] or [%s]"
+ , std_output
+ , stop_pattern1
+ , stop_pattern2);
+ }
+ ret_execra = EXECRA_NOT_RUNNING; /* stopped */
+ } else if ( TRUE == g_pattern_match_simple(running_pattern1
+ , lower_std_output) || TRUE ==
+ g_pattern_match_simple(running_pattern2
+ , std_output) ) {
+ if (idebuglevel) {
+ cl_log(LOG_DEBUG
+ , "RA output [%s] matched running"
+ " pattern [%s] or [%s]"
+ , std_output, running_pattern1
+ , running_pattern2);
+ }
+ ret_execra = EXECRA_OK; /* running */
+ } else {
+ /* It didn't say it was running - must be stopped */
+ cl_log(LOG_DEBUG, "RA output [%s] didn't match any pattern"
+ , std_output);
+ ret_execra = EXECRA_NOT_RUNNING; /* stopped */
+ }
+ g_free(lower_std_output);
+ }
+ /* For non-status operation return code */
+ if (ret_execra < 0) {
+ ret_execra = EXECRA_UNKNOWN_ERROR;
+ }
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ return get_runnable_list(RA_PATH, rsc_info);
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ GString * meta_data;
+
+ meta_data = g_string_new("");
+ g_string_sprintf( meta_data, meta_data_template, rsc_type
+ , rsc_type, rsc_type);
+ return meta_data->str;
+}
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
+ , __FUNCTION__, __LINE__);
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
+ "This will cause memory leak."
+ , __FUNCTION__, __LINE__);
+ }
+
+ /* Now temporarily make it fixed */
+ *providers = g_list_append(*providers, g_strdup("heartbeat"));
+
+ return g_list_length(*providers);
+}
diff --git a/lib/plugins/lrm/raexeclsb.c b/lib/plugins/lrm/raexeclsb.c
new file mode 100644
index 0000000..46d7546
--- /dev/null
+++ b/lib/plugins/lrm/raexeclsb.c
@@ -0,0 +1,609 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ *
+ * File: raexeclsb.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+/*
+ * Todo
+ * 1) Use flex&bison to make the analysis functions for lsb compliant comment?
+ * 2) Support multiple paths which contain lsb compliant RAs.
+ * 3) Optional and additional actions analysis?
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+#include <libgen.h>
+
+#include <libxml/entities.h>
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN lsb
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "lsb"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+
+/* meta-data template for lsb scripts */
+/* Note: As for optional actions -- extracted from lsb standard.
+ * The reload and the try-restart options are optional. Other init script
+ * actions may be defined by the init script.
+ */
+#define meta_data_template \
+"<?xml version=\"1.0\"?>\n"\
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
+"<resource-agent name=\"%s\" version=\"0.1\">\n"\
+" <version>1.0</version>\n"\
+" <longdesc lang=\"en\">\n"\
+" %s"\
+" </longdesc>\n"\
+" <shortdesc lang=\"en\">%s</shortdesc>\n"\
+" <parameters>\n"\
+" </parameters>\n"\
+" <actions>\n"\
+" <action name=\"start\" timeout=\"15\" />\n"\
+" <action name=\"stop\" timeout=\"15\" />\n"\
+" <action name=\"status\" timeout=\"15\" />\n"\
+" <action name=\"restart\" timeout=\"15\" />\n"\
+" <action name=\"force-reload\" timeout=\"15\" />\n"\
+" <action name=\"monitor\" timeout=\"15\" interval=\"15\" />\n"\
+" <action name=\"meta-data\" timeout=\"5\" />\n"\
+" </actions>\n"\
+" <special tag=\"LSB\">\n"\
+" <Provides>%s</Provides>\n"\
+" <Required-Start>%s</Required-Start>\n"\
+" <Required-Stop>%s</Required-Stop>\n"\
+" <Should-Start>%s</Should-Start>\n"\
+" <Should-Stop>%s</Should-Stop>\n"\
+" <Default-Start>%s</Default-Start>\n"\
+" <Default-Stop>%s</Default-Stop>\n"\
+" </special>\n"\
+"</resource-agent>\n"
+
+/* The keywords for lsb-compliant comment */
+#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
+#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
+#define PROVIDES "# Provides:"
+#define REQ_START "# Required-Start:"
+#define REQ_STOP "# Required-Stop:"
+#define SHLD_START "# Should-Start:"
+#define SHLD_STOP "# Should-Stop:"
+#define DFLT_START "# Default-Start:"
+#define DFLT_STOP "# Default-Stop:"
+#define SHORT_DSCR "# Short-Description:"
+#define DESCRIPTION "# Description:"
+
+#define ZAPXMLOBJ(m) \
+ if ( (m) != NULL ) { \
+ xmlFree(m); \
+ (m) = NULL; \
+ }
+
+#define RALSB_GET_VALUE(ptr, keyword) \
+ if ( (ptr == NULL) & (0 == strncasecmp(buffer, keyword, strlen(keyword))) ) { \
+ (ptr) = (char *)xmlEncodeEntitiesReentrant(NULL,BAD_CAST buffer+strlen(keyword)); \
+ continue; \
+ }
+/*
+ * Are there multiple paths? Now according to LSB init scripts, the answer
+ * is 'no', but should be 'yes' for lsb none-init scripts?
+ */
+static const char * RA_PATH = LSB_RA_DIR;
+/* Map to the return code of the 'monitor' operation defined in the OCF RA
+ * specification.
+ */
+static const int status_op_exitcode_map[] = {
+ EXECRA_OK, /* LSB_STATUS_OK */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_PID */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_VAR_LOCK */
+ EXECRA_NOT_RUNNING, /* LSB_STATUS_STOPPED */
+ EXECRA_UNKNOWN_ERROR /* LSB_STATUS_UNKNOWN */
+};
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_resource_list(GList ** rsc_info);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+
+const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+static int prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params, RA_ARGV params_argv);
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * Real work starts here ;-)
+ */
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ RA_ARGV params_argv;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ GString * debug_info;
+ char * inherit_debuglevel = NULL;
+ char * optype_tmp = NULL;
+ int index_tmp = 0;
+ int save_errno;
+
+ /* Specially handle the operation "metameta-data". To build up its
+ * output from templet, dummy data and its comment head.
+ */
+ if ( 0 == STRNCMP_CONST(op_type, "meta-data")) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(0);
+ }
+
+ /* To simulate the 'monitor' operation with 'status'.
+ * Now suppose there is no 'monitor' operation for LSB scripts.
+ */
+ if (0 == STRNCMP_CONST(op_type, "monitor")) {
+ optype_tmp = g_strdup("status");
+ } else {
+ optype_tmp = g_strdup(op_type);
+ }
+
+ /* Prepare the call parameter */
+ if ( prepare_cmd_parameters(rsc_type, optype_tmp, params, params_argv)
+ != 0) {
+ cl_log(LOG_ERR, "lsb RA: Error of preparing parameters");
+ g_free(optype_tmp);
+ return -1;
+ }
+ g_free(optype_tmp);
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+
+ /* let this log show only high loglevel. */
+ inherit_debuglevel = getenv(HADEBUGVAL);
+ if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
+ debug_info = g_string_new("");
+ do {
+ g_string_append(debug_info, params_argv[index_tmp]);
+ g_string_append(debug_info, " ");
+ } while (params_argv[++index_tmp] != NULL);
+ debug_info->str[debug_info->len-1] = '\0';
+
+ cl_log(LOG_DEBUG, "RA instance %s executing: lsb::%s"
+ , rsc_id, debug_info->str);
+
+ g_string_free(debug_info, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ execv(ra_pathname, params_argv);
+ /* oops, exec failed */
+ save_errno = errno; /* cl_perror may change errno */
+ cl_perror("(%s:%s:%d) execv failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+ errno = save_errno;
+ exit(get_failed_exec_rc());
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* Except op_type equals 'status', the UNIFORM_RET_EXECRA is compatible
+ * with the LSB standard.
+ */
+ if (ret_execra < 0) {
+ return EXECRA_UNKNOWN_ERROR;
+ }
+
+ if(ret_execra == EXECRA_NOT_INSTALLED) {
+ return ret_execra;
+ }
+
+ if ( 0 == STRNCMP_CONST(op_type, "status")
+ || 0 == STRNCMP_CONST(op_type, "monitor")) {
+ if (ret_execra < DIMOF(status_op_exitcode_map)) {
+ ret_execra = status_op_exitcode_map[ret_execra];
+ }
+ }
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE * fp;
+ gboolean next_continue, found_begin_tag, is_lsb_script;
+ int rc = 0;
+ GList *cur, *tmp;
+ const size_t BUFLEN = 80;
+ char buffer[BUFLEN];
+
+ if ((rc = get_runnable_list(RA_PATH, rsc_info)) <= 0) {
+ return rc;
+ }
+
+ /* Use the following comment line as the filter patterns to choose
+ * the real LSB-compliant scripts.
+ * "### BEGIN INIT INFO" and "### END INIT INFO"
+ */
+ cur = g_list_first(*rsc_info);
+ while ( cur != NULL ) {
+ get_ra_pathname(RA_PATH, cur->data, NULL, ra_pathname);
+ if ( (fp = fopen(ra_pathname, "r")) == NULL ) {
+ tmp = g_list_next(cur);
+ *rsc_info = g_list_remove(*rsc_info, cur->data);
+ if (cur->data)
+ g_free(cur->data);
+ cur = tmp;
+ continue;
+ }
+ is_lsb_script = FALSE;
+ next_continue = FALSE;
+ found_begin_tag = FALSE;
+ while (NULL != fgets(buffer, BUFLEN, fp)) {
+ /* Handle the lines over BUFLEN(80) columns, only
+ * the first part is compared.
+ */
+ if ( next_continue == TRUE ) {
+ continue;
+ }
+ if (strlen(buffer) == BUFLEN ) {
+ next_continue = TRUE;
+ } else {
+ next_continue = FALSE;
+ }
+ /* Shorten the search time */
+ if (buffer[0] != '#' && buffer[0] != ' '
+ && buffer[0] != '\n') {
+ break; /* donnot find */
+ }
+
+ if (found_begin_tag == TRUE && 0 == strncasecmp(buffer
+ , LSB_INITSCRIPT_INFOEND_TAG
+ , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) {
+ is_lsb_script = TRUE;
+ break;
+ }
+ if (found_begin_tag == FALSE && 0 == strncasecmp(buffer
+ , LSB_INITSCRIPT_INFOBEGIN_TAG
+ , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) {
+ found_begin_tag = TRUE;
+ }
+ }
+ fclose(fp);
+ tmp = g_list_next(cur);
+
+/*
+ * Temporarily remove the filter to the initscript, or many initscripts on
+ * many distros, such as RHEL4 and fedora5, cannot be used by management GUI.
+ * Please refer to the bug
+ * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250
+ */
+
+#if 0
+ if ( is_lsb_script != TRUE ) {
+ *rsc_info = g_list_remove(*rsc_info, cur->data);
+ g_free(cur->data);
+ }
+#else
+ (void) is_lsb_script;
+#endif
+ cur = tmp;
+ }
+
+ return g_list_length(*rsc_info);
+}
+
+static int
+prepare_cmd_parameters(const char * rsc_type, const char * op_type,
+ GHashTable * params_ht, RA_ARGV params_argv)
+{
+ int tmp_len;
+ int ht_size = 0;
+#if 0
+ /* Reserve it for possible furture use */
+ int index;
+ void * value_tmp = NULL;
+ char buf_tmp[20];
+#endif
+
+ if (params_ht) {
+ ht_size = g_hash_table_size(params_ht);
+ }
+
+ /* Need 3 additonal spaces for accomodating:
+ * argv[0] = RA_file_name(RA_TYPE)
+ * argv[1] = operation
+ * a terminal NULL
+ */
+ if ( ht_size+3 > MAX_PARAMETER_NUM ) {
+ cl_log(LOG_ERR, "Too many parameters");
+ return -1;
+ }
+
+ tmp_len = strnlen(rsc_type, MAX_LENGTH_OF_RSCNAME);
+ params_argv[0] = g_strndup(rsc_type, tmp_len);
+ /* Add operation code as the first argument */
+ tmp_len = strnlen(op_type, MAX_LENGTH_OF_OPNAME);
+ params_argv[1] = g_strndup(op_type, tmp_len);
+
+ /*
+ * No actual arguments needed except op_type.
+ * Add the teminating NULL pointer.
+ */
+ params_argv[2] = NULL;
+ if ( (ht_size != 0) && (0 != STRNCMP_CONST(op_type, "status")) ) {
+ cl_log(LOG_WARNING, "For LSB init script, no additional "
+ "parameters are needed.");
+ }
+
+/* Actually comment the following code, but I still think it may be used
+ * in the future for LSB none-initial scripts, so reserver it.
+ */
+#if 0
+ /* Now suppose the parameter formate stored in Hashtabe is like
+ * key="1", value="-Wl,soname=test"
+ * Moreover, the key is supposed as a string transfered from an integer.
+ * It may be changed in the future.
+ */
+ for (index = 1; index <= ht_size; index++ ) {
+ snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
+ value_tmp = g_hash_table_lookup(params_ht, buf_tmp);
+ /* suppose the key is consecutive */
+ if ( value_tmp == NULL ) {
+ cl_log(LOG_ERR, "Parameter ordering error in"\
+ "prepare_cmd_parameters, raexeclsb.c");
+ return -1;
+ }
+ params_argv[index+1] = g_strdup((char *)value_tmp);
+ }
+#endif
+
+ return 0;
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE * fp;
+ gboolean next_continue;
+ GString * meta_data;
+ const size_t BUFLEN = 132;
+ char buffer[BUFLEN];
+ char * provides = NULL,
+ * req_start = NULL,
+ * req_stop = NULL,
+ * shld_start = NULL,
+ * shld_stop = NULL,
+ * dflt_start = NULL,
+ * dflt_stop = NULL,
+ * s_dscrpt = NULL,
+ * xml_l_dscrpt = NULL;
+ GString * l_dscrpt = NULL;
+
+ /*
+ * Use the following tags to find the LSb-compliant comment block.
+ * "### BEGIN INIT INFO" and "### END INIT INFO"
+ */
+ get_ra_pathname(RA_PATH, rsc_type, NULL, ra_pathname);
+ if ( (fp = fopen(ra_pathname, "r")) == NULL ) {
+ cl_log(LOG_ERR, "Failed to open lsb RA %s. No meta-data gotten."
+ , rsc_type);
+ return NULL;
+ }
+ meta_data = g_string_new("");
+
+ next_continue = FALSE;
+
+/*
+ * Is not stick to the rule that the description should be located in the
+ * comment block between "### BEGIN INIT INFO" and "### END INIT INFO".
+ * Please refer to the bug
+ * http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1250
+ */
+#if 0
+ while (NULL != fgets(buffer, BUFLEN, fp)) {
+ /* Handle the lines over BUFLEN(80) columns, only
+ * the first part is compared.
+ */
+ if ( next_continue == TRUE ) {
+ continue;
+ }
+ if (strlen(buffer) == BUFLEN ) {
+ next_continue = TRUE;
+ } else {
+ next_continue = FALSE;
+ }
+
+ if ( 0 == strncasecmp(buffer , LSB_INITSCRIPT_INFOBEGIN_TAG
+ , strlen(LSB_INITSCRIPT_INFOBEGIN_TAG)) ) {
+ break;
+ }
+ }
+#else
+ (void) next_continue;
+#endif
+
+ /* Enter into the lsb-compliant comment block */
+ while ( NULL != fgets(buffer, BUFLEN, fp) ) {
+ /* Now suppose each of the following eight arguments contain
+ * only one line
+ */
+ RALSB_GET_VALUE(provides, PROVIDES)
+ RALSB_GET_VALUE(req_start, REQ_START)
+ RALSB_GET_VALUE(req_stop, REQ_STOP)
+ RALSB_GET_VALUE(shld_start, SHLD_START)
+ RALSB_GET_VALUE(shld_stop, SHLD_STOP)
+ RALSB_GET_VALUE(dflt_start, DFLT_START)
+ RALSB_GET_VALUE(dflt_stop, DFLT_STOP)
+ RALSB_GET_VALUE(s_dscrpt, SHORT_DSCR)
+
+ /* Long description may cross multiple lines */
+ if ( (l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION
+ , strlen(DESCRIPTION))) ) {
+ l_dscrpt = g_string_new(buffer+strlen(DESCRIPTION));
+ /* Between # and keyword, more than one space, or a tab
+ * character, indicates the continuation line.
+ * Extracted from LSB init script standard
+ */
+ while ( NULL != fgets(buffer, BUFLEN, fp) ) {
+ if ( (0 == strncmp(buffer, "# ", 3))
+ || (0 == strncmp(buffer, "#\t", 2)) ) {
+ buffer[0] = ' ';
+ l_dscrpt = g_string_append(l_dscrpt
+ , buffer);
+ } else {
+ fputs(buffer, fp);
+ break; /* Long description ends */
+ }
+ }
+ continue;
+ }
+ if( l_dscrpt )
+ xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL,
+ BAD_CAST (l_dscrpt->str));
+
+ if ( 0 == strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG
+ , strlen(LSB_INITSCRIPT_INFOEND_TAG)) ) {
+ /* Get to the out border of LSB comment block */
+ break;
+ }
+
+ if ( buffer[0] != '#' ) {
+ break; /* Out of comment block in the beginning */
+ }
+ }
+ fclose(fp);
+
+ g_string_sprintf( meta_data, meta_data_template, rsc_type
+ , (xml_l_dscrpt==NULL)? rsc_type : xml_l_dscrpt
+ , (s_dscrpt==NULL)? rsc_type : s_dscrpt
+ , (provides==NULL)? "" : provides
+ , (req_start==NULL)? "" : req_start
+ , (req_stop==NULL)? "" : req_stop
+ , (shld_start==NULL)? "" : shld_start
+ , (shld_stop==NULL)? "" : shld_stop
+ , (dflt_start==NULL)? "" : dflt_start
+ , (dflt_stop==NULL)? "" : dflt_stop );
+
+ ZAPXMLOBJ(xml_l_dscrpt);
+ ZAPXMLOBJ(s_dscrpt);
+ ZAPXMLOBJ(provides);
+ ZAPXMLOBJ(req_start);
+ ZAPXMLOBJ(req_stop);
+ ZAPXMLOBJ(shld_start);
+ ZAPXMLOBJ(shld_stop);
+ ZAPXMLOBJ(dflt_start);
+ ZAPXMLOBJ(dflt_stop);
+
+ if( l_dscrpt )
+ g_string_free(l_dscrpt, TRUE);
+ return meta_data->str;
+}
+
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: providers==NULL"
+ , __FUNCTION__, __LINE__);
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "%s:%d: Parameter error: *providers==NULL."
+ "This will cause memory leak."
+ , __FUNCTION__, __LINE__);
+ }
+
+ /* Now temporarily make it fixed */
+ *providers = g_list_append(*providers, g_strdup("heartbeat"));
+
+ return g_list_length(*providers);
+}
diff --git a/lib/plugins/lrm/raexecocf.c b/lib/plugins/lrm/raexecocf.c
new file mode 100644
index 0000000..f7cd7ed
--- /dev/null
+++ b/lib/plugins/lrm/raexecocf.c
@@ -0,0 +1,496 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ *
+ * File: raexecocf.c
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for LSB style.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/realtime.h>
+#include <pils/plugin.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <lrm/raexec.h>
+
+# define PIL_PLUGINTYPE RA_EXEC_TYPE
+# define PIL_PLUGINTYPE_S "RAExec"
+# define PIL_PLUGINLICENSE LICENSE_PUBDOM
+# define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+# define PIL_PLUGIN ocf
+# define PIL_PLUGIN_S "ocf"
+/*
+ * Are there multiple paths? Now according to OCF spec, the answer is 'no'.
+ * But actually or for future?
+ */
+static const char * RA_PATH = OCF_RA_DIR;
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra,
+ const char * op_type, const char * std_output);
+static int get_resource_list(GList ** rsc_info);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+static void add_OCF_prefix(GHashTable * params, GHashTable * new_params);
+static void add_OCF_env_vars(GHashTable * env, const char * rsc_id,
+ const char * rsc_type, const char * provider);
+static void add_prefix_foreach(gpointer key, gpointer value,
+ gpointer user_data);
+
+static void hash_to_str(GHashTable * , GString *);
+static void hash_to_str_foreach(gpointer key, gpointer value,
+ gpointer user_data);
+
+static int raexec_setenv(GHashTable * env_params);
+static void set_env(gpointer key, gpointer value, gpointer user_data);
+
+static gboolean let_remove_eachitem(gpointer key, gpointer value,
+ gpointer user_data);
+static int get_providers(const char* class_path, const char* op_type,
+ GList ** providers);
+static void merge_string_list(GList** old, GList* new);
+static gint compare_str(gconstpointer a, gconstpointer b);
+
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+/*
+ * Our plugin initialization and registration function
+ * It gets called when the plugin gets loaded.
+ */
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interfaces */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+/*
+ * The function to execute a RA.
+ */
+static int
+execra(const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ GHashTable * tmp_for_setenv;
+ GString * params_gstring;
+ char * inherit_debuglevel = NULL;
+ int save_errno;
+
+ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
+
+ /* Setup environment correctly */
+ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
+ add_OCF_prefix(params, tmp_for_setenv);
+ add_OCF_env_vars(tmp_for_setenv, rsc_id, rsc_type, provider);
+ raexec_setenv(tmp_for_setenv);
+ g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
+ g_hash_table_destroy(tmp_for_setenv);
+
+ /* let this log show only high loglevel. */
+ inherit_debuglevel = getenv(HADEBUGVAL);
+ if ((inherit_debuglevel != NULL) && (atoi(inherit_debuglevel) > 1)) {
+ params_gstring = g_string_new("");
+ hash_to_str(params, params_gstring);
+ cl_log(LOG_DEBUG, "RA instance %s executing: OCF::%s %s. Parameters: "
+ "{%s}", rsc_id, rsc_type, op_type, params_gstring->str);
+ g_string_free(params_gstring, TRUE);
+ }
+
+ closefiles(); /* don't leak open files */
+ /* execute the RA */
+ execl(ra_pathname, ra_pathname, op_type, (const char *)NULL);
+ /* oops, exec failed */
+ save_errno = errno; /* cl_perror may change errno */
+ cl_perror("(%s:%s:%d) execl failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, ra_pathname);
+ errno = save_errno;
+ exit(get_failed_exec_rc());
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* Because the UNIFORM_RET_EXECRA is compatible with OCF standard,
+ * no actual mapping except validating, which ensure the return code
+ * will be in the range 0 to 7. Too strict?
+ */
+ if (ret_execra < 0 || ret_execra > 9) {
+ cl_log(LOG_WARNING, "mapped the invalid return code %d."
+ , ret_execra);
+ ret_execra = EXECRA_UNKNOWN_ERROR;
+ }
+ return ret_execra;
+}
+
+static gint
+compare_str(gconstpointer a, gconstpointer b)
+{
+ return strncmp(a,b,RA_MAX_NAME_LENGTH);
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ struct dirent **namelist;
+ GList* item;
+ int file_num;
+ char subdir[FILENAME_MAX+1];
+
+ if ( rsc_info == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list");
+ return -2;
+ }
+
+ if ( *rsc_info != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_resource_list."\
+ "will cause memory leak.");
+ *rsc_info = NULL;
+ }
+ file_num = scandir(RA_PATH, &namelist, NULL, alphasort);
+ if (file_num < 0) {
+ return -2;
+ }
+ while (file_num--) {
+ GList* ra_subdir = NULL;
+ struct stat prop;
+ if ('.' == namelist[file_num]->d_name[0]) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ snprintf(subdir,FILENAME_MAX,"%s/%s",
+ RA_PATH, namelist[file_num]->d_name);
+
+ if (stat(subdir, &prop) == -1) {
+ cl_perror("%s:%s:%d: stat failed for %s"
+ , __FILE__, __FUNCTION__, __LINE__, subdir);
+ free(namelist[file_num]);
+ continue;
+ } else if (!S_ISDIR(prop.st_mode)) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ get_runnable_list(subdir,&ra_subdir);
+
+ merge_string_list(rsc_info,ra_subdir);
+
+ while (NULL != (item = g_list_first(ra_subdir))) {
+ ra_subdir = g_list_remove_link(ra_subdir, item);
+ g_free(item->data);
+ g_list_free_1(item);
+ }
+
+ free(namelist[file_num]);
+ }
+ free(namelist);
+
+ return 0;
+}
+
+static void
+merge_string_list(GList** old, GList* new)
+{
+ GList* item = NULL;
+ char* newitem;
+ for( item=g_list_first(new); NULL!=item; item=g_list_next(item)){
+ if (!g_list_find_custom(*old, item->data,compare_str)){
+ newitem = g_strndup(item->data,RA_MAX_NAME_LENGTH);
+ *old = g_list_append(*old, newitem);
+ }
+ }
+}
+
+static int
+get_provider_list(const char* ra_type, GList ** providers)
+{
+ int ret;
+ ret = get_providers(RA_PATH, ra_type, providers);
+ if (0>ret) {
+ cl_log(LOG_ERR, "scandir failed in OCF RA plugin");
+ }
+ return ret;
+}
+
+static char*
+get_resource_meta(const char* rsc_type, const char* provider)
+{
+ const int BUFF_LEN=4096;
+ int read_len = 0;
+ char buff[BUFF_LEN];
+ char* data = NULL;
+ GString* g_str_tmp = NULL;
+ char ra_pathname[RA_MAX_NAME_LENGTH];
+ FILE* file = NULL;
+ GHashTable * tmp_for_setenv;
+ struct timespec short_sleep = {0,200000000L}; /*20ms*/
+
+ get_ra_pathname(RA_PATH, rsc_type, provider, ra_pathname);
+
+ strncat(ra_pathname, " meta-data",RA_MAX_NAME_LENGTH-strlen(ra_pathname)-1);
+ tmp_for_setenv = g_hash_table_new(g_str_hash, g_str_equal);
+ add_OCF_env_vars(tmp_for_setenv, "DUMMY_INSTANCE", rsc_type, provider);
+ raexec_setenv(tmp_for_setenv);
+ g_hash_table_foreach_remove(tmp_for_setenv, let_remove_eachitem, NULL);
+ g_hash_table_destroy(tmp_for_setenv);
+
+ file = popen(ra_pathname, "r");
+ if (NULL==file) {
+ cl_log(LOG_ERR, "%s: popen failed: %s", __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+
+ g_str_tmp = g_string_new("");
+ while(!feof(file)) {
+ read_len = fread(buff, 1, BUFF_LEN - 1, file);
+ if (0<read_len) {
+ *(buff+read_len) = '\0';
+ g_string_append(g_str_tmp, buff);
+ }
+ else {
+ nanosleep(&short_sleep,NULL);
+ }
+ }
+ if( pclose(file) ) {
+ cl_log(LOG_ERR, "%s: pclose failed: %s", __FUNCTION__, strerror(errno));
+ }
+ if (0 == g_str_tmp->len) {
+ g_string_free(g_str_tmp, TRUE);
+ return NULL;
+ }
+ data = (char*)g_new(char, g_str_tmp->len+1);
+ data[0] = data[g_str_tmp->len] = 0;
+ strncpy(data, g_str_tmp->str, g_str_tmp->len);
+
+ g_string_free(g_str_tmp, TRUE);
+
+ return data;
+}
+
+static void
+add_OCF_prefix(GHashTable * env_params, GHashTable * new_env_params)
+{
+ if (env_params) {
+ g_hash_table_foreach(env_params, add_prefix_foreach,
+ new_env_params);
+ }
+}
+
+static void
+add_prefix_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ const int MAX_LENGTH_OF_ENV = 128;
+ int prefix = STRLEN_CONST("OCF_RESKEY_");
+ GHashTable * new_hashtable = (GHashTable *) user_data;
+ char * newkey;
+ int keylen = strnlen((char*)key, MAX_LENGTH_OF_ENV-prefix)+prefix+1;
+
+ newkey = g_new(gchar, keylen);
+ strncpy(newkey, "OCF_RESKEY_", keylen);
+ strncat(newkey, key, keylen-strlen(newkey)-1);
+ g_hash_table_insert(new_hashtable, (gpointer)newkey, g_strdup(value));
+}
+
+static void
+hash_to_str(GHashTable * params , GString * str)
+{
+ if (params) {
+ g_hash_table_foreach(params, hash_to_str_foreach, str);
+ }
+}
+
+static void
+hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ char buffer_tmp[60];
+ GString * str = (GString *)user_data;
+
+ snprintf(buffer_tmp, 60, "%s=%s ", (char *)key, (char *)value);
+ str = g_string_append(str, buffer_tmp);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ g_free(key);
+ g_free(value);
+ return TRUE;
+}
+
+static int
+raexec_setenv(GHashTable * env_params)
+{
+ if (env_params) {
+ g_hash_table_foreach(env_params, set_env, NULL);
+ }
+ return 0;
+}
+
+static void
+set_env(gpointer key, gpointer value, gpointer user_data)
+{
+ if (setenv(key, value, 1) != 0) {
+ cl_log(LOG_ERR, "setenv failed in raexecocf.");
+ }
+}
+
+static int
+get_providers(const char* class_path, const char* ra_type, GList ** providers)
+{
+ struct dirent **namelist;
+ int file_num;
+
+ if ( providers == NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_providers");
+ return -2;
+ }
+
+ if ( *providers != NULL ) {
+ cl_log(LOG_ERR, "Parameter error: get_providers."\
+ "will cause memory leak.");
+ *providers = NULL;
+ }
+
+ file_num = scandir(class_path, &namelist, 0, alphasort);
+ if (file_num < 0) {
+ return -2;
+ }else{
+ char tmp_buffer[FILENAME_MAX+1];
+ struct stat prop;
+
+ while (file_num--) {
+ if ('.' == namelist[file_num]->d_name[0]) {
+ free(namelist[file_num]);
+ continue;
+ }
+ snprintf(tmp_buffer,FILENAME_MAX,"%s/%s",
+ class_path, namelist[file_num]->d_name);
+ stat(tmp_buffer, &prop);
+ if (!S_ISDIR(prop.st_mode)) {
+ free(namelist[file_num]);
+ continue;
+ }
+
+ snprintf(tmp_buffer,FILENAME_MAX,"%s/%s/%s",
+ class_path, namelist[file_num]->d_name, ra_type);
+
+ if ( filtered(tmp_buffer) == TRUE ) {
+ *providers = g_list_append(*providers,
+ g_strdup(namelist[file_num]->d_name));
+ }
+ free(namelist[file_num]);
+ }
+ free(namelist);
+ }
+ return g_list_length(*providers);
+}
+
+static void
+add_OCF_env_vars(GHashTable * env, const char * rsc_id,
+ const char * rsc_type, const char * provider)
+{
+ if ( env == NULL ) {
+ cl_log(LOG_WARNING, "env should not be a NULL pointer.");
+ return;
+ }
+
+ g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MAJOR"),
+ g_strdup("1"));
+ g_hash_table_insert(env, g_strdup("OCF_RA_VERSION_MINOR"),
+ g_strdup("0"));
+ g_hash_table_insert(env, g_strdup("OCF_ROOT"),
+ g_strdup(OCF_ROOT_DIR));
+
+ if ( rsc_id != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_INSTANCE"),
+ g_strdup(rsc_id));
+ }
+
+ /* Currently the rsc_type=="the filename of the RA script/executable",
+ * It seems always correct even in the furture. ;-)
+ */
+ if ( rsc_type != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_TYPE"),
+ g_strdup(rsc_type));
+ }
+
+ /* Notes: this is not added to specification yet. Sept 10,2004 */
+ if ( provider != NULL ) {
+ g_hash_table_insert(env, g_strdup("OCF_RESOURCE_PROVIDER"),
+ g_strdup(provider));
+ }
+}
+
diff --git a/lib/plugins/lrm/raexecupstart.c b/lib/plugins/lrm/raexecupstart.c
new file mode 100644
index 0000000..baa0278
--- /dev/null
+++ b/lib/plugins/lrm/raexecupstart.c
@@ -0,0 +1,222 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ *
+ * File: raexecupstart.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ *
+ * Heavily based on raexeclsb.c and raexechb.c:
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This code implements the Resource Agent Plugin Module for Upstart.
+ * It's a part of Local Resource Manager. Currently it's used by lrmd only.
+ */
+
+#define PIL_PLUGINTYPE RA_EXEC_TYPE
+#define PIL_PLUGIN upstart
+#define PIL_PLUGINTYPE_S "RAExec"
+#define PIL_PLUGIN_S "upstart"
+#define PIL_PLUGINLICENSE LICENSE_PUBDOM
+#define PIL_PLUGINLICENSEURL URL_PUBDOM
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h> /* Add it for compiling on OSX */
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <pils/plugin.h>
+#include <lrm/raexec.h>
+#include <libgen.h>
+
+#include <glib-object.h>
+
+#include <libxml/entities.h>
+
+#include "upstart-dbus.h"
+
+#define meta_data_template \
+"<?xml version=\"1.0\"?>\n"\
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
+"<resource-agent name=\"%s\" version=\"0.1\">\n"\
+" <version>1.0</version>\n"\
+" <longdesc lang=\"en\">\n"\
+" %s"\
+" </longdesc>\n"\
+" <shortdesc lang=\"en\">%s</shortdesc>\n"\
+" <parameters>\n"\
+" </parameters>\n"\
+" <actions>\n"\
+" <action name=\"start\" timeout=\"15\" />\n"\
+" <action name=\"stop\" timeout=\"15\" />\n"\
+" <action name=\"status\" timeout=\"15\" />\n"\
+" <action name=\"restart\" timeout=\"15\" />\n"\
+" <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"\
+" <action name=\"meta-data\" timeout=\"5\" />\n"\
+" </actions>\n"\
+" <special tag=\"upstart\">\n"\
+" </special>\n"\
+"</resource-agent>\n"
+
+/* The begin of exported function list */
+static int execra(const char * rsc_id,
+ const char * rsc_type,
+ const char * provider,
+ const char * op_type,
+ const int timeout,
+ GHashTable * params);
+
+static uniform_ret_execra_t map_ra_retvalue(int ret_execra
+ , const char * op_type, const char * std_output);
+static char* get_resource_meta(const char* rsc_type, const char* provider);
+static int get_resource_list(GList ** rsc_info);
+static int get_provider_list(const char* ra_type, GList ** providers);
+
+/* The end of exported function list */
+
+/* The begin of internal used function & data list */
+#define MAX_PARAMETER_NUM 40
+
+const int MAX_LENGTH_OF_RSCNAME = 40,
+ MAX_LENGTH_OF_OPNAME = 40;
+
+typedef char * RA_ARGV[MAX_PARAMETER_NUM];
+
+/* The end of internal function & data list */
+
+/* Rource agent execution plugin operations */
+static struct RAExecOps raops =
+{ execra,
+ map_ra_retvalue,
+ get_resource_list,
+ get_provider_list,
+ get_resource_meta
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static void* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin * us, const PILPluginImports* imports)
+{
+ PluginImports = imports;
+ OurPlugin = us;
+
+ imports->register_plugin(us, &OurPIExports);
+
+ g_type_init ();
+
+ return imports->register_interface(us, PIL_PLUGINTYPE_S, PIL_PLUGIN_S,
+ &raops, NULL, &OurInterface, &OurImports,
+ interfprivate);
+}
+
+static int
+execra( const char * rsc_id, const char * rsc_type, const char * provider,
+ const char * op_type, const int timeout, GHashTable * params)
+{
+ UpstartJobCommand cmd;
+
+ if (!g_strcmp0(op_type, "meta-data")) {
+ printf("%s", get_resource_meta(rsc_type, provider));
+ exit(EXECRA_OK);
+ } else if (!g_strcmp0(op_type, "monitor") || !g_strcmp0(op_type, "status")) {
+ gboolean running = upstart_job_is_running (rsc_type);
+ printf("%s", running ? "running" : "stopped");
+
+ if (running)
+ exit(EXECRA_OK);
+ else
+ exit(EXECRA_NOT_RUNNING);
+ } else if (!g_strcmp0(op_type, "start")) {
+ cmd = UPSTART_JOB_START;
+ } else if (!g_strcmp0(op_type, "stop")) {
+ cmd = UPSTART_JOB_STOP;
+ } else if (!g_strcmp0(op_type, "restart")) {
+ cmd = UPSTART_JOB_RESTART;
+ } else {
+ exit(EXECRA_UNIMPLEMENT_FEATURE);
+ }
+
+ /* It'd be better if it returned GError, so we can distinguish
+ * between failure modes (can't contact upstart, no such job,
+ * or failure to do action. */
+ if (upstart_job_do(rsc_type, cmd, timeout)) {
+ exit(EXECRA_OK);
+ } else {
+ exit(EXECRA_NO_RA);
+ }
+}
+
+static uniform_ret_execra_t
+map_ra_retvalue(int ret_execra, const char * op_type, const char * std_output)
+{
+ /* no need to map anything, execra() returns correct exit code */
+ return ret_execra;
+}
+
+static int
+get_resource_list(GList ** rsc_info)
+{
+ gchar **jobs;
+ gint i;
+ *rsc_info = NULL;
+
+ jobs = upstart_get_all_jobs();
+
+ if (!jobs)
+ return 0;
+
+ for (i = 0; jobs[i] != NULL; i++) {
+ *rsc_info = g_list_prepend(*rsc_info, jobs[i]);
+ }
+
+ /* free the array, but not the strings */
+ g_free(jobs);
+
+ *rsc_info = g_list_reverse(*rsc_info);
+ return g_list_length(*rsc_info);
+}
+
+static char *
+get_resource_meta (const gchar *rsc_type, const gchar *provider)
+{
+ return g_strdup_printf(meta_data_template, rsc_type,
+ rsc_type, rsc_type);
+}
+
+static int
+get_provider_list (const gchar *ra_type, GList **providers)
+{
+ *providers = g_list_prepend(*providers, g_strdup("upstart"));
+ return g_list_length(*providers);
+}
+
diff --git a/lib/plugins/lrm/upstart-dbus.c b/lib/plugins/lrm/upstart-dbus.c
new file mode 100644
index 0000000..b994d87
--- /dev/null
+++ b/lib/plugins/lrm/upstart-dbus.c
@@ -0,0 +1,406 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ *
+ * File: upstart-dbus.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ *
+ *
+ * Each exported function is standalone, and creates a new connection to
+ * the upstart daemon. This is because lrmd plugins fork off for exec,
+ * and if we try and share the connection, the whole thing blocks
+ * indefinitely.
+ */
+
+#include "upstart-dbus.h"
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include <dbus/dbus.h>
+
+#include "dbus/Upstart.h"
+#include "dbus/Upstart_Job.h"
+#include "dbus/Upstart_Instance.h"
+
+#include <stdio.h>
+
+#define SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
+#define UPSTART_BUS_ADDRESS "unix:abstract=/com/ubuntu/upstart"
+#define UPSTART_SERVICE_NAME "com.ubuntu.Upstart"
+#define UPSTART_MANAGER_PATH "/com/ubuntu/Upstart"
+#define UPSTART_IFACE "com.ubuntu.Upstart0_6"
+#define UPSTART_JOB_IFACE UPSTART_IFACE ".Job"
+#define UPSTART_INSTANCE_IFACE UPSTART_IFACE ".Instance"
+#define UPSTART_ERROR_ALREADY_STARTED UPSTART_IFACE ".Error.AlreadyStarted"
+#define UPSTART_ERROR_UNKNOWN_INSTANCE UPSTART_IFACE ".Error.UnknownInstance"
+
+static DBusGConnection *
+get_connection(void)
+{
+ GError *error = NULL;
+ DBusGConnection *conn;
+
+ conn = dbus_g_bus_get_private(DBUS_BUS_SYSTEM, NULL, &error);
+
+ if (error)
+ {
+ g_error_free(error);
+ error = NULL;
+
+ conn = dbus_g_connection_open("unix:abstract=/com/ubuntu/upstart",
+ &error);
+
+ if (error)
+ {
+ g_warning("Can't connect to either system or Upstart "
+ "DBus bus.");
+ g_error_free(error);
+
+ return NULL;
+ }
+ }
+
+ return conn;
+}
+
+static DBusGProxy *
+new_proxy(DBusGConnection *conn, const gchar *object_path,
+ const gchar *iface)
+{
+ return dbus_g_proxy_new_for_name(conn,
+ UPSTART_SERVICE_NAME,
+ object_path,
+ iface);
+}
+
+static GHashTable *
+get_object_properties(DBusGProxy *obj, const gchar *iface)
+{
+ GError *error = NULL;
+ DBusGProxy *proxy;
+ GHashTable *asv;
+ GHashTable *retval;
+ GHashTableIter iter;
+ gpointer k, v;
+
+ proxy = dbus_g_proxy_new_from_proxy(obj,
+ DBUS_INTERFACE_PROPERTIES, NULL);
+
+ dbus_g_proxy_call(proxy, "GetAll", &error, G_TYPE_STRING,
+ iface, G_TYPE_INVALID,
+ dbus_g_type_get_map("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_VALUE),
+ &asv, G_TYPE_INVALID);
+
+ if (error) {
+ g_warning("Error getting %s properties: %s", iface, error->message);
+ g_error_free(error);
+ g_object_unref(proxy);
+ return NULL;
+ }
+
+ retval = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ g_hash_table_iter_init(&iter, asv);
+ while (g_hash_table_iter_next(&iter, &k, &v)) {
+ gchar *key = k;
+ GValue *val = v;
+
+ /* all known properties are strings */
+ if (G_VALUE_TYPE(val) == G_TYPE_STRING) {
+ g_hash_table_insert(retval, g_strdup(key),
+ g_value_dup_string(val));
+ }
+ }
+
+ g_hash_table_destroy(asv);
+
+ return retval;
+}
+
+gchar **
+upstart_get_all_jobs(void)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ GError *error = NULL;
+ GPtrArray *array;
+ gchar **retval = NULL;
+ gint i, j;
+
+ conn = get_connection();
+ if (!conn)
+ return NULL;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ dbus_g_proxy_call(manager, "GetAllJobs", &error, G_TYPE_INVALID,
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &array, G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Can't call GetAllJobs: %s", error->message);
+ g_error_free(error);
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+ return NULL;
+ }
+
+ retval = g_new0(gchar *, array->len + 1);
+
+ for (i = 0, j = 0; i < array->len; i++)
+ {
+ DBusGProxy *job;
+
+ job = new_proxy(conn, g_ptr_array_index(array, i),
+ UPSTART_JOB_IFACE);
+
+ if (job) {
+ GHashTable *props = get_object_properties(job,
+ UPSTART_JOB_IFACE);
+
+ if (props) {
+ gchar *name = g_hash_table_lookup(props,
+ "name");
+
+ if (name)
+ retval[j++] = g_strdup(name);
+
+ g_hash_table_destroy(props);
+ }
+
+ g_object_unref(job);
+ }
+ }
+
+ g_ptr_array_free(array, TRUE);
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+
+ return retval;
+}
+
+static DBusGProxy *
+upstart_get_job_by_name(DBusGConnection *conn, DBusGProxy *manager,
+ const gchar *name)
+{
+ GError *error = NULL;
+ gchar *object_path;
+ DBusGProxy *retval;
+
+ dbus_g_proxy_call(manager, "GetJobByName", &error, G_TYPE_STRING,
+ name, G_TYPE_INVALID, DBUS_TYPE_G_OBJECT_PATH, &object_path,
+ G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Error calling GetJobByName: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ retval = new_proxy(conn, object_path, UPSTART_JOB_IFACE);
+
+ g_free(object_path);
+
+ return retval;
+}
+
+static gchar **
+get_job_instances(DBusGProxy *job)
+{
+ GError *error = NULL;
+ GPtrArray *array;
+ gchar **retval;
+ gint i;
+
+ dbus_g_proxy_call(job, "GetAllInstances", &error, G_TYPE_INVALID,
+ dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &array, G_TYPE_INVALID);
+
+ if (error)
+ {
+ g_warning("Can't call GetAllInstances: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ retval = g_new0(gchar *, array->len + 1);
+
+ for (i = 0; i < array->len; i++)
+ {
+ retval[i] = g_ptr_array_index(array, i);
+ }
+
+ g_ptr_array_free(array, TRUE);
+
+ return retval;
+}
+
+static DBusGProxy *
+get_first_instance(DBusGConnection *conn, DBusGProxy *job)
+{
+ gchar **instances;
+ DBusGProxy *instance = NULL;
+
+ instances = get_job_instances(job);
+
+ if (!instances)
+ return NULL;
+
+ if (*instances)
+ {
+ instance = new_proxy(conn, instances[0],
+ UPSTART_INSTANCE_IFACE);
+ }
+
+ g_strfreev(instances);
+ return instance;
+}
+
+gboolean
+upstart_job_is_running(const gchar *name)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ DBusGProxy *job;
+ gboolean retval = FALSE;
+
+ conn = get_connection();
+ if (!conn)
+ return FALSE;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ job = upstart_get_job_by_name(conn, manager, name);
+ if (job) {
+ DBusGProxy *instance = get_first_instance(conn, job);
+
+ if (instance) {
+ GHashTable *props = get_object_properties(instance,
+ UPSTART_INSTANCE_IFACE);
+
+ if (props) {
+ const gchar *state = g_hash_table_lookup(props,
+ "state");
+
+ retval = !g_strcmp0(state, "running");
+
+ g_hash_table_destroy(props);
+ }
+
+ g_object_unref(instance);
+ }
+
+ g_object_unref(job);
+ }
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+
+ return retval;
+}
+
+gboolean
+upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout)
+{
+ DBusGConnection *conn;
+ DBusGProxy *manager;
+ DBusGProxy *job;
+ gboolean retval;
+
+ conn = get_connection();
+ if (!conn)
+ return FALSE;
+
+ manager = new_proxy(conn, UPSTART_MANAGER_PATH, UPSTART_IFACE);
+
+ job = upstart_get_job_by_name(conn, manager, name);
+ if (job) {
+ GError *error = NULL;
+ const gchar *cmd_name = NULL;
+ gchar *instance_path = NULL;
+ gchar *no_args[] = { NULL };
+
+ switch (cmd) {
+ case UPSTART_JOB_START:
+ cmd_name = "Start";
+ dbus_g_proxy_call_with_timeout (job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &instance_path,
+ G_TYPE_INVALID);
+ g_free (instance_path);
+ break;
+ case UPSTART_JOB_STOP:
+ cmd_name = "Stop";
+ dbus_g_proxy_call_with_timeout(job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ break;
+ case UPSTART_JOB_RESTART:
+ cmd_name = "Restart";
+ dbus_g_proxy_call_with_timeout (job, cmd_name,
+ timeout, &error,
+ G_TYPE_STRV, no_args,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &instance_path,
+ G_TYPE_INVALID);
+ g_free (instance_path);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (error) {
+ g_warning("Could not issue %s: %s", cmd_name,
+ error->message);
+
+ /* ignore "already started" or "not running" errors */
+ if (dbus_g_error_has_name(error,
+ UPSTART_ERROR_ALREADY_STARTED) ||
+ dbus_g_error_has_name(error,
+ UPSTART_ERROR_UNKNOWN_INSTANCE)) {
+ retval = TRUE;
+ } else {
+ retval = FALSE;
+ }
+ g_error_free(error);
+ } else {
+ retval = TRUE;
+ }
+
+ g_object_unref(job);
+ } else {
+ retval = FALSE;
+ }
+
+ g_object_unref(manager);
+ dbus_g_connection_unref(conn);
+ return retval;
+}
+
+
diff --git a/lib/plugins/lrm/upstart-dbus.h b/lib/plugins/lrm/upstart-dbus.h
new file mode 100644
index 0000000..bc72c95
--- /dev/null
+++ b/lib/plugins/lrm/upstart-dbus.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ *
+ * File: upstart-dbus.c
+ * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
+ * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
+ */
+#ifndef _UPSTART_DBUS_H_
+#define _UPSTART_DBUS_H_
+
+#include <glib.h>
+
+typedef enum {
+ UPSTART_JOB_START,
+ UPSTART_JOB_STOP,
+ UPSTART_JOB_RESTART
+} UpstartJobCommand;
+
+G_GNUC_INTERNAL gchar **upstart_get_all_jobs(void);
+G_GNUC_INTERNAL gboolean upstart_job_do(const gchar *name, UpstartJobCommand cmd, const int timeout);
+G_GNUC_INTERNAL gboolean upstart_job_is_running (const gchar *name);
+
+#endif /* _UPSTART_DBUS_H_ */
+
diff --git a/lib/plugins/stonith/Makefile.am b/lib/plugins/stonith/Makefile.am
new file mode 100644
index 0000000..01f2f4a
--- /dev/null
+++ b/lib/plugins/stonith/Makefile.am
@@ -0,0 +1,216 @@
+#
+# stonith: STONITH plugins for Linux-HA
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = external
+
+INCFILES = stonith_expect_helpers.h \
+ stonith_plugin_common.h \
+ stonith_signal.h \
+ stonith_config_xml.h
+
+idir=$(includedir)/stonith
+
+i_HEADERS = $(INCFILES)
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+
+AM_CFLAGS = @NON_FATAL_CFLAGS@
+
+#
+# We need "vacmclient_api.h" and -lvacmclient to make the VACM
+# plugin work.
+#
+if USE_VACM
+vacm_LIB = vacm.la
+else
+vacm_LIB =
+endif
+
+#
+# We need <ucd-snmp/asn1.h>, <ucd-snmp/snmp_api.h>, <ucd-snmp/snmp.h>
+# <ucd-snmp/snmp_client.h>, <ucd-snmp/mib.h>, -lsnmp and -lcrypto
+# for the apcmastersnmp plugin to work
+#
+
+if USE_APC_SNMP
+apcmastersnmp_LIB = apcmastersnmp.la wti_mpc.la
+else
+apcmastersnmp_LIB =
+endif
+if IPMILAN_BUILD
+OPENIPMI_LIB = -lOpenIPMI -lOpenIPMIposix -lOpenIPMIutils
+libipmilan_LIB = libipmilan.la
+ipmilan_LIB = ipmilan.la
+ipmilan_TEST = ipmilantest
+else
+OPENIPMI_LIB =
+libipmilan_LIB =
+ipmilan_LIB =
+endif
+#
+# We need <curl/curl.h>, <libxml/xmlmemory.h>,
+# <libxml/parser.h>, <libxml/xpath.h>,
+# -lcurl and -lxml2 for the drac3 plugin to work
+#
+if USE_DRAC3
+drac3_LIB = drac3.la
+else
+drac3_LIB =
+endif
+
+#
+# We need OpenHPI to make the IBM BladeCenter plugin work.
+#
+if USE_OPENHPI
+bladehpi_LIB = bladehpi.la
+else
+bladehpi_LIB =
+endif
+
+noinst_PROGRAMS = $(ipmilan_TEST)
+ipmilantest_SOURCES = ipmilan_test.c
+ipmilantest_LDADD = $(libipmilan_LIB) \
+ $(top_builddir)/lib/pils/libpils.la \
+ $(top_builddir)/lib/stonith/libstonith.la \
+ $(OPENIPMI_LIB)
+
+## libraries
+
+plugindir = $(stonith_plugindir)/stonith2
+
+plugin_LTLIBRARIES = apcmaster.la \
+ $(apcmastersnmp_LIB) \
+ apcsmart.la \
+ baytech.la \
+ $(bladehpi_LIB) \
+ cyclades.la \
+ $(drac3_LIB) \
+ external.la \
+ rhcs.la \
+ ibmhmc.la \
+ $(ipmilan_LIB) \
+ meatware.la \
+ null.la \
+ nw_rpc100s.la \
+ rcd_serial.la \
+ rps10.la \
+ ssh.la \
+ suicide.la \
+ $(vacm_LIB) \
+ wti_nps.la
+noinst_LTLIBRARIES = $(libipmilan_LIB)
+
+apcmaster_la_SOURCES = apcmaster.c $(INCFILES)
+apcmaster_la_LDFLAGS = -export-dynamic -module -avoid-version
+apcmaster_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+apcmastersnmp_la_SOURCES= apcmastersnmp.c $(INCFILES)
+apcmastersnmp_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+ @CRYPTOLIB@
+apcmastersnmp_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+apcsmart_la_SOURCES = apcsmart.c $(INCFILES)
+apcsmart_la_LDFLAGS = -export-dynamic -module -avoid-version
+apcsmart_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+baytech_la_SOURCES = baytech.c $(INCFILES)
+baytech_la_LDFLAGS = -export-dynamic -module -avoid-version
+baytech_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+bladehpi_la_SOURCES = bladehpi.c $(INCFILES)
+bladehpi_la_LDFLAGS = -export-dynamic -module -avoid-version
+bladehpi_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB) -lopenhpi
+
+cyclades_la_SOURCES = cyclades.c $(INCFILES)
+cyclades_la_LDFLAGS = -export-dynamic -module -avoid-version
+cyclades_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+drac3_la_SOURCES = drac3.c drac3_command.c drac3_command.h drac3_hash.c drac3_hash.h $(INCFILES)
+drac3_la_LDFLAGS = -export-dynamic -module -avoid-version
+drac3_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la -lcurl -lxml2 $(GLIBLIB)
+
+external_la_SOURCES = external.c $(INCFILES)
+external_la_LDFLAGS = -export-dynamic -module -avoid-version
+external_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+rhcs_la_SOURCES = rhcs.c $(INCFILES)
+rhcs_la_LDFLAGS = -export-dynamic -module -avoid-version
+rhcs_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+ibmhmc_la_SOURCES = ibmhmc.c $(INCFILES)
+ibmhmc_la_LDFLAGS = -export-dynamic -module -avoid-version
+ibmhmc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+ipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES)
+ipmilan_la_LDFLAGS = -export-dynamic -module -avoid-version
+ipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+libipmilan_la_SOURCES = ipmilan.c ipmilan.h ipmilan_command.c $(INCFILES)
+libipmilan_la_LDFLAGS = -version-info 1:0:0
+libipmilan_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+meatware_la_SOURCES = meatware.c $(INCFILES)
+meatware_la_LDFLAGS = -export-dynamic -module -avoid-version
+meatware_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+null_la_SOURCES = null.c $(INCFILES)
+null_la_LDFLAGS = -export-dynamic -module -avoid-version
+null_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+nw_rpc100s_la_SOURCES = nw_rpc100s.c $(INCFILES)
+nw_rpc100s_la_LDFLAGS = -export-dynamic -module -avoid-version
+nw_rpc100s_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+rcd_serial_la_SOURCES = rcd_serial.c $(INCFILES)
+rcd_serial_la_LDFLAGS = -export-dynamic -module -avoid-version
+rcd_serial_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+rps10_la_SOURCES = rps10.c $(INCFILES)
+rps10_la_LDFLAGS = -export-dynamic -module -avoid-version
+rps10_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+ssh_la_SOURCES = ssh.c $(INCFILES)
+ssh_la_LDFLAGS = -export-dynamic -module -avoid-version
+ssh_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la
+
+vacm_la_SOURCES = vacm.c $(INCFILES)
+vacm_la_LDFLAGS = -export-dynamic -module -avoid-version
+vacm_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la
+
+wti_nps_la_SOURCES = wti_nps.c $(INCFILES)
+wti_nps_la_LDFLAGS = -export-dynamic -module -avoid-version
+wti_nps_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(top_builddir)/replace/libreplace.la $(GLIBLIB)
+
+wti_mpc_la_SOURCES= wti_mpc.c $(INCFILES)
+wti_mpc_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+ @CRYPTOLIB@
+wti_mpc_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la $(GLIBLIB)
+
+suicide_la_SOURCES = suicide.c $(INCFILES)
+suicide_la_LDFLAGS = -export-dynamic -module -avoid-version
+suicide_la_LIBADD = $(top_builddir)/lib/stonith/libstonith.la
+
+stonithscriptdir = $(stonith_plugindir)/stonith2
+
+stonithscript_SCRIPTS = ribcl.py
diff --git a/lib/plugins/stonith/apcmaster.c b/lib/plugins/stonith/apcmaster.c
new file mode 100644
index 0000000..09a56d3
--- /dev/null
+++ b/lib/plugins/stonith/apcmaster.c
@@ -0,0 +1,822 @@
+/*
+*
+* Copyright 2001 Mission Critical Linux, Inc.
+*
+* All Rights Reserved.
+*/
+/*
+ * Stonith module for APC Master Switch (AP9211)
+ *
+ * Copyright (c) 2001 Mission Critical Linux, Inc.
+ * author: mike ledoux <mwl@mclinux.com>
+ * author: Todd Wheeling <wheeling@mclinux.com>
+ * mangled by Sun Jiang Dong, <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * Based strongly on original code from baytech.c by Alan Robertson.
+ *
+ * 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
+ *
+ */
+
+/* Observations/Notes
+ *
+ * 1. The APC MasterSwitch, unlike the BayTech network power switch,
+ * accepts only one (telnet) connection/session at a time. When one
+ * session is active, any subsequent attempt to connect to the MasterSwitch
+ * will result in a connection refused/closed failure. In a cluster
+ * environment or other environment utilizing polling/monitoring of the
+ * MasterSwitch (from multiple nodes), this can clearly cause problems.
+ * Obviously the more nodes and the shorter the polling interval, the more
+ * frequently such errors/collisions may occur.
+ *
+ * 2. We observed that on busy networks where there may be high occurances
+ * of broadcasts, the MasterSwitch became unresponsive. In some
+ * configurations this necessitated placing the power switch onto a
+ * private subnet.
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "APC MasterSwitch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN apcmaster
+#define PIL_PLUGIN_S "apcmaster"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * apcmaster_new(const char *);
+static void apcmaster_destroy(StonithPlugin *);
+static const char * const * apcmaster_get_confignames(StonithPlugin *);
+static int apcmaster_set_config(StonithPlugin *, StonithNVpair *);
+static const char * apcmaster_getinfo(StonithPlugin * s, int InfoType);
+static int apcmaster_status(StonithPlugin * );
+static int apcmaster_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcmaster_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcmasterOps ={
+ apcmaster_new, /* Create new STONITH object */
+ apcmaster_destroy, /* Destroy STONITH object */
+ apcmaster_getinfo, /* Return STONITH info string */
+ apcmaster_get_confignames, /* Get configuration parameters */
+ apcmaster_set_config, /* Set configuration */
+ apcmaster_status, /* Return STONITH device status */
+ apcmaster_reset_req, /* Request a reset */
+ apcmaster_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcmasterOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have an AP9211. This code has been tested with this switch.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * user;
+ char * passwd;
+};
+
+static const char * pluginid = "APCMS-Stonith";
+static const char * NOTpluginID = "APCMS device has been destroyed";
+
+/*
+ * Different expect strings that we get from the APC MasterSwitch
+ */
+
+#define APCMSSTR "American Power Conversion"
+
+static struct Etoken EscapeChar[] = { {"Escape character is '^]'.", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken login[] = { {"User Name :", 0, 0}, {NULL,0,0}};
+static struct Etoken password[] = { {"Password :", 0, 0} ,{NULL,0,0}};
+static struct Etoken Prompt[] = { {"> ", 0, 0} ,{NULL,0,0}};
+static struct Etoken LoginOK[] = { {APCMSSTR, 0, 0}
+ , {"User Name :", 1, 0} ,{NULL,0,0}};
+static struct Etoken Separator[] = { {"-----", 0, 0} ,{NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Processing[] = { {"Press <ENTER> to continue", 0, 0}
+ , {"Enter 'YES' to continue", 1, 0}
+ , {NULL,0,0}};
+
+#include "stonith_config_xml.h"
+
+static const char *apcmasterXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int MS_connect_device(struct pluginDevice * ms);
+static int MSLogin(struct pluginDevice * ms);
+static int MSRobustLogin(struct pluginDevice * ms);
+static int MSNametoOutlet(struct pluginDevice*, const char * name);
+static int MSReset(struct pluginDevice*, int outletNum, const char * host);
+static int MSLogout(struct pluginDevice * ms);
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int apcmaster_onoff(struct pluginDevice*, int outletnum, const char * unitid
+, int request);
+#endif
+
+/* Login to the APC Master Switch */
+
+static int
+MSLogin(struct pluginDevice * ms)
+{
+ EXPECT(ms->rdfd, EscapeChar, 10);
+
+ /*
+ * We should be looking at something like this:
+ * User Name :
+ */
+ EXPECT(ms->rdfd, login, 10);
+ SEND(ms->wrfd, ms->user);
+ SEND(ms->wrfd, "\r");
+
+ /* Expect "Password :" */
+ EXPECT(ms->rdfd, password, 10);
+ SEND(ms->wrfd, ms->passwd);
+ SEND(ms->wrfd, "\r");
+
+ switch (StonithLookFor(ms->rdfd, LoginOK, 30)) {
+
+ case 0: /* Good! */
+ LOG(PIL_INFO, "Successful login to %s.", ms->idinfo);
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", ms->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+
+ return(S_OK);
+}
+
+/* Attempt to login up to 20 times... */
+
+static int
+MSRobustLogin(struct pluginDevice * ms)
+{
+ int rc = S_OOPS;
+ int j = 0;
+
+ for ( ; ; ) {
+ if (MS_connect_device(ms) == S_OK) {
+ rc = MSLogin(ms);
+ if( rc == S_OK ) {
+ break;
+ }
+ }
+ if ((++j) == 20) {
+ break;
+ } else {
+ sleep(1);
+ }
+ }
+
+ return rc;
+}
+
+/* Log out of the APC Master Switch */
+
+static
+int MSLogout(struct pluginDevice* ms)
+{
+ int rc;
+
+ /* Make sure we're in the right menu... */
+ /*SEND(ms->wrfd, "\033\033\033\033\033\033\033"); */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect "> " */
+ rc = StonithLookFor(ms->rdfd, Prompt, 5);
+
+ /* "4" is logout */
+ SEND(ms->wrfd, "4\r");
+
+ close(ms->wrfd);
+ close(ms->rdfd);
+ ms->wrfd = ms->rdfd = -1;
+
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+/* Reset (power-cycle) the given outlets */
+static int
+MSReset(struct pluginDevice* ms, int outletNum, const char *host)
+{
+ char unum[32];
+
+ /* Make sure we're in the top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Select requested outlet */
+ EXPECT(ms->rdfd, Prompt, 5);
+ snprintf(unum, sizeof(unum), "%i\r", outletNum);
+ SEND(ms->wrfd, unum);
+
+ /* Select menu 1 (Control Outlet) */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "1\r");
+
+ /* Select menu 3 (Immediate Reboot) */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "3\r");
+
+ /* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
+ retry:
+ switch (StonithLookFor(ms->rdfd, Processing, 5)) {
+ case 0: /* Got "Press <ENTER>" Do so */
+ SEND(ms->wrfd, "\r");
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(ms->wrfd, "YES\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+
+ LOG(PIL_INFO, "Host being rebooted: %s", host);
+
+ /* Expect ">" */
+ if (StonithLookFor(ms->rdfd, Prompt, 10) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host: %s", host);
+
+ /* Return to top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ return(S_OK);
+}
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int
+apcmaster_onoff(struct pluginDevice* ms, int outletNum, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "1\r" : "2\r");
+ int rc;
+
+ if ((rc = MSRobustLogin(ms) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }
+
+ /* Make sure we're in the top level menu */
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Select requested outlet */
+ snprintf(unum, sizeof(unum), "%d\r", outletNum);
+ SEND(ms->wrfd, unum);
+
+ /* Select menu 1 (Control Outlet) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Send ON/OFF command for given outlet */
+ SEND(ms->wrfd, onoff);
+
+ /* Expect "Press <ENTER> " or "Enter 'YES'" (if confirmation turned on) */
+ retry:
+ switch (StonithLookFor(ms->rdfd, Processing, 5)) {
+ case 0: /* Got "Press <ENTER>" Do so */
+ SEND(ms->wrfd, "\r");
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(ms->wrfd, "YES\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ EXPECT(ms->rdfd, Prompt, 10);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to MS outlet(s) %d turned %s", outletNum, onoff);
+ /* Pop back to main menu */
+ SEND(ms->wrfd, "\033\033\033\033\033\033\033\r");
+ return(S_OK);
+}
+#endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+MSNametoOutlet(struct pluginDevice* ms, const char * name)
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ int times = 0;
+ int ret = -1;
+
+ /* Verify that we're in the top-level menu */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+
+ /* Expect ">" */
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ EXPECT(ms->rdfd, Separator, 5);
+ EXPECT(ms->rdfd, CRNL, 5);
+ EXPECT(ms->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ times++;
+ NameMapping[0] = EOS;
+ SNARF(ms->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d- %23c",&sockno, sockname) == 2) {
+
+ char * last = sockname+23;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strcasecmp(name, sockname) == 0) {
+ ret = sockno;
+ }
+ }
+ } while (strlen(NameMapping) > 2 && times < 8);
+
+ /* Pop back out to the top level menu */
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ EXPECT(ms->rdfd, Prompt, 5);
+ SEND(ms->wrfd, "\033");
+ return(ret);
+}
+
+static int
+apcmaster_status(StonithPlugin *s)
+{
+ struct pluginDevice* ms;
+ int rc;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ms = (struct pluginDevice*) s;
+
+ if ((rc = MSRobustLogin(ms) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }
+
+ /* Expect ">" */
+ SEND(ms->wrfd, "\033\r");
+ EXPECT(ms->rdfd, Prompt, 5);
+
+ return(MSLogout(ms));
+}
+
+/*
+ * Return the list of hosts (outlet names) for the devices on this MS unit
+ */
+
+static char **
+apcmaster_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* ms;
+ unsigned int i;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ms = (struct pluginDevice*) s;
+
+ if (MSRobustLogin(ms) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(NULL);
+ }
+
+ /* Expect ">" */
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+
+ /* Request menu 1 (Device Control) */
+ SEND(ms->wrfd, "1\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ NULLEXPECT(ms->rdfd, Separator, 5);
+ NULLEXPECT(ms->rdfd, CRNL, 5);
+ NULLEXPECT(ms->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+ do {
+ int sockno;
+ char sockname[64];
+ NameMapping[0] = EOS;
+ NULLSNARF(ms->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d- %23c",&sockno, sockname) == 2) {
+
+ char * last = sockname+23;
+ char * nm;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if ((nm = (char*)STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ }
+ } while (strlen(NameMapping) > 2);
+
+ /* Pop back out to the top level menu */
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+ SEND(ms->wrfd, "\033");
+ NULLEXPECT(ms->rdfd, Prompt, 10);
+
+
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)MSLogout(ms);
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+ return(NULL);
+}
+
+/*
+ * Connect to the given MS device. We should add serial support here
+ * eventually...
+ */
+static int
+MS_connect_device(struct pluginDevice * ms)
+{
+ int fd = OurImports->OpenStreamSocket(ms->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ ms->rdfd = ms->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this StonithPlugin device.
+ */
+static int
+apcmaster_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ int lorc = 0;
+ struct pluginDevice* ms;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ms = (struct pluginDevice*) s;
+
+ if ((rc = MSRobustLogin(ms)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", ms->idinfo);
+ return(rc);
+ }else{
+ int noutlet;
+ noutlet = MSNametoOutlet(ms, host);
+ if (noutlet < 1) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , ms->device, host);
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ rc = apcmaster_onoff(ms, noutlet, host, request);
+ break;
+ case ST_POWEROFF:
+ rc = apcmaster_onoff(ms, noutlet, host, request);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = MSReset(ms, noutlet, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+ }
+
+ lorc = MSLogout(ms);
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Get the configuration parameters names
+ */
+static const char * const *
+apcmaster_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters
+ */
+static int
+apcmaster_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->device = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->passwd = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+static const char *
+apcmaster_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ms;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ms = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ms->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = ms->device;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC MasterSwitch (via telnet)\n"
+ "NOTE: The APC MasterSwitch accepts only one (telnet)\n"
+ "connection/session a time. When one session is active,\n"
+ "subsequent attempts to connect to the MasterSwitch"
+ " will fail.";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmasterXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * APC MasterSwitch StonithPlugin destructor...
+ */
+static void
+apcmaster_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ms;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ms = (struct pluginDevice *)s;
+
+ ms->pluginid = NOTpluginID;
+ if (ms->rdfd >= 0) {
+ close(ms->rdfd);
+ ms->rdfd = -1;
+ }
+ if (ms->wrfd >= 0) {
+ close(ms->wrfd);
+ ms->wrfd = -1;
+ }
+ if (ms->device != NULL) {
+ FREE(ms->device);
+ ms->device = NULL;
+ }
+ if (ms->user != NULL) {
+ FREE(ms->user);
+ ms->user = NULL;
+ }
+ if (ms->passwd != NULL) {
+ FREE(ms->passwd);
+ ms->passwd = NULL;
+ }
+ FREE(ms);
+}
+
+/* Create a new APC Master Switch StonithPlugin device. */
+
+static StonithPlugin *
+apcmaster_new(const char *subplugin)
+{
+ struct pluginDevice* ms = ST_MALLOCT(struct pluginDevice);
+
+ if (ms == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ms, 0, sizeof(*ms));
+ ms->pluginid = pluginid;
+ ms->pid = -1;
+ ms->rdfd = -1;
+ ms->wrfd = -1;
+ ms->user = NULL;
+ ms->device = NULL;
+ ms->passwd = NULL;
+ ms->idinfo = DEVICE;
+ ms->sp.s_ops = &apcmasterOps;
+
+ return(&(ms->sp));
+}
diff --git a/lib/plugins/stonith/apcmastersnmp.c b/lib/plugins/stonith/apcmastersnmp.c
new file mode 100644
index 0000000..a9eeaeb
--- /dev/null
+++ b/lib/plugins/stonith/apcmastersnmp.c
@@ -0,0 +1,890 @@
+/*
+ * Stonith module for APC Masterswitch (SNMP)
+ * Copyright (c) 2001 Andreas Piesk <a.piesk@gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+/* device ID */
+#define DEVICE "APC MasterSwitch (SNMP)"
+
+#include "stonith_plugin_common.h"
+#undef FREE /* defined by snmp stuff */
+
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
+# include <net-snmp/net-snmp-config.h>
+# include <net-snmp/net-snmp-includes.h>
+# include <net-snmp/agent/net-snmp-agent-includes.h>
+# define INIT_AGENT() init_master_agent()
+#else
+# include <ucd-snmp/ucd-snmp-config.h>
+# include <ucd-snmp/ucd-snmp-includes.h>
+# include <ucd-snmp/ucd-snmp-agent-includes.h>
+# ifndef NETSNMP_DS_APPLICATION_ID
+# define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID
+# endif
+# ifndef NETSNMP_DS_AGENT_ROLE
+# define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE
+# endif
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define INIT_AGENT() init_master_agent(161, NULL, NULL)
+#endif
+
+#define PIL_PLUGIN apcmastersnmp
+#define PIL_PLUGIN_S "apcmastersnmp"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define DEBUGCALL \
+ if (Debug) { \
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \
+ }
+
+static StonithPlugin * apcmastersnmp_new(const char *);
+static void apcmastersnmp_destroy(StonithPlugin *);
+static const char * const * apcmastersnmp_get_confignames(StonithPlugin *);
+static int apcmastersnmp_set_config(StonithPlugin *, StonithNVpair *);
+static const char * apcmastersnmp_getinfo(StonithPlugin * s, int InfoType);
+static int apcmastersnmp_status(StonithPlugin * );
+static int apcmastersnmp_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcmastersnmp_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcmastersnmpOps ={
+ apcmastersnmp_new, /* Create new STONITH object */
+ apcmastersnmp_destroy, /* Destroy STONITH object */
+ apcmastersnmp_getinfo, /* Return STONITH info string */
+ apcmastersnmp_get_confignames, /* Get configuration parameters */
+ apcmastersnmp_set_config, /* Set configuration */
+ apcmastersnmp_status, /* Return STONITH device status */
+ apcmastersnmp_reset_req, /* Request a reset */
+ apcmastersnmp_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+ DEBUGCALL;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcmastersnmpOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * APCMaster tested with APC Masterswitch 9212
+ */
+
+/* outlet commands / status codes */
+#define OUTLET_ON 1
+#define OUTLET_OFF 2
+#define OUTLET_REBOOT 3
+#define OUTLET_NO_CMD_PEND 2
+
+/* oids */
+#define OID_IDENT ".1.3.6.1.4.1.318.1.1.12.1.5.0"
+#define OID_NUM_OUTLETS ".1.3.6.1.4.1.318.1.1.12.1.8.0"
+#define OID_OUTLET_NAMES ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.%i"
+#define OID_OUTLET_STATE ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%i"
+#define OID_OUTLET_COMMAND_PENDING ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.%i"
+#define OID_OUTLET_REBOOT_DURATION ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.%i"
+
+/*
+ snmpset -c private -v1 172.16.0.32:161
+ ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1
+ The last octet in the OID is the plug number. The value can
+ be 1 thru 8 because there are 8 power plugs on this device.
+ The integer that can be set is as follows: 1=on, 2=off, and
+ 3=reset
+*/
+
+/* own defines */
+#define MAX_STRING 128
+#define ST_PORT "port"
+
+/* structur of stonith object */
+struct pluginDevice {
+ StonithPlugin sp; /* StonithPlugin object */
+ const char* pluginid; /* id of object */
+ const char* idinfo; /* type of device */
+ struct snmp_session* sptr; /* != NULL->session created */
+ char * hostname; /* masterswitch's hostname */
+ /* or ip addr */
+ int port; /* snmp port */
+ char * community; /* snmp community (r/w) */
+ int num_outlets; /* number of outlets */
+};
+
+/* for checking hardware (issue a warning if mismatch) */
+static const char* APC_tested_ident[] = {"AP9606","AP7920","AP7921","AP7900","AP_other_well_tested"};
+
+/* constant strings */
+static const char *pluginid = "APCMS-SNMP-Stonith";
+static const char *NOTpluginID = "APCMS SNMP device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number on which the SNMP server is running on the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *apcmastersnmpXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_COMMUNITY_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+static void APC_error(struct snmp_session *sptr, const char *fn
+, const char *msg);
+static struct snmp_session *APC_open(char *hostname, int port
+, char *community);
+static void *APC_read(struct snmp_session *sptr, const char *objname
+, int type);
+static int APC_write(struct snmp_session *sptr, const char *objname
+, char type, char *value);
+
+static void
+APC_error(struct snmp_session *sptr, const char *fn, const char *msg)
+{
+ int snmperr = 0;
+ int cliberr = 0;
+ char *errstr;
+
+ snmp_error(sptr, &cliberr, &snmperr, &errstr);
+ LOG(PIL_CRIT
+ , "%s: %s (cliberr: %i / snmperr: %i / error: %s)."
+ , fn, msg, cliberr, snmperr, errstr);
+ free(errstr);
+}
+
+
+/*
+ * creates a snmp session
+ */
+static struct snmp_session *
+APC_open(char *hostname, int port, char *community)
+{
+ static struct snmp_session session;
+ struct snmp_session *sptr;
+
+ DEBUGCALL;
+
+ /* create session */
+ snmp_sess_init(&session);
+
+ /* fill session */
+ session.peername = hostname;
+ session.version = SNMP_VERSION_1;
+ session.remote_port = port;
+ session.community = (u_char *)community;
+ session.community_len = strlen(community);
+ session.retries = 5;
+ session.timeout = 1000000;
+
+ /* open session */
+ sptr = snmp_open(&session);
+
+ if (sptr == NULL) {
+ APC_error(&session, __FUNCTION__, "cannot open snmp session");
+ }
+
+ /* return pointer to opened session */
+ return (sptr);
+}
+
+/*
+ * parse config
+ */
+
+/*
+ * read value of given oid and return it as string
+ */
+static void *
+APC_read(struct snmp_session *sptr, const char *objname, int type)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct variable_list *vars;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+ static char response_str[MAX_STRING];
+ static int response_int;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return NULL if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (NULL);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) {
+
+ /* get-request have no values */
+ snmp_add_null_var(pdu, name, namelen);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) {
+
+ /* request succeed, got valid response ? */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* go through the returned vars */
+ for (vars = resp->variables; vars;
+ vars = vars->next_variable) {
+
+ /* return response as string */
+ if ((vars->type == type) && (type == ASN_OCTET_STR)) {
+ memset(response_str, 0, MAX_STRING);
+ strncpy(response_str, (char *)vars->val.string,
+ MIN(vars->val_len, MAX_STRING));
+ snmp_free_pdu(resp);
+ return ((void *) response_str);
+ }
+ /* return response as integer */
+ if ((vars->type == type) && (type == ASN_INTEGER)) {
+ response_int = *vars->val.integer;
+ snmp_free_pdu(resp);
+ return ((void *) &response_int);
+ }
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free repsonse pdu (necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ APC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error: return nothing */
+ return (NULL);
+}
+
+/*
+ * write value of given oid
+ */
+static int
+APC_write(struct snmp_session *sptr, const char *objname, char type,
+ char *value)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return FALSE if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (FALSE);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) {
+
+ /* add to be written value to pdu */
+ snmp_add_var(pdu, name, namelen, type, value);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) {
+
+ /* go through the returned vars */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* request successful done */
+ snmp_free_pdu(resp);
+ return (TRUE);
+
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ APC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free pdu (again: necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ APC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error */
+ return (FALSE);
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+apcmastersnmp_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+ char *ident;
+ int i;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ if ((ident = APC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__);
+ return (S_ACCESS);
+ }
+
+ /* issue a warning if ident mismatches */
+ for(i=DIMOF(APC_tested_ident) -1; i >=0 ; i--) {
+ if (strcmp(ident, APC_tested_ident[i]) == 0) {
+ break;
+ }
+ }
+
+ if (i<0) {
+ LOG(PIL_WARN
+ , "%s: module not tested with this hardware '%s'."
+ , __FUNCTION__, ident);
+ }
+ /* status ok */
+ return (S_OK);
+}
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+apcmastersnmp_hostlist(StonithPlugin * s)
+{
+ char **hl;
+ struct pluginDevice *ad;
+ int j, h, num_outlets;
+ char *outlet_name;
+ char objname[MAX_STRING];
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ /* allocate memory for array of up to NUM_OUTLETS strings */
+ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+ /* clear hostlist array */
+ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *));
+ num_outlets = 0;
+
+ /* read NUM_OUTLETS values and put them into hostlist array */
+ for (j = 0; j < ad->num_outlets; ++j) {
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, j + 1);
+
+ /* read outlet name */
+ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR)) ==
+ NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, j+1);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+
+ /* Check whether the host is already listed */
+ for (h = 0; h < num_outlets; ++h) {
+ if (strcasecmp(hl[h],outlet_name) == 0)
+ break;
+ }
+
+ if (h >= num_outlets) {
+ /* put outletname in hostlist */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: added %s to hostlist."
+ , __FUNCTION__, outlet_name);
+ }
+
+ if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+ strdown(hl[num_outlets]);
+ num_outlets++;
+ }
+ }
+
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets."
+ , __FUNCTION__, num_outlets, j);
+ }
+ /* return list */
+ return (hl);
+}
+
+/*
+ * reset the host
+ */
+
+static int
+apcmastersnmp_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *ad;
+ char objname[MAX_STRING];
+ char value[MAX_STRING];
+ char *outlet_name;
+ int req_oid = OUTLET_REBOOT;
+ int expect_state = OUTLET_ON;
+ int i, h, num_outlets, outlet, reboot_duration, *state, bad_outlets;
+ int outlets[8]; /* Assume that one node is connected to a
+ maximum of 8 outlets */
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ num_outlets = 0;
+ reboot_duration = 0;
+ bad_outlets = 0;
+
+ /* read max. as->num_outlets values */
+ for (outlet = 1; outlet <= ad->num_outlets; outlet++) {
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_NAMES, outlet);
+
+ /* read outlet name */
+ if ((outlet_name = APC_read(ad->sptr, objname, ASN_OCTET_STR))
+ == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name);
+ }
+
+ /* found one */
+ if (strcasecmp(outlet_name, host) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found %s at outlet %d."
+ , __FUNCTION__, host, outlet);
+ }
+ /* Check that the outlet is not administratively down */
+
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+
+ /* get outlet's state */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read state for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ /* prepare oid */
+ snprintf(objname, MAX_STRING, OID_OUTLET_REBOOT_DURATION
+ , outlet);
+
+ /* read reboot duration of the port */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read reboot duration for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (num_outlets == 0) {
+ /* save the inital value of the first port */
+ reboot_duration = *state;
+ } else if (reboot_duration != *state) {
+ LOG(PIL_WARN, "%s: outlet %d has a different reboot duration!"
+ , __FUNCTION__, outlet);
+ if (reboot_duration < *state)
+ reboot_duration = *state;
+ }
+
+ /* Ok, add it to the list of outlets to control */
+ outlets[num_outlets]=outlet;
+ num_outlets++;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet);
+ }
+
+ /* host not found in outlet names */
+ if (num_outlets < 1) {
+ LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host);
+ return (S_BADHOST);
+ }
+
+
+ /* choose the OID for the stonith request */
+ switch (request) {
+ case ST_POWERON:
+ req_oid = OUTLET_ON;
+ expect_state = OUTLET_ON;
+ break;
+ case ST_POWEROFF:
+ req_oid = OUTLET_OFF;
+ expect_state = OUTLET_OFF;
+ break;
+ case ST_GENERIC_RESET:
+ req_oid = OUTLET_REBOOT;
+ expect_state = OUTLET_ON;
+ break;
+ default: break;
+ }
+
+ /* Turn them all off */
+
+ for (outlet=outlets[0], i=0 ; i < num_outlets; i++, outlet = outlets[i]) {
+ /* prepare objname */
+ snprintf(objname, MAX_STRING, OID_OUTLET_COMMAND_PENDING, outlet);
+
+ /* are there pending commands ? */
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read pending commands for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ if (*state != OUTLET_NO_CMD_PEND) {
+ LOG(PIL_CRIT, "%s: command pending.", __FUNCTION__);
+ return (S_RESETFAIL);
+ }
+
+ /* prepare objnames */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+ snprintf(value, MAX_STRING, "%i", req_oid);
+
+ /* send reboot cmd */
+ if (!APC_write(ad->sptr, objname, 'i', value)) {
+ LOG(PIL_CRIT
+ , "%s: cannot send reboot command for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ }
+
+ /* wait max. 2*reboot_duration for all outlets to go back on */
+ for (i = 0; i < reboot_duration << 1; i++) {
+
+ sleep(1);
+
+ bad_outlets = 0;
+ for (outlet=outlets[0], h=0 ; h < num_outlets; h++,
+ outlet = outlets[h]) {
+
+ /* prepare objname of the first outlet */
+ snprintf(objname, MAX_STRING, OID_OUTLET_STATE, outlet);
+ /* get outlet's state */
+
+ if ((state = APC_read(ad->sptr, objname, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read state for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+
+ if (*state != expect_state)
+ bad_outlets++;
+ }
+
+ if (bad_outlets == 0)
+ return (S_OK);
+ }
+
+ if (bad_outlets == num_outlets) {
+ /* reset failed */
+ LOG(PIL_CRIT, "%s: stonith operation for '%s' failed."
+ , __FUNCTION__, host);
+ return (S_RESETFAIL);
+ } else {
+ /* Not all outlets back on, but at least one; implies node was */
+ /* rebooted correctly */
+ LOG(PIL_WARN,"%s: Not all outlets in the expected state!"
+ , __FUNCTION__);
+ return (S_OK);
+ }
+}
+
+/*
+ * Get the configuration parameter names.
+ */
+
+static const char * const *
+apcmastersnmp_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters.
+ */
+
+static int
+apcmastersnmp_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ int * i;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_COMMUNITY, NULL}
+ , {NULL, NULL}
+ };
+
+ DEBUGCALL;
+ ERRIFWRONGDEV(s,S_INVAL);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->hostname = namestocopy[0].s_value;
+ sd->port = atoi(namestocopy[1].s_value);
+ PluginImports->mfree(namestocopy[1].s_value);
+ sd->community = namestocopy[2].s_value;
+
+ /* try to resolve the hostname/ip-address */
+ if (gethostbyname(sd->hostname) != NULL) {
+ /* init snmp library */
+ init_snmp("apcmastersnmp");
+
+ /* now try to get a snmp session */
+ if ((sd->sptr = APC_open(sd->hostname, sd->port, sd->community)) != NULL) {
+
+ /* ok, get the number of outlets from the masterswitch */
+ if ((i = APC_read(sd->sptr, OID_NUM_OUTLETS, ASN_INTEGER))
+ == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read number of outlets."
+ , __FUNCTION__);
+ return (S_ACCESS);
+ }
+ /* store the number of outlets */
+ sd->num_outlets = *i;
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: number of outlets: %i."
+ , __FUNCTION__, sd->num_outlets );
+ }
+
+ /* Everything went well */
+ return (S_OK);
+ }else{
+ LOG(PIL_CRIT, "%s: cannot create snmp session."
+ , __FUNCTION__);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d."
+ , __FUNCTION__, sd->hostname, h_errno);
+ }
+
+ /* not a valid config */
+ return (S_BADCONFIG);
+}
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+apcmastersnmp_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad;
+ const char *ret = NULL;
+
+ DEBUGCALL;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->hostname;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC MasterSwitch (via SNMP)\n"
+ "The APC MasterSwitch can accept multiple simultaneous SNMP clients";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmastersnmpXML;
+ break;
+
+ }
+ return ret;
+}
+
+
+/*
+ * APC StonithPlugin destructor...
+ */
+
+static void
+apcmastersnmp_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+
+ DEBUGCALL;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ad = (struct pluginDevice *) s;
+
+ ad->pluginid = NOTpluginID;
+
+ /* release snmp session */
+ if (ad->sptr != NULL) {
+ snmp_close(ad->sptr);
+ ad->sptr = NULL;
+ }
+
+ /* reset defaults */
+ if (ad->hostname != NULL) {
+ PluginImports->mfree(ad->hostname);
+ ad->hostname = NULL;
+ }
+ if (ad->community != NULL) {
+ PluginImports->mfree(ad->community);
+ ad->community = NULL;
+ }
+ ad->num_outlets = 0;
+
+ PluginImports->mfree(ad);
+}
+
+/*
+ * Create a new APC StonithPlugin device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+apcmastersnmp_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ DEBUGCALL;
+
+ /* no memory for stonith-object */
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ /* clear stonith-object */
+ memset(ad, 0, sizeof(*ad));
+
+ /* set defaults */
+ ad->pluginid = pluginid;
+ ad->sptr = NULL;
+ ad->hostname = NULL;
+ ad->community = NULL;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &apcmastersnmpOps;
+
+ /* return the object */
+ return (&(ad->sp));
+}
diff --git a/lib/plugins/stonith/apcmastersnmp.cfg.example b/lib/plugins/stonith/apcmastersnmp.cfg.example
new file mode 100644
index 0000000..76fea08
--- /dev/null
+++ b/lib/plugins/stonith/apcmastersnmp.cfg.example
@@ -0,0 +1,39 @@
+#
+# this is an example config for the stonith module apcmastersnmp
+#
+# 1. what does the fields on the line mean ?
+#
+# all parameters must be given on a single line. blank lines and lines
+# starting with '#' are ignored. only the first not ignored line will
+# be processed. all subsequent lines will be ignored. the different
+# fields must be seperated by white-spaces (blanks and/or tabs).
+#
+# the first field is the either the hostname or the ip address. the
+# hostname must be resolvable. the second fields specifies the snmp port
+# the masterswitch is listening. for snmp the default is 161. the last
+# field contains the so called 'community' string. this must be the same
+# as the one in the masterswitch configuration.
+#
+#
+# 2. how must the masterswitch be configured ?
+#
+# as said above, the community string must be set to the same value entered
+# in this config. the different outlets must be named after the connected
+# hosts. that means, the outlet names must be the same as the node names
+# in /etc/ha.d/ha.cf. the reset values should be set to reasonable values.
+#
+# the module DON'T configure the module in any way!
+#
+#
+# 3. how does the module work ?
+#
+# in case of a stonith the module receives the nodename of the host, which
+# should be reset. the module looks up this nodename in the list of outlet
+# names. that's why the names must be identical (see 2.). if it finds the
+# name, it'll reset the appropriate outlet using the configured values
+# (eg. delay, duration). then the module waits for the outlet to coming
+# up. if it comes up, a successful stonith will be reported back. otherwise
+# the stonith failed and a failure code will be returned.
+#
+
+192.168.1.110 161 private
diff --git a/lib/plugins/stonith/apcsmart.c b/lib/plugins/stonith/apcsmart.c
new file mode 100644
index 0000000..18d1612
--- /dev/null
+++ b/lib/plugins/stonith/apcsmart.c
@@ -0,0 +1,1028 @@
+/*
+ * Stonith module for APCSmart Stonith device
+ * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
+ * 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
+ *
+ * Original version of this UPS code was taken from:
+ * 'Network UPS Tools' by Russell Kroll <rkroll@exploits.org>
+ * homepage: http://www.networkupstools.org/
+ *
+ * Significantly mangled by Alan Robertson <alanr@unix.sh>
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "APCSmart"
+
+#include "stonith_plugin_common.h"
+
+/*
+ * APCSmart (tested with old 900XLI, APC SmartUPS 700 and SmartUPS-1000)
+ *
+ * The reset is a combined reset: "S" and "@000"
+ * The "S" command tells the ups that if it is on-battery, it should
+ * remain offline until the power is back.
+ * If that command is not accepted, the "@000" command will be sent
+ * to tell the ups to turn off and back on right away.
+ * In both cases, if the UPS supports a 20 second shutdown grace
+ * period (such as on the 900XLI), the shutdown will delay that long,
+ * otherwise the shutdown will happen immediately (the code searches
+ * for the smallest possible delay).
+ */
+
+#define CFG_FILE "/etc/ha.d/apcsmart.cfg"
+
+#define MAX_DEVICES 1
+
+#define SERIAL_TIMEOUT 3 /* timeout in sec */
+#define SEND_DELAY 50000 /* in microseconds */
+#define ENDCHAR 10 /* use LF */
+#define MAX_STRING 512
+#define MAX_DELAY_STRING 16
+#define SWITCH_TO_NEXT_VAL "-" /* APC cmd for cycling through
+ * the values
+ */
+
+#define CMD_SMART_MODE "Y"
+#define RSP_SMART_MODE "SM"
+#define CMD_GET_STATUS "Q"
+#define RSP_GET_STATUS NULL
+#define CMD_RESET "S" /* turn off & stay off if on battery */
+#define CMD_RESET2 "@000" /* turn off & immediately turn on */
+#define RSP_RESET "*" /* RESET response from older models */
+#define RSP_RESET2 "OK" /* RESET response from newer models */
+#define RSP_NA "NA"
+#define CMD_READREG1 "~"
+#define CMD_OFF "Z"
+#define CMD_ON "\016" /* (control-n) */
+#define CMD_SHUTDOWN_DELAY "p"
+#define CMD_WAKEUP_DELAY "r"
+
+#define CR 13
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid; /* of object */
+ const char * idinfo; /* type of device */
+ char ** hostlist; /* served by the device (only 1) */
+ int hostcount;/* of hosts (1) */
+ char * upsdev; /* */
+ int upsfd; /* for serial port */
+ int retries;
+ char shutdown_delay[MAX_DELAY_STRING];
+ char old_shutdown_delay[MAX_DELAY_STRING];
+ char wakeup_delay[MAX_DELAY_STRING];
+ char old_wakeup_delay[MAX_DELAY_STRING];
+};
+
+/* saving old settings */
+/* FIXME! These should be part of pluginDevice struct above */
+static struct termios old_tio;
+
+static int f_serialtimeout; /* flag for timeout */
+static const char *pluginid = "APCSmart-Stonith";
+static const char *NOTpluginID = "APCSmart device has been destroyed";
+
+/*
+ * stonith prototypes
+ */
+
+#define PIL_PLUGIN apcsmart
+#define PIL_PLUGIN_S "apcsmart"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * apcsmart_new(const char *);
+static void apcsmart_destroy(StonithPlugin *);
+static const char * const * apcsmart_get_confignames(StonithPlugin*);
+static int apcsmart_set_config(StonithPlugin *, StonithNVpair*);
+static const char * apcsmart_get_info(StonithPlugin * s, int InfoType);
+static int apcsmart_status(StonithPlugin * );
+static int apcsmart_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** apcsmart_hostlist(StonithPlugin *);
+
+static struct stonith_ops apcsmartOps ={
+ apcsmart_new, /* Create new STONITH object */
+ apcsmart_destroy, /* Destroy STONITH object */
+ apcsmart_get_info, /* Return STONITH info string */
+ apcsmart_get_confignames, /* Return STONITH info string */
+ apcsmart_set_config, /* Get configuration from NVpairs */
+ apcsmart_status, /* Return STONITH device status */
+ apcsmart_reset_req, /* Request a reset */
+ apcsmart_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &apcsmartOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#include "stonith_config_xml.h"
+
+static const char *apcsmartXML =
+ XML_PARAMETERS_BEGIN
+ XML_TTYDEV_PARM
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+int APC_open_serialport(const char *port, speed_t speed);
+void APC_close_serialport(const char *port, int upsfd);
+void APC_sh_serial_timeout(int sig);
+int APC_send_cmd(int upsfd, const char *cmd);
+int APC_recv_rsp(int upsfd, char *rsp);
+int APC_enter_smartmode(int upsfd);
+int APC_set_ups_var(int upsfd, const char *cmd, char *newval);
+int APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay);
+int APC_init( struct pluginDevice *ad );
+void APC_deinit( struct pluginDevice *ad );
+
+/*
+ * Signal handler for serial port timeouts
+ */
+
+void
+APC_sh_serial_timeout(int sig)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: serial port timed out.", __FUNCTION__);
+ }
+
+ f_serialtimeout = TRUE;
+
+ return;
+}
+
+/*
+ * Open serial port and set it to b2400
+ */
+
+int
+APC_open_serialport(const char *port, speed_t speed)
+{
+ struct termios tio;
+ int fd;
+ int rc;
+ int errno_save;
+ int fflags;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if ((rc = OurImports->TtyLock(port)) < 0) {
+ LOG(PIL_CRIT, "%s: Could not lock tty %s [rc=%d]."
+ , __FUNCTION__, port, rc);
+ return -1;
+ }
+
+ STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
+ alarm(SERIAL_TIMEOUT);
+ f_serialtimeout = FALSE;
+
+ fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
+ errno_save = errno;
+
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ if (fd < 0) {
+ LOG(PIL_CRIT, "%s: Open of %s %s [%s].", __FUNCTION__
+ , port
+ , f_serialtimeout ? "timed out" : "failed"
+ , strerror(errno_save));
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ if ((fflags = fcntl(fd, F_GETFL)) < 0
+ || fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
+ LOG(PIL_CRIT, "%s: Setting flags on %s failed [%s]."
+ , __FUNCTION__
+ , port
+ , strerror(errno_save));
+ close(fd);
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ if (tcgetattr(fd, &old_tio) < 0) {
+ LOG(PIL_CRIT, "%s: tcgetattr of %s failed [%s].", __FUNCTION__
+ , port
+ , strerror(errno));
+ close(fd);
+ OurImports->TtyUnlock(port);
+ return -1;
+ }
+
+ memcpy(&tio, &old_tio, sizeof(struct termios));
+ tio.c_cflag = CS8 | CLOCAL | CREAD;
+ tio.c_iflag = IGNPAR;
+ tio.c_oflag = 0;
+ tio.c_lflag = 0;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+
+ cfsetispeed(&tio, speed);
+ cfsetospeed(&tio, speed);
+
+ tcflush(fd, TCIOFLUSH);
+ tcsetattr(fd, TCSANOW, &tio);
+
+ return (fd);
+}
+
+/*
+ * Close serial port and restore old settings
+ */
+
+void
+APC_close_serialport(const char *port, int upsfd)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ if (upsfd < 0) {
+ return;
+ }
+
+ tcflush(upsfd, TCIFLUSH);
+ tcsetattr(upsfd, TCSANOW, &old_tio);
+ close(upsfd);
+ if (port != NULL) {
+ OurImports->TtyUnlock(port);
+ }
+}
+
+/*
+ * Send a command to the ups
+ */
+
+int
+APC_send_cmd(int upsfd, const char *cmd)
+{
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s(\"%s\")", __FUNCTION__, cmd);
+ }
+
+ tcflush(upsfd, TCIFLUSH);
+ for (i = strlen(cmd); i > 0; i--) {
+ if (write(upsfd, cmd++, 1) != 1) {
+ return (S_ACCESS);
+ }
+
+ usleep(SEND_DELAY);
+ }
+ return (S_OK);
+}
+
+/*
+ * Get the response from the ups
+ */
+
+int
+APC_recv_rsp(int upsfd, char *rsp)
+{
+ char *p = rsp;
+ char inp;
+ int num = 0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ *p = '\0';
+
+ STONITH_SIGNAL(SIGALRM, APC_sh_serial_timeout);
+
+ alarm(SERIAL_TIMEOUT);
+
+ while (num < MAX_STRING) {
+
+ if (read(upsfd, &inp, 1) == 1) {
+
+ /* shutdown sends only a '*' without LF */
+ if ((inp == '*') && (num == 0)) {
+ *p++ = inp;
+ num++;
+ inp = ENDCHAR;
+ }
+
+ if (inp == ENDCHAR) {
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+
+ *p = '\0';
+ if (Debug) {
+ LOG(PIL_DEBUG, "return(\"%s\")/*%s*/;"
+ , rsp, __FUNCTION__);
+ }
+ return (S_OK);
+ }
+
+ if (inp != CR) {
+ *p++ = inp;
+ num++;
+ }
+ }else{
+ alarm(0);
+ STONITH_IGNORE_SIG(SIGALRM);
+ *p = '\0';
+ LOG(PIL_DEBUG, "%s: %s.", __FUNCTION__,
+ f_serialtimeout ? "timeout" :
+ "can't access device" );
+ return (f_serialtimeout ? S_TIMEOUT : S_ACCESS);
+ }
+ }
+ return (S_ACCESS);
+}
+
+/*
+ * Enter smart mode
+ */
+
+int
+APC_enter_smartmode(int upsfd)
+{
+ int rc;
+ char resp[MAX_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ strcpy(resp, RSP_SMART_MODE);
+
+ if (((rc = APC_send_cmd(upsfd, CMD_SMART_MODE)) == S_OK)
+ && ((rc = APC_recv_rsp(upsfd, resp)) == S_OK)
+ && (strcmp(RSP_SMART_MODE, resp) == 0)) {
+ return (S_OK);
+ }
+
+ return (S_ACCESS);
+}
+
+/*
+ * Set a value in the hardware using the <cmdchar> '-' (repeat) approach
+ */
+
+int
+APC_set_ups_var(int upsfd, const char *cmd, char *newval)
+{
+ char resp[MAX_STRING];
+ char orig[MAX_STRING];
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
+ return (rc);
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: var '%s' original val %s"
+ , __FUNCTION__, cmd, orig);
+ }
+
+ if (strcmp(orig, newval) == 0) {
+ return (S_OK); /* already set */
+ }
+
+ *resp = '\0';
+
+ while (strcmp(resp, orig) != 0) {
+ if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (strcmp(resp, newval) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: var '%s' set to %s"
+ , __FUNCTION__, cmd, newval);
+ }
+
+ strcpy(newval, orig); /* return the old value */
+ return (S_OK); /* got it */
+ }
+ }
+
+ LOG(PIL_CRIT, "%s(): Could not set variable '%s' to %s!"
+ , __FUNCTION__, cmd, newval);
+ LOG(PIL_CRIT, "%s(): This UPS may not support STONITH :-("
+ , __FUNCTION__);
+
+ return (S_OOPS);
+}
+
+/*
+ * Query the smallest delay supported by the hardware using the
+ * <cmdchar> '-' (repeat) approach and looping through all possible values,
+ * saving the smallest
+ */
+
+int
+APC_get_smallest_delay(int upsfd, const char *cmd, char *smdelay)
+{
+ char resp[MAX_DELAY_STRING];
+ char orig[MAX_DELAY_STRING];
+ int delay, smallest;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, orig)) != S_OK)) {
+ return (rc);
+ }
+
+ smallest = atoi(orig);
+ strcpy(smdelay, orig);
+
+ *resp = '\0';
+
+ /* search for smallest delay; need to loop through all possible
+ * values so that we leave delay the way we found it */
+ while (strcmp(resp, orig) != 0) {
+ if (((rc = APC_send_cmd(upsfd, SWITCH_TO_NEXT_VAL)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if (((rc = APC_enter_smartmode(upsfd)) != S_OK)
+ || ((rc = APC_send_cmd(upsfd, cmd)) != S_OK)
+ || ((rc = APC_recv_rsp(upsfd, resp)) != S_OK)) {
+ return (rc);
+ }
+
+ if ((delay = atoi(resp)) < smallest) {
+ smallest = delay;
+ strcpy(smdelay, resp);
+ }
+ }
+
+ return (S_OK);
+}
+
+/*
+ * Initialize the ups
+ */
+
+int
+APC_init(struct pluginDevice *ad)
+{
+ int upsfd;
+ char value[MAX_DELAY_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ /* if ad->upsfd != -1 device has already been configured. */
+ /* Just enter smart mode again because otherwise a SmartUPS-1000 */
+ /* has been observed to sometimes not respond. */
+ if(ad->upsfd >= 0) {
+ if(APC_enter_smartmode(ad->upsfd) != S_OK) {
+ return(S_OOPS);
+ }
+ return S_OK;
+ }
+
+ /* open serial port and store the fd in ad->upsfd */
+ if ((upsfd = APC_open_serialport(ad->upsdev, B2400)) == -1) {
+ return S_OOPS;
+ }
+
+ /* switch into smart mode */
+ if (APC_enter_smartmode(upsfd) != S_OK) {
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+
+ /* get the smallest possible delays for this particular hardware */
+ if (APC_get_smallest_delay(upsfd, CMD_SHUTDOWN_DELAY
+ , ad->shutdown_delay) != S_OK
+ || APC_get_smallest_delay(upsfd, CMD_WAKEUP_DELAY
+ , ad->wakeup_delay) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't retrieve smallest delay from UPS"
+ , __FUNCTION__);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+
+ /* get the old settings and store them */
+ strcpy(value, ad->shutdown_delay);
+ if (APC_set_ups_var(upsfd, CMD_SHUTDOWN_DELAY, value) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't set shutdown delay to %s"
+ , __FUNCTION__, ad->shutdown_delay);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+ strcpy(ad->old_shutdown_delay, value);
+ strcpy(value, ad->wakeup_delay);
+ if (APC_set_ups_var(upsfd, CMD_WAKEUP_DELAY, value) != S_OK) {
+ LOG(PIL_CRIT, "%s: couldn't set wakeup delay to %s"
+ , __FUNCTION__, ad->wakeup_delay);
+ APC_close_serialport(ad->upsdev, upsfd);
+ ad->upsfd = -1;
+ return S_OOPS;
+ }
+ strcpy(ad->old_wakeup_delay, value);
+
+ ad->upsfd = upsfd;
+ return S_OK;
+}
+
+/*
+ * Restore original settings and close the port
+ */
+
+void
+APC_deinit(struct pluginDevice *ad)
+{
+ APC_enter_smartmode( ad->upsfd );
+
+ APC_set_ups_var(ad->upsfd, CMD_SHUTDOWN_DELAY, ad->old_shutdown_delay);
+ APC_set_ups_var(ad->upsfd, CMD_WAKEUP_DELAY, ad->old_wakeup_delay);
+
+ /* close serial port */
+ if (ad->upsfd >= 0) {
+ APC_close_serialport(ad->upsdev, ad->upsfd);
+ ad->upsfd = -1;
+ }
+}
+static const char * const *
+apcsmart_get_confignames(StonithPlugin* sp)
+{
+ static const char * names[] = {ST_TTYDEV, ST_HOSTLIST, NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+/*
+ * Stash away the config info we've been given...
+ */
+
+static int
+apcsmart_set_config(StonithPlugin * s, StonithNVpair* list)
+{
+ struct pluginDevice * ad = (struct pluginDevice*)s;
+ StonithNamesToGet namestocopy [] =
+ { {ST_TTYDEV, NULL}
+ , {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ ad->upsdev = namestocopy[0].s_value;
+ ad->hostlist = OurImports->StringToHostList(namestocopy[1].s_value);
+ FREE(namestocopy[1].s_value);
+
+ if (ad->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (ad->hostcount = 0; ad->hostlist[ad->hostcount]
+ ; ad->hostcount++) {
+ strdown(ad->hostlist[ad->hostcount]);
+ }
+ if (access(ad->upsdev, R_OK|W_OK|F_OK) < 0) {
+ LOG(PIL_CRIT,"Cannot access tty [%s]", ad->upsdev);
+ return S_BADCONFIG;
+ }
+
+ return ad->hostcount ? S_OK : S_BADCONFIG;
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+apcsmart_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+ char resp[MAX_STRING];
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+
+ /* get status */
+ if (((rc = APC_init( ad )) == S_OK)
+ && ((rc = APC_send_cmd(ad->upsfd, CMD_GET_STATUS)) == S_OK)
+ && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)) {
+ return (S_OK); /* everything ok. */
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: failed, rc=%d.", __FUNCTION__, rc);
+ }
+ return (rc);
+}
+
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+apcsmart_hostlist(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ ERRIFNOTCONFIGED(s,NULL);
+
+ return OurImports->CopyHostList((const char **)(void*)ad->hostlist);
+}
+
+static gboolean
+apcsmart_RegisterBitsSet(struct pluginDevice * ad, int nreg, unsigned bits
+, gboolean* waserr)
+{
+ const char* reqregs[4] = {"?", "~", "'", "8"};
+ unsigned regval;
+ char resp[MAX_STRING];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+
+ if (APC_enter_smartmode(ad->upsfd) != S_OK
+ || APC_send_cmd(ad->upsfd, reqregs[nreg]) != S_OK
+ || APC_recv_rsp(ad->upsfd, resp) != S_OK
+ || (sscanf(resp, "%02x", &regval) != 1)) {
+ if (waserr){
+ *waserr = TRUE;
+ }
+ return FALSE;
+ }
+ if (waserr){
+ *waserr = FALSE;
+ }
+ return ((regval & bits) == bits);
+}
+
+#define apcsmart_IsPoweredOff(ad, err) apcsmart_RegisterBitsSet(ad,1,0x40,err)
+#define apcsmart_ResetHappening(ad,err) apcsmart_RegisterBitsSet(ad,3,0x08,err)
+
+
+static int
+apcsmart_ReqOnOff(struct pluginDevice * ad, int request)
+{
+ const char * cmdstr;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ cmdstr = (request == ST_POWEROFF ? CMD_OFF : CMD_ON);
+ /* enter smartmode, send on/off command */
+ if ((rc =APC_enter_smartmode(ad->upsfd)) != S_OK
+ || (rc = APC_send_cmd(ad->upsfd, cmdstr)) != S_OK) {
+ return rc;
+ }
+ sleep(2);
+ if ((rc = APC_send_cmd(ad->upsfd, cmdstr)) == S_OK) {
+ gboolean ison;
+ gboolean waserr;
+ sleep(1);
+ ison = !apcsmart_IsPoweredOff(ad, &waserr);
+ if (waserr) {
+ return S_RESETFAIL;
+ }
+ if (request == ST_POWEROFF) {
+ return ison ? S_RESETFAIL : S_OK;
+ }else{
+ return ison ? S_OK : S_RESETFAIL;
+ }
+ }
+ return rc;
+}
+
+/*
+ * reset the host
+ */
+
+static int
+apcsmart_ReqGenericReset(struct pluginDevice *ad)
+{
+ char resp[MAX_STRING];
+ int rc = S_RESETFAIL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ /* send reset command(s) */
+ if (((rc = APC_init(ad)) == S_OK)
+ && ((rc = APC_send_cmd(ad->upsfd, CMD_RESET)) == S_OK)) {
+ if (((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
+ && (strcmp(resp, RSP_RESET) == 0
+ || strcmp(resp, RSP_RESET2) == 0)) {
+ /* first kind of reset command was accepted */
+ } else if (((rc = APC_send_cmd(ad->upsfd, CMD_RESET2)) == S_OK)
+ && ((rc = APC_recv_rsp(ad->upsfd, resp)) == S_OK)
+ && (strcmp(resp, RSP_RESET) == 0
+ || strcmp(resp, RSP_RESET2) == 0)) {
+ /* second kind of command was accepted */
+ } else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "APC: neither reset command "
+ "was accepted");
+ }
+ rc = S_RESETFAIL;
+ }
+ }
+ if (rc == S_OK) {
+ /* we wait grace period + up to 10 seconds after shutdown */
+ int maxdelay = atoi(ad->shutdown_delay)+10;
+ int j;
+
+ for (j=0; j < maxdelay; ++j) {
+ gboolean err;
+ if (apcsmart_ResetHappening(ad, &err)) {
+ return err ? S_RESETFAIL : S_OK;
+ }
+ sleep(1);
+ }
+ LOG(PIL_CRIT, "%s: timed out waiting for reset to end."
+ , __FUNCTION__);
+ return S_RESETFAIL;
+
+ }else{
+ if (strcmp(resp, RSP_NA) == 0){
+ gboolean iserr;
+ /* This means it's currently powered off */
+ /* or busy on a previous command... */
+ if (apcsmart_IsPoweredOff(ad, &iserr)) {
+ if (iserr) {
+ LOG(PIL_DEBUG, "%s: power off "
+ "detection failed.", __FUNCTION__);
+ return S_RESETFAIL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "APC: was powered off, "
+ "powering back on.");
+ }
+ return apcsmart_ReqOnOff(ad, ST_POWERON);
+ }
+ }
+ }
+ strcpy(resp, "?");
+
+ /* reset failed */
+
+ return S_RESETFAIL;
+}
+
+static int
+apcsmart_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ char ** hl;
+ int b_found=FALSE;
+ struct pluginDevice * ad = (struct pluginDevice *) s;
+ int rc;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ if (host == NULL) {
+ LOG(PIL_CRIT, "%s: invalid hostname argument.", __FUNCTION__);
+ return (S_INVAL);
+ }
+
+ /* look through the hostlist */
+ hl = ad->hostlist;
+
+ while (*hl && !b_found ) {
+ if( strcasecmp( *hl, host ) == 0 ) {
+ b_found = TRUE;
+ break;
+ }else{
+ ++hl;
+ }
+ }
+
+ /* host not found in hostlist */
+ if( !b_found ) {
+ LOG(PIL_CRIT, "%s: host '%s' not in hostlist."
+ , __FUNCTION__, host);
+ return S_BADHOST;
+ }
+ if ((rc = APC_init(ad)) != S_OK) {
+ return rc;
+ }
+
+ if (request == ST_POWERON || request == ST_POWEROFF) {
+ return apcsmart_ReqOnOff(ad, request);
+ }
+ return apcsmart_ReqGenericReset(ad);
+}
+
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+apcsmart_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+ const char *ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->upsdev;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "APC Smart UPS\n"
+ " (via serial port - NOT USB!). \n"
+ " Works with higher-end APC UPSes, like\n"
+ " Back-UPS Pro, Smart-UPS, Matrix-UPS, etc.\n"
+ " (Smart-UPS may have to be >= Smart-UPS 700?).\n"
+ " See http://www.networkupstools.org/protocols/apcsmart.html\n"
+ " for protocol compatibility details.";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.apc.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcsmartXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * APC Stonith destructor...
+ */
+
+static void
+apcsmart_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad = (struct pluginDevice *) s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ VOIDERRIFWRONGDEV(s);
+
+ if (ad->upsfd >= 0 && ad->upsdev) {
+ APC_deinit( ad );
+ }
+
+ ad->pluginid = NOTpluginID;
+
+ if (ad->hostlist) {
+ stonith_free_hostlist(ad->hostlist);
+ ad->hostlist = NULL;
+ }
+ if (ad->upsdev != NULL) {
+ FREE(ad->upsdev);
+ ad->upsdev = NULL;
+ }
+
+ ad->hostcount = -1;
+ ad->upsfd = -1;
+
+ FREE(ad);
+
+}
+
+/*
+ * Create a new APC Stonith device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+apcsmart_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ memset(ad, 0, sizeof(*ad));
+
+ ad->pluginid = pluginid;
+ ad->hostlist = NULL;
+ ad->hostcount = -1;
+ ad->upsfd = -1;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &apcsmartOps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: returning successfully.", __FUNCTION__);
+ }
+ return &(ad->sp);
+}
diff --git a/lib/plugins/stonith/apcsmart.cfg.example b/lib/plugins/stonith/apcsmart.cfg.example
new file mode 100644
index 0000000..278f925
--- /dev/null
+++ b/lib/plugins/stonith/apcsmart.cfg.example
@@ -0,0 +1 @@
+/dev/ups hostname
diff --git a/lib/plugins/stonith/baytech.c b/lib/plugins/stonith/baytech.c
new file mode 100644
index 0000000..33093ad
--- /dev/null
+++ b/lib/plugins/stonith/baytech.c
@@ -0,0 +1,924 @@
+/*
+ * Stonith module for BayTech Remote Power Controllers (RPC-x devices)
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#define DEVICE "BayTech power switch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN baytech
+#define PIL_PLUGIN_S "baytech"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * baytech_new(const char *);
+static void baytech_destroy(StonithPlugin *);
+static int baytech_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * baytech_get_confignames(StonithPlugin * s);
+static const char * baytech_get_info(StonithPlugin * s, int InfoType);
+static int baytech_status(StonithPlugin *);
+static int baytech_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** baytech_hostlist(StonithPlugin *);
+
+static struct stonith_ops baytechOps ={
+ baytech_new, /* Create new STONITH object */
+ baytech_destroy, /* Destroy STONITH object */
+ baytech_get_info, /* Return STONITH info string */
+ baytech_get_confignames, /* Return STONITH config vars */
+ baytech_set_config, /* set configuration from vars */
+ baytech_status, /* Return STONITH device status */
+ baytech_reset_req, /* Request a reset */
+ baytech_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+#define MAXOUTLET 32
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &baytechOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have an RPC-5. This code has been tested with this switch.
+ *
+ * The BayTech switches are quite nice, but the dialogues are a bit of a
+ * pain for mechanical parsing.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * unitid;
+ const struct BayTechModelInfo* modelinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * user;
+ char * passwd;
+};
+
+struct BayTechModelInfo {
+ const char * type; /* Baytech model info */
+ size_t socklen; /* Length of socket name string */
+ struct Etoken * expect; /* Expect string before outlet list */
+};
+
+static int parse_socket_line(struct pluginDevice*,const char *
+, int *, char *);
+
+static const char * pluginid = "BayTech-Stonith";
+static const char * NOTpluginID = "BayTech device has been destroyed";
+
+/*
+ * Different expect strings that we get from the Baytech
+ * Remote Power Controllers...
+ */
+
+#define BAYTECHASSOC "Bay Technical Associates"
+
+static struct Etoken BayTechAssoc[] = { {BAYTECHASSOC, 0, 0}, {NULL,0,0}};
+static struct Etoken UnitId[] = { {"Unit ID: ", 0, 0}, {NULL,0,0}};
+static struct Etoken login[] = { {"username>", 0, 0} ,{NULL,0,0}};
+static struct Etoken password[] = { {"password>", 0, 0}
+ , {"username>", 0, 0} ,{NULL,0,0}};
+static struct Etoken Selection[] = { {"election>", 0, 0} ,{NULL,0,0}};
+static struct Etoken RPC[] = { {"RPC", 0, 0} ,{NULL,0,0}};
+static struct Etoken LoginOK[] = { {"RPC", 0, 0}, {"Invalid password", 1, 0}
+ , {NULL,0,0}};
+static struct Etoken GTSign[] = { {">", 0, 0} ,{NULL,0,0}};
+static struct Etoken Menu[] = { {"Menu:", 0, 0} ,{NULL,0,0}};
+static struct Etoken Temp[] = { {"emperature: ", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken Break[] = { {"Status", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken PowerApplied[] = { {"ower applied to outlet", 0, 0}
+ , {NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Rebooting[] = { {"ebooting selected outlet", 0, 0}
+ , {"(Y/N)>", 1, 0}
+ , {"already off.", 2, 0}
+ , {NULL,0,0}};
+
+static struct Etoken TurningOnOff[] = { {"RPC", 0, 0}
+ , {"(Y/N)>", 1, 0}
+ , {"already ", 2, 0}
+ , {NULL,0,0}};
+
+
+static struct BayTechModelInfo ModelInfo [] = {
+ {"BayTech RPC-5", 18, Temp},/* This first model will be the default */
+ {"BayTech RPC-3", 10, Break},
+ {"BayTech RPC-3A", 10, Break},
+ {NULL, 0, NULL},
+};
+
+#include "stonith_config_xml.h"
+
+static const char *baytechXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int RPC_connect_device(struct pluginDevice * bt);
+static int RPCLogin(struct pluginDevice * bt);
+static int RPCRobustLogin(struct pluginDevice * bt);
+static int RPCNametoOutletList(struct pluginDevice*, const char * name
+, int outletlist[]);
+static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
+static int RPCLogout(struct pluginDevice * bt);
+
+
+static int RPC_onoff(struct pluginDevice*, int unitnum, const char * unitid
+, int request);
+
+/* Login to the Baytech Remote Power Controller (RPC) */
+
+static int
+RPCLogin(struct pluginDevice * bt)
+{
+ char IDinfo[128];
+ static char IDbuf[128];
+ char * idptr = IDinfo;
+ char * delim;
+ int j;
+
+ EXPECT(bt->rdfd, RPC, 10);
+
+ /* Look for the unit type info */
+ if (EXPECT_TOK(bt->rdfd, BayTechAssoc, 2, IDinfo
+ , sizeof(IDinfo), Debug) < 0) {
+ LOG(PIL_CRIT, "No initial response from %s.", bt->idinfo);
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ idptr += strspn(idptr, WHITESPACE);
+ /*
+ * We should be looking at something like this:
+ * RPC-5 Telnet Host
+ * Revision F 4.22, (C) 1999
+ * Bay Technical Associates
+ */
+
+ /* Truncate the result after the RPC-5 part */
+ if ((delim = strchr(idptr, ' ')) != NULL) {
+ *delim = EOS;
+ }
+ snprintf(IDbuf, sizeof(IDbuf), "BayTech RPC%s", idptr);
+ REPLSTR(bt->idinfo, IDbuf);
+ if (bt->idinfo == NULL) {
+ return(S_OOPS);
+ }
+
+ bt->modelinfo = &ModelInfo[0];
+
+ for (j=0; ModelInfo[j].type != NULL; ++j) {
+ /*
+ * TIMXXX -
+ * Look at device ID as this really describes the model.
+ */
+ if (strcasecmp(ModelInfo[j].type, IDbuf) == 0) {
+ bt->modelinfo = &ModelInfo[j];
+ break;
+ }
+ }
+
+ /* Look for the unit id info */
+ EXPECT(bt->rdfd, UnitId, 10);
+ SNARF(bt->rdfd, IDbuf, 2);
+ delim = IDbuf + strcspn(IDbuf, WHITESPACE);
+ *delim = EOS;
+ REPLSTR(bt->unitid, IDbuf);
+ if (bt->unitid == NULL) {
+ return(S_OOPS);
+ }
+
+ /* Expect "username>" */
+ EXPECT(bt->rdfd, login, 2);
+
+ SEND(bt->wrfd, bt->user);
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "password>" */
+
+ switch (StonithLookFor(bt->rdfd, password, 5)) {
+ case 0: /* Good! */
+ break;
+
+ case 1: /* OOPS! got another username prompt */
+ LOG(PIL_CRIT, "Invalid username for %s.", bt->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+
+ SEND(bt->wrfd, bt->passwd);
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+
+ switch (StonithLookFor(bt->rdfd, LoginOK, 5)) {
+
+ case 0: /* Good! */
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", bt->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ EXPECT(bt->rdfd, Menu, 2);
+
+ return(S_OK);
+}
+
+static int
+RPCRobustLogin(struct pluginDevice * bt)
+{
+ int rc=S_OOPS;
+ int j;
+
+ for (j=0; j < 20 && rc != S_OK; ++j) {
+
+
+ if (RPC_connect_device(bt) != S_OK) {
+ continue;
+ }
+
+ rc = RPCLogin(bt);
+ }
+ return rc;
+}
+
+/* Log out of the Baytech RPC */
+
+static int
+RPCLogout(struct pluginDevice* bt)
+{
+ int rc;
+
+ /* Make sure we're in the right menu... */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "Selection>" */
+ rc = StonithLookFor(bt->rdfd, Selection, 5);
+
+ /* Option 6 is Logout */
+ SEND(bt->wrfd, "6\r");
+
+ close(bt->wrfd);
+ close(bt->rdfd);
+ bt->wrfd = bt->rdfd = -1;
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+
+/* Reset (power-cycle) the given outlet number */
+static int
+RPCReset(struct pluginDevice* bt, int unitnum, const char * rebootid)
+{
+ char unum[32];
+
+
+ SEND(bt->wrfd, "\r");
+
+ /* Make sure we're in the top level menu */
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+
+ /* Send REBOOT command for given outlet */
+ snprintf(unum, sizeof(unum), "REBOOT %d\r", unitnum);
+ SEND(bt->wrfd, unum);
+
+ /* Expect "ebooting "... or "(Y/N)" (if confirmation turned on) */
+
+ retry:
+ switch (StonithLookFor(bt->rdfd, Rebooting, 5)) {
+ case 0: /* Got "Rebooting" Do nothing */
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(bt->wrfd, "Y\r");
+ goto retry;
+
+ case 2: /* Outlet is turned off */
+ LOG(PIL_CRIT, "Host is OFF: %s.", rebootid);
+ return(S_ISOFF);
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+ LOG(PIL_INFO, "Host %s (outlet %d) being rebooted."
+ , rebootid, unitnum);
+
+ /* Expect "ower applied to outlet" */
+ if (StonithLookFor(bt->rdfd, PowerApplied, 30) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host %s (outlet %d)."
+ , rebootid, unitnum);
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC,5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+ /* Pop back to main menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(S_OK);
+}
+
+static int
+RPC_onoff(struct pluginDevice* bt, int unitnum, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "on" : "off");
+ int rc;
+
+
+ if ((rc = RPCRobustLogin(bt) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(rc);
+ }
+ SEND(bt->wrfd, "\r");
+
+ /* Make sure we're in the top level menu */
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+
+ /* Send ON/OFF command for given outlet */
+ snprintf(unum, sizeof(unum), "%s %d\r"
+ , onoff, unitnum);
+ SEND(bt->wrfd, unum);
+
+ /* Expect "RPC->x "... or "(Y/N)" (if confirmation turned on) */
+
+ if (StonithLookFor(bt->rdfd, TurningOnOff, 10) == 1) {
+ /* They've turned on that annoying command confirmation :-( */
+ SEND(bt->wrfd, "Y\r");
+ EXPECT(bt->rdfd, TurningOnOff, 10);
+ }
+
+ EXPECT(bt->rdfd, GTSign, 10);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to host %s (outlet %d) turned %s."
+ , unitid, unitnum, onoff);
+ /* Pop back to main menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(S_OK);
+}
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+RPCNametoOutletList(struct pluginDevice* bt, const char * name
+, int outletlist[])
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ int maxfound = 0;
+
+
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, GTSign, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(bt->wrfd, "STATUS\r");
+
+ /* Expect: "emperature:" so we can skip over it... */
+ EXPECT(bt->rdfd, bt->modelinfo->expect, 5);
+ EXPECT(bt->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ char * last;
+ NameMapping[0] = EOS;
+ SNARF(bt->rdfd, NameMapping, 5);
+
+ if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) {
+ continue;
+ }
+
+ last = sockname+bt->modelinfo->socklen;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strcasecmp(name, sockname) == 0) {
+ outletlist[maxfound] = sockno;
+ ++maxfound;
+ }
+ } while (strlen(NameMapping) > 2 && maxfound < MAXOUTLET);
+
+ /* Pop back out to the top level menu */
+ SEND(bt->wrfd, "MENU\r");
+ return(maxfound);
+}
+
+static int
+baytech_status(StonithPlugin *s)
+{
+ struct pluginDevice* bt;
+ int rc;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ bt = (struct pluginDevice*) s;
+
+ if ((rc = RPCRobustLogin(bt) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(rc);
+ }
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ EXPECT(bt->rdfd, RPC, 5);
+ EXPECT(bt->rdfd, Menu, 5);
+
+ return(RPCLogout(bt));
+}
+/*
+ * Return the list of hosts (outlet names) for the devices on this BayTech unit
+ */
+
+static char **
+baytech_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* bt;
+ unsigned int i;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ bt = (struct pluginDevice*) s;
+
+ if (RPCRobustLogin(bt) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ return(NULL);
+ }
+
+ /* Verify that we're in the top-level menu */
+ SEND(bt->wrfd, "\r");
+
+ /* Expect "RPC-x Menu" */
+ NULLEXPECT(bt->rdfd, RPC, 5);
+ NULLEXPECT(bt->rdfd, Menu, 5);
+
+ /* OK. Request sub-menu 1 (Outlet Control) */
+ SEND(bt->wrfd, "1\r");
+
+ /* Verify that we're in the sub-menu */
+
+ /* Expect: "RPC-x>" */
+ NULLEXPECT(bt->rdfd, RPC, 5);
+ NULLEXPECT(bt->rdfd, GTSign, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(bt->wrfd, "STATUS\r");
+
+ /* Expect: "emperature:" so we can skip over it... */
+ NULLEXPECT(bt->rdfd, bt->modelinfo->expect, 5);
+ NULLEXPECT(bt->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ int sockno;
+ char sockname[64];
+ char * last;
+ char * nm;
+
+ NameMapping[0] = EOS;
+
+ NULLSNARF(bt->rdfd, NameMapping, 5);
+
+ if (!parse_socket_line(bt, NameMapping, &sockno, sockname)) {
+ continue;
+ }
+
+ last = sockname+bt->modelinfo->socklen;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if ((nm = (char*)STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ } while (strlen(NameMapping) > 2);
+
+ /* Pop back out to the top level menu */
+ SEND(bt->wrfd, "MENU\r");
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)RPCLogout(bt);
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+ return(NULL);
+}
+
+/*
+ * Connect to the given BayTech device.
+ * We should add serial support here eventually...
+ */
+static int
+RPC_connect_device(struct pluginDevice * bt)
+{
+ int fd = OurImports->OpenStreamSocket(bt->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ bt->rdfd = bt->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+baytech_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = 0;
+ struct pluginDevice* bt;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ bt = (struct pluginDevice*) s;
+
+ if ((rc = RPCRobustLogin(bt)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s."
+ , bt->idinfo ? bt->idinfo : DEVICE);
+ }else{
+ int noutlets;
+ int outlets[MAXOUTLET];
+ int j;
+ noutlets = RPCNametoOutletList(bt, host, outlets);
+
+ if (noutlets < 1) {
+ LOG(PIL_CRIT, "%s %s doesn't control host [%s]"
+ , bt->idinfo, bt->unitid, host);
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+ case ST_POWERON:
+ case ST_POWEROFF:
+ for (j=0; rc == S_OK && j < noutlets;++j) {
+ rc = RPC_onoff(bt, outlets[j], host, request);
+ }
+ break;
+ case ST_GENERIC_RESET:
+ /*
+ * Our strategy here:
+ * 1. Power off all outlets except the last one
+ * 2. reset the last outlet
+ * 3. power the other outlets back on
+ */
+
+ for (j=0; rc == S_OK && j < noutlets-1; ++j) {
+ rc = RPC_onoff(bt,outlets[j],host
+ , ST_POWEROFF);
+ }
+ if (rc == S_OK) {
+ rc = RPCReset(bt, outlets[j], host);
+ }
+ for (j=0; rc == S_OK && j < noutlets-1; ++j) {
+ rc = RPC_onoff(bt, outlets[j], host
+ , ST_POWERON);
+ }
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+ }
+
+ lorc = RPCLogout(bt);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+static const char * const *
+baytech_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+baytech_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* bt = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (bt->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc =OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ bt->device = namestocopy[0].s_value;
+ bt->user = namestocopy[1].s_value;
+ bt->passwd = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+static const char *
+baytech_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* bt;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ bt = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+
+ case ST_DEVICEID: /* What type of device? */
+ ret = bt->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = bt->device;
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "Bay Technical Associates (Baytech) RPC "
+ "series power switches (via telnet).\n"
+ "The RPC-5, RPC-3 and RPC-3A switches are well tested"
+ ".";
+ break;
+
+ case ST_DEVICEURL: /* Manufacturer's web site */
+ ret = "http://www.baytech.net/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = baytechXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Baytech Stonith destructor...
+ */
+static void
+baytech_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* bt;
+
+ VOIDERRIFWRONGDEV(s);
+
+ bt = (struct pluginDevice *)s;
+
+ bt->pluginid = NOTpluginID;
+ if (bt->rdfd >= 0) {
+ close(bt->rdfd);
+ bt->rdfd = -1;
+ }
+ if (bt->wrfd >= 0) {
+ close(bt->wrfd);
+ bt->wrfd = -1;
+ }
+ if (bt->device != NULL) {
+ FREE(bt->device);
+ bt->device = NULL;
+ }
+ if (bt->user != NULL) {
+ FREE(bt->user);
+ bt->user = NULL;
+ }
+ if (bt->passwd != NULL) {
+ FREE(bt->passwd);
+ bt->passwd = NULL;
+ }
+ if (bt->idinfo != NULL) {
+ FREE(bt->idinfo);
+ bt->idinfo = NULL;
+ }
+ if (bt->unitid != NULL) {
+ FREE(bt->unitid);
+ bt->unitid = NULL;
+ }
+ FREE(bt);
+}
+
+/* Create a new BayTech Stonith device. */
+
+static StonithPlugin *
+baytech_new(const char *subplugin)
+{
+ struct pluginDevice* bt = ST_MALLOCT(struct pluginDevice);
+
+ if (bt == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(bt, 0, sizeof(*bt));
+ bt->pluginid = pluginid;
+ bt->pid = -1;
+ bt->rdfd = -1;
+ bt->wrfd = -1;
+ REPLSTR(bt->idinfo, DEVICE);
+ if (bt->idinfo == NULL) {
+ FREE(bt);
+ return(NULL);
+ }
+ bt->modelinfo = &ModelInfo[0];
+ bt->sp.s_ops = &baytechOps;
+
+ return &(bt->sp); /* same as "bt" */
+}
+
+static int
+parse_socket_line(struct pluginDevice * bt, const char *NameMapping
+, int *sockno, char *sockname)
+{
+#if 0
+ char format[64];
+ snprintf(format, sizeof(format), "%%7d %%%dc"
+ , bt->modelinfo->socklen);
+ /* 7 digits, 7 blanks, then 'socklen' characters */
+ /* [0-6]: digits, NameMapping[13] begins the sockname */
+ /* NameMapping strlen must be >= socklen + 14 */
+
+ if (sscanf(NameMapping, format, sockno, sockname) != 2) {
+ return FALSE;
+ }
+#else
+# define OFFSET 14
+
+ if (sscanf(NameMapping, "%7d", sockno) != 1
+ || strlen(NameMapping) < OFFSET+bt->modelinfo->socklen) {
+ return FALSE;
+ }
+ strncpy(sockname, NameMapping+OFFSET, bt->modelinfo->socklen);
+ sockname[bt->modelinfo->socklen] = EOS;
+#endif
+ return TRUE;
+}
diff --git a/lib/plugins/stonith/bladehpi.c b/lib/plugins/stonith/bladehpi.c
new file mode 100644
index 0000000..ae9a4cf
--- /dev/null
+++ b/lib/plugins/stonith/bladehpi.c
@@ -0,0 +1,1101 @@
+/*
+ * Stonith module for BladeCenter via OpenHPI, an implementation of Service
+ * Availability Forum's Hardware Platfrom Interface
+ *
+ * Author: Dave Blaschke <debltc@us.ibm.com>
+ *
+ * Copyright (c) 2005 International Business Machines
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "IBM BladeCenter (OpenHPI)"
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN bladehpi
+#define PIL_PLUGIN_S "bladehpi"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include <openhpi/SaHpi.h>
+
+/* Maximum number of seconds to wait for host to power off */
+#define MAX_POWEROFF_WAIT 60
+
+/* entity_root, the one required plugin parameter */
+#define ST_ENTITYROOT "entity_root"
+
+/* String format of entity_root */
+#define SYSTEM_CHASSIS_FMT "{SYSTEM_CHASSIS,%d}"
+
+/* soft_reset, the one optional plugin parameter */
+#define ST_SOFTRESET "soft_reset"
+
+#define OPENHPIURL "http://www.openhpi.org/"
+
+/* OpenHPI resource types of interest to this plugin */
+#define OHRES_NONE 0
+#define OHRES_BLADECENT 1
+#define OHRES_MGMTMOD 2
+#define OHRES_BLADE 3
+
+/* IBMBC_WAIT_FOR_OFF - This constant has to do with the problem that
+ saHpiResourcePowerStateSet can return before the desired state has been
+ achieved by the blade. In the SAHPI_POWER_OFF case this is not good,
+ as whoever calls this plugin assumes that the power is actually off
+ when the plugin returns with a successful return code. Define this
+ constant to build code that loops in one second intervals after calling
+ saHpiResourcePowerStateSet(SAHPI_POWER_OFF) to make sure the power is
+ really off.
+#define IBMBC_WAIT_FOR_OFF */
+
+static StonithPlugin * bladehpi_new(const char *);
+static void bladehpi_destroy(StonithPlugin *);
+static const char * bladehpi_getinfo(StonithPlugin *, int);
+static const char * const * bladehpi_get_confignames(StonithPlugin *);
+static int bladehpi_status(StonithPlugin *);
+static int bladehpi_reset_req(StonithPlugin *, int, const char *);
+static char ** bladehpi_hostlist(StonithPlugin *);
+static int bladehpi_set_config(StonithPlugin *, StonithNVpair *);
+
+static struct stonith_ops bladehpiOps = {
+ bladehpi_new, /* Create new STONITH object */
+ bladehpi_destroy, /* Destroy STONITH object */
+ bladehpi_getinfo, /* Return STONITH info string */
+ bladehpi_get_confignames, /* Return configuration parameters */
+ bladehpi_set_config, /* Set configuration */
+ bladehpi_status, /* Return STONITH device status */
+ bladehpi_reset_req, /* Request a reset */
+ bladehpi_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports * PluginImports;
+static PILPlugin * OurPlugin;
+static PILInterface * OurInterface;
+static StonithImports * OurImports;
+static void * interfprivate;
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin *us, const PILPluginImports *imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us
+ , PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &bladehpiOps
+ , NULL /* close */
+ , &OurInterface
+ , (void *)&OurImports
+ , &interfprivate);
+}
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * device;
+ int softreset;
+ GList * hostlist;
+ SaHpiVersionT ohver; /* OpenHPI interface version */
+ SaHpiSessionIdT ohsession; /* session ID */
+ SaHpiUint32T ohrptcnt; /* RPT count for hostlist */
+ SaHpiResourceIdT ohdevid; /* device resource ID */
+ SaHpiResourceIdT ohsensid; /* sensor resource ID */
+ SaHpiSensorNumT ohsensnum; /* sensor number */
+};
+
+static int open_hpi_session(struct pluginDevice *dev);
+static void close_hpi_session(struct pluginDevice *dev);
+
+static const char *pluginid = "BladeCenterDevice-Stonith";
+static const char *NOTpluginID = "IBM BladeCenter device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_ENTITYROOT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_ENTITYROOT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_ENTITYROOT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The entity_root of the STONITH device from the OpenHPI config file" \
+ XML_PARM_LONGDESC_END
+
+#define XML_ENTITYROOT_PARM \
+ XML_PARAMETER_BEGIN(ST_ENTITYROOT, "string", "1", "0") \
+ XML_ENTITYROOT_SHORTDESC \
+ XML_ENTITYROOT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_SOFTRESET_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_SOFTRESET \
+ XML_PARM_SHORTDESC_END
+
+#define XML_SOFTRESET_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "Soft reset indicator, true|1 if STONITH device should use soft reset (power cycle) to reset nodes, false|0 if device should use hard reset (power off, wait, power on); default is false" \
+ XML_PARM_LONGDESC_END
+
+#define XML_SOFTRESET_PARM \
+ XML_PARAMETER_BEGIN(ST_SOFTRESET, "string", "0", "0") \
+ XML_SOFTRESET_SHORTDESC \
+ XML_SOFTRESET_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *bladehpiXML =
+ XML_PARAMETERS_BEGIN
+ XML_ENTITYROOT_PARM
+ XML_SOFTRESET_PARM
+ XML_PARAMETERS_END;
+
+static int get_resource_type(char *, SaHpiRptEntryT *);
+static int get_sensor_num(SaHpiSessionIdT, SaHpiResourceIdT);
+static int get_bladehpi_hostlist(struct pluginDevice *);
+static void free_bladehpi_hostlist(struct pluginDevice *);
+static int get_num_tokens(char *str);
+
+struct blade_info {
+ char * name; /* blade name */
+ SaHpiResourceIdT resourceId; /* blade resource ID */
+ SaHpiCapabilitiesT resourceCaps; /* blade capabilities */
+};
+
+
+static int
+bladehpi_status(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+ SaErrorT ohrc;
+ SaHpiDomainInfoT ohdi;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return rc;
+
+ /* Refresh the hostlist only if RPTs updated */
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ }
+
+ /* At this point, hostlist is up to date */
+ if (dev->ohsensid && dev->ohsensnum) {
+ /*
+ * For accurate status, need to make a call that goes out to
+ * BladeCenter MM because the calls made so far by this
+ * function (and perhaps get_bladehpi_hostlist) only retrieve
+ * information from memory cached by OpenHPI
+ */
+ ohrc = saHpiSensorReadingGet(dev->ohsession
+ , dev->ohsensid, dev->ohsensnum, NULL, NULL);
+ if (ohrc == SA_ERR_HPI_BUSY || ohrc == SA_ERR_HPI_NO_RESPONSE) {
+ LOG(PIL_CRIT, "Unable to connect to BladeCenter in %s"
+ , __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+done:
+ close_hpi_session(dev);
+ return (rc == S_OK) ? (dev->ohdevid ? S_OK : S_OOPS) : rc;
+}
+
+
+/*
+ * Return the list of hosts configured for this HMC device
+ */
+
+static char **
+bladehpi_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+ int numnames = 0, j;
+ char ** ret = NULL;
+ GList * node = NULL;
+ SaErrorT ohrc;
+ SaHpiDomainInfoT ohdi;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, NULL);
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return NULL;
+
+ /* Refresh the hostlist only if RPTs updated */
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ goto done;
+ }
+ }
+
+ /* At this point, hostlist is up to date */
+ numnames = g_list_length(dev->hostlist);
+ if (numnames < 0) {
+ LOG(PIL_CRIT, "Unconfigured stonith object in %s"
+ , __FUNCTION__);
+ goto done;
+ }
+
+ ret = (char **)MALLOC((numnames+1) * sizeof(char *));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "Out of memory for malloc in %s", __FUNCTION__);
+ goto done;
+ }
+
+ memset(ret, 0, (numnames+1) * sizeof(char *));
+ for (node = g_list_first(dev->hostlist), j = 0
+ ; NULL != node
+ ; j++, node = g_list_next(node)) {
+ ret[j] = STRDUP(((struct blade_info *)node->data)->name);
+ if (ret[j] == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s"
+ , __FUNCTION__);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ goto done;
+ }
+ strdown(ret[j]);
+ }
+
+done:
+ close_hpi_session(dev);
+ return ret;
+}
+
+
+static const char * const *
+bladehpi_get_confignames(StonithPlugin *s)
+{
+ static const char * names[] = {ST_ENTITYROOT, NULL};
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ return names;
+}
+
+
+/*
+ * Reset the given host, and obey the request type.
+ */
+
+static int
+bladehpi_reset_req(StonithPlugin *s, int request, const char *host)
+{
+ GList * node = NULL;
+ struct pluginDevice * dev = NULL;
+ struct blade_info * bi = NULL;
+ SaHpiPowerStateT ohcurstate, ohnewstate;
+ SaHpiDomainInfoT ohdi;
+ SaErrorT ohrc;
+ int rc = S_OK;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, request=%d, host=%s"
+ , __FUNCTION__, request, host);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (host == NULL) {
+ LOG(PIL_CRIT, "Invalid host argument to %s", __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ dev = (struct pluginDevice *)s;
+ rc = open_hpi_session(dev);
+ if( rc != S_OK )
+ return rc;
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ rc = S_BADCONFIG;
+ goto done;
+ }
+ if (dev->ohrptcnt != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if (get_bladehpi_hostlist(dev) != S_OK) {
+ LOG(PIL_CRIT, "Unable to obtain list of hosts in %s"
+ , __FUNCTION__);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+ for (node = g_list_first(dev->hostlist)
+ ; node != NULL
+ ; node = g_list_next(node)) {
+ bi = ((struct blade_info *)node->data);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Found host %s in hostlist", bi->name);
+ }
+
+ if (!strcasecmp(bi->name, host)) {
+ break;
+ }
+ }
+
+ if (!node || !bi) {
+ LOG(PIL_CRIT
+ , "Host %s is not configured in this STONITH module, "
+ "please check your configuration information", host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ /* Make sure host has proper capabilities for get */
+ if (!(bi->resourceCaps & SAHPI_CAPABILITY_POWER)) {
+ LOG(PIL_CRIT
+ , "Host %s does not have power capability", host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession, bi->resourceId
+ , &ohcurstate);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get host %s power state (%d)"
+ , host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ switch (request) {
+ case ST_POWERON:
+ if (ohcurstate == SAHPI_POWER_ON) {
+ LOG(PIL_INFO, "Host %s already on", host);
+ goto done;
+ }
+ ohnewstate = SAHPI_POWER_ON;
+
+ break;
+
+ case ST_POWEROFF:
+ if (ohcurstate == SAHPI_POWER_OFF) {
+ LOG(PIL_INFO, "Host %s already off", host);
+ goto done;
+ }
+ ohnewstate = SAHPI_POWER_OFF;
+
+ break;
+
+ case ST_GENERIC_RESET:
+ if (ohcurstate == SAHPI_POWER_OFF) {
+ ohnewstate = SAHPI_POWER_ON;
+ } else {
+ ohnewstate = SAHPI_POWER_CYCLE;
+ }
+
+ break;
+
+ default:
+ LOG(PIL_CRIT, "Invalid request argument to %s"
+ , __FUNCTION__);
+ rc = S_INVAL;
+ goto done;
+ }
+
+ if (!dev->softreset && (ohnewstate == SAHPI_POWER_CYCLE)) {
+ int maxwait;
+
+ ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, SAHPI_POWER_OFF);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state to"
+ " OFF (%d)", host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ /*
+ * Must wait for power off here or subsequent power on request
+ * may take place while power is still on and thus ignored
+ */
+ maxwait = MAX_POWEROFF_WAIT;
+ do {
+ maxwait--;
+ sleep(1);
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession
+ , bi->resourceId, &ohcurstate);
+ } while ((ohrc == SA_OK)
+ && (ohcurstate != SAHPI_POWER_OFF)
+ && (maxwait > 0));
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waited %d seconds for power off"
+ , MAX_POWEROFF_WAIT - maxwait);
+ }
+
+ ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, SAHPI_POWER_ON);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state to"
+ " ON (%d)", host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+ } else {
+ /* Make sure host has proper capabilities to reset */
+ if ((ohnewstate == SAHPI_POWER_CYCLE) &&
+ (!(bi->resourceCaps & SAHPI_CAPABILITY_RESET))) {
+ LOG(PIL_CRIT
+ , "Host %s does not have reset capability"
+ , host);
+ rc = S_OOPS;
+ goto done;
+ }
+
+ if ((ohrc = saHpiResourcePowerStateSet(dev->ohsession
+ , bi->resourceId, ohnewstate)) != SA_OK) {
+ LOG(PIL_CRIT, "Unable to set host %s power state (%d)"
+ , host, ohrc);
+ rc = S_OOPS;
+ goto done;
+ }
+ }
+
+#ifdef IBMBC_WAIT_FOR_OFF
+ if (ohnewstate == SAHPI_POWER_OFF) {
+ int maxwait = MAX_POWEROFF_WAIT;
+
+ do {
+ maxwait--;
+ sleep(1);
+ ohrc = saHpiResourcePowerStateGet(dev->ohsession
+ , bi->resourceId, &ohcurstate);
+ } while ((ohrc == SA_OK)
+ && (ohcurstate != SAHPI_POWER_OFF)
+ && (maxwait > 0));
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waited %d seconds for power off"
+ , MAX_POWEROFF_WAIT - maxwait);
+ }
+ }
+#endif
+
+ LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
+
+done:
+ close_hpi_session(dev);
+ return rc;
+}
+
+
+/*
+ * Parse the information in the given configuration file,
+ * and stash it away...
+ */
+
+static int
+bladehpi_set_config(StonithPlugin *s, StonithNVpair *list)
+{
+ struct pluginDevice * dev = NULL;
+ StonithNamesToGet namestocopy [] =
+ { {ST_ENTITYROOT, NULL}
+ , {NULL, NULL}
+ };
+ int rc, i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ dev = (struct pluginDevice *)s;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s conditionally compiled with:"
+#ifdef IBMBC_WAIT_FOR_OFF
+ " IBMBC_WAIT_FOR_OFF"
+#endif
+ , dev->pluginid);
+ }
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s = %s", ST_ENTITYROOT
+ , namestocopy[0].s_value);
+ }
+
+ if (get_num_tokens(namestocopy[0].s_value) == 1) {
+ /* name=value pairs on command line, look for soft_reset */
+ const char *softreset =
+ OurImports->GetValue(list, ST_SOFTRESET);
+ if (softreset != NULL) {
+ if (!strcasecmp(softreset, "true") ||
+ !strcmp(softreset, "1")) {
+ dev->softreset = 1;
+ } else if (!strcasecmp(softreset, "false") ||
+ !strcmp(softreset, "0")) {
+ dev->softreset = 0;
+ } else {
+ LOG(PIL_CRIT, "Invalid %s %s, must be "
+ "true, 1, false or 0"
+ , ST_SOFTRESET, softreset);
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+ } else {
+ /* -p or -F option with args "entity_root [soft_reset]..." */
+ char *pch = namestocopy[0].s_value;
+
+ /* skip over entity_root and null-terminate */
+ pch += strcspn(pch, WHITESPACE);
+ *pch = EOS;
+
+ /* skip over white-space up to next token */
+ pch++;
+ pch += strspn(pch, WHITESPACE);
+ if (!strcasecmp(pch, "true") || !strcmp(pch, "1")) {
+ dev->softreset = 1;
+ } else if (!strcasecmp(pch, "false") || !strcmp(pch, "0")) {
+ dev->softreset = 0;
+ } else {
+ LOG(PIL_CRIT, "Invalid %s %s, must be "
+ "true, 1, false or 0"
+ , ST_SOFTRESET, pch);
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+
+ dev->device = STRDUP(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ if (dev->device == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s", __FUNCTION__);
+ return S_OOPS;
+ }
+
+ if (strcspn(dev->device, WHITESPACE) != strlen(dev->device) ||
+ sscanf(dev->device, SYSTEM_CHASSIS_FMT, &i) != 1 || i < 0) {
+ LOG(PIL_CRIT, "Invalid %s %s, must be of format %s"
+ , ST_ENTITYROOT, dev->device, SYSTEM_CHASSIS_FMT);
+ return S_BADCONFIG;
+ }
+
+ dev->ohver = saHpiVersionGet();
+ if (dev->ohver > SAHPI_INTERFACE_VERSION) {
+ LOG(PIL_CRIT, "Installed OpenHPI interface (%x) greater than "
+ "one used by plugin (%x), incompatibilites may exist"
+ , dev->ohver, SAHPI_INTERFACE_VERSION);
+ return S_BADCONFIG;
+ }
+ return S_OK;
+}
+
+static int
+open_hpi_session(struct pluginDevice *dev)
+{
+ SaErrorT ohrc;
+
+ ohrc = saHpiSessionOpen(SAHPI_UNSPECIFIED_DOMAIN_ID
+ , &dev->ohsession, NULL);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to open HPI session (%d)", ohrc);
+ return S_BADCONFIG;
+ }
+
+ ohrc = saHpiDiscover(dev->ohsession);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to discover resources (%d)", ohrc);
+ return S_BADCONFIG;
+ }
+
+ return S_OK;
+}
+static void
+close_hpi_session(struct pluginDevice *dev)
+{
+ if (dev && dev->ohsession) {
+ saHpiSessionClose(dev->ohsession);
+ dev->ohsession = 0;
+ }
+}
+
+static const char *
+bladehpi_getinfo(StonithPlugin *s, int reqtype)
+{
+ struct pluginDevice * dev;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, reqtype=%d"
+ , __FUNCTION__, reqtype);
+ }
+
+ ERRIFWRONGDEV(s, NULL);
+
+ dev = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = dev->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = dev->device;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IBM BladeCenter via OpenHPI\n"
+ "Use for IBM xSeries systems managed by BladeCenter\n"
+ " Required parameter name " ST_ENTITYROOT " is "
+ "a string (no white-space) of\n"
+ "the format \""SYSTEM_CHASSIS_FMT"\" "
+ "which is entity_root of BladeCenter\n"
+ "from OpenHPI config file, where %d is a positive "
+ "integer\n"
+ " Optional parameter name " ST_SOFTRESET " is "
+ "true|1 if STONITH device should\n"
+ "use soft reset (power cycle) to reset nodes or "
+ "false|0 if device should\n"
+ "use hard reset (power off, wait, power on); "
+ "default is false";
+ break;
+
+ case ST_DEVICEURL:
+ ret = OPENHPIURL;
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = bladehpiXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ * HMC Stonith destructor...
+ */
+
+static void
+bladehpi_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * dev;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ dev = (struct pluginDevice *)s;
+
+ dev->pluginid = NOTpluginID;
+ if (dev->device) {
+ FREE(dev->device);
+ dev->device = NULL;
+ }
+ if (dev->idinfo) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ free_bladehpi_hostlist(dev);
+
+ if (dev->ohsession) {
+ saHpiSessionClose(dev->ohsession);
+ dev->ohsession = 0;
+ }
+
+ FREE(dev);
+}
+
+
+static StonithPlugin *
+bladehpi_new(const char *subplugin)
+{
+ struct pluginDevice * dev = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called", __FUNCTION__);
+ }
+
+ if (dev == NULL) {
+ LOG(PIL_CRIT, "Out of memory in %s", __FUNCTION__);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->pluginid = pluginid;
+ dev->device = NULL;
+ dev->hostlist = NULL;
+ REPLSTR(dev->idinfo, DEVICE);
+ if (dev->idinfo == NULL) {
+ FREE(dev);
+ return NULL;
+ }
+ dev->sp.s_ops = &bladehpiOps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: returning successfully", __FUNCTION__);
+ }
+
+ return ((void *)dev);
+}
+
+
+static int
+get_resource_type(char *entityRoot, SaHpiRptEntryT *ohRPT)
+{
+ int i, rc = OHRES_NONE;
+ int foundBlade = 0, foundExp = 0, foundMgmt = 0;
+ int foundRoot = 0, foundOther = 0;
+ char rootName[64];
+ SaHpiEntityPathT * ohep = &ohRPT->ResourceEntity;
+
+ if (ohep == NULL || entityRoot == NULL) {
+ return 0;
+ }
+
+ /* First find root of entity path, which is last entity in entry */
+ for (i = 0; i < SAHPI_MAX_ENTITY_PATH; i++) {
+ if (ohep->Entry[i].EntityType == SAHPI_ENT_ROOT) {
+ break;
+ }
+ }
+
+ /* Then back up through entries looking for specific entity */
+ for (i--; i >= 0; i--) {
+ switch (ohep->Entry[i].EntityType) {
+ case SAHPI_ENT_SBC_BLADE:
+ foundBlade = 1;
+ break;
+
+ case SAHPI_ENT_SYS_EXPANSION_BOARD:
+ foundExp = 1;
+ break;
+
+ case SAHPI_ENT_SYS_MGMNT_MODULE:
+ if (ohep->Entry[i].EntityLocation == 0) {
+ foundMgmt = 1;
+ }
+ break;
+
+ case SAHPI_ENT_SYSTEM_CHASSIS:
+ snprintf(rootName, sizeof(rootName)
+ , SYSTEM_CHASSIS_FMT
+ , ohep->Entry[i].EntityLocation);
+ if (!strcmp(entityRoot, rootName)) {
+ foundRoot = 1;
+ }
+ break;
+
+ default:
+ foundOther = 1;
+ break;
+ }
+ }
+
+ /* We are only interested in specific entities on specific device */
+ if (foundRoot) {
+ if (foundMgmt && !(foundBlade||foundExp||foundOther)) {
+ rc = OHRES_MGMTMOD;
+ } else if (!(foundMgmt||foundBlade||foundExp||foundOther)) {
+ rc = OHRES_BLADECENT;
+ } else if (foundBlade && !foundExp) {
+ rc = OHRES_BLADE;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+get_sensor_num(SaHpiSessionIdT ohsession, SaHpiResourceIdT ohresid)
+{
+ SaErrorT ohrc = SA_OK;
+ SaHpiEntryIdT ohnextid;
+ SaHpiRdrT ohRDR;
+
+ ohnextid = SAHPI_FIRST_ENTRY;
+ do {
+ ohrc = saHpiRdrGet(ohsession, ohresid, ohnextid
+ , &ohnextid, &ohRDR);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get RDR entry in %s (%d)"
+ , __FUNCTION__, ohrc);
+ } else if (ohRDR.RdrType == SAHPI_SENSOR_RDR) {
+ return ohRDR.RdrTypeUnion.SensorRec.Num;
+ }
+ } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
+
+ return 0;
+}
+
+
+/*
+ * Get RPT update count
+ * Loop through all RPT entries
+ * If entry is BladeCenter, save resource ID in dev->ohdevid
+ * If entry is MgmtMod and has sensor, save resource ID in dev->ohsensid
+ * and sensor number in dev->ohsensnum
+ * If entry is blade, save blade_info and add to dev->hostlist
+ * Get RPT update count
+ * If RPT update count changed since start of loop, repeat loop
+ * Save RPT update count in dev->ohrptcnt
+ *
+ * Note that not only does this function update hostlist, it also
+ * updates ohrptcnt, ohdevid, ohsensid and ohsensnum. However, with
+ * this logic it does not need to be called again until the RPT update
+ * count changes.
+ */
+
+static int
+get_bladehpi_hostlist(struct pluginDevice *dev)
+{
+ struct blade_info * bi;
+ SaErrorT ohrc;
+ SaHpiEntryIdT ohnextid;
+ SaHpiRptEntryT ohRPT;
+ SaHpiDomainInfoT ohdi;
+ SaHpiUint32T ohupdate;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called, dev->device=%s"
+ , __FUNCTION__, dev->device);
+ }
+
+ if (dev->device == NULL || *dev->device == 0) {
+ LOG(PIL_CRIT, "Unconfigured stonith object in %s"
+ , __FUNCTION__);
+ return S_BADCONFIG;
+ }
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ return S_BADCONFIG;
+ }
+
+try_again:
+ ohupdate = ohdi.RptUpdateCount;
+ dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
+ ohnextid = SAHPI_FIRST_ENTRY;
+ do {
+ char blname[SAHPI_MAX_TEXT_BUFFER_LENGTH];
+ int blnum;
+
+ ohrc = saHpiRptEntryGet(dev->ohsession, ohnextid
+ , &ohnextid, &ohRPT);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get RPT entry in %s (%d)"
+ , __FUNCTION__, ohrc);
+ free_bladehpi_hostlist(dev);
+ return S_BADCONFIG;
+ }
+
+ switch (get_resource_type(dev->device, &ohRPT)) {
+ case OHRES_BLADECENT:
+ dev->ohdevid = ohRPT.ResourceId;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "BladeCenter '%s' has id %d"
+ , (char*)ohRPT.ResourceTag.Data
+ , dev->ohdevid);
+ }
+ break;
+
+ case OHRES_MGMTMOD:
+ if (ohRPT.ResourceCapabilities&SAHPI_CAPABILITY_SENSOR){
+ dev->ohsensnum = get_sensor_num(dev->ohsession
+ , ohRPT.ResourceId);
+
+ if (dev->ohsensnum) {
+ dev->ohsensid = ohRPT.ResourceId;
+
+ if (Debug) {
+ LOG(PIL_DEBUG
+ , "MgmtModule '%s' has id %d "
+ "with sensor #%d"
+ , (char*)ohRPT.ResourceTag.Data
+ , dev->ohsensid
+ , dev->ohsensnum);
+ }
+ }
+ }
+ break;
+
+ case OHRES_BLADE:
+ if ((bi = (struct blade_info *)
+ MALLOC(sizeof(struct blade_info))) == NULL) {
+ LOG(PIL_CRIT, "Out of memory in %s"
+ , __FUNCTION__);
+ free_bladehpi_hostlist(dev);
+ return S_OOPS;
+ }
+
+ /*
+ * New format consists of "Blade N - name" while older
+ * format consists only of "name"; we only need to
+ * stash name because ResourceID is the important info
+ */
+ if (sscanf((char*)ohRPT.ResourceTag.Data, "Blade %d - %s"
+ , &blnum, blname) == 2) {
+ bi->name = STRDUP(blname);
+ } else {
+ bi->name = STRDUP((char*)ohRPT.ResourceTag.Data);
+ }
+ if (bi->name == NULL) {
+ LOG(PIL_CRIT, "Out of memory for strdup in %s"
+ , __FUNCTION__);
+ free_bladehpi_hostlist(dev);
+ return S_OOPS;
+ }
+
+ bi->resourceId = ohRPT.ResourceId;
+ bi->resourceCaps = ohRPT.ResourceCapabilities;
+ dev->hostlist = g_list_append(dev->hostlist, bi);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Blade '%s' has id %d, caps %x"
+ , bi->name, bi->resourceId, bi->resourceCaps);
+ }
+ break;
+ }
+ } while (ohrc == SA_OK && ohnextid != SAHPI_LAST_ENTRY);
+
+ ohrc = saHpiDomainInfoGet(dev->ohsession, &ohdi);
+ if (ohrc != SA_OK) {
+ LOG(PIL_CRIT, "Unable to get domain info in %s (%d)"
+ , __FUNCTION__, ohrc);
+ free_bladehpi_hostlist(dev);
+ return S_BADCONFIG;
+ }
+
+ if (ohupdate != ohdi.RptUpdateCount) {
+ free_bladehpi_hostlist(dev);
+ if(Debug){
+ LOG(PIL_DEBUG, "Looping through entries again,"
+ " count changed from %d to %d"
+ , ohupdate, ohdi.RptUpdateCount);
+ }
+ goto try_again;
+ }
+
+ dev->ohrptcnt = ohupdate;
+
+ return S_OK;
+}
+
+
+static void
+free_bladehpi_hostlist(struct pluginDevice *dev)
+{
+ if (dev->hostlist) {
+ GList *node;
+ while (NULL != (node = g_list_first(dev->hostlist))) {
+ dev->hostlist =
+ g_list_remove_link(dev->hostlist, node);
+ FREE(((struct blade_info *)node->data)->name);
+ FREE(node->data);
+ g_list_free(node);
+ }
+ dev->hostlist = NULL;
+ }
+ dev->ohdevid = dev->ohsensid = dev->ohsensnum = 0;
+}
+
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
diff --git a/lib/plugins/stonith/cyclades.c b/lib/plugins/stonith/cyclades.c
new file mode 100644
index 0000000..6744cd4
--- /dev/null
+++ b/lib/plugins/stonith/cyclades.c
@@ -0,0 +1,650 @@
+/*
+ * Stonith module for Cyclades AlterPath PM
+ * Bases off the SSH plugin
+ *
+ * Copyright (c) 2004 Cyclades corp.
+ *
+ * Author: Jon Taylor <jon.taylor@cyclades.com>
+ *
+ * Rewritten from scratch using baytech.c structure and code
+ * and currently maintained by
+ * Marcelo Tosatti <marcelo.tosatti@cyclades.com>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "Cyclades AlterPath PM"
+
+#define DOESNT_USE_STONITHSCANLINE
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN cyclades
+#define PIL_PLUGIN_S "cyclades"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * cyclades_new(const char *);
+static void cyclades_destroy(StonithPlugin *);
+static int cyclades_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * cyclades_get_confignames(StonithPlugin * s);
+static const char * cyclades_get_info(StonithPlugin * s, int InfoType);
+static int cyclades_status(StonithPlugin *);
+static int cyclades_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** cyclades_hostlist(StonithPlugin *);
+
+
+
+static struct stonith_ops cycladesOps ={
+ cyclades_new, /* Create new STONITH object */
+ cyclades_destroy, /* Destroy STONITH object */
+ cyclades_get_info, /* Return STONITH info string */
+ cyclades_get_confignames, /* Return STONITH config vars */
+ cyclades_set_config, /* set configuration from vars */
+ cyclades_status, /* Return STONITH device status */
+ cyclades_reset_req, /* Request a reset */
+ cyclades_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &cycladesOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Cyclades STONITH device
+ *
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char * device;
+ char * user;
+
+ int serial_port;
+
+ /* pid of ssh client process and its in/out file descriptors */
+ pid_t pid;
+ int rdfd, wrfd;
+};
+
+static struct Etoken StatusOutput[] = {
+ { "Outlet\t\tName\t\tStatus\t\tUsers\t\tInterval (s)", 1, 0},
+ { "Outlet\tName\t\t\tStatus\t\tInterval (s)\tUsers", 2, 0},
+ { "Outlet Name Status Post-on Delay(s)", 3, 0},
+ { NULL, 0, 0}
+};
+
+static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+
+/* Commands of PM devices */
+static char status_all[] = "status all";
+static char cycle[] = "cycle";
+
+static int CYC_robust_cmd(struct pluginDevice *, char *);
+
+static const char * pluginid = "CycladesDevice-Stonith";
+static const char * NOTpluginID = "Cyclades device has been destroyed";
+
+#define MAX_OUTLETS 128
+
+#define ST_SERIALPORT "serialport"
+
+#define ZEROEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(0); \
+ }
+
+#define RESETEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) { \
+ FREE(outletstr); \
+ return(errno == ETIMEDOUT \
+ ? S_RESETFAIL : S_OOPS); \
+ } \
+ }
+
+#include "stonith_config_xml.h"
+
+#define XML_SERIALPORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_SERIALPORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_SERIALPORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The serial port of the IPDU which can powercycle the node" \
+ XML_PARM_LONGDESC_END
+
+#define XML_SERIALPORT_PARM \
+ XML_PARAMETER_BEGIN(ST_SERIALPORT, "string", "1", "0") \
+ XML_SERIALPORT_SHORTDESC \
+ XML_SERIALPORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *cycladesXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_LOGIN_PARM
+ XML_SERIALPORT_PARM
+ XML_PARAMETERS_END;
+
+static int
+CYCScanLine(struct pluginDevice *sd, int timeout, char * buf, int max)
+{
+ if (EXPECT_TOK(sd->rdfd, CRNL, timeout, buf, max, Debug) < 0) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ return(S_OOPS);
+ }
+ return(S_OK);
+}
+
+static int
+cyclades_status(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char *cmd = status_all;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return(S_OOPS);
+ }
+
+ EXPECT(sd->rdfd, StatusOutput, 50);
+
+ return(S_OK);
+}
+
+static int CYC_run_command(struct pluginDevice *sd, char *cmd)
+{
+ char SshCommand[MAX_OUTLETS*4];
+
+ snprintf(SshCommand, sizeof(SshCommand),
+ "exec ssh -q %s@%s /bin/pmCommand %d %s 2>/dev/null",
+ sd->user, sd->device, sd->serial_port, cmd);
+
+ sd->pid = STARTPROC(SshCommand, &sd->rdfd, &sd->wrfd);
+
+ if (sd->pid <= 0) {
+ return(S_OOPS);
+ }
+
+ return(S_OK);
+}
+
+static int
+CYC_robust_cmd(struct pluginDevice *sd, char *cmd)
+{
+ int rc = S_OOPS;
+ int i;
+
+ for (i=0; i < 20 && rc != S_OK; i++) {
+
+ if (sd->pid > 0) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ }
+
+ if (CYC_run_command(sd, cmd) != S_OK) {
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ continue;
+ }
+ rc = S_OK;
+ }
+
+ return rc;
+}
+
+#define MAXSAVE 512
+static int CYCNametoOutlet(struct pluginDevice *sd, const char *host, int *outlets, int maxoutlet)
+{
+ char *cmd = status_all;
+ char savebuf[MAXSAVE];
+ int err;
+ int outlet, numoutlet = 0;
+ char name[17], locked[11], on[4];
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return 0;
+ }
+
+ ZEROEXPECT(sd->rdfd, StatusOutput, 50);
+
+ ZEROEXPECT(sd->rdfd, CRNL, 50);
+
+ do {
+
+ memset(savebuf, 0, sizeof(savebuf));
+ memset(name, 0, sizeof(name));
+ memset(locked, 0, sizeof(locked));
+ memset(on, 0, sizeof(on));
+
+ err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf));
+
+ if ((err == S_OK) &&
+ (sscanf(savebuf,"%3d %16s %10s %3s", &outlet,
+ name, locked, on) > 0)) {
+ if (!strncasecmp(name, host, strlen(host))) {
+ if (numoutlet >= maxoutlet) {
+ LOG(PIL_CRIT, "too many outlets");
+ return 0;
+ }
+ outlets[numoutlet++] = outlet;
+ }
+ }
+
+ } while (err == S_OK);
+
+ return (numoutlet);
+}
+
+
+/*
+ * Return the list of hosts configured for this Cyclades device
+ */
+
+static char **
+cyclades_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ char *cmd = status_all;
+ char savebuf[MAXSAVE];
+ int err, i;
+ int outlet;
+ int numnames = 0;
+ char name[17], locked[11], on[4];
+ char *NameList[MAX_OUTLETS];
+ char **ret = NULL;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run status all command");
+ return (NULL);
+ }
+
+ memset(savebuf, 0, sizeof(savebuf));
+
+ NULLEXPECT(sd->rdfd, StatusOutput, 50);
+
+ NULLEXPECT(sd->rdfd, CRNL, 50);
+
+ do {
+ char *nm;
+
+ memset(savebuf, 0, sizeof(savebuf));
+ memset(name, 0, sizeof(name));
+ memset(locked, 0, sizeof(locked));
+ memset(on, 0, sizeof(on));
+
+ err = CYCScanLine(sd, 2, savebuf, sizeof(savebuf));
+
+ if ((err == S_OK) &&
+ (sscanf(savebuf,"%3d %16s %10s %3s", &outlet,
+ name, locked, on) > 0)) {
+ nm = (char *) STRDUP (name);
+ if (!nm) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ numnames++;
+ NameList[numnames] = NULL;
+ }
+
+ } while (err == S_OK);
+
+ if (numnames) {
+
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ } else {
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ return (ret);
+ }
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+
+ return (NULL);
+}
+
+
+static char *cyclades_outletstr(int *outlet, int numoutlet)
+{
+ int i, len;
+ char *ret;
+
+ /* maximum length per outlet is currently four (outlet is one to
+ * three digits, followed by either a comma or null), so add one
+ * for good measure */
+ len = numoutlet * 5 * sizeof(char);
+ if ((ret = MALLOC(len)) != NULL) {
+ snprintf(ret, len, "%d", outlet[0]);
+ for (i = 1; i < numoutlet; i++) {
+ char buf[5];
+ snprintf(buf, sizeof(buf), ",%d", outlet[i]);
+ strcat(ret, buf);
+ }
+ }
+ return(ret);
+}
+
+
+static int cyclades_onoff(struct pluginDevice *sd, int *outlet, int numoutlet,
+ const char *unitid, int req)
+{
+ const char * onoff;
+ char cmd[MAX_OUTLETS*4], expstring[64];
+ struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}};
+ char *outletstr;
+ int i;
+
+ onoff = (req == ST_POWERON ? "on" : "off");
+
+ memset(cmd, 0, sizeof(cmd));
+
+ outletstr = cyclades_outletstr(outlet, numoutlet);
+ if (outletstr == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (S_OOPS);
+ }
+ snprintf(cmd, sizeof(cmd), "%s %s", onoff, outletstr);
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run %s command", onoff);
+ FREE(outletstr);
+ return(S_OOPS);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring), "%d: Outlet turned %s."
+ , outlet[i], onoff);
+
+ exp[0].string = expstring;
+
+ /* FIXME: should handle "already powered on/off" case and inform
+ to log */
+
+ EXPECT(sd->rdfd, exp, 50);
+ }
+
+ LOG(PIL_DEBUG, "Power to host %s turned %s", unitid, onoff);
+
+ FREE(outletstr);
+ return (S_OK);
+}
+
+static int cyclades_reset(struct pluginDevice *sd, int *outlet, int numoutlet,
+ const char *unitid)
+{
+ char cmd[MAX_OUTLETS*4], expstring[64];
+ struct Etoken exp[] = {{NULL, 0, 0}, {NULL, 0, 0}};
+ char *outletstr;
+ int i;
+
+ memset(cmd, 0, sizeof(cmd));
+
+ outletstr = cyclades_outletstr(outlet, numoutlet);
+ if (outletstr == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (S_OOPS);
+ }
+ snprintf(cmd, sizeof(cmd), "%s %s", cycle, outletstr);
+
+ LOG(PIL_INFO, "Host %s being rebooted.", unitid);
+
+ if (CYC_robust_cmd(sd, cmd) != S_OK) {
+ LOG(PIL_CRIT, "can't run cycle command");
+ FREE(outletstr);
+ return(S_OOPS);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring)
+ , "%d: Outlet turned off.", outlet[i]);
+
+ exp[0].string = expstring;
+ RESETEXPECT(sd->rdfd, exp, 50);
+ }
+
+ for (i = 0; i < numoutlet; i++) {
+ memset(expstring, 0, sizeof(expstring));
+ snprintf(expstring, sizeof(expstring)
+ , "%d: Outlet turned on.", outlet[i]);
+
+ exp[0].string = expstring;
+ RESETEXPECT(sd->rdfd, exp, 50);
+ }
+
+ FREE(outletstr);
+ return (S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+cyclades_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice *sd;
+ int rc = 0;
+ int numoutlet, outlets[MAX_OUTLETS];
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+
+ numoutlet = CYCNametoOutlet(sd, host, outlets, MAX_OUTLETS);
+
+ if (!numoutlet) {
+ LOG(PIL_CRIT, "Unknown host %s to Cyclades PM", host);
+ return (S_OOPS);
+ }
+
+
+ switch (request) {
+ case ST_POWERON:
+ case ST_POWEROFF:
+ rc = cyclades_onoff(sd, outlets, numoutlet, host, request);
+ break;
+
+ case ST_GENERIC_RESET:
+ rc = cyclades_reset(sd, outlets, numoutlet, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static const char * const *
+cyclades_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_LOGIN, ST_SERIALPORT, NULL};
+ return ret;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+cyclades_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy[] =
+ { {ST_IPADDR, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_SERIALPORT, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->device = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->serial_port = atoi(namestocopy[2].s_value);
+ FREE(namestocopy[2].s_value);
+
+ return(S_OK);
+}
+
+static const char *
+cyclades_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice * sd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ sd = (struct pluginDevice*) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID: /* What type of device? */
+ /* FIXME: could inform the exact PM model */
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* What particular device? */
+ ret = sd->device;
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "Cyclades AlterPath PM "
+ "series power switches (via TS/ACS/KVM).";
+ break;
+
+ case ST_DEVICEURL: /* Manufacturer's web site */
+ ret = "http://www.cyclades.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = cycladesXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Cyclades Stonith destructor...
+ */
+static void
+cyclades_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice*) s;
+
+ sd->pluginid = NOTpluginID;
+ Stonithkillcomm(&sd->rdfd, &sd->wrfd, &sd->pid);
+ if (sd->device != NULL) {
+ FREE(sd->device);
+ sd->device = NULL;
+ }
+ if (sd->user != NULL) {
+ FREE(sd->user);
+ sd->user = NULL;
+ }
+
+ FREE(sd);
+}
+
+/* Create a new cyclades Stonith device */
+static StonithPlugin *
+cyclades_new(const char *plugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->pid = -1;
+ sd->rdfd = -1;
+ sd->wrfd = -1;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &cycladesOps;
+
+ return &(sd->sp); /* same as sd */
+}
diff --git a/lib/plugins/stonith/drac3.c b/lib/plugins/stonith/drac3.c
new file mode 100644
index 0000000..95be775
--- /dev/null
+++ b/lib/plugins/stonith/drac3.c
@@ -0,0 +1,359 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ * Tiny bits Copyright 2005 International Business Machines
+ * Significantly Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * (Using snippets of other stonith modules code)
+ *
+ * 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
+ *
+ */
+
+#define DEVICE "Dell DRACIII Card"
+#include "stonith_plugin_common.h"
+
+#include <curl/curl.h>
+#include "drac3_command.h"
+
+#define PIL_PLUGIN drac3
+#define PIL_PLUGIN_S "drac3"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+#include "stonith_signal.h"
+
+static StonithPlugin * drac3_new(const char *);
+static void drac3_destroy(StonithPlugin *);
+static const char * const * drac3_get_confignames(StonithPlugin *);
+static int drac3_set_config(StonithPlugin *, StonithNVpair *);
+static const char * drac3_getinfo(StonithPlugin * s, int InfoType);
+static int drac3_status(StonithPlugin * );
+static int drac3_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** drac3_hostlist(StonithPlugin *);
+
+static struct stonith_ops drac3Ops ={
+ drac3_new, /* Create new STONITH object */
+ drac3_destroy, /* Destroy STONITH object */
+ drac3_getinfo, /* Return STONITH info string */
+ drac3_get_confignames, /* Return configuration parameters */
+ drac3_set_config, /* Set configuration */
+ drac3_status, /* Return STONITH device status */
+ drac3_reset_req, /* Request a reset */
+ drac3_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &drac3Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define BUFLEN 1024
+#define ST_HOST "host"
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char *pluginid;
+ const char *idinfo;
+ CURL *curl;
+ char *host;
+ char *user;
+ char *pass;
+};
+
+static const char *pluginid = "Dell-DRACIII-Stonith";
+static const char *NOTpluginID = "Dell DRACIII device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_HOST_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_HOST \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOST_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hostname of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_HOST_PARM \
+ XML_PARAMETER_BEGIN(ST_HOST, "string", "1", "1") \
+ XML_HOST_SHORTDESC \
+ XML_HOST_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *drac3XML =
+ XML_PARAMETERS_BEGIN
+ XML_HOST_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/* ------------------------------------------------------------------ */
+/* STONITH PLUGIN API */
+/* ------------------------------------------------------------------ */
+static StonithPlugin *
+drac3_new(const char *subplugin)
+{
+ struct pluginDevice *drac3d = ST_MALLOCT(struct pluginDevice);
+
+ if (drac3d == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(drac3d, 0, sizeof(*drac3d));
+ drac3d->pluginid = pluginid;
+ drac3d->curl = curl_easy_init();
+ drac3InitCurl(drac3d->curl);
+ drac3d->host = NULL;
+ drac3d->user = NULL;
+ drac3d->pass = NULL;
+ drac3d->idinfo = DEVICE;
+ drac3d->sp.s_ops = &drac3Ops;
+ return (&(drac3d->sp));
+}
+
+/* ------------------------------------------------------------------ */
+static void
+drac3_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *drac3d;
+
+ VOIDERRIFWRONGDEV(s);
+
+ drac3d = (struct pluginDevice *) s;
+
+ drac3d->pluginid = NOTpluginID;
+
+ /* release curl connection */
+ if (drac3d->curl != NULL) {
+ drac3Logout(drac3d->curl, drac3d->host);
+ curl_easy_cleanup(drac3d->curl);
+ drac3d->curl = NULL;
+ }
+
+ if (drac3d->host != NULL) {
+ FREE(drac3d->host);
+ drac3d->host = NULL;
+ }
+ if (drac3d->user != NULL) {
+ FREE(drac3d->user);
+ drac3d->user = NULL;
+ }
+ if (drac3d->pass != NULL) {
+ FREE(drac3d->pass);
+ drac3d->pass = NULL;
+ }
+
+ /* release stonith-object itself */
+ FREE(drac3d);
+}
+
+/* ------------------------------------------------------------------ */
+static const char * const *
+drac3_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_HOST, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+static int
+drac3_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOST, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->host = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->pass = namestocopy[2].s_value;
+
+ return(S_OK);
+}
+
+/* ------------------------------------------------------------------ */
+const char *
+drac3_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *drac3d;
+ const char *ret = NULL;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ drac3d = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = drac3d->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = drac3d->host;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Dell DRACIII (via HTTPS)\n"
+ "The Dell Remote Access Controller accepts XML "
+ "commands over HTTPS";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.dell.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = drac3XML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return(ret);
+}
+
+/* ------------------------------------------------------------------ */
+int
+drac3_status(StonithPlugin *s)
+{
+ struct pluginDevice *drac3d;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ drac3d = (struct pluginDevice *) s;
+
+ if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
+ if (drac3Login(drac3d->curl, drac3d->host,
+ drac3d->user, drac3d->pass)) {
+ LOG(PIL_CRIT, "%s: cannot log into %s at %s",
+ __FUNCTION__,
+ drac3d->idinfo,
+ drac3d->host);
+ return(S_ACCESS);
+ }
+ }
+
+ if (drac3GetSysInfo(drac3d->curl, drac3d->host)) {
+ return(S_ACCESS);
+ }else{
+ return(S_OK);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+int
+drac3_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *drac3d;
+ int rc = S_OK;
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ drac3d = (struct pluginDevice *) s;
+
+ if (strcasecmp(host, drac3d->host)) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , drac3d->idinfo, host);
+ return(S_BADHOST);
+ }
+
+ if (drac3VerifyLogin(drac3d->curl, drac3d->host)) {
+ if (drac3Login(drac3d->curl, drac3d->host,
+ drac3d->user, drac3d->pass)) {
+ LOG(PIL_CRIT, "%s: cannot log into %s at %s",
+ __FUNCTION__,
+ drac3d->idinfo,
+ drac3d->host);
+ return(S_ACCESS);
+ }
+ }
+
+ switch(request) {
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ case ST_POWEROFF:
+ /* TODO... */
+#endif
+ case ST_GENERIC_RESET:
+ if (drac3PowerCycle(drac3d->curl, drac3d->host))
+ rc = S_ACCESS;
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ return(rc);
+}
+
+/* ------------------------------------------------------------------ */
+char **
+drac3_hostlist(StonithPlugin * s)
+{
+ struct pluginDevice *drac3d;
+ char **hl;
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ drac3d = (struct pluginDevice *) s;
+
+ hl = OurImports->StringToHostList(drac3d->host);
+ if (hl == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ } else {
+ strdown(hl[0]);
+ }
+
+ return(hl);
+}
diff --git a/lib/plugins/stonith/drac3_command.c b/lib/plugins/stonith/drac3_command.c
new file mode 100644
index 0000000..4d9002d
--- /dev/null
+++ b/lib/plugins/stonith/drac3_command.c
@@ -0,0 +1,342 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <curl/curl.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "drac3_command.h"
+#include "drac3_hash.h"
+
+#define BUFLEN 1024 /* buffer */
+#define SBUFLEN 256 /* small buffer */
+#define MD5LEN 16 /* md5 buffer */
+
+#define DEBUG 0
+
+/* Hardcoded XML commands and response codes */
+#define CMD_POWERCYCLE "<?XML version=\"1.0\"?><?RMCXML version=\"1.0\"?><RMCSEQ><REQ CMD=\"serveraction\"><ACT>powercycle</ACT></REQ></RMCSEQ>\n"
+#define CMD_GETSYSINFO "<?XML version=\"1.0\"?><?RMCXML version=\"1.0\"?><RMCSEQ><REQ CMD=\"xml2cli\"><CMDINPUT>getsysinfo -A</CMDINPUT></REQ></RMCSEQ>\n"
+#define RC_OK "0x0\n"
+
+struct Chunk {
+ char *memory;
+ size_t size;
+};
+
+/* prototypes */
+int xmlGetXPathString (const char *str, const char * expr, char * rc, const int len);
+size_t writeFunction (void *ptr, size_t size, size_t nmemb, void *data);
+
+
+/* ---------------------------------------------------------------------- *
+ * XML PARSING *
+ * ---------------------------------------------------------------------- */
+
+int
+xmlGetXPathString (const char *str,
+ const char * expr,
+ char * rc,
+ const int len)
+{
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+ xmlXPathContextPtr ctx;
+ xmlXPathObjectPtr path;
+ xmlChar *xmlRC;
+
+ if (!strchr(str,'<')) {
+ fprintf(stderr,"%s malformed\n", str);
+ rc[0] = 0x00;
+ return(1);
+ }
+
+ doc = xmlParseMemory(str, strlen(str));
+ xmlXPathInit();
+ ctx = xmlXPathNewContext(doc);
+ path = xmlXPathEvalExpression((const xmlChar *)expr, ctx);
+ cur = path->nodesetval->nodeTab[0];
+
+ if (cur != NULL) {
+ xmlRC = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+ snprintf(rc, len, "%s\n", xmlRC);
+ xmlFree(xmlRC);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ xmlXPathFreeObject(path);
+ xmlXPathFreeContext(ctx);
+
+ return(0);
+ } else {
+ fprintf(stderr,"error in obtaining XPath %s\n", expr);
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ xmlXPathFreeObject(path);
+ xmlXPathFreeContext(ctx);
+
+ rc[0] = 0x00;
+ return(1);
+ }
+}
+
+
+/* ---------------------------------------------------------------------- *
+ * CURL CALLBACKS *
+ * ---------------------------------------------------------------------- */
+
+size_t
+writeFunction (void *ptr, size_t size, size_t nmemb, void *data)
+{
+
+ register int realsize = size * nmemb;
+ struct Chunk *mem = (struct Chunk *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+
+/* ---------------------------------------------------------------------- *
+ * DRAC3 CURL COMMANDS *
+ * ---------------------------------------------------------------------- */
+
+int
+drac3InitCurl (CURL *curl)
+{
+#ifdef CURLOPT_NOSIGNAL
+ if (curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)) return(1);
+#endif
+ if (curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_VERBOSE, 0)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/dev/null")) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0)) return(1);
+ return(0);
+}
+
+int
+drac3Login (CURL *curl,
+ const char *host,
+ const char *user,
+ const char *pass)
+{
+ char url[BUFLEN];
+ char chall[BUFLEN];
+ char token[BUFLEN];
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk))
+ return(1);
+
+ /* ask for challenge */
+ snprintf(url, BUFLEN, "https://%s/cgi/challenge", host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url))
+ return(1);
+ if (curl_easy_perform(curl))
+ return(1);
+
+ /* extract challenge */
+ status = xmlGetXPathString(chunk.memory, "//CHALLENGE", chall, BUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ /* calculate authToken */
+ drac3AuthHash(chall, pass, token, BUFLEN);
+
+ if (DEBUG) printf("T: %s\n", token);
+
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ if (status) return(1);
+ chunk.memory = NULL;
+ chunk.size = 0;
+
+ /* sends authToken */
+ snprintf(url, BUFLEN, "https://%s/cgi/login?user=%s&hash=%s",
+ host,
+ user,
+ token);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url))
+ return(1);
+ if (curl_easy_perform(curl))
+ return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+int
+drac3PowerCycle (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char cmd[]=CMD_POWERCYCLE;
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/bin",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+
+int
+drac3GetSysInfo (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char cmd[]=CMD_GETSYSINFO;
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/bin",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_setopt(curl, CURLOPT_POSTFIELDS, cmd)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+
+int
+drac3Logout (CURL *curl,
+ const char *host)
+{
+ char url[BUFLEN];
+ char rc[SBUFLEN];
+ int status;
+ struct Chunk chunk;
+
+ chunk.memory = NULL;
+ chunk.size = 0;
+ if (curl_easy_setopt(curl, CURLOPT_FILE, (void *)&chunk)) return(1);
+
+ snprintf(url, BUFLEN, "https://%s/cgi/logout",
+ host);
+ url[BUFLEN-1] = 0x00;
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url)) return(1);
+ if (curl_easy_perform(curl)) return(1);
+
+ if (DEBUG) printf("R: %s\n", chunk.memory);
+ status = xmlGetXPathString(chunk.memory, "//RC", rc, SBUFLEN);
+ if (status) {
+ free(chunk.memory);
+ return(1);
+ }
+ if (DEBUG) printf("RC: %s\n", rc);
+
+ status = (strcmp(rc, RC_OK) == 0) ? 0 : 1;
+ free(chunk.memory);
+ return(status);
+}
+
+int
+drac3VerifyLogin (CURL *curl,
+ const char *host)
+{
+ /*We try to do a GetSysInfo */
+ return(drac3GetSysInfo (curl, host));
+}
+
+/* -------------------------------------------------------------------- */
+
diff --git a/lib/plugins/stonith/drac3_command.h b/lib/plugins/stonith/drac3_command.h
new file mode 100644
index 0000000..cd03e15
--- /dev/null
+++ b/lib/plugins/stonith/drac3_command.h
@@ -0,0 +1,29 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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
+ *
+ */
+
+int drac3InitCurl (CURL *curl);
+int drac3Login (CURL *curl, const char *host, const char *user, const char *pass);
+int drac3PowerCycle (CURL *curl, const char *host);
+int drac3GetSysInfo (CURL *curl, const char *host);
+int drac3Logout (CURL *curl, const char *host);
+int drac3VerifyLogin (CURL *curl, const char *host);
+
diff --git a/lib/plugins/stonith/drac3_hash.c b/lib/plugins/stonith/drac3_hash.c
new file mode 100644
index 0000000..605a126
--- /dev/null
+++ b/lib/plugins/stonith/drac3_hash.c
@@ -0,0 +1,106 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <clplumbing/base64.h>
+#include <clplumbing/md5.h>
+#include <glib.h>
+
+#include "drac3_hash.h"
+
+#define BUFLEN 1024 /* buffer */
+#define SBUFLEN 256 /* small buffer */
+#define MD5LEN 16 /* md5 buffer */
+
+/* Hash functions for DRAC3 authentication */
+
+guint16
+drac3Crc16(const char *str,
+ const int l) {
+
+ int i,j;
+ guint16 crc = 0;
+
+ for (i=0; i<l; i++) {
+ crc = crc ^ (str[i] << 8);
+ for (j=0; j<8; j++)
+ crc = ( (crc & 0x8000) == 32768 ? (crc<<1) ^ 0x1021 : crc<<1);
+ }
+ crc = crc & 0xFFFF;
+ return crc;
+}
+
+void
+drac3AuthHash(const char * chall,
+ const char * pass,
+ char * token,
+ int len) {
+
+ char * chall_dup;
+ char challBytes[MD5LEN];
+ char passMD5[MD5LEN];
+ char xorBytes[MD5LEN];
+ char xorBytesMD5[MD5LEN];
+ guint16 crc;
+ char response[MD5LEN+2];
+ char responseb64[SBUFLEN];
+ int i;
+
+ /* decodes chall -> challBytes */
+ memset(challBytes, 0, MD5LEN);
+ chall_dup = g_strdup(chall);
+ if (chall_dup[strlen(chall_dup) - 1] == '\n' ) {
+ chall_dup[strlen(chall_dup) - 1] = '\0';
+ }
+ base64_to_binary(chall_dup, strlen(chall_dup), challBytes, MD5LEN);
+
+ /* gets MD5 from pass -> passMD5 */
+ MD5((const unsigned char *)pass, strlen(pass), (unsigned char *)passMD5);
+
+ /* calculate challBytes and passMD5 xor -> xorBytes */
+ for (i=0; i<MD5LEN; i++) {
+ xorBytes[i] = challBytes[i] ^ passMD5[i];
+ }
+
+ /* calculate xorBytes MD5 -> xorBytesMD5 */
+ MD5((unsigned char *)xorBytes, MD5LEN, (unsigned char *)xorBytesMD5);
+
+ /* calculate xorBytesMD5 crc16 */
+ crc = drac3Crc16(xorBytesMD5, MD5LEN);
+
+ /* joins xorBytesMD5 and crc16 -> response */
+ memcpy(response, xorBytesMD5, MD5LEN);
+ memcpy(response+MD5LEN, &crc, 2);
+
+ /* calculate response base64 -> responseb64 */
+ memset(responseb64, 0, SBUFLEN);
+ binary_to_base64(response, MD5LEN+2, responseb64, SBUFLEN);
+
+ /* assuring null-termination */
+ responseb64[SBUFLEN-1]=0x00;
+
+ snprintf(token, len, "%s", responseb64);
+ token[len-1]=0x00;
+}
diff --git a/lib/plugins/stonith/drac3_hash.h b/lib/plugins/stonith/drac3_hash.h
new file mode 100644
index 0000000..fab2f58
--- /dev/null
+++ b/lib/plugins/stonith/drac3_hash.h
@@ -0,0 +1,28 @@
+/*
+ * Stonith module for Dell DRACIII (Dell Remote Access Card)
+ *
+ * Copyright (C) 2003 Alfa21 Outsourcing
+ * Copyright (C) 2003 Roberto Moreda <moreda@alfa21.com>
+ *
+ * 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
+ *
+ */
+
+#include <sys/types.h>
+#include <glib.h>
+
+guint16 drac3Crc16(const char *str, const int l);
+void drac3AuthHash(const char *chall, const char *pass, char *token, int len);
+
diff --git a/lib/plugins/stonith/external.c b/lib/plugins/stonith/external.c
new file mode 100644
index 0000000..da03665
--- /dev/null
+++ b/lib/plugins/stonith/external.c
@@ -0,0 +1,868 @@
+/*
+ * Stonith module for EXTERNAL Stonith device
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ * Portions Copyright (c) 2004, tummy.com, ltd.
+ *
+ * Based on ssh.c, Authors: Joachim Gleissner <jg@suse.de>,
+ * Lars Marowsky-Bree <lmb@suse.de>
+ * Modified for external.c: Scott Kleihege <scott@tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo@tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb@suse.de>, so the circle
+ * closes...
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke
+ * <debltc@us.ibm.com>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <dirent.h>
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN external
+#define PIL_PLUGIN_S "external"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#include <pils/plugin.h>
+
+static StonithPlugin * external_new(const char *);
+static void external_destroy(StonithPlugin *);
+static int external_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * external_get_confignames(StonithPlugin *);
+static const char * external_getinfo(StonithPlugin * s, int InfoType);
+static int external_status(StonithPlugin * );
+static int external_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** external_hostlist(StonithPlugin *);
+
+static struct stonith_ops externalOps ={
+ external_new, /* Create new STONITH object */
+ external_destroy, /* Destroy STONITH object */
+ external_getinfo, /* Return STONITH info string */
+ external_get_confignames, /* Return STONITH info string */
+ external_set_config, /* Get configuration from NVpairs */
+ external_status, /* Return STONITH device status */
+ external_reset_req, /* Request a reset */
+ external_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &externalOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * EXTERNAL STONITH device
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ GHashTable * cmd_opts;
+ char * subplugin;
+ char ** confignames;
+ char * outputbuf;
+};
+
+static const char * pluginid = "ExternalDevice-Stonith";
+static const char * NOTpluginID = "External device has been destroyed";
+
+/* Prototypes */
+
+/* Run the command with op and return the exit status + the output
+ * (NULL -> discard output) */
+static int external_run_cmd(struct pluginDevice *sd, const char *op,
+ char **output);
+/* Just free up the configuration and the memory, if any */
+static void external_unconfig(struct pluginDevice *sd);
+
+static int
+external_status(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ const char * op = "status";
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ rc = external_run_cmd(sd, op, NULL);
+ if (rc != 0) {
+ LOG(PIL_WARN, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static char **
+external_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ const char * op = "gethosts";
+ int rc, i, namecount;
+ char ** ret;
+ char * output = NULL;
+ char * tmp;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return NULL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+
+ if (!output) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ return NULL;
+ }
+
+ namecount = get_num_tokens(output);
+ ret = MALLOC((namecount+1)*sizeof(char *));
+ if (!ret) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ FREE(output);
+ return NULL;
+ }
+ memset(ret, 0, (namecount+1)*sizeof(char *));
+
+ /* White-space split the output here */
+ i = 0;
+ tmp = strtok(output, WHITESPACE);
+ while (tmp != NULL) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s host %s",
+ __FUNCTION__, sd->subplugin, tmp);
+ }
+ ret[i] = STRDUP(tmp);
+ if (!ret[i]) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ FREE(output);
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+
+ FREE(output);
+
+ if (i == 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ }
+
+ return(ret);
+}
+
+static int
+external_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice * sd;
+ const char * op;
+ int rc;
+ char * args1and2;
+ int argslen;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Host external-reset initiating on %s", host);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ switch (request) {
+ case ST_GENERIC_RESET:
+ op = "reset";
+ break;
+
+ case ST_POWEROFF:
+ op = "off";
+ break;
+
+ case ST_POWERON:
+ op = "on";
+ break;
+
+ default:
+ LOG(PIL_CRIT, "%s: Unknown stonith request %d",
+ __FUNCTION__, request);
+ return S_OOPS;
+ break;
+ }
+
+ argslen = strlen(op) + strlen(host) + 2 /* 1 for blank, 1 for EOS */;
+ args1and2 = (char *)MALLOC(argslen);
+ if (args1and2 == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ return S_OOPS;
+ }
+ rc = snprintf(args1and2, argslen, "%s %s", op, host);
+ if (rc <= 0 || rc >= argslen) {
+ FREE(args1and2);
+ return S_OOPS;
+ }
+
+ rc = external_run_cmd(sd, args1and2, NULL);
+ FREE(args1and2);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, host, rc);
+ return S_RESETFAIL;
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ return S_OK;
+ }
+
+}
+
+static int
+external_parse_config_info(struct pluginDevice* sd, StonithNVpair * info)
+{
+ char * key;
+ char * value;
+ StonithNVpair * nv;
+
+ sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* TODO: Maybe treat "" as delimeters too so
+ * whitespace can be passed to the plugins... */
+ for (nv = info; nv->s_name; nv++) {
+ if (!nv->s_name || !nv->s_value) {
+ continue;
+ }
+
+ key = STRDUP(nv->s_name);
+ if (!key) {
+ goto err_mem;
+ }
+ value = STRDUP(nv->s_value);
+ if (!value) {
+ FREE(key);
+ goto err_mem;
+ }
+ g_hash_table_insert(sd->cmd_opts, key, value);
+ }
+
+ return(S_OK);
+
+err_mem:
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ external_unconfig(sd);
+
+ return(S_OOPS);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ if (key) {
+ FREE(key);
+ }
+ if (value) {
+ FREE(value);
+ }
+ return TRUE;
+}
+
+static void
+external_unconfig(struct pluginDevice *sd) {
+ if (sd->cmd_opts) {
+ g_hash_table_foreach_remove(sd->cmd_opts,
+ let_remove_eachitem, NULL);
+ g_hash_table_destroy(sd->cmd_opts);
+ sd->cmd_opts = NULL;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+external_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* make sure that command has not already been set */
+ if (s->isconfigured) {
+ return(S_OOPS);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ if (sd->confignames == NULL) {
+ /* specified by name=value pairs, check required parms */
+ if (external_get_confignames(s) == NULL) {
+ return(S_OOPS);
+ }
+
+ for (p = sd->confignames; *p; p++) {
+ if (OurImports->GetValue(list, *p) == NULL) {
+ LOG(PIL_DEBUG, "Cannot get parameter %s from "
+ "StonithNVpair", *p);
+ }
+ }
+ }
+
+ return external_parse_config_info(sd, list);
+}
+
+
+/* Only interested in regular files that are also executable */
+static int
+exec_select(const struct dirent *dire)
+{
+ struct stat statf;
+ char filename[FILENAME_MAX];
+ int rc;
+
+ rc = snprintf(filename, FILENAME_MAX, "%s/%s",
+ STONITH_EXT_PLUGINDIR, dire->d_name);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ return 0;
+ }
+
+ if ((stat(filename, &statf) == 0) &&
+ (S_ISREG(statf.st_mode)) &&
+ (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ if (statf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_WARN, "Executable file %s ignored "
+ "(writable by group/others)", filename);
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+external_get_confignames(StonithPlugin* p)
+{
+ struct pluginDevice * sd;
+ const char * op = "getconfignames";
+ int i, rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ sd = (struct pluginDevice *)p;
+
+ if (sd->subplugin != NULL) {
+ /* return list of subplugin's required parameters */
+ char *output = NULL, *pch;
+ int namecount;
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return NULL;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_DEBUG, "plugin output: %s", output);
+ }
+ }
+
+ namecount = get_num_tokens(output);
+ sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *));
+ if (sd->confignames == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ if (output) { FREE(output); }
+ return NULL;
+ }
+
+ /* now copy over confignames */
+ pch = strtok(output, WHITESPACE);
+ for (i = 0; i < namecount; i++) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s configname %s",
+ __FUNCTION__, sd->subplugin, pch);
+ }
+ sd->confignames[i] = STRDUP(pch);
+ pch = strtok(NULL, WHITESPACE);
+ }
+ FREE(output);
+ sd->confignames[namecount] = NULL;
+ }else{
+ /* return list of subplugins in external directory */
+ struct dirent ** files = NULL;
+ int dircount;
+
+ /* get the external plugin's confignames (list of subplugins) */
+ dircount = scandir(STONITH_EXT_PLUGINDIR, &files,
+ SCANSEL_CAST exec_select, NULL);
+ if (dircount < 0) {
+ return NULL;
+ }
+
+ sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *));
+ if (!sd->confignames) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < dircount; i++) {
+ sd->confignames[i] = STRDUP(files[i]->d_name);
+ free(files[i]);
+ files[i] = NULL;
+ }
+ free(files);
+ sd->confignames[dircount] = NULL;
+ }
+
+ return (const char * const *)sd->confignames;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+external_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd;
+ char * output = NULL;
+ const char * op;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ sd = (struct pluginDevice *)s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ op = "getinfo-devid";
+ break;
+
+ case ST_DEVICENAME:
+ op = "getinfo-devname";
+ break;
+
+ case ST_DEVICEDESCR:
+ op = "getinfo-devdescr";
+ break;
+
+ case ST_DEVICEURL:
+ op = "getinfo-devurl";
+ break;
+
+ case ST_CONF_XML:
+ op = "getinfo-xml";
+ break;
+
+ default:
+ return NULL;
+ }
+
+ rc = external_run_cmd(sd, op, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ }
+ sd->outputbuf = output;
+ return(output);
+ }
+ return(NULL);
+}
+
+/*
+ * EXTERNAL Stonith destructor...
+ */
+static void
+external_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginID;
+ external_unconfig(sd);
+ if (sd->confignames != NULL) {
+ for (p = sd->confignames; *p; p++) {
+ FREE(*p);
+ }
+ FREE(sd->confignames);
+ sd->confignames = NULL;
+ }
+ if (sd->subplugin != NULL) {
+ FREE(sd->subplugin);
+ sd->subplugin = NULL;
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ FREE(sd);
+}
+
+/* Create a new external Stonith device */
+static StonithPlugin *
+external_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ if (subplugin != NULL) {
+ sd->subplugin = STRDUP(subplugin);
+ if (sd->subplugin == NULL) {
+ FREE(sd);
+ return(NULL);
+ }
+ }
+ sd->sp.s_ops = &externalOps;
+ return &(sd->sp);
+}
+
+static void
+ext_add_to_env(gpointer key, gpointer value, gpointer user_data)
+{
+ if (setenv((char *)key, (char *)value, 1) != 0) {
+ LOG(PIL_CRIT, "%s: setenv failed.", __FUNCTION__);
+ }
+}
+
+static void
+ext_del_from_env(gpointer key, gpointer value, gpointer user_data)
+{
+ unsetenv((char *)key);
+}
+
+#define LOGTAG_VAR "HA_LOGTAG"
+
+/* Run the command with op as command line argument(s) and return the exit
+ * status + the output */
+static int
+external_run_cmd(struct pluginDevice *sd, const char *op, char **output)
+{
+ const int BUFF_LEN=4096;
+ char buff[BUFF_LEN];
+ int read_len = 0;
+ int status, rc;
+ char * data = NULL;
+ FILE * file;
+ char cmd[FILENAME_MAX+64];
+ struct stat buf;
+ int slen;
+ char *path, *new_path, *logtag, *savevar = NULL;
+ int new_path_len, logtag_len;
+ gboolean nodata;
+
+ rc = snprintf(cmd, FILENAME_MAX, "%s/%s",
+ STONITH_EXT_PLUGINDIR, sd->subplugin);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__);
+ return -1;
+ }
+
+ if (stat(cmd, &buf) != 0) {
+ LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s",
+ __FUNCTION__, cmd, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(buf.st_mode)
+ || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
+ LOG(PIL_CRIT, "%s: %s found NOT to be executable.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (buf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_CRIT, "%s: %s found to be writable by group/others, "
+ "NOT executing for security purposes.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ strcat(cmd, " ");
+ strcat(cmd, op);
+
+ /* We only have a global environment to use here. So we add our
+ * options to it, and then later remove them again. */
+ if (sd->cmd_opts) {
+ g_hash_table_foreach(sd->cmd_opts, ext_add_to_env, NULL);
+ }
+
+ /* external plugins need path to ha_log.sh */
+ path = getenv("PATH");
+ if (strncmp(GLUE_SHARED_DIR,path,strlen(GLUE_SHARED_DIR))) {
+ new_path_len = strlen(path)+strlen(GLUE_SHARED_DIR)+2;
+ new_path = (char *)g_malloc(new_path_len);
+ snprintf(new_path, new_path_len, "%s:%s", GLUE_SHARED_DIR, path);
+ setenv("PATH", new_path, 1);
+ g_free(new_path);
+ }
+
+ /* set the logtag appropriately */
+ logtag_len = strlen(PIL_PLUGIN_S)+strlen(sd->subplugin)+2;
+ logtag = (char *)g_malloc(logtag_len);
+ snprintf(logtag, logtag_len, "%s/%s", PIL_PLUGIN_S, sd->subplugin);
+ if (getenv(LOGTAG_VAR)) {
+ savevar = g_strdup(getenv(LOGTAG_VAR));
+ }
+ setenv(LOGTAG_VAR, logtag, 1);
+ g_free(logtag);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd );
+ }
+ file = popen(cmd, "r");
+ if (NULL==file) {
+ LOG(PIL_CRIT, "%s: Calling '%s' failed",
+ __FUNCTION__, cmd);
+ rc = -1;
+ goto out;
+ }
+
+ if (output) {
+ slen=0;
+ data = MALLOC(1);
+ data[slen] = EOS;
+ }
+ while (!feof(file)) {
+ nodata = TRUE;
+ if (output) {
+ read_len = fread(buff, 1, BUFF_LEN, file);
+ if (read_len > 0) {
+ data = REALLOC(data, slen+read_len+1);
+ if (data == NULL) {
+ break;
+ }
+ memcpy(data + slen, buff, read_len);
+ slen += read_len;
+ data[slen] = EOS;
+ nodata = FALSE;
+ }
+ } else {
+ if (fgets(buff, BUFF_LEN, file)) {
+ LOG(PIL_INFO, "%s: '%s' output: %s", __FUNCTION__, cmd, buff);
+ nodata = FALSE;
+ }
+ }
+ if (nodata) {
+ sleep(1);
+ }
+ }
+ if (output && !data) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ rc = -1;
+ goto out;
+ }
+
+ status = pclose(file);
+ if (WIFEXITED(status)) {
+ rc = WEXITSTATUS(status);
+ if (rc != 0 && Debug) {
+ LOG(PIL_DEBUG,
+ "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc);
+ }
+ } else {
+ if (WIFSIGNALED(status)) {
+ LOG(PIL_CRIT, "%s: '%s' got signal %d",
+ __FUNCTION__, cmd, WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ LOG(PIL_INFO, "%s: '%s' stopped with signal %d",
+ __FUNCTION__, cmd, WSTOPSIG(status));
+ } else {
+ LOG(PIL_CRIT, "%s: '%s' exited abnormally (core dumped?)",
+ __FUNCTION__, cmd);
+ }
+ rc = -1;
+ }
+ if (Debug && output && data) {
+ LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
+ }
+
+out:
+ if (savevar) {
+ setenv(LOGTAG_VAR, savevar, 1);
+ g_free(savevar);
+ } else {
+ unsetenv(LOGTAG_VAR);
+ }
+ if (sd->cmd_opts) {
+ g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL);
+ }
+ if (!rc) {
+ if (output) {
+ *output = data;
+ }
+ } else {
+ if (data) {
+ FREE(data);
+ }
+ if (output) {
+ *output = NULL;
+ }
+ }
+ return rc;
+}
diff --git a/lib/plugins/stonith/external/Makefile.am b/lib/plugins/stonith/external/Makefile.am
new file mode 100644
index 0000000..42e0046
--- /dev/null
+++ b/lib/plugins/stonith/external/Makefile.am
@@ -0,0 +1,33 @@
+# Makefile.am for OCF RAs
+#
+# Author: Sun Jing Dong
+# Copyright (C) 2004 IBM
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+EXTRA_DIST = drac5 dracmc-telnet ibmrsa-telnet ipmi rackpdu vmware vcenter xen0 \
+ xen0-ha-dom0-stonith-helper kdumpcheck nut
+
+extdir = $(stonith_ext_plugindir)
+
+helperdir = $(stonith_plugindir)
+
+ext_SCRIPTS = drac5 dracmc-telnet ibmrsa ibmrsa-telnet ipmi riloe ssh vmware vcenter rackpdu xen0 hmchttp \
+ xen0-ha kdumpcheck ippower9258 nut libvirt \
+ hetzner
+
+helper_SCRIPTS = xen0-ha-dom0-stonith-helper
diff --git a/lib/plugins/stonith/external/drac5.in b/lib/plugins/stonith/external/drac5.in
new file mode 100644
index 0000000..218cbd3
--- /dev/null
+++ b/lib/plugins/stonith/external/drac5.in
@@ -0,0 +1,113 @@
+#!/bin/sh
+#
+# External STONITH module for DRAC5 adapters.
+#
+# Author: Jun Wang
+# License: GNU General Public License (GPL)
+#
+
+trap 'if [ -n "$outf" ]; then ha_log.sh err "`cat $outf`"; rm -f "$outf"; fi' 0
+outf=`mktemp` || {
+ ha_log.sh err "mktemp failed"
+ exit 1
+}
+
+sshlogin() {
+ if [ x = "x$ipaddr" -o x = "x$userid" ]
+ then
+ ha_log.sh err "ipaddr or userid missing; check configuration"
+ return 1
+ fi
+ @SSH@ -q -x -n $userid@$ipaddr racadm serveraction "$1" >$outf 2>&1
+}
+
+drac_reset() {
+ sshlogin hardreset
+}
+
+drac_on() {
+ sshlogin poweron
+}
+
+drac_off() {
+ sshlogin poweroff
+}
+
+drac_status() {
+ sshlogin powerstatus
+}
+
+case $1 in
+gethosts)
+ echo $hostname
+ ;;
+on)
+ drac_poweron
+ ;;
+off)
+ drac_poweroff
+ ;;
+reset)
+ drac_reset
+ ;;
+status)
+ drac_status
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid; do
+ echo $i
+ done
+ ;;
+getinfo-devid)
+ echo "DRAC5 STONITH device"
+ ;;
+getinfo-devname)
+ echo "DRAC5 STONITH device"
+ ;;
+getinfo-devdescr)
+ echo "DRAC5 host reset/poweron/poweroff"
+ ;;
+getinfo-devurl)
+ echo "http://www.dell.com"
+ ;;
+getinfo-xml)
+ cat <<EOF
+<parameters>
+
+<parameter name="hostname" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The hostname of the host to be managed by this STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used for logging in to the STONITH device
+</longdesc>
+</parameter>
+
+</parameters>
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/dracmc-telnet b/lib/plugins/stonith/external/dracmc-telnet
new file mode 100644
index 0000000..d993961
--- /dev/null
+++ b/lib/plugins/stonith/external/dracmc-telnet
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# vim: set filetype=python
+#######################################################################
+#
+# dracmc-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+# Connects to Dell Drac/MC Blade Enclosure via a Cyclades
+# terminal server with telnet and switches power of named
+# blade servers appropriatelly.
+#
+# Required parameters:
+# nodename: The name of the server you want to touch on your network
+# cyclades_ip: The IP address of the cyclades terminal server
+# cyclades_port: The port for telnet to access on the cyclades (i.e. 7032)
+# servername: The DRAC/MC server name of the blade (i.e. Server-7)
+# username: The login user name for the DRAC/MC
+# password: The login password for the DRAC/MC
+#
+# Author: Alex Tsariounov <alext@novell.com>
+#
+# Based on ibmrsa-telnet external stonith plugin by Andreas Mock
+# (andreas.mock@web.de), Copyright by Adreas Mock and released as part
+# of HAv2.
+#
+# History:
+# 2009-10-12 First release.
+#
+# Copyright (c) 2009 Novell, Inc.
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 or later of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+#######################################################################
+import sys
+import os
+import time
+import telnetlib
+import random
+import subprocess
+
+LOGINRETRIES = 10
+
+class TimeoutException(Exception):
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class DracMC(telnetlib.Telnet):
+ def __init__(self, *args, **kwargs):
+ telnetlib.Telnet.__init__(self)
+ self._timeout = 4
+ self._loggedin = 0
+ self._history = []
+ self._appl = os.path.basename(sys.argv[0])
+ self._server = args[0]
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def write(self, buffer):
+ self._history.append(self._get_timestamp() + ': WRITE: ' + repr(buffer))
+ telnetlib.Telnet.write(self, buffer)
+
+ def read_until(self, what, timeout=2):
+ line = telnetlib.Telnet.read_until(self, what, timeout)
+ self._history.append(self._get_timestamp() + ': READ : ' + repr(line))
+ if not line.endswith(what):
+ raise TimeoutException("Timeout while waiting for '%s'." % (what, ))
+ return line
+
+ def login(self, user, passwd):
+ time.sleep(0.3)
+ try:
+ line = self.read_until('Login: ', self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.read_until('Password: ', self._timeout)
+ self.write(passwd)
+ self.write('\r')
+ except:
+ self.write("\r")
+ line = self.read_until('Login: ', self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.read_until('Password: ', self._timeout)
+ self.write(passwd)
+ self.write('\r')
+ try:
+ line = self.read_until('DRAC/MC:', self._timeout)
+ except:
+ self.write("\r")
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def hardreset(self):
+ self.write('serveraction -s %s hardreset\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def powercycle(self):
+ self.write('serveraction -s %s powercycle\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def on(self):
+ self.write('serveraction -s %s powerup\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def off(self):
+ self.write('serveraction -s %s powerdown\r' % self._server)
+ line = self.read_until('OK', 10)
+ line = self.read_until('DRAC/MC:', self._timeout)
+
+ def exit(self):
+ self.write('exit\r')
+
+ def get_history(self):
+ return "\n".join(self._history)
+
+
+class DracMCStonithPlugin:
+ def __init__(self):
+ # define the external stonith plugin api
+ self._required_cmds = \
+ 'reset gethosts status getconfignames getinfo-devid ' \
+ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \
+ 'getinfo-xml'
+ self._optional_cmds = 'on off'
+ self._required_cmds_list = self._required_cmds.split()
+ self._optional_cmds_list = self._optional_cmds.split()
+
+ # who am i
+ self._appl = os.path.basename(sys.argv[0])
+
+ # telnet connection object
+ self._connection = None
+
+ # the list of configuration names
+ self._confignames = ['nodename', 'cyclades_ip', 'cyclades_port',
+ 'servername', 'username', 'password']
+
+ # catch the parameters provided by environment
+ self._parameters = {}
+ for name in self._confignames:
+ try:
+ self._parameters[name] = os.environ.get(name, '').split()[0]
+ except IndexError:
+ self._parameters[name] = ''
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def _echo_debug(self, *args):
+ subprocess.call("ha_log.sh debug '%s'" % ' '.join(args), shell=True)
+
+ def echo(self, *args):
+ what = ''.join([str(x) for x in args])
+ sys.stdout.write(what)
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self._echo_debug("STDOUT:", what)
+
+ def echo_log(self, level, *args):
+ subprocess.call("ha_log.sh %s '%s'" % (level,' '.join(args)), shell=True)
+
+ def _get_connection(self):
+ if not self._connection:
+ c = DracMC(self._parameters['servername'])
+ self._echo_debug("Connecting to '%s:%s'" %
+ (self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port']))
+ tries = 0
+ while tries < LOGINRETRIES:
+ try:
+ c.open(self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port'])
+ c.login(self._parameters['username'],
+ self._parameters['password'])
+ except Exception, args:
+ if "Connection reset by peer" in str(args):
+ self._echo_debug("Someone is already logged in... retry=%s" % tries)
+ c.close()
+ time.sleep(random.uniform(1.0, 5.0))
+ else:
+ raise
+ else:
+ break
+ tries += 1
+
+ if tries == LOGINRETRIES:
+ c.close()
+ raise Exception("Could not log in to %s:%s" %
+ (self._parameters['cyclades_ip'],
+ self._parameters['cyclades_port']))
+ self._connection = c
+
+ def _end_connection(self):
+ if self._connection:
+ self._connection.exit()
+ self._connection.close()
+
+ def reset(self):
+ self._get_connection()
+ # self._connection.hardreset()
+ self._connection.powercycle()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Reset of node '%s' done" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def on(self):
+ self._get_connection()
+ self._connection.on()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' ON" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def off(self):
+ self._get_connection()
+ self._connection.off()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' OFF" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def gethosts(self):
+ self.echo(self._parameters['nodename'])
+ return(0)
+
+ def status(self):
+ self._get_connection()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ return(0)
+
+ def getconfignames(self):
+ for name in ['nodename', 'cyclades_ip', 'cyclades_port', 'servername',
+ 'username', 'password']:
+ self.echo(name)
+ return(0)
+
+ def getinfo_devid(self):
+ self.echo("External Stonith Plugin for Dell DRAC/MC via Cyclades")
+ return(0)
+
+ def getinfo_devname(self):
+ self.echo("External Stonith Plugin for Dell Drac/MC connecting "
+ "via Telnet to a Cyclades port")
+ return(0)
+
+ def getinfo_devdescr(self):
+ self.echo("External stonith plugin for HAv2 which connects to "
+ "a Dell DRAC/MC connected via a Cyclades port with telnet. "
+ "Commands to turn on/off power and to reset server are sent "
+ "appropriately. "
+ "(c) 2009 by Novell, Inc. (alext@novell.com)")
+ return(0)
+
+ def getinfo_devurl(self):
+ self.echo("http://support.dell.com/support/edocs/software/smdrac3/dracmc/1.3/en/index.htm")
+
+ def getinfo_xml(self):
+ info = """<parameters>
+ <parameter name="nodename" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">nodename to shoot</shortdesc>
+ <longdesc lang="en">
+ Name of the node to be stonithed.
+ </longdesc>
+ </parameter>
+ <parameter name="cyclades_ip" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">hostname or ip address of cyclades</shortdesc>
+ <longdesc lang="en">
+ Hostname or IP address of Cyclades connected to DRAC/MC.
+ </longdesc>
+ </parameter>
+ <parameter name="cyclades_port" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">telnet port to use on cyclades</shortdesc>
+ <longdesc lang="en">
+ Port used with the Cyclades telnet interface which is connected to the DRAC/MC.
+ </longdesc>
+ </parameter>
+ <parameter name="servername" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">DRAC/MC name of blade to be stonithed</shortdesc>
+ <longdesc lang="en">
+ Name of server blade to be stonithed on the DRAC/MC (example: Server-7)
+ </longdesc>
+ </parameter>
+ <parameter name="username" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">username to login on the DRAC/MC</shortdesc>
+ <longdesc lang="en">
+ Username to login to the DRAC/MC once connected via the Cyclades port.
+ </longdesc>
+ </parameter>
+ <parameter name="password" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">password to login on the DRAC/MC</shortdesc>
+ <longdesc lang="en">
+ Password to login to the DRAC/MC once connected via the Cyclades port.
+ </longdesc>
+ </parameter>
+ </parameters>
+ """
+ self.echo(info)
+ return(0)
+
+ def not_implemented(self, cmd):
+ self.echo_log("err", "Command '%s' not implemented." % (cmd,))
+ return(1)
+
+ def usage(self):
+ usage = "Call me with one of the allowed commands: %s, %s" % (
+ ', '.join(self._required_cmds_list),
+ ', '.join(self._optional_cmds_list))
+ return usage
+
+ def process(self, argv):
+ self._echo_debug("========== Start =============")
+ if len(argv) < 1:
+ self.echo_log("err", 'At least one commandline argument required.')
+ return(1)
+ cmd = argv[0]
+ self._echo_debug("cmd:", cmd)
+ if cmd not in self._required_cmds_list and \
+ cmd not in self._optional_cmds_list:
+ self.echo_log("err", "Command '%s' not supported." % (cmd,))
+ return(1)
+ try:
+ cmd = cmd.lower().replace('-', '_')
+ func = getattr(self, cmd, self.not_implemented)
+ rc = func()
+ return(rc)
+ except Exception, args:
+ self.echo_log("err", 'Exception raised:', str(args))
+ if self._connection:
+ self.echo_log("err", self._connection.get_history())
+ self._connection.close()
+ return(1)
+
+
+if __name__ == '__main__':
+ stonith = DracMCStonithPlugin()
+ rc = stonith.process(sys.argv[1:])
+ sys.exit(rc)
diff --git a/lib/plugins/stonith/external/hetzner b/lib/plugins/stonith/external/hetzner
new file mode 100755
index 0000000..2b3e675
--- /dev/null
+++ b/lib/plugins/stonith/external/hetzner
@@ -0,0 +1,139 @@
+#!/bin/sh
+#
+# External STONITH module for Hetzner.
+#
+# Copyright (c) 2011 MMUL S.a.S. - Raoul Scarazzini <rasca@mmul.it>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+# Read parameters from config file, format is based upon the hetzner OCF resource agent
+# developed by Kumina: http://blog.kumina.nl/2011/02/hetzner-failover-ip-ocf-script/
+conf_file="/etc/hetzner.cfg"
+
+case $1 in
+ get*) ;; # don't print errors if conf_file not present
+ *)
+ user=`sed -n 's/^user.*=\ *//p' $conf_file`
+ pass=`sed -n 's/^pass.*=\ *//p' $conf_file`
+ ;;
+esac
+
+hetzner_server="https://robot-ws.your-server.de"
+
+check_http_response() {
+ # If the response is 200 then return 0
+ if [ $1 = 200 ]
+ then
+ return 0
+ else
+ # If the response is not 200 then display a description of the problem and return 1
+ case $1 in
+ 400) ha_log.sh err "INVALID_INPUT - Invalid input parameters"
+ ;;
+ 404) ha_log.sh err "SERVER_NOT_FOUND - Server with ip $remote_ip not found"
+ ;;
+ 409) ha_log.sh err "RESET_MANUAL_ACTIVE - There is already a running manual reset"
+ ;;
+ 500) ha_log.sh err "RESET_FAILED - Resetting failed due to an internal error"
+ ;;
+ esac
+ return 1
+ fi
+}
+
+case $1 in
+gethosts)
+ echo $hostname
+ exit 0
+ ;;
+on)
+ # Can't really be implemented because Hetzner's webservice cannot power on a system
+ ha_log.sh err "Power on is not available since Hetzner's webservice can't do this operation."
+ exit 1
+ ;;
+off)
+ # Can't really be implemented because Hetzner's webservice cannot power on a system
+ ha_log.sh err "Power off is not available since Hetzner's webservice can't do this operation."
+ exit 1
+ ;;
+reset)
+ # Launching the reset action via webservice
+ check_http_response $(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/reset/$remote_ip -d type=hw)
+ exit $?
+ ;;
+status)
+ # Check if we can contact the webservice
+ check_http_response "$(curl --silent -o /dev/null -w '%{http_code}' -u $user:$pass $hetzner_server/server/$remote_ip)"
+ exit $?
+ ;;
+getconfignames)
+ echo "hostname"
+ echo "remote_ip"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "Hetzner STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "Hetzner STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "Hetzner host reset"
+ echo "Manages the remote webservice for reset a remote server."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://wiki.hetzner.de/index.php/Robot_Webservice_en"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << HETZNERXML
+<parameters>
+<parameter name="hostname" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="remote_ip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Remote IP
+</shortdesc>
+<longdesc lang="en">
+The address of the remote IP that manages this server.
+</longdesc>
+</parameter>
+</parameters>
+HETZNERXML
+ exit 0
+ ;;
+*)
+ ha_log.sh err "Don't know what to do for '$remote_ip'"
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/hmchttp b/lib/plugins/stonith/external/hmchttp
new file mode 100644
index 0000000..9d111bc
--- /dev/null
+++ b/lib/plugins/stonith/external/hmchttp
@@ -0,0 +1,218 @@
+#!/bin/sh
+# External STONITH module for HMC web console
+#
+# Copyright (c) 2007 Xinwei Hu <hxinwei@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#set -x
+hostlist=`echo $hostlist | tr ',' ' '`
+
+trap '[ ! -e "$COOKIEFILE" ] || rm -f "$COOKIEFILE"' 0
+COOKIEFILE=`mktemp` || exit 1
+
+: ${CURLBIN="/usr/bin/curl"}
+: ${user=admin}
+: ${password=admin}
+
+check_parameter() {
+ if [ ! -x $CURLBIN ]
+ then
+ ha_log.sh err "Curl can't be found in normal place. Set CURLBIN to override the default value"
+ exit 1
+ fi
+
+ if [ -z $hmc_ipaddr ]
+ then
+ ha_log.sh err "The address of HMC web console is not specified"
+ exit 1
+ fi
+}
+
+HMCUSERNAME=$user
+HMCPASSWORD=$password
+
+HMC_LOGIN_COMMAND="$CURLBIN -3 -k -c $COOKIEFILE -d user=$HMCUSERNAME -d password=$HMCPASSWORD -d lang=0 -d submit=Log+in "
+HMC_LOGOUT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d submit=Log+out "
+HMC_TOC_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=2 "
+HMC_POWERON_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 -d sp=255 -d is=0 -d om=4 -d id=1 -d ip=2 -d plt=1 -d pm=0 -d on=Save+settings+and+power+on "
+HMC_POWERSTATE_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=60 "
+HMC_POWEROFF_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=30 -d submit=Continue "
+HMC_REBOOT_COMMAND="$CURLBIN -3 -k -b $COOKIEFILE -d form=74 -d submit=Continue "
+
+hmc_login() {
+ iamin=0
+ while [ $iamin -eq 0 ]; do
+ $HMC_LOGIN_COMMAND https://$hmc_ipaddr/cgi-bin/cgi >/dev/null 2>&1
+ $HMC_TOC_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Too many users"
+ iamin=$?
+ sleep 2
+ done
+}
+hmc_logout() {
+ $HMC_LOGOUT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+
+hmc_reboot() {
+ check_parameter
+ $HMC_REBOOT_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+hmc_poweron() {
+ r=1
+ while [ 0 -ne $r ]; do
+ $HMC_POWERON_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null | grep -q "Operation completed successfully"
+ r=$?
+ done
+}
+hmc_poweroff() {
+ check_parameter
+ $HMC_POWEROFF_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null
+}
+hmc_powerstate() {
+ check_parameter
+ r=`$HMC_POWERSTATE_COMMAND https://$hmc_ipaddr/cgi-bin/cgi 2>/dev/null| grep "Current system power state:" | sed 's/<br>//g' | awk '{print $5}'`
+ echo $r
+}
+
+hmc_poweroffon() {
+ check_parameter
+ hmc_poweroff
+ while [ 1 ]; do
+ r=`hmc_powerstate`
+ ha_log.sh debug "power state: $r"
+ if [ $r = "Off" ]; then
+ break
+ fi
+ sleep 5
+ done
+ sleep 3
+ hmc_poweron
+}
+
+case $1 in
+gethosts)
+ for h in $hostlist; do
+ echo $h
+ done
+ exit 0
+ ;;
+status)
+ if
+ ping -w1 -c1 "$hmc_ipaddr" 2>&1
+ then
+ exit 0
+ fi
+ exit 1
+ ;;
+getconfignames)
+ for f in hostlist hmc_ipaddr user password; do
+ echo $f
+ done
+ exit 0
+ ;;
+getinfo-devid)
+ echo "HMC web console STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "HMC web console STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "HMC web console based host power control"
+ echo "Use for i5, p5, pSeries and OpenPower systems that are managed via "
+ echo "web console through a direct connection to system's HMC port."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.ibm.com"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << HMCXML
+<parameters>
+
+<parameter name="hostlist" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">Hostlist</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="hmc_ipaddr" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">HMC IPAddr</shortdesc>
+<longdesc lang="en">
+The IP address of the HMC web console
+</longdesc>
+</parameter>
+
+<parameter name="user" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">User</shortdesc>
+<longdesc lang="en">
+User name to log into HMC web console
+</longdesc>
+</parameter>
+
+<parameter name="password" unique="1" required="1">
+<content type="string"/>
+<shortdesc lang="en">Password</shortdesc>
+<longdesc lang="en">
+The password of user name to log into HMC web console
+</longdesc>
+</parameter>
+
+</parameters>
+HMCXML
+ exit 0
+ ;;
+esac
+
+case $1 in
+on|off|reset|powerstate|poweroffon)
+ hmc_login
+ case $1 in
+ on)
+ hmc_poweron $hmc_ipaddr
+ ;;
+ off)
+ hmc_poweroff $hmc_ipaddr
+ ;;
+ reset)
+# hmc_reboot $hmc_ipaddr
+ hmc_poweroffon $hmc_ipaddr
+ ;;
+ powerstate)
+ hmc_powerstate
+ ;;
+ poweroffon)
+ hmc_poweroffon
+ ;;
+ esac
+ hmc_logout
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ibmrsa b/lib/plugins/stonith/external/ibmrsa
new file mode 100644
index 0000000..7408465
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Dejan Muhamedagic <dmuhamedagic@at.ibm.com>, IBM Austria
+#
+# External STONITH module for IBM RSA adapters.
+# External STONITH module for IBM BMC.
+# This STONITH module depends on IBMmpcli.
+#
+
+trap 'rm -f "$outf"' 0
+outf=`mktemp` || {
+ ha_log.sh err 'mktemp failed'
+ exit 1
+}
+
+chkmpcli() {
+ test -x /opt/IBMmpcli/bin/MPCLI.sh
+}
+mpcli() {
+ chkmpcli || {
+ ha_log.sh err "IBM mpcli not installed"
+ return 1
+ }
+ if [ x = "x$ipaddr" -o x = "x$userid" -o x = "x$passwd" ]
+ then
+ ha_log.sh err "ipaddr, userid, or passwd missing; check configuration"
+ return 1
+ fi
+ type=${type:-"ibm"}
+
+ goodstg="SUCCESS"
+ failstg="FAILURE"
+ (
+ echo "logonip -h $ipaddr -u $userid -p $passwd -t $type"
+ echo "outputfile $outf"
+ cat
+ ) | /opt/IBMmpcli/bin/MPCLI.sh | grep -w $goodstg >/dev/null 2>&1
+ rc=$?
+ grep -w $failstg $outf >/dev/null
+ if [ $rc -eq 0 -a $? -eq 1 ]; then
+ return 0
+ else
+ ha_log.sh err "MPCLI.sh failed: `cat $outf`"
+ return 1
+ fi
+}
+ibmrsa_reboot() {
+ echo restart -now | mpcli
+}
+ibmrsa_poweron() {
+ echo poweron | mpcli
+}
+ibmrsa_poweroff() {
+ echo poweroff | mpcli
+}
+ibmrsa_status() {
+ echo | mpcli
+}
+
+hostname=`echo ${hostname} | tr ',' ' '`
+
+case $1 in
+gethosts)
+ echo $hostname
+ ;;
+on)
+ ibmrsa_poweron
+ ;;
+off)
+ ibmrsa_poweroff
+ ;;
+reset)
+ ibmrsa_reboot
+ ;;
+status)
+ ibmrsa_status
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid passwd type; do
+ echo $i
+ done
+ ;;
+getinfo-devid)
+ echo "IBM MP STONITH device"
+ ;;
+getinfo-devname)
+ echo "IBM MP STONITH device"
+ ;;
+getinfo-devdescr)
+ echo "IBM MP host reboot/poweron/poweroff"
+ ;;
+getinfo-devurl)
+ echo "http://www.ibm.com"
+ ;;
+getinfo-xml)
+ cat <<EOF
+<parameters>
+
+<parameter name="hostname" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The hostname of the host to be managed by this STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used to login into the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="passwd" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password used to login into the STONITH device
+</longdesc>
+</parameter>
+
+<parameter name="type" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Management processor type
+</shortdesc>
+<longdesc lang="en">
+The type of the management processor. Possible values are
+"ibm" (default, typically used for RSA) and "ipmi"
+(for IPMI compliant processors such as BMC).
+</longdesc>
+</parameter>
+
+</parameters>
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ibmrsa-telnet b/lib/plugins/stonith/external/ibmrsa-telnet
new file mode 100644
index 0000000..4d75d9a
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa-telnet
@@ -0,0 +1,320 @@
+#!/usr/bin/python
+# vim: set filetype=python
+#######################################################################
+#
+# ibmrsa-telnet - External stonith plugin for HAv2 (http://linux-ha.org/wiki)
+# Connects to IBM RSA Board via telnet and switches power
+# of server appropriately.
+#
+# Author: Andreas Mock (andreas.mock@web.de)
+#
+# History:
+# 2007-10-19 Fixed bad commandline handling in case of stonithing
+# 2007-10-11 First release.
+#
+# Comment: Please send bug fixes and enhancements.
+# I hope the functionality of communicating via telnet is encapsulated
+# enough so that someone can use it for similar purposes.
+#
+# Description: IBM offers Remote Supervisor Adapters II for several
+# servers. These RSA boards can be accessed in different ways.
+# One of that is via telnet. Once logged in you can use 'help' to
+# show all available commands. With 'power' you can reset, power on and
+# off the controlled server. This command is used in combination
+# with python's standard library 'telnetlib' to do it automatically.
+#
+# cib-snippet: Please see README.ibmrsa-telnet for examples.
+#
+# Copyright (c) 2007 Andreas Mock (andreas.mock@web.de)
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 or later of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+#######################################################################
+import sys
+import os
+import time
+import telnetlib
+import subprocess
+
+class TimeoutException(Exception):
+ def __init__(self, value=None):
+ Exception.__init__(self)
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class RSABoard(telnetlib.Telnet):
+ def __init__(self, *args, **kwargs):
+ telnetlib.Telnet.__init__(self, *args, **kwargs)
+ self._timeout = 10
+ self._loggedin = 0
+ self._history = []
+ self._appl = os.path.basename(sys.argv[0])
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def write(self, buffer, nolog = False):
+ self._history.append(self._get_timestamp() + ': WRITE: ' +
+ (nolog and '******' or repr(buffer)))
+ telnetlib.Telnet.write(self, buffer)
+
+ def expect(self, what, timeout=20):
+ line = telnetlib.Telnet.expect(self, what, timeout)
+ self._history.append(self._get_timestamp() + ': READ : ' + repr(line))
+ if not line:
+ raise TimeoutException("Timeout while waiting for '%s'." % (what, ))
+ return line
+
+ def login(self, user, passwd):
+ time.sleep(1)
+ line = self.expect(['\nlogin : ', '\nusername: '], self._timeout)
+ self.write(user)
+ self.write('\r')
+ line = self.expect(['\nPassword: ', '\npassword: '], self._timeout)
+ self.write(passwd, nolog = True)
+ self.write('\r')
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def reset(self):
+ self.write('power cycle\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def on(self):
+ self.write('power on\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def off(self):
+ self.write('power off\r')
+ line = self.expect(['\nok'], self._timeout)
+ line = self.expect(['\nsystem>', '> '], self._timeout)
+
+ def exit(self):
+ self.write('exit\r')
+
+ def get_history(self):
+ return "\n".join(self._history)
+
+
+class RSAStonithPlugin:
+ def __init__(self):
+ # define the external stonith plugin api
+ self._required_cmds = \
+ 'reset gethosts status getconfignames getinfo-devid ' \
+ 'getinfo-devname getinfo-devdescr getinfo-devurl ' \
+ 'getinfo-xml'
+ self._optional_cmds = 'on off'
+ self._required_cmds_list = self._required_cmds.split()
+ self._optional_cmds_list = self._optional_cmds.split()
+
+ # who am i
+ self._appl = os.path.basename(sys.argv[0])
+
+ # telnet connection object
+ self._connection = None
+
+ # the list of configuration names
+ self._confignames = ['nodename', 'ip_address', 'username', 'password']
+
+ # catch the parameters provided by environment
+ self._parameters = {}
+ for name in self._confignames:
+ try:
+ self._parameters[name] = os.environ.get(name, '').split()[0]
+ except IndexError:
+ self._parameters[name] = ''
+
+ def _get_timestamp(self):
+ ct = time.time()
+ msecs = (ct - long(ct)) * 1000
+ return "%s,%03d" % (time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(ct)), msecs)
+
+ def _echo_debug(self, *args):
+ self.echo_log('debug', *args)
+
+ def echo(self, *args):
+ what = ''.join([str(x) for x in args])
+ sys.stdout.write(what)
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ self._echo_debug("STDOUT:", what)
+
+ def echo_log(self, level, *args):
+ subprocess.call(('ha_log.sh', level) + args)
+
+ def _get_connection(self):
+ if not self._connection:
+ c = RSABoard()
+ self._echo_debug("Connect to '%s'" %
+ (self._parameters['ip_address'],))
+ c.open(self._parameters['ip_address'])
+ self._echo_debug("Connection established")
+ c.login(self._parameters['username'],
+ self._parameters['password'])
+ self._connection = c
+
+ def _end_connection(self):
+ if self._connection:
+ self._connection.exit()
+ self._connection.close()
+
+ def reset(self):
+ self._get_connection()
+ self._connection.reset()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Reset of node '%s' done" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def on(self):
+ self._get_connection()
+ self._connection.on()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' ON" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def off(self):
+ self._get_connection()
+ self._connection.off()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ self.echo_log("info", "Switched node '%s' OFF" %
+ (self._parameters['nodename'],))
+ return(0)
+
+ def gethosts(self):
+ self.echo(self._parameters['nodename'])
+ return(0)
+
+ def status(self):
+ self._get_connection()
+ self._end_connection()
+ self._echo_debug(self._connection.get_history())
+ return(0)
+
+ def getconfignames(self):
+ for name in ['nodename', 'ip_address', 'username', 'password']:
+ self.echo(name)
+ return(0)
+
+ def getinfo_devid(self):
+ self.echo("External Stonith Plugin for IBM RSA Boards")
+ return(0)
+
+ def getinfo_devname(self):
+ self.echo("External Stonith Plugin for IBM RSA Boards connecting "
+ "via Telnet")
+ return(0)
+
+ def getinfo_devdescr(self):
+ self.echo("External stonith plugin for HAv2 which connects to "
+ "a RSA board on IBM servers via telnet. Commands to "
+ "turn on/off power and to reset server are sent "
+ "appropriately. "
+ "(c) 2007 by Andreas Mock (andreas.mock@web.de)")
+ return(0)
+
+ def getinfo_devurl(self):
+ self.echo("http://www.ibm.com/Search/?q=remote+supervisor+adapter")
+
+ def getinfo_xml(self):
+ info = """<parameters>
+ <parameter name="nodename" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">nodename to shoot</shortdesc>
+ <longdesc lang="en">
+ Name of the node which has to be stonithed in case.
+ </longdesc>
+ </parameter>
+ <parameter name="ip_address" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">hostname or ip address of RSA</shortdesc>
+ <longdesc lang="en">
+ Hostname or ip address of RSA board used to reset node.
+ </longdesc>
+ </parameter>
+ <parameter name="username" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">username to login on RSA board</shortdesc>
+ <longdesc lang="en">
+ Username to login on RSA board.
+ </longdesc>
+ </parameter>
+ <parameter name="password" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">password to login on RSA board</shortdesc>
+ <longdesc lang="en">
+ Password to login on RSA board.
+ </longdesc>
+ </parameter>
+ </parameters>
+ """
+ self.echo(info)
+ return(0)
+
+ def not_implemented(self, cmd):
+ self.echo_log("err", "Command '%s' not implemented." % (cmd,))
+ return(1)
+
+ def usage(self):
+ usage = "Call me with one of the allowed commands: %s, %s" % (
+ ', '.join(self._required_cmds_list),
+ ', '.join(self._optional_cmds_list))
+ return usage
+
+ def process(self, argv):
+ self._echo_debug("========== Start =============")
+ if len(argv) < 1:
+ self.echo_log("err", 'At least one commandline argument required.')
+ return(1)
+ cmd = argv[0]
+ self._echo_debug("cmd:", cmd)
+ if cmd not in self._required_cmds_list and \
+ cmd not in self._optional_cmds_list:
+ self.echo_log("err", "Command '%s' not supported." % (cmd,))
+ return(1)
+ try:
+ cmd = cmd.lower().replace('-', '_')
+ func = getattr(self, cmd, self.not_implemented)
+ rc = func()
+ return(rc)
+ except Exception, args:
+ self.echo_log("err", 'Exception raised:', str(args))
+ if self._connection:
+ self.echo_log("err", self._connection.get_history())
+ self._connection.close()
+ return(1)
+
+
+if __name__ == '__main__':
+ stonith = RSAStonithPlugin()
+ rc = stonith.process(sys.argv[1:])
+ sys.exit(rc)
diff --git a/lib/plugins/stonith/external/ipmi b/lib/plugins/stonith/external/ipmi
new file mode 100644
index 0000000..abadd5a
--- /dev/null
+++ b/lib/plugins/stonith/external/ipmi
@@ -0,0 +1,276 @@
+#!/bin/sh
+#
+# External STONITH module using IPMI.
+# This modules uses uses the ipmitool program available from
+# http://ipmitool.sf.net/ for actual communication with the
+# managed device.
+#
+# Copyright (c) 2007 Martin Bene <martin.bene@icomedias.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+# Initialization -- fix locale settings so we can parse output from
+# binaries if we need it
+LANG=C
+LC_ALL=C
+
+RESET="power reset"
+POWEROFF="power off"
+POWERON="power on"
+STATUS="power status"
+IPMITOOL=${ipmitool:-"`which ipmitool 2>/dev/null`"}
+
+have_ipmi() {
+ test -x "${IPMITOOL}"
+}
+
+# Wrapper function for ipmitool that sets the correct host IP address,
+# username, and password, and invokes ipmitool with any arguments
+# passed in
+run_ipmitool() {
+ local ipmitool_opts privlvl=""
+ have_ipmi || {
+ ha_log.sh err "ipmitool not installed"
+ return 1
+ }
+ if [ -z "${ipaddr}" -o -z "${userid}" -o -z "${passwd}" ]; then
+ ha_log.sh err "ipaddr, userid or passwd missing; check configuration"
+ return 1
+ fi
+
+ if [ -z "${interface}" ]; then
+ # default to "lan" interface
+ interface="lan"
+ fi
+ if [ -n "${priv}" ]; then
+ # default to "lan" interface
+ privlvl="-L $priv"
+ fi
+
+ ipmitool_opts="-I ${interface} -H ${ipaddr} $privlvl"
+
+ case "${passwd_method}" in
+ param|'')
+ passwd_method=param
+ M="-P"
+ ;;
+ env)
+ M="-E"
+ ;;
+ file)
+ M="-f"
+ ;;
+ *)
+ ha_log.sh err "invalid passwd_method: \"${passwd_method}\""
+ return 1
+ esac
+
+ action="$*"
+
+ if [ $passwd_method = env ]
+ then
+ IPMI_PASSWORD="${passwd}" ${IPMITOOL} $ipmitool_opts -U "${userid}" -E ${action}
+ else
+ ${IPMITOOL} $ipmitool_opts -U "${userid}" $M "${passwd}" ${action}
+ fi 2>&1
+}
+
+# Yet another convenience wrapper that invokes run_ipmitool, captures
+# its output, logs the output, returns either 0 (on success) or 1 (on
+# any error)
+do_ipmi() {
+ if outp=`run_ipmitool $*`; then
+ ha_log.sh debug "ipmitool output: `echo $outp`"
+ return 0
+ else
+ ha_log.sh err "error executing ipmitool: `echo $outp`"
+ return 1
+ fi
+}
+
+# Check if the managed node is powered on. To do so, issue the "power
+# status" command. Should return either "Chassis Power is on" or
+# "Chassis Power is off".
+ipmi_is_power_on() {
+ local outp
+ outp=`run_ipmitool ${STATUS}`
+ case "${outp}" in
+ *on)
+ return 0
+ ;;
+ *off)
+ return 1
+ ;;
+ esac
+}
+
+
+case ${1} in
+gethosts)
+ echo $hostname
+ exit 0
+ ;;
+on)
+ do_ipmi "${POWERON}"
+ exit
+ ;;
+off)
+ do_ipmi "${POWEROFF}"
+ exit
+ ;;
+reset)
+ if ipmi_is_power_on; then
+ do_ipmi "${RESET}"
+ else
+ do_ipmi "${POWERON}"
+ fi
+ exit
+ ;;
+status)
+ # "status" reflects the status of the stonith _device_, not
+ # the managed node. Hence, only check if we can contact the
+ # IPMI device with "power status" command, don't pay attention
+ # to whether the node is in fact powered on or off.
+ do_ipmi "${STATUS}"
+ exit $?
+ ;;
+getconfignames)
+ for i in hostname ipaddr userid passwd interface; do
+ echo $i
+ done
+ exit 0
+ ;;
+getinfo-devid)
+ echo "IPMI STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "IPMI STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ipmitool based power management. Apparently, the power off"
+ echo "method of ipmitool is intercepted by ACPI which then makes"
+ echo "a regular shutdown. If case of a split brain on a two-node"
+ echo "it may happen that no node survives. For two-node clusters"
+ echo "use only the reset method."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://ipmitool.sf.net/"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << IPMIXML
+<parameters>
+<parameter name="hostname" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostname
+</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="ipaddr" unique="1">
+<content type="string" />
+<shortdesc lang="en">
+IP Address
+</shortdesc>
+<longdesc lang="en">
+The IP address of the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="userid" unique="0">
+<content type="string" />
+<shortdesc lang="en">
+Login
+</shortdesc>
+<longdesc lang="en">
+The username used for logging in to the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="passwd" unique="0">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password used for logging in to the STONITH device.
+</longdesc>
+</parameter>
+
+<parameter name="passwd_method" unique="0">
+<content type="string" default="param"/>
+<shortdesc lang="en">
+Method for passing passwd parameter
+</shortdesc>
+<longdesc lang="en">
+Method for passing the passwd parameter to ipmitool
+ param: pass as parameter (-P)
+ env: pass via environment (-E)
+ file: value of "passwd" is actually a file name, pass with (-f)
+</longdesc>
+</parameter>
+
+<parameter name="interface" unique="0">
+<content type="string" default="lan"/>
+<shortdesc lang="en">
+IPMI interface
+</shortdesc>
+<longdesc lang="en">
+IPMI interface to use, such as "lan" or "lanplus".
+</longdesc>
+</parameter>
+
+<parameter name="priv" unique="0">
+<content type="string" default=""/>
+<shortdesc lang="en">
+The privilege level of the user.
+</shortdesc>
+<longdesc lang="en">
+The privilege level of the user, for instance OPERATOR. If
+unspecified the privilege level is ADMINISTRATOR. See
+ipmitool(1) -L option for more information.
+</longdesc>
+</parameter>
+
+<parameter name="ipmitool" unique="0">
+<content type="string" default=""/>
+<shortdesc lang="en">
+IPMI command(ipmitool)
+</shortdesc>
+<longdesc lang="en">
+Specify the full path to IPMI command.
+</longdesc>
+</parameter>
+
+</parameters>
+IPMIXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/ippower9258.in b/lib/plugins/stonith/external/ippower9258.in
new file mode 100755
index 0000000..6ae7e02
--- /dev/null
+++ b/lib/plugins/stonith/external/ippower9258.in
@@ -0,0 +1,316 @@
+#!/bin/sh
+#
+# External STONITH module using IP Power 9258 or compatible devices.
+#
+# Copyright (c) 2010 Helmut Weymann (Helmut (at) h-weymann (dot) de)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#
+# Basic commands & parameters independent from individual device
+
+DEVICE="IP Power 9258"
+IPPowerOn="1"
+IPPowerOff="0"
+IPGetPower="Set.cmd?CMD=GetPower"
+IPSetPower="Set.cmd?CMD=SetPower"
+IPPort_name="P"
+IPPort0=60
+HTTP_COMMAND="wget -q -O - --"
+LOG_ERROR="ha_log.sh err"
+LOG_WARNING="ha_log.sh warn"
+LOG_INFO="ha_log.sh info"
+LOG_DEBUG="ha_log.sh debug"
+MY_COOKIES="cookies.txt"
+MY_TEMPFILE="temp.htm"
+PORT_STATUS="iocontrol.htm"
+UNDEFINED_HOSTNAME="*not-defined*"
+
+#
+# check MY_ROOT_PATH for IP Power 9258 and create it if necessary
+MY_ROOT_PATH="@GLUE_STATE_DIR@/heartbeat/rsctmp/ippower9258"
+
+#
+# script functions
+#
+
+get_challenge() {
+ #
+ # device sends a challenge for md5 encryption of username, password and challenge
+ send_web_command - "http://$deviceip/" | grep Challenge | grep input | cut -d '"' -f 6
+}
+
+get_cookie_from_device(){
+ # the form on the login page has these fields:
+ # Username, Password, Challenge, Response, ScreenWidth
+ #
+ challenge=`get_challenge`
+ response=`echo -n "$username$password$challenge" | md5sum | cut -b -32`
+ postdata="Username=$username&Password=&Challenge=&Response=$response&ScreenWidth=1024"
+ send_web_command " $MY_PATH/$MY_TEMPFILE --post-data=$postdata" "http://$deviceip/tgi/login.tgi"
+ if grep -qs "Invalid User name or Password" $MY_PATH/$MY_TEMPFILE
+ then
+ $LOG_ERROR "Login to device $deviceip failed."
+ $LOG_ERROR "Received Challenge = <<<$challenge>>>."
+ $LOG_ERROR "Sent postdata = <<<$postdata>>>."
+ exit 1
+ fi
+}
+
+get_data_from_device() {
+ # If successful all device info is available in MY_PATH
+ rm -f "$MY_PATH/$PORT_STATUS"
+ send_web_command "$MY_PATH/$PORT_STATUS" "http://$deviceip/$PORT_STATUS"
+ if grep -qs "Cookie Time Out" $MY_PATH/$PORT_STATUS
+ then
+ $LOG_ERROR "received no port data from $deviceip (Cookie Time Out)"
+ exit 1
+ fi
+}
+
+send_http_request() {
+ # ececution of http commands supported by the device
+ $HTTP_COMMAND "http://$username:$password@$deviceip/$1"
+}
+
+send_web_command(){
+ # ececution of web commands through the web-interface
+ WEB_COMMAND="wget -q --keep-session-cookies"
+ WEB_COMMAND="$WEB_COMMAND --load-cookies $MY_PATH/$MY_COOKIES"
+ WEB_COMMAND="$WEB_COMMAND --save-cookies $MY_PATH/$MY_COOKIES"
+ $WEB_COMMAND -O $1 -- $2
+}
+
+name2port() {
+ local name=$1
+ local i=$IPPort0
+ for h in $device_hostlist ; do
+ if [ $h = $name ]; then
+ echo $IPPort_name$i
+ return
+ fi
+ i=`expr $i + 1`
+ done
+ echo "invalid"
+}
+
+set_port() {
+ #
+ # port status is always set. Even if requested status is current status.
+ # host status is not considered.
+ local host=$1
+ local requested_status=$2 # 0 or 1
+ local port=`name2port $host`
+ if [ "$port" = "invalid" ]
+ then
+ $LOG_ERROR "Host $host is not in hostlist ($hostlist) for $deviceip."
+ exit 1
+ fi
+ ret=`send_http_request "$IPSetPower+$port=$requested_status" | cut -b 11`
+ if [ "$ret" != "$requested_status" ]
+ then
+ $LOG_ERROR "$DEVICE at $deviceip responds with wrong status $ret for host $host at port $port."
+ exit 1
+ fi
+ return 0
+}
+
+build_device_hostlist() {
+ #
+ # hostnames are available from http://$deviceip/iocontrol.htm"
+ # check for number of ports
+ #
+ device_hostlist=$(
+ w3m -dump $MY_PATH/$PORT_STATUS | grep 'Power[1-8]' |
+ sed 's/[^[]*\[//;s/\].*//;s/ *//' |
+ while read h; do
+ [ -z "$h" ] &&
+ echo $UNDEFINED_HOSTNAME ||
+ echo $h
+ done
+ )
+ if [ x = x"$device_hostlist" ]; then
+ $LOG_ERROR "cannot get hostlist for $deviceip"
+ exit 1
+ fi
+ $LOG_DEBUG "Got new hostlist ($device_hostlist) from $deviceip"
+}
+
+filter_device_hostlist() {
+ # check the given hostlist against the device hostlist
+ local host
+ for host in $device_hostlist; do
+ [ "$host" != "$UNDEFINED_HOSTNAME" ] &&
+ echo $host
+ done
+}
+
+check_hostlist() {
+ # check the given hostlist against the device hostlist
+ local cnt=`echo "$hostlist" | wc -w`
+ local cnt2=0
+ local host
+ for host in $hostlist; do
+ if [ `name2port $host` != "invalid" ]; then
+ cnt2=$((cnt2+1))
+ else
+ $LOG_ERROR "host $host not defined at $deviceip"
+ fi
+ done
+ [ $cnt -ne $cnt2 ] &&
+ exit 1
+}
+
+get_http_status() {
+ pattern="P60=[01],P61=[01],P62=[01],P63=[01],P64=[01],P65=[01],P66=[01],P67=[01]"
+ ret=`send_http_request "$IPGetPower" | grep $pattern`
+ if [ "X$ret" = "X" ]
+ then
+ $LOG_ERROR "$DEVICE at $deviceip returns invalid or no string."
+ exit 1
+ fi
+}
+
+hostlist=`echo $hostlist | tr ',' ' '`
+case $1 in
+gethosts|on|off|reset|status)
+ # need environment from stonithd
+ # and device information from individual device
+ #
+ # default device username is admin
+ # IP Power 9258 does not allow user management.
+ #
+ if [ "X$username" = "X" ]
+ then
+ username="admin"
+ fi
+
+ mkdir -p $MY_ROOT_PATH
+ tmp_path="$deviceip" # ensure a simple unique pathname
+ MY_PATH="$MY_ROOT_PATH/$tmp_path"
+ mkdir -p $MY_PATH
+ get_cookie_from_device
+ get_data_from_device
+ build_device_hostlist
+ if [ "X$hostlist" = "X" ]; then
+ hostlist="`filter_device_hostlist`"
+ else
+ check_hostlist
+ fi
+ ;;
+*)
+ # the client is asking for meta-data
+ ;;
+esac
+
+target=`echo $2 | sed 's/[.].*//'`
+# the necessary actions for stonithd
+case $1 in
+gethosts)
+ echo $hostlist
+ ;;
+on)
+ set_port $target $IPPowerOn
+ ;;
+off)
+ set_port $target $IPPowerOff
+ ;;
+reset)
+ set_port $target $IPPowerOff
+ sleep 5
+ set_port $target $IPPowerOn
+ ;;
+status)
+ # werify http command interface
+ get_http_status
+ ;;
+getconfignames)
+ # return all the config names
+ for ipparam in deviceip username password hostlist
+ do
+ echo $ipparam
+ done
+ ;;
+getinfo-devid)
+ echo "IP Power 9258"
+ ;;
+getinfo-devname)
+ echo "IP Power 9258 power switch"
+ ;;
+getinfo-devdescr)
+ echo "Power switch IP Power 9258 with 4 or 8 power outlets."
+ echo "WARNING: It is different from IP Power 9258 HP"
+ ;;
+getinfo-devurl)
+ echo "http://www.aviosys.com/manual.htm"
+ ;;
+getinfo-xml)
+ cat << IPPOWERXML
+<parameters>
+<parameter name="deviceip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+IP address or hostname of the device.
+</shortdesc>
+<longdesc lang="en">
+The IP Address or the hostname of the device.
+</longdesc>
+</parameter>
+
+<parameter name="password" unique="0" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Password
+</shortdesc>
+<longdesc lang="en">
+The password to log in with.
+</longdesc>
+</parameter>
+
+<parameter name="hostlist" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the device controls.
+If you leave this list empty, we will retrieve the hostnames from the device.
+</longdesc>
+</parameter>
+
+<parameter name="username" unique="0" required="0">
+<content type="string" default="admin"/>
+<shortdesc lang="en">
+Account Name
+</shortdesc>
+<longdesc lang="en">
+The user to log in with.
+</longdesc>
+</parameter>
+
+</parameters>
+IPPOWERXML
+ ;;
+*)
+ $LOG_ERROR "Unexpected command $1 for $DEVICE at $deviceip."
+ exit 1;
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/kdumpcheck.in b/lib/plugins/stonith/external/kdumpcheck.in
new file mode 100644
index 0000000..7f3f752
--- /dev/null
+++ b/lib/plugins/stonith/external/kdumpcheck.in
@@ -0,0 +1,274 @@
+#!/bin/sh
+#
+# External STONITH module to check kdump.
+#
+# Copyright (c) 2008 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n"
+#Set default user name.
+USERNAME="kdumpchecker"
+#Initialize identity file-path options for ssh command
+IDENTITY_OPTS=""
+
+#Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo ${hostlist} | tr ',' ' '`
+
+##
+# Check the parameter hostlist is set or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_hostlist() {
+ if [ -z "${hostlist}" ]; then
+ ha_log.sh err "hostlist is empty"
+ exit 6 #ERR_CONFIGURED
+ fi
+}
+
+##
+# Set kdump check user name to USERNAME.
+# always return 0.
+##
+get_username() {
+ kdump_conf="/etc/kdump.conf"
+
+ if [ ! -f "${kdump_conf}" ]; then
+ ha_log.sh debug "${kdump_conf} doesn't exist"
+ return 0
+ fi
+
+ tmp=""
+ while read config_opt config_val; do
+ if [ "${config_opt}" = "kdump_check_user" ]; then
+ tmp="${config_val}"
+ fi
+ done < "${kdump_conf}"
+ if [ -n "${tmp}" ]; then
+ USERNAME="${tmp}"
+ fi
+
+ ha_log.sh debug "kdump check user name is ${USERNAME}."
+}
+
+##
+# Check the specified or default identity file exists or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_identity_file() {
+ IDENTITY_OPTS=""
+ if [ -n "${identity_file}" ]; then
+ if [ ! -f "${identity_file}" ]; then
+ ha_log.sh err "${identity_file} doesn't exist."
+ exit 6 #ERR_CONFIGURED
+ fi
+ IDENTITY_OPTS="-i ${identity_file}"
+ else
+ flg_file_exists=0
+ homedir=`eval echo "~${USERNAME}"`
+ for filename in "${homedir}/.ssh/id_rsa" \
+ "${homedir}/.ssh/id_dsa" \
+ "${homedir}/.ssh/identity"
+ do
+ if [ -f "${filename}" ]; then
+ flg_file_exists=1
+ IDENTITY_OPTS="${IDENTITY_OPTS} -i ${filename}"
+ fi
+ done
+ if [ ${flg_file_exists} -eq 0 ]; then
+ ha_log.sh err "${USERNAME}'s identity file for ssh command" \
+ " doesn't exist."
+ exit 6 #ERR_CONFIGURED
+ fi
+ fi
+}
+
+##
+# Check the user to check doing kdump exists or not.
+# If not, exit with 6 (ERR_CONFIGURED).
+##
+check_user_existence() {
+
+ # Get kdump check user name and check whether he exists or not.
+ grep -q "^${USERNAME}:" /etc/passwd > /dev/null 2>&1
+ ret=$?
+ if [ ${ret} != 0 ]; then
+ ha_log.sh err "user ${USERNAME} doesn't exist." \
+ "please confirm \"kdump_check_user\" setting in /etc/kdump.conf." \
+ "(default user name is \"kdumpchecker\")"
+ exit 6 #ERR_CONFIGURED
+ fi
+}
+
+##
+# Check the target node is kdumping or not.
+# arg1 : target node name.
+# ret : 0 -> the target is kdumping.
+# : 1 -> the target is _not_ kdumping.
+# : else -> failed to check.
+##
+check_kdump() {
+ target_node="$1"
+
+ # Get kdump check user name.
+ get_username
+ check_user_existence
+ exec_cmd="${SSH_COMMAND} -l ${USERNAME}"
+
+ # Specify kdump check user's identity file for ssh command.
+ check_identity_file
+ exec_cmd="${exec_cmd} ${IDENTITY_OPTS}"
+
+ # Now, check the target!
+ # In advance, Write the following setting at the head of
+ # kdump_check_user's public key in authorized_keys file on target node.
+ # command="test -s /proc/vmcore", \
+ # no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
+ ha_log.sh debug "execute the command [${exec_cmd} ${target_node}]."
+ ${exec_cmd} ${target_node} > /dev/null 2>&1
+ ret=$?
+ ha_log.sh debug "the command's result is ${ret}."
+
+ #ret -> 0 : vmcore file's size is not zero. the node is kdumping.
+ #ret -> 1 : the node is _not_ kdumping (vmcore didn't exist or
+ # its size is zero). It still needs to be STONITH'ed.
+ #ret -> 255 : ssh command is failed.
+ # else : Maybe command strings in authorized_keys is wrong...
+ return ${ret}
+}
+
+###
+#
+# Main function.
+#
+###
+case $1 in
+gethosts)
+ check_hostlist
+ for hostname in ${hostlist} ; do
+ echo "${hostname}"
+ done
+ exit 0
+ ;;
+on)
+ # This plugin does only check whether a target node is kdumping or not.
+ exit 1
+ ;;
+reset|off)
+ check_hostlist
+ ret=1
+ h_target=`echo $2 | tr A-Z a-z`
+ for hostname in ${hostlist}
+ do
+ hostname=`echo $hostname | tr A-Z a-z`
+ if [ "${hostname}" != "$h_target" ]; then
+ continue
+ fi
+ while [ 1 ]
+ do
+ check_kdump "$2"
+ ret=$?
+ if [ ${ret} -ne 255 ]; then
+ exit ${ret}
+ fi
+ #255 means ssh command itself is failed.
+ #For example, connection failure as if network doesn't start yet
+ #in 2nd kernel on the target node.
+ #So, retry to check after a little while.
+ sleep 1
+ done
+ done
+ exit ${ret}
+ ;;
+status)
+ check_hostlist
+ for hostname in ${hostlist}
+ do
+ if ping -w1 -c1 "${hostname}" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ get_username
+ check_user_existence
+ check_identity_file
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist identity_file"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "kdump check STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "kdump check STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based kdump checker"
+ echo "To check whether a target node is dumping or not."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "kdump -> http://lse.sourceforge.net/kdump/"
+ echo "ssh -> http://openssh.org"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="identity_file" unique="1" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Identity file's full path for kdump check user
+</shortdesc>
+<longdesc lang="en">
+The full path of kdump check user's identity file for ssh command.
+The identity in the specified file have to be restricted to execute
+only the following command.
+"test -s /proc/vmcore"
+Default: kdump check user's default identity file path.
+NOTE: You can specify kdump check user name in /etc/kdump.conf.
+ The parameter name is "kdump_check_user".
+ Default user is "kdumpchecker".
+</longdesc>
+</parameter>
+
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/libvirt b/lib/plugins/stonith/external/libvirt
new file mode 100644
index 0000000..494b048
--- /dev/null
+++ b/lib/plugins/stonith/external/libvirt
@@ -0,0 +1,298 @@
+#!/bin/sh
+#
+# External STONITH module for a libvirt managed hypervisor (kvm/Xen).
+# Uses libvirt as a STONITH device to control guest.
+#
+# Copyright (c) 2010 Holger Teutsch <holger.teutsch@web.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+# start a domain
+libvirt_start() {
+ out=$($VIRSH -c $hypervisor_uri start $domain_id 2>&1)
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was started"
+ return 0
+ fi
+
+ $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 |
+ egrep -q '^State:.*(running|idle)|already active'
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id is already active"
+ return 0
+ fi
+
+ ha_log.sh err "Failed to start domain $domain_id"
+ ha_log.sh err "$out"
+ return 1
+}
+# reboot a domain
+# return
+# 0: success
+# 1: error
+libvirt_reboot() {
+ local rc out
+ out=$($VIRSH -c $hypervisor_uri reboot $domain_id 2>&1)
+ rc=$?
+ if [ $rc -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was rebooted"
+ return 0
+ fi
+ ha_log.sh err "Failed to reboot domain $domain_id (exit code: $rc)"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# stop a domain
+# return
+# 0: success
+# 1: error
+# 2: was already stopped
+libvirt_stop() {
+ out=$($VIRSH -c $hypervisor_uri destroy $domain_id 2>&1)
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id was stopped"
+ return 0
+ fi
+
+ $VIRSH -c $hypervisor_uri dominfo $domain_id 2>&1 |
+ egrep -q '^State:.*shut off|not found|not running'
+ if [ $? -eq 0 ]
+ then
+ ha_log.sh notice "Domain $domain_id is already stopped"
+ return 2
+ fi
+
+ ha_log.sh err "Failed to stop domain $domain_id"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# get status of stonith device (*NOT* of the domain).
+# If we can retrieve some info from the hypervisor
+# the stonith device is OK.
+libvirt_status() {
+ out=$($VIRSH -c $hypervisor_uri version 2>&1)
+ if [ $? -eq 0 ]
+ then
+ return 0
+ fi
+
+ ha_log.sh err "Failed to get status for $hypervisor_uri"
+ ha_log.sh err "$out"
+ return 1
+}
+
+# check config and set variables
+# does not return on error
+libvirt_check_config() {
+ VIRSH=`which virsh 2>/dev/null`
+
+ if [ ! -x "$VIRSH" ]
+ then
+ ha_log.sh err "virsh not installed"
+ exit 1
+ fi
+
+ if [ -z "$hostlist" -o -z "$hypervisor_uri" ]
+ then
+ ha_log.sh err "hostlist or hypervisor_uri missing; check configuration"
+ exit 1
+ fi
+
+ case "$reset_method" in
+ power_cycle|reboot) : ;;
+ *)
+ ha_log.sh err "unrecognized reset_method: $reset_method"
+ exit 1
+ ;;
+ esac
+}
+
+# set variable domain_id for the host specified as arg
+libvirt_set_domain_id ()
+{
+ for h in $hostlist
+ do
+ case $h in
+ $1:*)
+ domain_id=`expr $h : '.*:\(.*\)'`
+ return
+ ;;
+
+ $1)
+ domain_id=$1
+ return
+ esac
+ done
+
+ ha_log.sh err "Should never happen: Called for host $1 but $1 is not in $hostlist."
+ exit 1
+}
+
+libvirt_info() {
+cat << LVIRTXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+List of hostname[:domain_id]..
+</shortdesc>
+<longdesc lang="en">
+List of controlled hosts: hostname[:domain_id]..
+The optional domain_id defaults to the hostname.
+</longdesc>
+</parameter>
+
+<parameter name="hypervisor_uri" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hypervisor URI
+</shortdesc>
+<longdesc lang="en">
+URI for connection to the hypervisor.
+driver[+transport]://[username@][hostlist][:port]/[path][?extraparameters]
+e.g.
+qemu+ssh://my_kvm_server.mydomain.my/system (uses ssh for root)
+xen://my_kvm_server.mydomain.my/ (uses TLS for client)
+
+virsh must be installed (e.g. libvir-client package) and access control must
+be configured for your selected URI.
+</longdesc>
+</parameter>
+
+<parameter name="reset_method" required="0">
+<content type="string" default="power_cycle"/>
+<shortdesc lang="en">
+How to reset a guest.
+</shortdesc>
+<longdesc lang="en">
+A guest reset may be done by a sequence of off and on commands
+(power_cycle) or by the reboot command. Which method works
+depend on the hypervisor and guest configuration management.
+</longdesc>
+</parameter>
+</parameters>
+LVIRTXML
+exit 0
+}
+
+#############
+# Main code #
+#############
+
+# don't fool yourself when testing with stonith(8)
+# and transport ssh
+unset SSH_AUTH_SOCK
+
+# support , as a separator as well
+hostlist=`echo $hostlist| sed -e 's/,/ /g'`
+
+reset_method=${reset_method:-"power_cycle"}
+
+case $1 in
+ gethosts)
+ hostnames=`echo $hostlist|sed -e 's/:[^ ]*//g'`
+ for h in $hostnames
+ do
+ echo $h
+ done
+ exit 0
+ ;;
+
+ on)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ libvirt_start
+ exit $?
+ ;;
+
+ off)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ libvirt_stop
+ [ $? = 1 ] && exit 1
+ exit 0
+ ;;
+
+ reset)
+ libvirt_check_config
+ libvirt_set_domain_id $2
+
+ if [ "$reset_method" = "power_cycle" ]; then
+ libvirt_stop
+ [ $? = 1 ] && exit 1
+ sleep 2
+ libvirt_start
+ else
+ libvirt_reboot
+ fi
+ exit $?
+ ;;
+
+ status)
+ libvirt_check_config
+ libvirt_status
+ exit $?
+ ;;
+
+ getconfignames)
+ echo "hostlist hypervisor_uri reboot_method"
+ exit 0
+ ;;
+
+ getinfo-devid)
+ echo "libvirt STONITH device"
+ exit 0
+ ;;
+
+ getinfo-devname)
+ echo "libvirt STONITH external device"
+ exit 0
+ ;;
+
+ getinfo-devdescr)
+ echo "libvirt-based host reset for Xen/KVM guest domain through hypervisor"
+ exit 0
+ ;;
+
+ getinfo-devurl)
+ echo "http://libvirt.org/uri.html http://linux-ha.org/wiki"
+ exit 0
+ ;;
+
+ getinfo-xml)
+ libvirt_info
+ echo 0;
+ ;;
+
+ *)
+ exit 1
+ ;;
+esac
+
+# vi:et:ts=4:sw=4
diff --git a/lib/plugins/stonith/external/nut b/lib/plugins/stonith/external/nut
new file mode 100644
index 0000000..9e51bb8
--- /dev/null
+++ b/lib/plugins/stonith/external/nut
@@ -0,0 +1,302 @@
+#!/bin/sh
+
+# External STONITH module that uses the NUT daemon to control an external UPS.
+# See the comments below, and the various NUT man pages, for how this
+# script works. It should work unchanged with most modern "smart" APC UPSes in
+# a Redhat/Fedora/RHEL-style distribution with the nut package installed.
+
+# Author: William Seligman <seligman@nevis.columbia.edu>
+# License: GPLv2
+
+# As you're designing your UPS and STONITH set-up, it may help to consider that
+# there can be potentially three computers involved:
+# 1) the machine running this STONITH module;
+# 2) the machine being controlled by this STONITH module ($hostname);
+# 3) the machine that can send commands to the UPS.
+
+# On my cluster, all the UPSes have SNMP smartcards, so every host can communicate
+# with every UPS; in other words, machines (1) and (3) are the same. If your UPSes
+# are controlled via serial or USB connections, then you might have a
+# situation in which $hostname is plugged into a UPS, which has a serial connection
+# to some master "power-control" computer, and can potentially be STONITHed
+# by any other machine in your cluster.
+
+# In general, you'll probably need the nut daemon running on both the hosts (1) and
+# (3) in the above list. The NUT daemon will also have to run on (2) if you want the
+# reset command to gracefully reboot $hostname.
+
+# The NUT command default locations. In the RHEL-type nut packages, these binaries
+# are in /usr/bin.
+RHELUPSCMD="/usr/bin/upscmd"
+RHELUPSC="/usr/bin/upsc"
+
+# Defaults for APC smart UPSes:
+
+# Reset = reboot $hostname; this will be a graceful reboot if the host
+# is running NUT and monitoring $ups.
+APCRESET="shutdown.return"
+
+# Poweroff = turn off $hostname immediately by cutting the power on $ups.
+# For a graceful shutdown, use shutdown.stayoff instead of load.off,
+# but it might take a few minutes to shutdown in this way.
+APCPOWEROFF="load.off"
+
+# Poweron = turn on the power to $ups, which will presumably turn on $hostname.
+# (Did you set $hostname's BIOS to boot up on AC power restore, as opposed to
+# "last state"?)
+APCPOWERON="load.on"
+
+# Status = returns a short string with the $ups status; OL = on-line, OFF = off-line, etc.
+APCSTATUSVAR="ups.status"
+
+
+# Stick in the defaults, if needed.
+if [ -z "${poweron}" ]; then
+ poweron=${APCPOWERON}
+fi
+if [ -z "${poweroff}" ]; then
+ poweroff=${APCPOWEROFF}
+fi
+if [ -z "${reset}" ]; then
+ reset=${APCRESET}
+fi
+if [ -z "${statusvar}" ]; then
+ statusvar=${APCSTATUSVAR}
+fi
+if [ -z "${upscmd}" ]; then
+ upscmd=${RHELUPSCMD}
+fi
+if [ -z "${upsc}" ]; then
+ upsc=${RHELUPSC}
+fi
+
+
+# Define the command to fetch the UPS status.
+STATUSCMD="${upsc} ${ups} ${statusvar}"
+
+usage() {
+ echo "Usage: $0 {on|off|reset|status|gethosts|getconfignames|getinfo-devid|getinfo-devname|getinfo-devdescr|getinfo-devurl|getinfo-xml}"
+}
+
+# Can we find the NUT binary?
+have_nut() {
+ test -x "${upscmd}"
+}
+have_upsc() {
+ test -x "${upsc}"
+}
+
+do_nut() {
+ have_nut || {
+ echo "Can't find NUT upscmd command"
+ return 1
+ }
+ if [ -z "${username}" -o -z "${password}" -o -z "${ups}" ]; then
+ echo "username, password or ups name missing; check configuration"
+ return 1
+ fi
+ # Execute the command given in argument 1.
+ ${upscmd} -u ${username} -p ${password} ${ups} ${1} || {
+ echo "error executing nut command"
+ return 1
+ }
+}
+
+case ${1} in
+gethosts)
+ echo ${hostname}
+ exit 0
+ ;;
+on)
+ result=1
+ do_nut "${poweron}"
+ result=$?
+ exit ${result}
+ ;;
+off)
+ result=1
+ do_nut "${poweroff}"
+ result=$?
+ exit ${result}
+ ;;
+reset)
+ result=1
+ do_nut "${reset}"
+ result=$?
+ exit $result
+ ;;
+status)
+ have_upsc || {
+ echo "Can't find NUT upsc command"
+ exit 1
+ }
+ ${STATUSCMD}
+ exit $?
+ ;;
+getconfignames)
+ echo "hostname ups username password poweron poweroff reset statusvar upscmd upsc"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "NUT STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "NUT STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "A STONITH device based on NUT (Network UPS Tools)."
+ echo " "
+ echo "For this STONITH script to work, the following conditions have"
+ echo "to be met:"
+ echo " "
+ echo "- NUT has to be installed on both the host running this script"
+ echo " and the host that controls the UPS (on RHEL systems, NUT is"
+ echo " in packages nut and nut-client) and the nut daemon services"
+ echo " (normally called the ups or upsd service) must be running"
+ echo " on both systems."
+ echo " "
+ echo "- The UPS name has to be defined in ups.conf on the host"
+ echo " that controls the UPS."
+ echo " "
+ echo "- The username/password to access the UPS must be defined in"
+ echo " upsd.users on the host that controls the UPS, with the instcmds"
+ echo " for poweron, poweroff, and reset allowed."
+ echo " "
+ echo "- The host that is running this script must be allowed access"
+ echo " via upsd.conf and upsd.users on the host the controls the UPS."
+ echo " "
+ echo "On RHEL systems, the files listed above are in /etc/ups."
+ echo " "
+ echo "The defaults will probably work with APC UPS devices. It might"
+ echo "work on others; 'upscmd -l (ups)' and 'upsc (ups)' will list"
+ echo "the commands and variables, and you can change the values"
+ echo "for poweron, poweroff, reset, and statusvar to suit your UPS."
+ echo "Change upscmd and upsc if your NUT binaries are not in /usr/bin."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.networkupstools.org/"
+ exit 0
+ ;;
+getinfo-xml)
+cat << nutXML
+<parameters>
+
+<parameter name="hostname" unique="1" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Hostname</shortdesc>
+<longdesc lang="en">
+The name of the host to be managed by this STONITH device.
+The nut daemon must be running on the host controllng the
+UPS _and_ on the host running this script; this script does
+not start/stop the daemons for you.
+</longdesc>
+</parameter>
+
+<parameter name="ups" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">UPS name</shortdesc>
+<longdesc lang="en">
+The name of the UPS as defined in ups.conf on the host
+controlling the UPS. The format for this option is
+upsname[@controlhost[:port]]. The default controlhost is
+"localhost".
+</longdesc>
+</parameter>
+
+<parameter name="username" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Username</shortdesc>
+<longdesc lang="en">
+The username used for accessing the UPS. This is defined in
+upsd.conf on the host controlling the UPS.
+</longdesc>
+</parameter>
+
+<parameter name="password" required="1">
+<content type="string" default="" />
+<shortdesc lang="en">Password</shortdesc>
+<longdesc lang="en">
+The password used for logging in to the UPS for the host
+controlling the UPS, as defined in upsd.conf on that host.
+</longdesc>
+</parameter>
+
+<parameter name="poweron">
+<content type="string" default="$APCPOWERON" />
+<shortdesc lang="en">UPS Power On command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to turn on the UPS. The default
+should work for most "smart" APC UPSes. For a list of
+commands that your UPS can support, type 'upscmd -l (ups)'
+on the command line.</longdesc>
+</parameter>
+
+<parameter name="poweroff">
+<content type="string" default="$APCPOWEROFF" />
+<shortdesc lang="en">UPS Power Off command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to turn off the UPS. On most APC
+"smart" UPSes, the command shutdown.stayoff will result
+in a graceful shutdown, provided the host is running the
+nut daemon, but this might take a few minutes; load.off
+will cut the power immediately. For a list of commands that
+your UPS can support, type 'upscmd -l (ups)' on the command
+line.
+</longdesc>
+</parameter>
+
+<parameter name="reset">
+<content type="string" default="$APCRESET" />
+<shortdesc lang="en">UPS Reset command</shortdesc>
+<longdesc lang="en">
+The NUT hardware command to reset the host. On most APC
+"smart" UPSes, the command shutdown.return will result
+in a graceful shutdown, with power restored after perhaps
+a short interval. For a list of commands that your UPS can
+ support, type 'upscmd -l (ups)' on the command line.
+</longdesc>
+</parameter>
+
+<parameter name="statusvar">
+<content type="string" default="$APCSTATUSVAR" />
+<shortdesc lang="en">UPS Status variable</shortdesc>
+<longdesc lang="en">
+The NUT variable that returns the status of the UPS. On APC
+UPSes, the value of ups.status will be "OL" if the UPS is
+"on-line." For a list of variables that your UPS supports,
+type 'upsc (ups)' on the command line.
+</longdesc>
+</parameter>
+
+<parameter name="upscmd">
+<content type="string" default="$RHELUPSCMD" />
+<shortdesc lang="en">upscmd binary location</shortdesc>
+<longdesc lang="en">
+The full path to the NUT binary command 'upscmd'. On RHEL
+systems with the nut RPM installed, this location is
+/usr/bin/upscmd.
+</longdesc>
+</parameter>
+
+<parameter name="upsc">
+<content type="string" default="$RHELUPSC" />
+<shortdesc lang="en">upsc binary location</shortdesc>
+<longdesc lang="en">
+The full path to the NUT binary command 'upsc'. On RHEL
+systems with the nut RPM installed, this location is
+/usr/bin/upsc.
+</longdesc>
+</parameter>
+
+</parameters>
+nutXML
+exit 0
+;;
+*)
+ usage
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/rackpdu b/lib/plugins/stonith/external/rackpdu
new file mode 100644
index 0000000..7d0e20b
--- /dev/null
+++ b/lib/plugins/stonith/external/rackpdu
@@ -0,0 +1,280 @@
+#!/bin/sh
+#
+# External STONITH module for APC Switched Rack PDU
+#
+# Copyright (c) 2008 Sergey Maznichenko <msergeyb@gmail.com> <inbox@it-consultant.su>
+# Version 1.2
+#
+# See http://www.it-consultant.su/rackpdu
+# for additional information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SWITCH_ON="1"
+SWITCH_OFF="2"
+SWITCH_RESET="3"
+
+DEFAULT_NAMES_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2"
+DEFAULT_COMMAND_OID=".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4"
+
+if [ -z "$oid" ]; then
+ oid=$DEFAULT_COMMAND_OID
+fi
+
+if [ -z "$names_oid" ]; then
+ names_oid=$DEFAULT_NAMES_OID
+fi
+
+if [ -z "$outlet_config" ]; then
+ outlet_config="none"
+fi
+
+GetOutletNumber() {
+ local nodename=$1
+
+ if [ "$outlet_config" != "none" ]; then
+ # Get outlet number from file
+
+ if [ -f "$outlet_config" ]; then
+ local outlet_num=`grep $nodename $outlet_config | tr -d ' ' | cut -f2 -d'='`
+ if [ -z "$outlet_num" ]; then
+ ha_log.sh err "Outlet number not found for node $nodename. Check configuration file $outlet_config"
+ return 0
+ fi
+ return $outlet_num
+ else
+ ha_log.sh err "File $outlet_config not found."
+ return 0
+ fi
+ else
+ # Get outlet number from device
+
+ local outlet_num=1
+ local snmp_result
+ snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result"
+ return 0
+ fi
+
+ local names
+ names=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '`
+ for name in $names; do
+ if [ "$name" != "$nodename" ]; then
+ local outlet_num=`expr $outlet_num + 1`
+ continue
+ fi
+
+ return $outlet_num
+ done
+
+ ha_log.sh err "Outlet number not found for node $nodename. Result: $snmp_result"
+ return 0
+ fi
+}
+
+SendCommand() {
+
+ local host=$1
+ local command=$2
+
+ GetOutletNumber $host
+ local outlet=$?
+
+ if [ $outlet -gt 0 ]; then
+ local set_result
+ set_result=`snmpset -v1 -c $community $pduip $oid.$outlet i $command 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command failed. Result: $set_result"
+ return 1
+ fi
+ if echo "$set_result" | grep -qs "Timeout"; then
+ ha_log.sh err "Write SNMP to $pduip value $oid.$outlet=$command timed out. Result: $set_result"
+ return 1
+ fi
+ return 0
+ else
+ return 1
+ fi
+}
+
+hostlist=`echo $hostlist | tr ',' ' '`
+incommand=$1
+innode=$2
+
+case $incommand in
+gethosts)
+ if [ "$hostlist" = "AUTO" ]; then
+ snmp_result=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+ if [ $? -ne 0 ]; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid failed. Result: $snmp_result"
+ exit 1
+ fi
+ if echo "$snmp_result" | grep -qs "Timeout"; then
+ ha_log.sh err "snmpwalk $community $pduip $names_oid timed out. Result: $snmp_result"
+ exit 1
+ else
+ hostlist=`echo "$snmp_result" | cut -f2 -d'"' | tr ' ' '_' | tr '\012' ' '`
+ fi
+ fi
+
+ for h in $hostlist ; do
+ echo $h
+ done
+
+ exit 0
+ ;;
+on)
+ if
+ SendCommand $innode $SWITCH_ON
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+off)
+ if
+ SendCommand $innode $SWITCH_OFF
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+reset)
+ if
+ SendCommand $innode $SWITCH_RESET
+ then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+status)
+ if [ -z "$pduip" ]; then
+ exit 1
+ fi
+
+ if ping -w1 -c1 $pduip >/dev/null 2>&1; then
+ exit 0
+ else
+ exit 1
+ fi
+ ;;
+getconfignames)
+ echo "hostlist pduip community"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "rackpdu STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "rackpdu STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "APC Switched Rack PDU"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://www.apcc.com/products/family/index.cfm?id=30"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << PDUXML
+<parameters>
+ <parameter name="hostlist" unique="1" required="1">
+ <content type="string" default="AUTO" />
+ <shortdesc lang="en">Hostlist</shortdesc>
+ <longdesc lang="en">
+The list of hosts that the STONITH device controls (comma or space separated).
+If you set value of this parameter to AUTO, list of hosts will be get from Rack PDU device.
+ </longdesc>
+ </parameter>
+
+ <parameter name="pduip" unique="1" required="1">
+ <content type="string" />
+ <shortdesc lang="en">Name or IP address of Rack PDU device.</shortdesc>
+ <longdesc lang="en">Name or IP address of Rack PDU device.</longdesc>
+ </parameter>
+
+ <parameter name="community" unique="1" required="1">
+ <content type="string" default="private" />
+ <shortdesc lang="en">Name of write community.</shortdesc>
+ <longdesc lang="en">Name of write community.</longdesc>
+ </parameter>
+
+ <parameter name="oid" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">
+ The OID without the outlet number.
+ </shortdesc>
+ <longdesc lang="en">
+The SNMP OID for the PDU. minus the outlet number.
+Try .1.3.6.1.4.1.318.1.1.12.3.3.1.1.4 (default value)
+or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/
+Varies on different APC hardware and firmware.
+Warning! No dot at the end of OID
+ </longdesc>
+ </parameter>
+
+ <parameter name="names_oid" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">The OID for getting names of outlets.</shortdesc>
+ <longdesc lang="en">
+The SNMP OID for getting names of outlets.
+It is required to recognize outlet number by nodename.
+Try ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2" (default value)
+or use mib from ftp://ftp.apcc.com/apc/public/software/pnetmib/mib/
+Names of nodes must be equal names of outlets, in other way use outlet_config parameter.
+If you set 'names_oid' parameter then parameter outlet_config must not be use.
+Varies on different APC hardware and firmware.
+Warning! No dot at the end of OID
+ </longdesc>
+ </parameter>
+
+ <parameter name="outlet_config" unique="1" required="0">
+ <content type="string" />
+ <shortdesc lang="en">Configuration file. Other way to recognize outlet number by nodename.</shortdesc>
+ <longdesc lang="en">
+Configuration file. Other way to recognize outlet number by nodename.
+Configuration file which contains
+node_name=outlet_number
+strings.
+
+Example:
+server1=1
+server2=2
+
+If you use outlet_config parameter then names_oid parameter can have any value and it is not uses.
+ </longdesc>
+ </parameter>
+
+</parameters>
+PDUXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/riloe b/lib/plugins/stonith/external/riloe
new file mode 100644
index 0000000..ce98847
--- /dev/null
+++ b/lib/plugins/stonith/external/riloe
@@ -0,0 +1,530 @@
+#!/usr/bin/env python
+#
+# Stonith module for RILOE Stonith device
+#
+# Copyright (c) 2004 Alain St-Denis <alain.st-denis@ec.gc.ca>
+#
+# Modified by Alan Robertson <alanr@unix.sh> for STONITH external compatibility.
+#
+# Extended and merged by Tijl Van den broeck <subspawn@gmail.com>
+# with ilo-v2 script from Guy Coates
+#
+# Cleanup by Andrew Beekhof <abeekhof@suse.de>
+#
+# Rewritten by Dejan Muhamedagic <dejan@suse.de>
+# Now, the plugin actually reads replies from iLO.
+#
+# Extended by Jochen Roeder <jochen.roeder@novell.com>
+# to enable access via proxies
+#
+# 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
+#
+import sys
+import os
+import socket
+import subprocess
+import xml.dom.minidom
+import httplib
+import time
+import re
+
+def log_msg(level,msg):
+ subprocess.call("ha_log.sh %s '%s'" % (level,msg), shell=True)
+def my_err(msg):
+ log_msg("err", msg)
+def my_warn(msg):
+ log_msg("warn", msg)
+def my_debug(msg):
+ log_msg("debug", msg)
+def fatal(msg):
+ my_err(msg)
+ sys.exit(1)
+
+argv = sys.argv
+
+try:
+ cmd = argv[1]
+except IndexError:
+ my_err("Not enough arguments")
+ sys.exit(1)
+
+legacy_RI_HOST = os.environ.get('RI_HOST', '')
+legacy_RI_HOSTRI = os.environ.get('RI_HOSTRI', '')
+legacy_RI_LOGIN = os.environ.get('RI_LOGIN', 'Administrator')
+legacy_RI_PASSWORD = os.environ.get('RI_PASSWORD', '')
+
+reset_ok = os.environ.get('ilo_can_reset', '0')
+ilo_protocol = os.environ.get('ilo_protocol', '1.2')
+power_method = os.environ.get('ilo_powerdown_method', 'power')
+
+realhost = os.environ.get('hostlist', legacy_RI_HOST)
+rihost = os.environ.get('ilo_hostname', legacy_RI_HOSTRI)
+ilouser = os.environ.get('ilo_user', legacy_RI_LOGIN)
+ilopass = os.environ.get('ilo_password', legacy_RI_PASSWORD)
+iloproxyhost = os.environ.get('ilo_proxyhost', '')
+try:
+ iloproxyport = int(os.environ.get('ilo_proxyport', 3128))
+except ValueError:
+ my_err("ilo_proxyport is not a number")
+ sys.exit(1)
+
+xmlinfo = '''<parameters>
+ <parameter name="hostlist" unique="1" required="1">
+ <content type="string"/>
+ <shortdesc lang="en">ilo target hostname</shortdesc>
+ <longdesc lang="en">
+ Contains the hostname that the ilo controls
+ </longdesc>
+ </parameter>
+<parameter name="ilo_hostname" unique="1" required="1">
+ <content type="string"/>
+ <shortdesc lang="en">ilo device hostname</shortdesc>
+ <longdesc lang="en">
+ The hostname of the ilo device
+ </longdesc>
+ </parameter>
+<parameter name="ilo_user" unique="0" required="1">
+ <content type="string" default="Administrator"/>
+ <shortdesc lang="en">ilo user</shortdesc>
+ <longdesc lang="en">
+ The user for connecting to the ilo device
+ </longdesc>
+ </parameter>
+<parameter name="ilo_password" unique="0" required="1">
+ <content type="string" default=""/>
+ <shortdesc lang="en">password</shortdesc>
+ <longdesc lang="en">
+ The password for the ilo device user
+ </longdesc>
+ </parameter>
+<parameter name="ilo_can_reset" unique="0" required="0">
+ <content type="string" default="0"/>
+ <shortdesc lang="en">Device can reset</shortdesc>
+ <longdesc lang="en">
+ Does the ILO device support RESET commands (hint: older ones cannot)
+ </longdesc>
+ </parameter>
+<parameter name="ilo_protocol" unique="0" required="0">
+ <content type="string" default="1.2"/>
+ <shortdesc lang="en">ILO Protocol</shortdesc>
+ <longdesc lang="en">
+ Protocol version supported by the ILO device.
+ Known supported versions: 1.2, 2.0
+ </longdesc>
+ </parameter>
+<parameter name="ilo_powerdown_method" unique="0" required="0">
+ <content type="string" default="power"/>
+ <shortdesc lang="en">Power down method</shortdesc>
+ <longdesc lang="en">
+ The method to powerdown the host in question.
+ * button - Emulate holding down the power button
+ * power - Emulate turning off the machines power
+
+ NB: A button request takes around 20 seconds. The power method
+ about half a minute.
+ </longdesc>
+ </parameter>
+<parameter name="ilo_proxyhost" unique="0" required="0">
+ <content type="string" default=""/>
+ <shortdesc lang="en">Proxy hostname</shortdesc>
+ <longdesc lang="en">
+ proxy hostname if required to access ILO board
+ </longdesc>
+ </parameter>
+<parameter name="ilo_proxyport" unique="0" required="0">
+ <content type="string" default="3128"/>
+ <shortdesc lang="en">Proxy port</shortdesc>
+ <longdesc lang="en">
+ proxy port if required to access ILO board
+ parameter will be ignored if proxy hostname is not set
+ </longdesc>
+ </parameter>
+
+</parameters>'''
+
+info = {
+ 'getinfo-devid': 'iLO2',
+ 'getinfo-devname': 'ilo2 ' + rihost,
+ 'getinfo-devdescr': 'HP/COMPAQ iLO2 STONITH device',
+ 'getinfo-devurl': 'http://www.hp.com/',
+ 'gethosts': realhost,
+ 'getinfo-xml': xmlinfo
+}
+
+if cmd in info:
+ print info[cmd]
+ sys.exit(0)
+
+if cmd == 'getconfignames':
+ for arg in [ "hostlist", "ilo_hostname", "ilo_user", "ilo_password", "ilo_can_reset", "ilo_protocol", "ilo_powerdown_method", "ilo_proxyhost", "ilo_proxyport"]:
+ print arg
+ sys.exit(0)
+
+if not rihost:
+ fatal("ILO device hostname not specified")
+
+if not realhost:
+ fatal("Host controlled by this ILO device not specified")
+
+if not power_method in ("power","button"):
+ my_err('unknown power method %s, setting to "power"')
+ power_method = "power"
+
+# XML elements
+E_RIBCL = "RIBCL"
+E_LOGIN = "LOGIN"
+E_SERVER_INFO = "SERVER_INFO"
+
+# power mgmt methods
+E_RESET = "RESET_SERVER" # error if powered off
+E_COLD_BOOT = "COLD_BOOT_SERVER" # error if powered off
+E_WARM_BOOT = "WARM_BOOT_SERVER" # error if powered off
+E_PRESS_BUTTON = "PRESS_PWR_BTN"
+E_HOLD_BUTTON = "HOLD_PWR_BTN"
+
+# get/set status elements
+E_SET_POWER = "SET_HOST_POWER"
+E_GET_PSTATUS = "GET_HOST_POWER_STATUS"
+
+# whatever this means, but we have to use it to get good XML
+E_LOCFG = "LOCFG"
+LOCFG_VER = '2.21'
+
+# attributes
+A_VERSION = "VERSION" # ilo_protocol
+A_USER = "USER_LOGIN"
+A_PWD = "PASSWORD"
+A_MODE = "MODE" # info mode (read or write)
+A_POWER_SW = "HOST_POWER" # "Y" or "N"
+A_POWER_STATE = "HOST_POWER" # "ON" or "OFF"
+
+def new_power_req(tag, name = None, value = None):
+ '''
+ Create a new RIBCL request (as XML).
+ '''
+ my_debug("creating power request: %s,%s,%s"%(tag,name,value))
+ doc = xml.dom.minidom.Document()
+ locfg = doc.createElement(E_LOCFG)
+ locfg.setAttribute(A_VERSION,LOCFG_VER)
+ ribcl = doc.createElement(E_RIBCL)
+ ribcl.setAttribute(A_VERSION,ilo_protocol)
+ login = doc.createElement(E_LOGIN)
+ login.setAttribute(A_USER,ilouser)
+ login.setAttribute(A_PWD,ilopass)
+ serv_info = doc.createElement(E_SERVER_INFO)
+ # read or write, it doesn't really matter, i.e. even if we
+ # say "write" that doesn't mean we can't read
+ serv_info.setAttribute(A_MODE,"write")
+ doc.appendChild(locfg)
+ locfg.appendChild(ribcl)
+ ribcl.appendChild(login)
+ login.appendChild(serv_info)
+ el_node = doc.createElement(tag)
+ if name:
+ el_node.setAttribute(name,value)
+ serv_info.appendChild(el_node)
+ s = doc.toprettyxml()
+ doc.unlink()
+ # work around an iLO bug: last line containing "</LOCFG>"
+ # produces a syntax error
+ lines = s.split('\n')
+ return '\n'.join(lines[:-2])
+
+E_RESPONSE = "RESPONSE"
+E_HOST_POWER = "GET_HOST_POWER"
+A_STATUS = "STATUS"
+# documentation mentions both; better safe than sorry
+A_MSG = "MSG"
+A_MSG2 = "MESSAGE"
+
+def is_element(xmlnode):
+ return xmlnode.nodeType == xmlnode.ELEMENT_NODE
+
+def read_resp(node):
+ '''
+ Check if the RESPONSE XML is OK.
+ '''
+ msg = ""
+ str_status = ""
+ for attr in node.attributes.keys():
+ if attr == A_STATUS:
+ str_status = node.getAttribute(attr)
+ elif attr == A_MSG:
+ msg = node.getAttribute(attr)
+ elif attr == A_MSG2:
+ msg = node.getAttribute(attr)
+ else:
+ my_warn("unexpected attribute %s in %s" % (attr,E_RESPONSE))
+ if not str_status:
+ my_err("no status in response")
+ return -1
+ try:
+ status = int(str_status,16)
+ except ValueError:
+ my_err("unexpected status %s in response" % str_status)
+ return -1
+ if status != 0:
+ my_err("%s (rc: %s)"%(msg,str_status))
+ return -1
+ return 0
+
+def read_power(node):
+ '''
+ Read the power from the XML node. Set the global power
+ variable correspondingly.
+ '''
+ global power
+ for attr in node.attributes.keys():
+ if attr == A_POWER_STATE:
+ power_state = node.getAttribute(attr).upper()
+ else:
+ my_warn("unexpected attribute %s in %s" % (attr,node.tagName))
+ if not power_state:
+ my_err("no %s attribute in %s" % (A_POWER_STATE,node.tagName))
+ return -1
+ if power_state not in ("ON","OFF"):
+ my_err("unexpected value for %s: %s" % (A_POWER_STATE,power_state))
+ return -1
+ power = (power_state == "ON")
+ my_debug("Host has power: %s"%power)
+ return 0
+
+el_parsers = {
+ E_RESPONSE:read_resp,
+ E_HOST_POWER:read_power
+}
+def proc_resp(doc):
+ '''
+ Process one iLO reply. Real work is done in el_parsers.
+ '''
+ ribcl = doc.childNodes[0]
+ if not is_element(ribcl) or ribcl.tagName != E_RIBCL:
+ my_err("unexpected top element in response")
+ return -1
+ for child in ribcl.childNodes:
+ if not is_element(child):
+ continue
+ if child.tagName in el_parsers:
+ rc = el_parsers[child.tagName](child)
+ if rc != 0:
+ return -1
+ else:
+ my_warn("unexpected element in response: %s" % child.toxml())
+ return 0
+
+def open_ilo(host):
+ # open https connection
+ try:
+ if iloproxyhost != "" and iloproxyport != 0:
+ proxy=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
+ proxy.connect((iloproxyhost, iloproxyport))
+ proxy_connect='CONNECT %s:%s HTTP/1.1\r\n'%(host,443)
+ user_agent='User-Agent: python\r\n'
+ proxy_pieces=proxy_connect+user_agent+'\r\n'
+ proxy.sendall(proxy_pieces)
+ response=proxy.recv(8192)
+ status=response.split()[1]
+ if status!=str(200):
+ fatal("Error status=: %s" %(response))
+ import ssl
+ sock = ssl.wrap_socket(proxy)
+ h=httplib.HTTPConnection('localhost')
+ h.sock=sock
+ return h
+ else:
+ return httplib.HTTPSConnection(host)
+ except socket.gaierror, msg:
+ fatal("%s: %s" %(msg,host))
+ except socket.sslerror, msg:
+ fatal("%s for %s" %(msg,host))
+ except socket.error, msg:
+ fatal("%s while talking to %s" %(msg,host))
+ except ImportError, msg:
+ fatal("ssl support missing (%s)" %msg)
+
+def send_request(req,proc_f):
+ '''
+ 1. After every request, the iLO closes the connection.
+ 2. For every request, there are multiple replies. Each reply
+ is an XML document. Most of replies are just a kind of
+ (verbose) XML "OK".
+ '''
+ t_begin = time.time()
+ c = open_ilo(rihost)
+ try:
+ c.send(req+'\r\n')
+ except socket.error, msg:
+ fatal("%s, while talking to %s" %(msg,rihost))
+ t_end = time.time()
+ my_debug("request sent in %0.2f s" % ((t_end-t_begin)))
+
+ t_begin = time.time()
+ result = []
+ while True:
+ try:
+ reply = c.sock.recv(1024)
+ if not reply:
+ break
+ result.append(reply)
+ except socket.error, msg:
+ if msg[0] == 6: # connection closed
+ break
+ my_err("%s, while talking to %s" %(msg,rihost))
+ return -1
+ c.close()
+ t_end = time.time()
+
+ if not result:
+ fatal("no response from %s within %0.2f s"%(rihost,(t_end-t_begin)))
+ for reply in result:
+ # work around the iLO bug, i.e. element RIBCL closed twice
+ if re.search("</RIBCL", reply) and re.search("<RIBCL.*/>", reply):
+ reply = re.sub("<(RIBCL.*)/>", r"<\1>", reply)
+ try:
+ doc = xml.dom.minidom.parseString(reply)
+ except xml.parsers.expat.ExpatError,msg:
+ fatal("malformed response: %s\n%s"%(msg,reply))
+ rc = proc_f(doc)
+ doc.unlink()
+ if rc != 0:
+ break
+ my_debug("iLO processed request (rc=%d) in %0.2f s" % (rc,(t_end-t_begin)))
+ return rc
+
+def manage_power(cmd):
+ '''
+ Before trying to send a request we have to check the power
+ state.
+ '''
+ rc = 0
+ req = ''
+ # it won't do to turn it on if it's already on!
+ if cmd == "on" and not power:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"Y")
+ # also to turn it off if it's already off
+ elif cmd == "off" and power:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"N")
+ elif cmd == "cold_boot" and power:
+ req = new_power_req(E_COLD_BOOT)
+ elif cmd == "warm_boot" and power:
+ req = new_power_req(E_WARM_BOOT)
+ elif cmd == "reset":
+ if power:
+ req = new_power_req(E_RESET)
+ # reset doesn't work if the host's off
+ else:
+ req = new_power_req(E_SET_POWER,A_POWER_SW,"Y")
+ if req:
+ rc = send_request(req,proc_resp)
+ return rc
+def power_on():
+ '''
+ Update the power variable without checking the power state.
+ The iLO is slow at times to report the actual power state, so
+ we assume that it changed if the request succeeded.
+ '''
+ rc = manage_power("on")
+ if rc == 0:
+ global power
+ power = True
+ return rc
+def power_off():
+ rc = manage_power("off")
+ if rc == 0:
+ global power
+ power = False
+ return rc
+def cold_boot():
+ rc = manage_power("cold_boot")
+ return rc
+def warm_boot():
+ rc = manage_power("warm_boot")
+ return rc
+def reset():
+ rc = manage_power("reset")
+ if rc == 0:
+ global power
+ power = True
+ return rc
+def hold_button():
+ '''
+ Hold the power button. Got this error message when tried
+ without the TOGGLE attribute:
+ Command without TOGGLE="Yes" attribute is ignored
+ when host power is off. (rc: 0x0054)
+ Didn't find any documentation about TOGGLE.
+ '''
+ if power:
+ req = new_power_req(E_HOLD_BUTTON)
+ else:
+ req = new_power_req(E_HOLD_BUTTON,"TOGGLE","Yes")
+ rc = send_request(req,proc_resp)
+ return rc
+def read_power_state():
+ req = new_power_req(E_GET_PSTATUS)
+ rc = send_request(req,proc_resp)
+ return rc
+
+def reset_power():
+ '''
+ Three methods to reset:
+ - hold power button
+ - reset (only if host has power and user said that reset is ok)
+ - power off/on
+ '''
+ do_power_on = False
+ if power_method == 'button':
+ rc = hold_button()
+ elif reset_ok != '0':
+ if power:
+ return reset()
+ else:
+ return power_on()
+ else:
+ do_power_on = True
+ rc = power_off()
+ if rc == 0:
+ rc = read_power_state()
+ if do_power_on:
+ while rc == 0 and power: # wait for the power state to go off
+ time.sleep(5)
+ rc = read_power_state()
+ if rc == 0 and do_power_on and not power:
+ rc = power_on()
+ return rc
+
+# track state of host power
+power = -1
+
+todo = {
+'reset':reset_power,
+'on':power_on,
+'off':power_off,
+'cold':cold_boot,
+'warm':warm_boot,
+'status':lambda: 0 # just return 0, we already read the state
+}
+
+rc = read_power_state()
+if rc == 0:
+ if cmd in todo:
+ rc = todo[cmd]()
+ else:
+ fatal('Invalid command: %s' % cmd)
+if rc != 0:
+ fatal("request failed")
+sys.exit(rc)
+
+# vi:ts=4:sw=4:et:
diff --git a/lib/plugins/stonith/external/ssh.in b/lib/plugins/stonith/external/ssh.in
new file mode 100644
index 0000000..2a8eb73
--- /dev/null
+++ b/lib/plugins/stonith/external/ssh.in
@@ -0,0 +1,176 @@
+#!/bin/sh
+#
+# External STONITH module for ssh.
+#
+# Copyright (c) 2004 SUSE LINUX AG - Lars Marowsky-Bree <lmb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+SSH_COMMAND="@SSH@ -q -x -o PasswordAuthentication=no -o StrictHostKeyChecking=no -n -l root"
+#SSH_COMMAND="@SSH@ -q -x -n -l root"
+
+REBOOT_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+# Warning: If you select this poweroff command, it'll physically
+# power-off the machine, and quite a number of systems won't be remotely
+# revivable.
+# TODO: Probably should touch a file on the server instead to just
+# prevent heartbeat et al from being started after the reboot.
+# POWEROFF_COMMAND="echo 'sleep 2; /sbin/poweroff -nf' | SHELL=/bin/sh at now >/dev/null 2>&1"
+POWEROFF_COMMAND="echo 'sleep 2; @REBOOT@ @REBOOT_OPTIONS@' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+is_host_up() {
+ for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ do
+ if
+ ping -w1 -c1 "$1" >/dev/null 2>&1
+ then
+ sleep 1
+ else
+ return 1
+ fi
+ done
+ return 0
+}
+
+
+case $1 in
+gethosts)
+ for h in $hostlist ; do
+ echo $h
+ done
+ exit 0
+ ;;
+on)
+ # Can't really be implemented because ssh cannot power on a system
+ # when it is powered off.
+ exit 1
+ ;;
+off)
+ # Shouldn't really be implemented because if ssh cannot power on a
+ # system, it shouldn't be allowed to power it off.
+ exit 1
+ ;;
+reset)
+ h_target=`echo $2 | tr A-Z a-z`
+ for h in $hostlist
+ do
+ h=`echo $h | tr A-Z a-z`
+ [ "$h" != "$h_target" ] &&
+ continue
+ if
+ case ${livedangerously} in
+ [Yy]*) is_host_up $h;;
+ *) true;;
+ esac
+ then
+ $SSH_COMMAND "$2" "$REBOOT_COMMAND"
+ # Good thing this is only for testing...
+ if
+ is_host_up $h
+ then
+ exit 1
+ else
+ exit 0
+ fi
+ else
+ # well... Let's call it successful, after all this is only for testing...
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+status)
+ if
+ [ -z "$hostlist" ]
+ then
+ exit 1
+ fi
+ for h in $hostlist
+ do
+ if
+ ping -w1 -c1 "$h" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "ssh STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "ssh STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based host reset"
+ echo "Fine for testing, but not suitable for production!"
+ echo "Only reboot action supported, no poweroff, and, surprisingly enough, no poweron."
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://openssh.org"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of hosts that the STONITH device controls
+</longdesc>
+</parameter>
+
+<parameter name="livedangerously" unique="0" required="0">
+<content type="enum" />
+<shortdesc lang="en">
+Live Dangerously!!
+</shortdesc>
+<longdesc lang="en">
+Set to "yes" if you want to risk your system's integrity.
+Of course, since this plugin isn't for production, using it
+in production at all is a bad idea. On the other hand,
+setting this parameter to yes makes it an even worse idea.
+Viva la Vida Loca!
+</longdesc>
+</parameter>
+
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/vcenter b/lib/plugins/stonith/external/vcenter
new file mode 100755
index 0000000..71a6302
--- /dev/null
+++ b/lib/plugins/stonith/external/vcenter
@@ -0,0 +1,280 @@
+#!/usr/bin/env perl
+#
+# External STONITH module for VMWare vCenter/ESX
+#
+# Author: Nhan Ngo Dinh
+# License: GNU General Public License (GPL)
+#
+
+require 5.010;
+
+use strict;
+use warnings;
+
+sub dielog {
+ my $msg = "[";
+ $msg .= "$ARGV[0]" if defined($ARGV[0]);
+ $msg .= " $ARGV[1]" if defined($ARGV[1]);
+ $msg .= "]";
+ ( $_ ) = @_;
+ $msg .= " $_";
+ system("ha_log.sh", "err", "$msg");
+ die();
+}
+
+# Define command groups
+my @configCommands = qw{getconfignames getinfo-devid getinfo-devname getinfo-devdescr getinfo-devurl getinfo-xml};
+my @actionCommands = qw{reset on off};
+my @netCommands = (@actionCommands, qw{status gethosts listvms});
+
+# Process command line arguments
+my $command = $ARGV[0] || dielog("No command specified\n");
+
+# Command belongs to the group of commands that do not require any connection to VMware vCenter
+if ($command ~~ @configCommands) {
+ if ($command eq "getconfignames") {
+ print "VI_SERVER\nVI_PORTNUMBER\nVI_PROTOCOL\nVI_SERVICEPATH\nVI_CREDSTORE\nHOSTLIST\nRESETPOWERON\n";
+ }
+ elsif ($command eq "getinfo-devid") {
+ print "VMware vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devname") {
+ print "VMware vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devdescr") {
+ print "VMWare vCenter STONITH device\n";
+ }
+ elsif ($command eq "getinfo-devurl") {
+ print "http://www.vmware.com/\n";
+ }
+ elsif ($command eq "getinfo-xml") {
+ print q{<parameters>
+<parameter name="HOSTLIST" required="1">
+<content type="string"/>
+<shortdesc lang="en">List of hosts and virtual machines (required)</shortdesc>
+<longdesc lang="en">
+The list of hosts that the VMware vCenter STONITH device controls.
+Syntax is:
+ hostname1[=VirtualMachineName1];hostname2[=VirtualMachineName2]
+
+NOTE: omit =VirtualMachineName if hostname and virtual machine names are identical
+
+Example:
+ cluster1=VMCL1;cluster2=VMCL2
+</longdesc>
+</parameter>
+<parameter name="VI_SERVER">
+<content type="string" default="localhost"/>
+<shortdesc lang="en">VMware vCenter address</shortdesc>
+<longdesc lang="en">
+The VMware vCenter address
+</longdesc>
+</parameter>
+<parameter name="VI_PROTOCOL">
+<content type="string" default="https"/>
+<shortdesc lang="en">VMware vCenter protocol</shortdesc>
+<longdesc lang="en">
+The VMware vCenter protocol
+</longdesc>
+</parameter>
+<parameter name="VI_PORTNUMBER">
+<content type="string" default="443"/>
+<shortdesc lang="en">VMware vCenter port number</shortdesc>
+<longdesc lang="en">
+The VMware vCenter port number
+</longdesc>
+</parameter>
+<parameter name="VI_SERVICEPATH">
+<content type="string" default="/sdk"/>
+<shortdesc lang="en">VMware vCenter service path</shortdesc>
+<longdesc lang="en">
+The VMware vCenter services path
+</longdesc>
+</parameter>
+<parameter name="VI_CREDSTORE" required="1">
+<content type="string"/>
+<shortdesc lang="en">VMware vCenter credentials store file</shortdesc>
+<longdesc lang="en">
+VMware vCenter credentials store file
+</longdesc>
+</parameter>
+<parameter name="RESETPOWERON">
+<content type="string" default="1"/>
+<shortdesc lang="en">PowerOnVM on reset</shortdesc>
+<longdesc lang="en">
+Enable/disable a PowerOnVM on reset when the target virtual machine is off
+Allowed values: 0, 1
+</longdesc>
+</parameter>
+<parameter name="PERL_LWP_SSL_VERIFY_HOSTNAME">
+<content type="string"/>
+<shortdesc lang="en">Enable or disable SSL hostname verification</shortdesc>
+<longdesc lang="en">
+To disable SSL hostname verification set this option to 0.
+To enable hostname verification, set this option to 1.
+This option is actually part of the LWP Perl library.
+See LWP(3pm) for more information.
+</longdesc>
+</parameter>
+</parameters>} . "\n";
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+}
+
+# Command belongs to the group of commands that require connecting to VMware vCenter
+elsif ($command ~~ @netCommands) {
+
+ eval { require VMware::VIRuntime; }
+ or dielog("Missing perl module VMware::VIRuntime. Download and install 'VMware Infrastructure (VI) Perl Toolkit', available at http://www.vmware.com/support/developer/viperltoolkit/ \n");
+
+ # A valid VI_CREDSTORE is required to avoid interactive prompt
+ ( exists $ENV{'VI_CREDSTORE'} ) || dielog("VI_CREDSTORE not specified\n");
+
+ # HOSTLIST is mandatory
+ exists $ENV{'HOSTLIST'} || dielog("HOSTLIST not specified\n");
+
+ # Parse HOSTLIST to %host_to_vm and %vm_to_host
+ my @hostlist = split(';', $ENV{'HOSTLIST'});
+ my %host_to_vm = ();
+ my %vm_to_host = ();
+ foreach my $host (@hostlist) {
+ my @config = split(/=/, $host);
+ my $key = $config[0]; my $value = $config[1];
+ if (!defined($value)) { $value = $config[0]; }
+ $host_to_vm{$key} = $value;
+ $vm_to_host{(lc $value)} = $key;
+ }
+
+ eval {
+ # VI API: reads options from the environment variables into appropriate data structures for validation.
+ Opts::parse();
+ # VI API: ensures that input values from environment variable are complete, consistent and valid.
+ Opts::validate();
+ # VI API: establishes a session with the VirtualCenter Management Server or ESX Server Web service
+ Util::connect();
+ };
+ if ($@) {
+ # This is just a placeholder for any error handling procedure
+ dielog($@);
+ }
+
+ # Command belongs to the group of commands that performs actions on Virtual Machines
+ if ($command ~~ @actionCommands) {
+
+ my $targetHost = $ARGV[1] || dielog("No target specified\n");
+
+ # Require that specified target host exists in the specified HOSTLIST
+ if (exists $host_to_vm{$targetHost}) {
+
+ my $vm;
+ my $esx;
+ eval {
+ # VI API: searches the inventory tree for a VirtualMachine managed entity whose name matches
+ # the name of the virtual machine assigned to the target host in HOSTLIST
+ $vm = Vim::find_entity_view(view_type => "VirtualMachine", filter => { name => qr/^\Q$host_to_vm{$targetHost}\E/i });
+ if (!defined $vm) {
+ dielog("Machine $targetHost was not found");
+ }
+
+ # VI API: retrieves the properties of the managed object reference runtime.host of the VirtualMachine
+ # managed entity obtained by the previous command
+ # NOTE: This is essentially a workaround to vSphere Perl SDK
+ # to allow pointing to the right HostSystem. This is probably
+ # done by changing the current HostSystem in the Web Service
+ # session context. WARNING: Do not use the same session for any
+ # other concurrent operation.
+ $esx = Vim::get_view(mo_ref => $vm->{"runtime"}{"host"})->name;
+ };
+ if ($@) {
+ if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); }
+ dielog($@);
+ }
+
+ my $powerState = $vm->get_property('runtime.powerState')->val;
+ if ($powerState eq "suspended") {
+ # This implementation assumes that suspending a cluster node can cause
+ # severe failures on shared resources, thus any failover operation should
+ # be blocked.
+ dielog("Machine $esx:$vm->{'name'} is in a suspended state\n");
+ }
+
+ eval {
+ if ($command eq "reset") {
+ if ($powerState eq "poweredOn") {
+ $vm->ResetVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been reset");
+ } else {
+ system("ha_log.sh", "warn", "Tried to ResetVM $esx:$vm->{'name'} that was $powerState");
+ # Start a virtual machine on reset only if explicitly allowed by RESETPOWERON
+ if ($powerState eq "poweredOff" && (! exists $ENV{'RESETPOWERON'} || $ENV{'RESETPOWERON'} ne 0)) {
+ $vm->PowerOnVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on");
+ } else {
+ dielog("Could not complete $esx:$vm->{'name'} power cycle");
+ }
+ }
+ }
+ elsif ($command eq "off") {
+ if ($powerState eq "poweredOn") {
+ $vm->PowerOffVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered off");
+ } else {
+ system("ha_log.sh", "warn", "Tried to PowerOffVM $esx:$vm->{'name'} that was $powerState");
+
+ }
+ }
+ elsif ($command eq "on") {
+ if ($powerState eq "poweredOff") {
+ $vm->PowerOnVM();
+ system("ha_log.sh", "info", "Machine $esx:$vm->{'name'} has been powered on");
+ } else {
+ system("ha_log.sh", "warn", "Tried to PowerOnVM $esx:$vm->{'name'} that was $powerState");
+ }
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+ };
+ if ($@) {
+ if (ref($@) eq "SoapFault") { dielog("$@->detail\n"); }
+ dielog($@);
+ }
+
+ } else { dielog("Invalid target specified\n"); }
+ } else {
+ # Command belongs to the group of commands that lookup the status of VMware vCenter and/or virtual machines
+ if ($command eq "status") {
+ # we already connect to the vcenter, no need to do
+ # anything else in status
+ ;
+ }
+ elsif ($command eq "gethosts") {
+ foreach my $key (keys(%host_to_vm)) {
+ print "$key \n";
+ }
+ }
+ elsif ($command eq "listvms") {
+ eval {
+ # VI API: Searches the inventory tree for all VirtualMachine managed objects
+ my $vms = Vim::find_entity_views(view_type => "VirtualMachine");
+ if (defined $vms) {
+ printf(STDERR "%-50s %-20s\n", "VM Name", "Power state");
+ print STDERR "-" x 70 . "\n";
+ foreach my $vm (@$vms) {
+ my $powerState = $vm->get_property('runtime.powerState')->val;
+ printf("%-50s %-20s\n", $vm->{name}, $powerState);
+ }
+ }
+ };
+ }
+ else { dielog("Invalid command specified: $command\n"); }
+ }
+ eval {
+ Util::disconnect();
+ };
+ if ($@) {
+ # This is just a placeholder for any error handling procedure
+ dielog($@);
+ }
+}
+else { dielog("Invalid command specified: $command\n"); }
+
+exit(0);
diff --git a/lib/plugins/stonith/external/vmware b/lib/plugins/stonith/external/vmware
new file mode 100644
index 0000000..55966ba
--- /dev/null
+++ b/lib/plugins/stonith/external/vmware
@@ -0,0 +1,216 @@
+#!/usr/bin/perl
+# External STONITH module for VMWare Server Guests
+#
+# Copyright (c) 2004 SUSE LINUX AG - Andrew Beekhof <abeekhof@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+sub supply_default
+{
+ my $name = $_[0];
+ my $value = $_[1];
+
+ if ( defined $ENV{$name} ) {
+ #print "Set: $name=$ENV{$name}\n";
+ } else {
+ $ENV{$name} = $value;
+ #print "Default: $name=$ENV{$name}\n";
+ }
+}
+
+sub vmware_command
+{
+ my $config = $_[0];
+ my $action = $_[1];
+ my @lines;
+
+ my $device = $ENV{'device_host'};
+
+ if ( $device =~ /localhost/ ) {
+ @lines = readpipe "vmware-cmd $config $action";
+
+ } else {
+ @lines = readpipe "ssh $device \"vmware-cmd \\\"$config\\\" $action\"";
+ }
+
+ #print @lines;
+ return @lines;
+}
+
+sub is_host_active
+{
+ my $config = config_for_host($_[0]);
+ my @lines = vmware_command($config, "getstate");
+ foreach $line (@lines) {
+ if ( $line =~ /getstate.* = on/ ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub supported_hosts
+{
+ my $line;
+ my @lines;
+
+ if ( defined $ENV{'host_map'} ) {
+ @lines = split(/;/, $ENV{'host_map'});
+ foreach $line (@lines){
+ @config = split(/=/, $line);
+ $host = $config[0];
+ if ( is_host_active($host) == 1 ) {
+ print "$host\n";
+ }
+ }
+
+ } else {
+ @lines = vmware_command("-l");
+ foreach $line (@lines){
+ my @elements = split(/\//, $line);
+ $host = $elements[$#elements-1];
+ if ( is_host_active($host) == 1 ) {
+ print "$host\n";
+ }
+ }
+ }
+}
+
+sub config_for_host
+{
+ my $line;
+ my @lines;
+ my $target = $_[0];
+
+ if ( defined $ENV{'host_map'} ) {
+ @lines = split(/;/, $ENV{'host_map'});
+ foreach $line (@lines){
+ if ( $line =~ /^$target=/ ) {
+ @config = split(/=/, $line);
+ return $config[1];
+ }
+ }
+
+ } else {
+ @lines = vmware_command("-l");
+
+ foreach $line (@lines){
+ if ( $line =~ /\/$target\// ) {
+ chop($line);
+ return $line;
+ }
+ }
+ }
+}
+
+$command = $ARGV[0];
+if ( defined $ARGV[1] ) {
+ $targetHost = $ARGV[1];
+}
+
+supply_default("device_host", "localhost");
+
+if ( $command =~ /^gethosts$/ ) {
+ supported_hosts;
+
+} elsif ( $command =~ /^getconfignames$/ ) {
+ print "device_host\n";
+
+} elsif ( $command =~ /^getinfo-devid$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devname$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devdescr$/ ) {
+ print "VMware Server STONITH device\n";
+} elsif ( $command =~ /^getinfo-devurl$/ ) {
+ print "http://www.vmware.com/";
+
+} elsif ( $command =~ /^on$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "start hard");
+ print @lines;
+
+} elsif ( $command =~ /^off$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "stop hard");
+ print @lines;
+
+} elsif ( $command =~ /^reset$/ ) {
+ $config = config_for_host($targetHost);
+ my @lines = vmware_command($config, "reset hard");
+ print @lines;
+
+} elsif ( $command =~ /^status$/ ) {
+ my $rc = 7;
+ my $device = $ENV{'device_host'};
+ if ( $device =~ /localhost/ ) {
+ $rc = 0;
+ # TODO: Check for the vmware process
+ print "Local version: always running\n";
+
+ } else {
+ print "Remote version: running ping\n";
+ @lines = readpipe "ping -c1 $device";
+ print @lines;
+
+ foreach $line ( @lines ) {
+ if ( $line =~ /0% packet loss/ ) {
+ $rc = 0;
+ last;
+ }
+ }
+ }
+ exit($rc);
+
+} elsif ( $command =~ /^getinfo-xml$/ ) {
+ $metadata = <<END;
+<parameters>
+<parameter name="host_map" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Host Map
+</shortdesc>
+<longdesc lang="en">
+A mapping of hostnames to config paths supported by this device.
+Eg. host1=/config/path/1;host2=/config/path/2;host3=/config/path/3;
+</longdesc>
+</parameter>
+<parameter name="device_host" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Device Host
+</shortdesc>
+<longdesc lang="en">
+The machine _hosting_ the virtual machines
+</longdesc>
+</parameter>
+</parameters>
+END
+
+print $metadata;
+
+} else {
+ print "Command $command: not supported\n";
+ exit(3); # Not implemented
+}
+
+
+exit(0);
diff --git a/lib/plugins/stonith/external/xen0 b/lib/plugins/stonith/external/xen0
new file mode 100644
index 0000000..ef1ee40
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0
@@ -0,0 +1,253 @@
+#!/bin/sh
+#
+# External STONITH module for Xen Dom0 through ssh.
+#
+# Description: Uses Xen Dom0 Domain as a STONITH device
+# to control DomUs.
+#
+#
+# Author: Serge Dubrouski (sergeyfd@gmail.com)
+# Inspired by Lars Marowsky-Bree's external/ssh agent.
+#
+# Copyright 2007 Serge Dubrouski <sergeyfd@gmail.com>
+# License: GNU General Public License (GPL)
+#
+
+STOP_COMMAND="xm destroy"
+START_COMMAND="xm create"
+DUMP_COMMAND="xm dump-core"
+DEFAULT_XEN_DIR="/etc/xen"
+SSH_COMMAND="/usr/bin/ssh -q -x -n"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+CheckIfDead() {
+ for j in 1 2 3 4 5
+ do
+ if ! ping -w1 -c1 "$1" >/dev/null 2>&1
+ then
+ return 0
+ fi
+ sleep 1
+ done
+
+ return 1
+}
+
+CheckHostList() {
+ if [ "x" = "x$hostlist" ]
+ then
+ ha_log.sh err "hostlist isn't set"
+ exit 1
+ fi
+}
+
+CheckDom0() {
+ if [ "x" = "x$dom0" ]
+ then
+ ha_log.sh err "dom0 isn't set"
+ exit 1
+ fi
+}
+
+RunCommand() {
+ CheckHostList
+ CheckDom0
+
+ for h in $hostlist
+ do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ if [ "x" = "x$node" ]
+ then
+ ha_log.sh err "Syntax error in host list"
+ exit 1
+ fi
+
+ if [ "x" = "x$cfg" ]
+ then
+ cfg="${DEFAULT_XEN_DIR}/${node}.cfg"
+ fi
+
+ if [ "$node" != "$1" ]
+ then
+ continue
+ fi
+
+ case $2 in
+ stop)
+ kill_node=`$SSH_COMMAND $dom0 "grep ^[[:space:]]*name $cfg" | cut -f 2 -d '=' | sed -e 's,",,g'`
+ if [ "x" = "x$kill_node" ]
+ then
+ ha_log.sh err "Couldn't find a node name to stop"
+ exit 1
+ fi
+
+ if [ "x$run_dump" != "x" ]
+ then
+ #Need to run core dump
+ if [ "x$dump_dir" != "x" ]
+ then
+ #Dump with the specified core file
+ TIMESTAMP=`date +%Y-%m%d-%H%M.%S`
+ DOMAINNAME=`printf "%s" $kill_node`
+ COREFILE=$dump_dir/$TIMESTAMP-$DOMAINNAME.core
+ $SSH_COMMAND $dom0 "(mkdir -p $dump_dir; $DUMP_COMMAND $kill_node $COREFILE) >/dev/null 2>&1"
+ else
+ $SSH_COMMAND $dom0 "$DUMP_COMMAND $kill_node >/dev/null 2>&1"
+ fi
+ fi
+ $SSH_COMMAND $dom0 "(sleep 2; $STOP_COMMAND $kill_node) >/dev/null 2>&1 &"
+ break;;
+ start)
+ $SSH_COMMAND $dom0 "(sleep 2; $START_COMMAND $cfg) >/dev/null 2>&1 &"
+ break;;
+ esac
+ exit 0
+ done
+}
+
+
+# Main code
+
+case $1 in
+gethosts)
+ CheckHostList
+
+ for h in $hostlist ; do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ echo $node
+ done
+ exit 0
+ ;;
+on)
+ RunCommand $2 start
+ exit $?
+ ;;
+off)
+ if RunCommand $2 stop
+ then
+ if CheckIfDead $2
+ then
+ exit 0
+ fi
+ fi
+
+ exit 1
+ ;;
+reset)
+ RunCommand $2 stop
+
+ if CheckIfDead $2
+ then
+ RunCommand $2 start
+ exit 0
+ fi
+
+ exit 1
+ ;;
+status)
+ CheckHostList
+
+ for h in $hostlist
+ do
+ CIFS=$IFS
+ IFS=:
+ read node cfg << -!
+$h
+-!
+ IFS=$CIFS
+
+ echo $node
+ if ping -w1 -c1 "$node" 2>&1 | grep "unknown host"
+ then
+ exit 1
+ fi
+ done
+ exit 0
+ ;;
+getconfignames)
+ echo "hostlist dom0"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "xen0 STONITH device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "xen0 STONITH external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "ssh-based host reset for Xen DomU trough Dom0"
+ echo "Fine for testing, but not really suitable for production!"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://openssh.org http://www.xensource.com/ http://linux-ha.org/wiki"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of controlled nodes in a format node[:config_file].
+For example: "node1:/opt/xen/node1.cfg node2"
+If config file isn't set it defaults to /etc/xen/{node_name}.cfg
+</longdesc>
+</parameter>
+<parameter name="dom0" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Dom0
+</shortdesc>
+<longdesc lang="en">
+Name of the Dom0 Xen node. Root user shall be able to ssh to that node.
+</longdesc>
+</parameter>
+<parameter name="run_dump" unique="0" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Run dump-core
+</shortdesc>
+<longdesc lang="en">
+If set plugin will call "xm dump-core" before killing DomU
+</longdesc>
+</parameter>
+<parameter name="dump_dir" unique="1" required="0">
+<content type="string" />
+<shortdesc lang="en">
+Run dump-core with the specified directory
+</shortdesc>
+<longdesc lang="en">
+This parameter can indicate the dump destination.
+Should be set as a full path format, ex.) "/var/log/dump"
+The above example would dump the core, like;
+/var/log/dump/2009-0316-1403.37-domU.core
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper b/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper
new file mode 100755
index 0000000..b313f8b
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0-ha-dom0-stonith-helper
@@ -0,0 +1,72 @@
+#!/bin/bash
+# Author: Lars Marowsky-Bree
+#
+# Copyright 2008 Lars Marowsky-Bree
+# License: GNU General Public License (GPL)
+
+# This is not an external/stonith plugin by itself, but instead a helper
+# script which gets installed in Dom0.
+
+# TODO:
+# - Error handling
+# - How to handle if the DomU resource doesn't exist?
+# - Does this truly work with split-brain?
+# - Is the handling of non-existent resources adequate?
+# ...
+# Basically: more testing. This is proof-of-concept and works, but deserves
+# validation.
+
+CMD="$1"
+DOMU="$2"
+TIMEOUT="$3"
+
+# Make sure the timeout is an integer:
+if [ "0$TIMEOUT" -eq 0 ]; then
+ TIMEOUT=300
+fi
+
+SetTargetRole() {
+ local new_role="$1"
+ crm_resource -r $DOMU --meta -p target_role -v $new_role
+
+ local timeout="$TIMEOUT"
+
+ # We only need to wait for "stopped".
+ if [ "$new_role" != "stopped" ]; then
+ return 0
+ fi
+
+ while [ $timeout -gt 0 ]; do
+ local rc
+ crm_resource -W -r $DOMU 2>&1 | grep -q "is NOT running"
+ rc=$?
+ if [ $rc -eq 0 ]; then
+ return 0
+ fi
+ timeout=$[timeout-1];
+ sleep 1
+ done
+ return 1
+}
+
+
+case $CMD in
+on) SetTargetRole started
+ exit $?
+ ;;
+off) SetTargetRole stopped
+ exit $?
+ ;;
+reset) SetTargetRole stopped || exit 1
+ SetTargetRole started
+ exit $?
+ ;;
+status) exit 0
+ ;;
+*) ha_log.sh err "Called with unknown command: $CMD"
+ exit 1
+ ;;
+esac
+
+exit 1
+
diff --git a/lib/plugins/stonith/external/xen0-ha.in b/lib/plugins/stonith/external/xen0-ha.in
new file mode 100755
index 0000000..cb42cbc
--- /dev/null
+++ b/lib/plugins/stonith/external/xen0-ha.in
@@ -0,0 +1,96 @@
+#!/bin/bash
+#
+# This STONITH script integrates a cluster running within DomUs
+# with the CRM/Pacemaker cluster running in Dom0.
+#
+# Author: Lars Marowsky-Bree
+# Copyright: 2008 Lars Marowsky-Bree
+# License: GNU General Public License (GPL)
+#
+
+SSH_COMMAND="@SSH@ -q -x -n"
+HVM_HELPER="@stonith_plugindir@/xen0-ha-dom0-stonith-helper"
+
+# Rewrite the hostlist to accept "," as a delimeter for hostnames too.
+hostlist=`echo $hostlist | tr ',' ' '`
+
+# Runs a command on the host, waiting for it to return
+RunHVMCommand() {
+ $SSH_COMMAND $dom0_cluster_ip "$HVM_HELPER $1 $2 $stop_timeout"
+}
+
+# Main code
+case $1 in
+gethosts)
+ echo $hostlist
+ exit 0
+ ;;
+on|off|reset|status)
+ RunHVMCommand $1 $2
+ exit $?
+ ;;
+getconfignames)
+ echo "hostlist dom0_cluster_ip timeout"
+ exit 0
+ ;;
+getinfo-devid)
+ echo "xen0-ha DomU/Dom0 device"
+ exit 0
+ ;;
+getinfo-devname)
+ echo "xen0-ha DomU/Dom0 external device"
+ exit 0
+ ;;
+getinfo-devdescr)
+ echo "Allows STONITH to control DomUs managed by a CRM/Pacemaker Dom0."
+ echo "Requires Xen + CRM/Pacemaker at both layers."
+ echo "Proof-of-concept code!"
+ exit 0
+ ;;
+getinfo-devurl)
+ echo "http://linux-ha.org/wiki/DomUClusters"
+ exit 0
+ ;;
+getinfo-xml)
+ cat << SSHXML
+<parameters>
+<parameter name="hostlist" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Hostlist
+</shortdesc>
+<longdesc lang="en">
+The list of controlled DomUs, separated by whitespace.
+These must be configured as Xen RA resources with a name with a matching
+id.
+For example: "xen-1 xen-2 xen-3"
+</longdesc>
+</parameter>
+<parameter name="dom0_cluster_ip" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+Dom0 cluster ip
+</shortdesc>
+<longdesc lang="en">
+The cluster IP address associated with Dom0.
+Root user must be able to ssh to that node.
+</longdesc>
+</parameter>
+<parameter name="stop_timeout">
+<content type="integer" />
+<shortdesc lang="en">
+Stop timeout
+</shortdesc>
+<longdesc lang="en">
+The timeout, in seconds, for which to wait for Dom0 to report that the
+DomU has been stopped, before aborting with a failure.
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+ exit 0
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/lib/plugins/stonith/ibmhmc.c b/lib/plugins/stonith/ibmhmc.c
new file mode 100644
index 0000000..d33fea9
--- /dev/null
+++ b/lib/plugins/stonith/ibmhmc.c
@@ -0,0 +1,1261 @@
+/*
+ * Stonith module for IBM Hardware Management Console (HMC)
+ *
+ * Author: Huang Zhen <zhenh@cn.ibm.com>
+ * Support for HMC V4+ added by Dave Blaschke <debltc@us.ibm.com>
+ *
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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
+ *
+ */
+
+/*
+ *
+ * This code has been tested in following environment:
+ *
+ * Hardware Management Console (HMC): Release 3, Version 2.4
+ * - Both FullSystemPartition and LPAR Partition:
+ * - p630 7028-6C4 two LPAR partitions
+ * - p650 7038-6M2 one LPAR partition and FullSystemPartition
+ *
+ * Hardware Management Console (HMC): Version 4, Release 2.1
+ * - OP720 1000-6CA three LPAR partitions
+ *
+ * Note: Only SSH access to the HMC devices are supported.
+ *
+ * This command would make a nice status command:
+ *
+ * lshmc -r -F ssh
+ *
+ * The following V3 command will get the list of systems we control and their
+ * mode:
+ *
+ * lssyscfg -r sys -F name:mode --all
+ *
+ * 0 indicates full system partition
+ * 255 indicates the system is partitioned
+ *
+ * The following V4 command will get the list of systems we control:
+ *
+ * lssyscfg -r sys -F name
+ *
+ * The following V3 command will get the list of partitions for a given managed
+ * system running partitioned:
+ *
+ * lssyscfg -m managed-system -r lpar -F name --all
+ *
+ * Note that we should probably only consider partitions whose boot mode
+ * is normal (1). (that's my guess, anyway...)
+ *
+ * The following V4 command will get the list of partitions for a given managed
+ * system running partitioned:
+ *
+ * lssyscfg -m managed-system -r lpar -F name
+ *
+ * The following V3 commands provide the reset/on/off actions:
+ *
+ * FULL SYSTEM:
+ * on: chsysstate -m %1 -r sys -o on -n %1 -c full
+ * off: chsysstate -m %1 -r sys -o off -n %1 -c full -b norm
+ * reset:chsysstate -m %1 -r sys -o reset -n %1 -c full -b norm
+ *
+ * Partitioned SYSTEM:
+ * on: chsysstate -m %1 -r lpar -o on -n %2
+ * off: reset_partition -m %1 -p %2 -t hard
+ * reset:do off action above, followed by on action...
+ *
+ * where %1 is managed-system, %2 is-lpar name
+ *
+ * The following V4 commands provide the reset/on/off actions:
+ *
+ * on: chsysstate -m %1 -r lpar -o on -n %2 -f %3
+ * off: chsysstate -m %1 -r lpar -o shutdown -n %2 --immed
+ * reset:chsysstate -m %1 -r lpar -o shutdown -n %2 --immed --restart
+ *
+ * where %1 is managed-system, %2 is lpar-name, %3 is profile-name
+ *
+ * Of course, to do all this, we need to track which partition name goes with
+ * which managed system's name, and which systems on the HMC are partitioned
+ * and which ones aren't...
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "IBM HMC"
+
+#include "stonith_plugin_common.h"
+
+#ifndef SSH_CMD
+# define SSH_CMD "ssh"
+#endif
+#ifndef HMCROOT
+# define HMCROOT "hscroot"
+#endif
+
+#define PIL_PLUGIN ibmhmc
+#define PIL_PLUGIN_S "ibmhmc"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define MAX_HOST_NAME_LEN (256*4)
+#define MAX_CMD_LEN 2048
+#define FULLSYSTEMPARTITION "FullSystemPartition"
+#define MAX_POWERON_RETRY 10
+#define MAX_HMC_NAME_LEN 256
+
+#define ST_MANSYSPAT "managedsyspat"
+#define NOPASS "nopass"
+
+#define STATE_UNKNOWN -1
+#define STATE_OFF 0
+#define STATE_ON 1
+#define STATE_INVALID 2
+
+#define HMCURL "http://publib-b.boulder.ibm.com/redbooks.nsf/RedbookAbstracts"\
+ "/SG247038.html"
+
+static StonithPlugin * ibmhmc_new(const char *);
+static void ibmhmc_destroy(StonithPlugin *);
+static const char * ibmhmc_getinfo(StonithPlugin * s, int InfoType);
+static const char * const * ibmhmc_get_confignames(StonithPlugin* p);
+static int ibmhmc_status(StonithPlugin * );
+static int ibmhmc_reset_req(StonithPlugin * s,int request,const char* host);
+static char ** ibmhmc_hostlist(StonithPlugin *);
+static int ibmhmc_set_config(StonithPlugin *, StonithNVpair*);
+
+static struct stonith_ops ibmhmcOps = {
+ ibmhmc_new, /* Create new STONITH object */
+ ibmhmc_destroy, /* Destroy STONITH object */
+ ibmhmc_getinfo, /* Return STONITH info string */
+ ibmhmc_get_confignames, /* Return configuration parameters */
+ ibmhmc_set_config, /* Set configuration */
+ ibmhmc_status, /* Return STONITH device status */
+ ibmhmc_reset_req, /* Request a reset */
+ ibmhmc_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &ibmhmcOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ char * idinfo;
+ char * hmc;
+ GList* hostlist;
+ int hmcver;
+ char * password;
+ char ** mansyspats;
+};
+
+static const char * pluginid = "HMCDevice-Stonith";
+static const char * NOTpluginID = "IBM HMC device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_MANSYSPAT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MANSYSPAT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MANSYSPAT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "White-space delimited list of patterns used to match managed system names; if last character is '*', all names that begin with the pattern are matched" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MANSYSPAT_PARM \
+ XML_PARAMETER_BEGIN(ST_MANSYSPAT, "string", "0", "0") \
+ XML_MANSYSPAT_SHORTDESC \
+ XML_MANSYSPAT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_OPTPASSWD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "Password for " HMCROOT " if passwordless ssh access to HMC has NOT been setup (to do so, it is necessary to create a public/private key pair with empty passphrase - see \"Configure the OpenSSH Client\" in the redbook at " HMCURL " for more details)" \
+ XML_PARM_LONGDESC_END
+
+#define XML_OPTPASSWD_PARM \
+ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "0", "0") \
+ XML_PASSWD_SHORTDESC \
+ XML_OPTPASSWD_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *ibmhmcXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_MANSYSPAT_PARM
+ XML_OPTPASSWD_PARM
+ XML_PARAMETERS_END;
+
+static int get_hmc_hostlist(struct pluginDevice* dev);
+static void free_hmc_hostlist(struct pluginDevice* dev);
+static int get_hmc_mansyspats(struct pluginDevice* dev, const char* mansyspats);
+static void free_hmc_mansyspats(struct pluginDevice* dev);
+static char* do_shell_cmd(const char* cmd, int* status, const char* password);
+static int check_hmc_status(struct pluginDevice* dev);
+static int get_num_tokens(char *str);
+static gboolean pattern_match(char **patterns, char *string);
+/* static char* do_shell_cmd_fake(const char* cmd, int* status); */
+
+static int
+ibmhmc_status(StonithPlugin *s)
+{
+ struct pluginDevice* dev = NULL;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ dev = (struct pluginDevice*) s;
+
+ return check_hmc_status(dev);
+}
+
+
+/*
+ * Return the list of hosts configured for this HMC device
+ */
+
+static char **
+ibmhmc_hostlist(StonithPlugin *s)
+{
+ int j;
+ struct pluginDevice* dev;
+ int numnames = 0;
+ char** ret = NULL;
+ GList* node = NULL;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ dev = (struct pluginDevice*) s;
+
+ /* refresh the hostlist */
+ free_hmc_hostlist(dev);
+ if (S_OK != get_hmc_hostlist(dev)){
+ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s"
+ , __FUNCTION__);
+ return NULL;
+ }
+
+ numnames = g_list_length(dev->hostlist);
+ if (numnames < 0) {
+ LOG(PIL_CRIT, "unconfigured stonith object in %s"
+ , __FUNCTION__);
+ return(NULL);
+ }
+
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+
+ memset(ret, 0, (numnames+1)*sizeof(char*));
+ for (node = g_list_first(dev->hostlist), j = 0
+ ; NULL != node
+ ; j++, node = g_list_next(node)) {
+ char* host = strchr((char*)node->data, '/');
+ ret[j] = STRDUP(++host);
+ if (ret[j] == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ strdown(ret[j]);
+ }
+ return ret;
+}
+
+
+static const char * const *
+ibmhmc_get_confignames(StonithPlugin* p)
+{
+ static const char * names[] = {ST_IPADDR, NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+
+/*
+ * Reset the given host, and obey the request type.
+ * We should reset without power cycle for the non-partitioned case
+ */
+
+static int
+ibmhmc_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ GList* node = NULL;
+ struct pluginDevice* dev = NULL;
+ char off_cmd[MAX_CMD_LEN];
+ char on_cmd[MAX_CMD_LEN];
+ char reset_cmd[MAX_CMD_LEN];
+ gchar** names = NULL;
+ int i;
+ int is_lpar = FALSE;
+ int status;
+ char* pch;
+ char* output = NULL;
+ char state_cmd[MAX_CMD_LEN];
+ int state = STATE_UNKNOWN;
+
+ status = 0;
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, host=%s\n", __FUNCTION__, host);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (NULL == host) {
+ LOG(PIL_CRIT, "invalid argument to %s", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ dev = (struct pluginDevice*) s;
+
+ for (node = g_list_first(dev->hostlist)
+ ; NULL != node
+ ; node = g_list_next(node)) {
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: node->data=%s\n"
+ , __FUNCTION__, (char*)node->data);
+ }
+
+ if ((pch = strchr((char*)node->data, '/')) != NULL
+ && 0 == strcasecmp(++pch, host)) {
+ break;
+ }
+ }
+
+ if (!node) {
+ LOG(PIL_CRIT
+ , "Host %s is not configured in this STONITH module. "
+ "Please check your configuration information.", host);
+ return (S_OOPS);
+ }
+
+ names = g_strsplit((char*)node->data, "/", 2);
+ /* names[0] will be the name of managed system */
+ /* names[1] will be the name of the lpar partition */
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: names[0]=%s, names[1]=%s\n"
+ , __FUNCTION__, names[0], names[1]);
+ }
+
+ if (dev->hmcver < 4) {
+ if (0 == strcasecmp(names[1], FULLSYSTEMPARTITION)) {
+ is_lpar = FALSE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o off -n %s -c full"
+ , dev->hmc, dev->hmc, names[0]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o on -n %s -c full -b norm"
+ , dev->hmc, names[0], names[0]);
+
+ snprintf(reset_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r sys -m %s -o reset -n %s -c full -b norm"
+ , dev->hmc, names[0], names[0]);
+
+ *state_cmd = 0;
+ }else{
+ is_lpar = TRUE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s reset_partition"
+ " -m %s -p %s -t hard"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -r lpar -m %s -o on -n %s"
+ , dev->hmc, names[0], names[1]);
+
+ *reset_cmd = 0;
+
+ snprintf(state_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -r lpar -m %s -F state -n %s"
+ , dev->hmc, names[0], names[1]);
+ }
+ }else{
+ is_lpar = TRUE;
+
+ snprintf(off_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o shutdown -n \"%s\" --immed"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -m %s -r lpar -F \"default_profile\""
+ " --filter \"lpar_names=%s\""
+ , dev->hmc, names[0], names[1]);
+
+ output = do_shell_cmd(on_cmd, &status, dev->password);
+ if (output == NULL) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return (S_OOPS);
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ snprintf(on_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o on -n %s -f %s"
+ , dev->hmc, names[0], names[1], output);
+ FREE(output);
+ output = NULL;
+
+ snprintf(reset_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s chsysstate"
+ " -m %s -r lpar -o shutdown -n %s --immed --restart"
+ , dev->hmc, names[0], names[1]);
+
+ snprintf(state_cmd, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lssyscfg"
+ " -m %s -r lpar -F state --filter \"lpar_names=%s\""
+ , dev->hmc, names[0], names[1]);
+ }
+ g_strfreev(names);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: off_cmd=%s, on_cmd=%s,"
+ "reset_cmd=%s, state_cmd=%s\n"
+ , __FUNCTION__, off_cmd, on_cmd, reset_cmd, state_cmd);
+ }
+
+ output = do_shell_cmd(state_cmd, &status, dev->password);
+ if (output == NULL) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return S_OOPS;
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ if (strcmp(output, "Running") == 0
+ || strcmp(output, "Starting") == 0
+ || strcmp(output, "Open Firmware") == 0) {
+ state = STATE_ON;
+ }else if (strcmp(output, "Shutting Down") == 0
+ || strcmp(output, "Not Activated") == 0
+ || strcmp(output, "Ready") == 0) {
+ state = STATE_OFF;
+ }else if (strcmp(output, "Not Available") == 0
+ || strcmp(output, "Error") == 0) {
+ state = STATE_INVALID;
+ }
+ FREE(output);
+ output = NULL;
+
+ if (state == STATE_INVALID) {
+ LOG(PIL_CRIT, "host %s in invalid state", host);
+ return S_OOPS;
+ }
+
+ switch (request) {
+ case ST_POWERON:
+ if (state == STATE_ON) {
+ LOG(PIL_INFO, "host %s already on", host);
+ return S_OK;
+ }
+
+ output = do_shell_cmd(on_cmd, &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", on_cmd);
+ return S_OOPS;
+ }
+ break;
+ case ST_POWEROFF:
+ if (state == STATE_OFF) {
+ LOG(PIL_INFO, "host %s already off", host);
+ return S_OK;
+ }
+
+ output = do_shell_cmd(off_cmd, &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", off_cmd);
+ return S_OOPS;
+ }
+ break;
+ case ST_GENERIC_RESET:
+ if (dev->hmcver < 4) {
+ if (is_lpar) {
+ if (state == STATE_ON) {
+ output = do_shell_cmd(off_cmd
+ , &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s "
+ "failed", off_cmd);
+ return S_OOPS;
+ }
+ }
+ for (i = 0; i < MAX_POWERON_RETRY; i++) {
+ char *output2;
+ output2 = do_shell_cmd(on_cmd
+ , &status, dev->password);
+ if (output2 != NULL) {
+ FREE(output2);
+ }
+ if (0 != status) {
+ sleep(1);
+ }else{
+ break;
+ }
+ }
+ if (MAX_POWERON_RETRY == i) {
+ LOG(PIL_CRIT, "command %s failed"
+ , on_cmd);
+ return S_OOPS;
+ }
+ }else{
+ output = do_shell_cmd(reset_cmd
+ , &status, dev->password);
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed" , reset_cmd);
+ return S_OOPS;
+ }
+ break;
+ }
+ }else{
+ if (state == STATE_ON) {
+ output = do_shell_cmd(reset_cmd
+ , &status, dev->password);
+ }else{
+ output = do_shell_cmd(on_cmd
+ , &status, dev->password);
+ }
+ if (0 != status) {
+ LOG(PIL_CRIT, "command %s failed", reset_cmd);
+ return S_OOPS;
+ }
+ }
+ break;
+ default:
+ return S_INVAL;
+ }
+
+ if (output != NULL) {
+ FREE(output);
+ }
+
+ LOG(PIL_INFO, "Host %s %s %d.", host, __FUNCTION__, request);
+
+ return S_OK;
+}
+
+
+/*
+ * Parse the information in the given configuration file,
+ * and stash it away...
+ */
+
+static int
+ibmhmc_set_config(StonithPlugin * s, StonithNVpair* list)
+{
+ struct pluginDevice* dev = NULL;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+ char get_hmcver[MAX_CMD_LEN];
+ char firstchar;
+ int firstnum;
+ char* output = NULL;
+ int status;
+ const char *mansyspats;
+ int len;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ dev = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: ipaddr=%s\n", __FUNCTION__
+ , namestocopy[0].s_value);
+ }
+
+ if (get_num_tokens(namestocopy[0].s_value) == 1) {
+ /* name=value pairs on command line, look for managedsyspat */
+ mansyspats = OurImports->GetValue(list, ST_MANSYSPAT);
+ if (mansyspats != NULL) {
+ if (get_hmc_mansyspats(dev, mansyspats) != S_OK) {
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+ }
+ /* look for password */
+ dev->password = STRDUP(OurImports->GetValue(list, ST_PASSWD));
+ dev->hmc = namestocopy[0].s_value;
+ }else{
+ /* -p or -F option with args "ipaddr [managedsyspat]..." */
+ char *pch = namestocopy[0].s_value;
+
+ /* skip over ipaddr and null-terminate */
+ pch += strcspn(pch, WHITESPACE);
+ *pch = EOS;
+
+ /* skip over white-space up to next token */
+ pch++;
+ pch += strspn(pch, WHITESPACE);
+ if (get_hmc_mansyspats(dev, pch) != S_OK) {
+ FREE(namestocopy[0].s_value);
+ return S_OOPS;
+ }
+
+ dev->hmc = STRDUP(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ }
+
+ /* check whether the HMC has ssh command enabled */
+ if (check_hmc_status(dev) != S_OK) {
+ LOG(PIL_CRIT, "HMC %s does not have remote "
+ "command execution using the ssh facility enabled", dev->hmc);
+ return S_BADCONFIG;
+ }
+
+ /* get the HMC's version info */
+ snprintf(get_hmcver, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lshmc -v | grep RM", dev->hmc);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: get_hmcver=%s", __FUNCTION__, get_hmcver);
+ }
+
+ output = do_shell_cmd(get_hmcver, &status, dev->password);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: output=%s\n", __FUNCTION__
+ , output ? output : "(nil)");
+ }
+ if (output == NULL) {
+ return S_BADCONFIG;
+ }
+
+ /* parse the HMC's version info (i.e. "*RM V4R2.1" or "*RM R3V2.6") */
+ if ((sscanf(output, "*RM %c%1d", &firstchar, &firstnum) == 2)
+ && ((firstchar == 'V') || (firstchar == 'R'))) {
+ dev->hmcver = firstnum;
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: HMC %s version is %d"
+ , __FUNCTION__, dev->hmc, dev->hmcver);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: unable to determine HMC %s version"
+ , __FUNCTION__, dev->hmc);
+ FREE(output);
+ return S_BADCONFIG;
+ }
+
+ len = strlen(output+4) + sizeof(DEVICE) + 1;
+ if (dev->idinfo != NULL) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ dev->idinfo = MALLOC(len * sizeof(char));
+ if (dev->idinfo == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ FREE(output);
+ return S_OOPS;
+ }
+ snprintf(dev->idinfo, len, "%s %s", DEVICE, output+4);
+ FREE(output);
+
+ if (S_OK != get_hmc_hostlist(dev)){
+ LOG(PIL_CRIT, "unable to obtain list of managed systems in %s"
+ , __FUNCTION__);
+ return S_BADCONFIG;
+ }
+
+ return S_OK;
+}
+
+
+static const char*
+ibmhmc_getinfo(StonithPlugin* s, int reqtype)
+{
+ struct pluginDevice* dev;
+ const char* ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ dev = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = dev->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = dev->hmc;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IBM Hardware Management Console (HMC)\n"
+ "Use for IBM i5, p5, pSeries and OpenPower systems "
+ "managed by HMC\n"
+ " Optional parameter name " ST_MANSYSPAT " is "
+ "white-space delimited list of\n"
+ "patterns used to match managed system names; if last "
+ "character is '*',\n"
+ "all names that begin with the pattern are matched\n"
+ " Optional parameter name " ST_PASSWD " is password "
+ "for " HMCROOT " if passwordless\n"
+ "ssh access to HMC has NOT been setup (to do so, it "
+ "is necessary to create\n"
+ "a public/private key pair with empty passphrase - "
+ "see \"Configure the\n"
+ "OpenSSH client\" in the redbook for more details)";
+ break;
+
+ case ST_DEVICEURL:
+ ret = HMCURL;
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = ibmhmcXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * HMC Stonith destructor...
+ */
+
+static void
+ibmhmc_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* dev;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s : called\n", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ dev = (struct pluginDevice *)s;
+
+ dev->pluginid = NOTpluginID;
+ if (dev->hmc) {
+ FREE(dev->hmc);
+ dev->hmc = NULL;
+ }
+ if (dev->password) {
+ FREE(dev->password);
+ dev->password = NULL;
+ }
+ if (dev->idinfo) {
+ FREE(dev->idinfo);
+ dev->idinfo = NULL;
+ }
+ free_hmc_hostlist(dev);
+ free_hmc_mansyspats(dev);
+
+ FREE(dev);
+}
+
+
+static StonithPlugin *
+ibmhmc_new(const char *subplugin)
+{
+ struct pluginDevice* dev = ST_MALLOCT(struct pluginDevice);
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called\n", __FUNCTION__);
+ }
+
+ if (dev == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return(NULL);
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->pluginid = pluginid;
+ dev->hmc = NULL;
+ dev->password = NULL;
+ dev->hostlist = NULL;
+ dev->mansyspats = NULL;
+ dev->hmcver = -1;
+ REPLSTR(dev->idinfo, DEVICE);
+ if (dev->idinfo == NULL) {
+ FREE(dev);
+ return(NULL);
+ }
+ dev->sp.s_ops = &ibmhmcOps;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: returning successfully\n", __FUNCTION__);
+ }
+
+ return((void *)dev);
+}
+
+static int
+get_hmc_hostlist(struct pluginDevice* dev)
+{
+ int i, j, status;
+ char* output = NULL;
+ char get_syslist[MAX_CMD_LEN];
+ char host[MAX_HOST_NAME_LEN];
+ gchar** syslist = NULL;
+ gchar** name_mode = NULL;
+ char get_lpar[MAX_CMD_LEN];
+ gchar** lparlist = NULL;
+ char* pch;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, dev->hmc=%s\n", __FUNCTION__
+ , dev->hmc);
+ }
+
+ if (dev->hmc == NULL || *dev->hmc == 0){
+ return S_BADCONFIG;
+ }
+
+ /* get the managed system's names of the hmc */
+ if (dev->hmcver < 4) {
+ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -r sys -F name:mode --all", dev->hmc);
+ }else{
+ snprintf(get_syslist, MAX_CMD_LEN, SSH_CMD
+ " -l " HMCROOT " %s lssyscfg -r sys -F name", dev->hmc);
+ }
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_syslist=%s", __FUNCTION__, get_syslist);
+ }
+
+ output = do_shell_cmd(get_syslist, &status, dev->password);
+ if (output == NULL) {
+ return S_BADCONFIG;
+ }
+ syslist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each managed system */
+ for (i = 0; syslist[i] != NULL && syslist[i][0] != 0; i++) {
+ if (dev->hmcver < 4) {
+ name_mode = g_strsplit(syslist[i], ":", 2);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: name_mode0=%s, name_mode1=%s\n"
+ , __FUNCTION__, name_mode[0], name_mode[1]);
+ }
+
+ if (dev->mansyspats != NULL
+ && !pattern_match(dev->mansyspats, name_mode[0])) {
+ continue;
+ }
+
+ /* if it is in fullsystempartition */
+ if (NULL != name_mode[1]
+ && 0 == strncmp(name_mode[1], "0", 1)) {
+ /* add the FullSystemPartition */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/FullSystemPartition", name_mode[0]);
+ dev->hostlist = g_list_append(dev->hostlist
+ , STRDUP(host));
+ }else if (NULL != name_mode[1]
+ && 0 == strncmp(name_mode[1], "255", 3)){
+ /* get its lpars */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r lpar -F name --all"
+ , dev->hmc, name_mode[0]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar
+ , &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(name_mode);
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ lparlist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each lpar */
+ for (j = 0
+ ; NULL != lparlist[j] && 0 != lparlist[j][0]
+ ; j++) {
+ /* skip the full system partition */
+ if (0 == strncmp(lparlist[j]
+ , FULLSYSTEMPARTITION
+ , strlen(FULLSYSTEMPARTITION))) {
+ continue;
+ }
+ /* add the lpar */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/%s", name_mode[0]
+ , lparlist[j]);
+ dev->hostlist =
+ g_list_append(dev->hostlist
+ , STRDUP(host));
+ }
+ g_strfreev(lparlist);
+ }
+ g_strfreev(name_mode);
+ }else{
+ if (dev->mansyspats != NULL
+ && !pattern_match(dev->mansyspats, syslist[i])) {
+ continue;
+ }
+
+ /* get its state */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r sys -F state"
+ , dev->hmc, syslist[i]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar, &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ if ((pch = strchr(output, '\n')) != NULL) {
+ *pch = 0;
+ }
+ if (!strcmp(output, "No Connection")){
+ FREE(output);
+ continue;
+ }
+ FREE(output);
+
+ /* get its lpars */
+ snprintf(get_lpar, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT
+ " %s lssyscfg -m %s -r lpar -F name"
+ , dev->hmc, syslist[i]);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: get_lpar=%s\n"
+ , __FUNCTION__, get_lpar);
+ }
+
+ output = do_shell_cmd(get_lpar, &status, dev->password);
+ if (output == NULL) {
+ g_strfreev(syslist);
+ return S_BADCONFIG;
+ }
+ lparlist = g_strsplit(output, "\n", 0);
+ FREE(output);
+
+ /* for each lpar */
+ for (j = 0
+ ; NULL != lparlist[j] && 0 != lparlist[j][0]
+ ; j++) {
+ /* add the lpar */
+ snprintf(host, MAX_HOST_NAME_LEN
+ , "%s/%s", syslist[i],lparlist[j]);
+ dev->hostlist = g_list_append(dev->hostlist
+ , STRDUP(host));
+ }
+ g_strfreev(lparlist);
+ }
+ }
+ g_strfreev(syslist);
+
+ return S_OK;
+}
+
+static void
+free_hmc_hostlist(struct pluginDevice* dev)
+{
+ if (dev->hostlist) {
+ GList* node;
+ while (NULL != (node=g_list_first(dev->hostlist))) {
+ dev->hostlist = g_list_remove_link(dev->hostlist, node);
+ FREE(node->data);
+ g_list_free(node);
+ }
+ dev->hostlist = NULL;
+ }
+}
+
+static int
+get_hmc_mansyspats(struct pluginDevice * dev, const char *mansyspats)
+{
+ char *patscopy;
+ int numpats;
+ int i;
+ char *tmp;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, mansyspats=%s\n"
+ , __FUNCTION__, mansyspats);
+ }
+
+ patscopy = STRDUP(mansyspats);
+ if (patscopy == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return S_OOPS;
+ }
+
+ numpats = get_num_tokens(patscopy);
+ if (numpats > 0) {
+ dev->mansyspats = MALLOC((numpats+1)*sizeof(char *));
+ if (dev->mansyspats == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory"
+ , __FUNCTION__);
+ FREE(patscopy);
+ return S_OOPS;
+ }
+
+ memset(dev->mansyspats, 0, (numpats+1)*sizeof(char *));
+
+ /* White-space split the output here */
+ i = 0;
+ tmp = strtok(patscopy, WHITESPACE);
+ while (tmp != NULL) {
+ dev->mansyspats[i] = STRDUP(tmp);
+ if (dev->mansyspats[i] == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory"
+ , __FUNCTION__);
+ free_hmc_mansyspats(dev);
+ dev->mansyspats = NULL;
+ FREE(patscopy);
+ return S_OOPS;
+ }
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: adding pattern %s\n"
+ , __FUNCTION__, dev->mansyspats[i]);
+ }
+
+ /* no patterns necessary if all specified */
+ if (strcmp(dev->mansyspats[i], "*") == 0) {
+ stonith_free_hostlist(dev->mansyspats);
+ dev->mansyspats = NULL;
+ break;
+ }
+
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+ }
+ FREE(patscopy);
+ return S_OK;
+}
+
+static void
+free_hmc_mansyspats(struct pluginDevice* dev)
+{
+ if (dev->mansyspats) {
+ stonith_free_hostlist(dev->mansyspats);
+ dev->mansyspats = NULL;
+ }
+}
+
+static char*
+do_shell_cmd(const char* cmd, int* status, const char* password)
+{
+ const int BUFF_LEN=4096;
+ int read_len = 0;
+ char buff[BUFF_LEN];
+ char cmd_password[MAX_CMD_LEN];
+ char* data = NULL;
+ GString* g_str_tmp = NULL;
+
+ FILE* file;
+ if (NULL == password) {
+ file = popen(cmd, "r");
+ } else {
+ snprintf(cmd_password, MAX_CMD_LEN
+ ,"umask 077;"
+ "if [ ! -d " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc ];"
+ "then mkdir " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc 2>/dev/null;"
+ "fi;"
+ "export ibmhmc_tmp=`mktemp -p " HA_VARRUNDIR "/heartbeat/rsctmp/ibmhmc/`;"
+ "echo \"echo '%s'\">$ibmhmc_tmp;"
+ "chmod +x $ibmhmc_tmp;"
+ "unset SSH_AGENT_SOCK SSH_AGENT_PID;"
+ "SSH_ASKPASS=$ibmhmc_tmp DISPLAY=ibmhmc_foo setsid %s;"
+ "rm $ibmhmc_tmp -f;"
+ "unset ibmhmc_tmp"
+ ,password, cmd);
+ file = popen(cmd_password, "r");
+ }
+ if (NULL == file) {
+ return NULL;
+ }
+
+ g_str_tmp = g_string_new("");
+ while(!feof(file)) {
+ memset(buff, 0, BUFF_LEN);
+ read_len = fread(buff, 1, BUFF_LEN, file);
+ if (0 < read_len) {
+ g_string_append(g_str_tmp, buff);
+ }else{
+ sleep(1);
+ }
+ }
+ data = (char*)MALLOC(g_str_tmp->len+1);
+ if (data != NULL) {
+ data[0] = data[g_str_tmp->len] = 0;
+ strncpy(data, g_str_tmp->str, g_str_tmp->len);
+ }
+ g_string_free(g_str_tmp, TRUE);
+ *status = pclose(file);
+ return data;
+}
+
+static int
+check_hmc_status(struct pluginDevice* dev)
+{
+ int status;
+ char check_status[MAX_CMD_LEN];
+ char* output = NULL;
+ int rc = S_OK;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, hmc=%s\n", __FUNCTION__, dev->hmc);
+ }
+
+ snprintf(check_status, MAX_CMD_LEN
+ , SSH_CMD " -l " HMCROOT " %s lshmc -r -F ssh", dev->hmc);
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: check_status %s\n", __FUNCTION__
+ , check_status);
+ }
+
+ output = do_shell_cmd(check_status, &status, dev->password);
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: status=%d, output=%s\n", __FUNCTION__
+ , status, output ? output : "(nil)");
+ }
+
+ if (NULL == output || strncmp(output, "enable", 6) != 0) {
+ rc = S_BADCONFIG;
+ }
+ if (NULL != output) {
+ FREE(output);
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static gboolean
+pattern_match(char **patterns, char *string)
+{
+ char **pattern;
+
+ if(Debug){
+ LOG(PIL_DEBUG, "%s: called, string=%s\n", __FUNCTION__, string);
+ }
+
+ for (pattern = patterns; *pattern; pattern++) {
+ int patlen = strlen(*pattern);
+
+ if (pattern[0][patlen-1] == '*') {
+ /* prefix match */
+ if (strncmp(string, *pattern, patlen-1) == 0) {
+ return TRUE;
+ }
+ }else{
+ /* exact match */
+ if (strcmp(string, *pattern) == 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+static char*
+do_shell_cmd_fake(const char* cmd, int* status)
+{
+ printf("%s()\n", __FUNCTION__);
+ printf("cmd:%s\n", cmd);
+ *status=0;
+ return NULL;
+}
+*/
diff --git a/lib/plugins/stonith/ipmi_os_handler.c b/lib/plugins/stonith/ipmi_os_handler.c
new file mode 100644
index 0000000..bdb6d6e
--- /dev/null
+++ b/lib/plugins/stonith/ipmi_os_handler.c
@@ -0,0 +1,257 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ *
+ * Copyright Intel Corp.
+ * Yixiong.Zou@intel.com
+ *
+ * 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
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <OpenIPMI/os_handler.h>
+#include <OpenIPMI/selector.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+#include <OpenIPMI/ipmi_int.h>
+
+#include <time.h>
+
+extern selector_t *os_sel;
+
+#if 0
+static void check_no_locks(os_handler_t *handler);
+#define CHECK_NO_LOCKS(handler) check_no_locks(handler)
+#else
+#define CHECK_NO_LOCKS(handler) do {} while(0)
+#endif
+
+struct os_hnd_fd_id_s
+{
+ int fd;
+ void *cb_data;
+ os_data_ready_t data_ready;
+ os_handler_t *handler;
+};
+
+static void
+fd_handler(int fd, void *data)
+{
+
+ os_hnd_fd_id_t *fd_data = (os_hnd_fd_id_t *) data;
+
+ CHECK_NO_LOCKS(fd_data->handler);
+ fd_data->data_ready(fd, fd_data->cb_data, fd_data);
+ CHECK_NO_LOCKS(fd_data->handler);
+}
+
+static int
+add_fd(os_handler_t *handler,
+ int fd,
+ os_data_ready_t data_ready,
+ void *cb_data,
+ os_hnd_fd_id_t **id)
+{
+ os_hnd_fd_id_t *fd_data;
+
+ fd_data = ipmi_mem_alloc(sizeof(*fd_data));
+ if (!fd_data)
+ return ENOMEM;
+
+ fd_data->fd = fd;
+ fd_data->cb_data = cb_data;
+ fd_data->data_ready = data_ready;
+ fd_data->handler = handler;
+ sel_set_fd_handlers(os_sel, fd, fd_data, fd_handler, NULL, NULL, NULL);
+ sel_set_fd_read_handler(os_sel, fd, SEL_FD_HANDLER_ENABLED);
+ sel_set_fd_write_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED);
+ sel_set_fd_except_handler(os_sel, fd, SEL_FD_HANDLER_DISABLED);
+
+ *id = fd_data;
+ return 0;
+}
+
+static int
+remove_fd(os_handler_t *handler, os_hnd_fd_id_t *fd_data)
+{
+ sel_clear_fd_handlers(os_sel, fd_data->fd);
+ sel_set_fd_read_handler(os_sel, fd_data->fd, SEL_FD_HANDLER_DISABLED);
+ ipmi_mem_free(fd_data);
+ return 0;
+}
+
+struct os_hnd_timer_id_s
+{
+ void *cb_data;
+ os_timed_out_t timed_out;
+ sel_timer_t *timer;
+ int running;
+ os_handler_t *handler;
+};
+
+static void
+timer_handler(selector_t *sel,
+ sel_timer_t *timer,
+ void *data)
+{
+ os_hnd_timer_id_t *timer_data = (os_hnd_timer_id_t *) data;
+ void *cb_data;
+ os_timed_out_t timed_out;
+
+ CHECK_NO_LOCKS(timer_data->handler);
+ timed_out = timer_data->timed_out;
+ cb_data = timer_data->cb_data;
+ timer_data->running = 0;
+ timed_out(cb_data, timer_data);
+ CHECK_NO_LOCKS(timer_data->handler);
+}
+
+static int
+start_timer(os_handler_t *handler,
+ os_hnd_timer_id_t *id,
+ struct timeval *timeout,
+ os_timed_out_t timed_out,
+ void *cb_data)
+{
+ struct timeval now;
+
+ if (id->running)
+ return EBUSY;
+
+ id->running = 1;
+ id->cb_data = cb_data;
+ id->timed_out = timed_out;
+
+ gettimeofday(&now, NULL);
+ now.tv_sec += timeout->tv_sec;
+ now.tv_usec += timeout->tv_usec;
+ while (now.tv_usec >= 1000000) {
+ now.tv_usec -= 1000000;
+ now.tv_sec += 1;
+ }
+
+ return sel_start_timer(id->timer, &now);
+}
+
+static int
+stop_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data)
+{
+ return sel_stop_timer(timer_data->timer);
+}
+
+static int
+alloc_timer(os_handler_t *handler,
+ os_hnd_timer_id_t **id)
+{
+ os_hnd_timer_id_t *timer_data;
+ int rv;
+
+ timer_data = ipmi_mem_alloc(sizeof(*timer_data));
+ if (!timer_data)
+ return ENOMEM;
+
+ timer_data->running = 0;
+ timer_data->timed_out = NULL;
+ timer_data->handler = handler;
+
+ rv = sel_alloc_timer(os_sel, timer_handler, timer_data,
+ &(timer_data->timer));
+ if (rv) {
+ ipmi_mem_free(timer_data);
+ return rv;
+ }
+
+ *id = timer_data;
+ return 0;
+}
+
+static int
+free_timer(os_handler_t *handler, os_hnd_timer_id_t *timer_data)
+{
+ sel_free_timer(timer_data->timer);
+ ipmi_mem_free(timer_data);
+ return 0;
+}
+
+static int
+get_random(os_handler_t *handler, void *data, unsigned int len)
+{
+ int fd = open("/dev/urandom", O_RDONLY);
+ int rv;
+
+ if (fd == -1)
+ return errno;
+
+ rv = read(fd, data, len);
+
+ close(fd);
+ return rv;
+}
+
+static void
+sui_log(os_handler_t *handler,
+ enum ipmi_log_type_e log_type,
+ char *format,
+ ...)
+{
+ return;
+}
+
+static void
+sui_vlog(os_handler_t *handler,
+ enum ipmi_log_type_e log_type,
+ char *format,
+ va_list ap)
+{
+ return;
+}
+
+
+os_handler_t ipmi_os_cb_handlers =
+{
+ .add_fd_to_wait_for = add_fd,
+ .remove_fd_to_wait_for = remove_fd,
+
+ .start_timer = start_timer,
+ .stop_timer = stop_timer,
+ .alloc_timer = alloc_timer,
+ .free_timer = free_timer,
+
+ .create_lock = NULL,
+ .destroy_lock = NULL,
+ .is_locked = NULL,
+ .lock = NULL,
+ .unlock = NULL,
+ .create_rwlock = NULL,
+ .destroy_rwlock = NULL,
+ .read_lock = NULL,
+ .write_lock = NULL,
+ .read_unlock = NULL,
+ .write_unlock = NULL,
+ .is_readlocked = NULL,
+ .is_writelocked = NULL,
+
+ .get_random = get_random,
+
+ .log = sui_log,
+ .vlog = sui_vlog
+};
+
+
diff --git a/lib/plugins/stonith/ipmilan.c b/lib/plugins/stonith/ipmilan.c
new file mode 100644
index 0000000..1efdfee
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.c
@@ -0,0 +1,587 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005.
+ * And passed the compiling with OpenIPMI-1.4.8.
+ *
+ * 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
+ *
+ */
+
+
+/*
+ * See README.ipmi for information regarding this plugin.
+ *
+ */
+
+#define DEVICE "IPMI Over LAN"
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN ipmilan
+#define PIL_PLUGIN_S "ipmilan"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#include <OpenIPMI/ipmi_types.h>
+#include <OpenIPMI/ipmi_auth.h>
+
+#include "ipmilan.h"
+
+static StonithPlugin * ipmilan_new(const char *);
+static void ipmilan_destroy(StonithPlugin *);
+static const char * const * ipmilan_get_confignames(StonithPlugin *);
+static int ipmilan_set_config(StonithPlugin *, StonithNVpair *);
+static const char * ipmilan_getinfo(StonithPlugin * s, int InfoType);
+static int ipmilan_status(StonithPlugin * );
+static int ipmilan_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** ipmilan_hostlist(StonithPlugin *);
+
+static struct stonith_ops ipmilanOps ={
+ ipmilan_new, /* Create new STONITH object */
+ ipmilan_destroy, /* Destroy STONITH object */
+ ipmilan_getinfo, /* Return STONITH info string */
+ ipmilan_get_confignames,/* Get configuration parameter names */
+ ipmilan_set_config, /* Set configuration */
+ ipmilan_status, /* Return STONITH device status */
+ ipmilan_reset_req, /* Request a reset */
+ ipmilan_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug);
+const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &ipmilanOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * ipmilan STONITH device.
+ *
+ * ipmilanHostInfo is a double linked list. Where the prev of the head always
+ * points to the tail. This is a little wierd. But it saves me from looping
+ * around to find the tail when destroying the list.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ int hostcount;
+ struct ipmilanHostInfo * hostlist;
+};
+
+static const char * pluginid = "IPMI-LANDevice-Stonith";
+static const char * NOTpluginid = "IPMI-LAN device has been destroyed";
+
+#define ST_HOSTNAME "hostname"
+#define ST_PORT "port"
+#define ST_AUTH "auth"
+#define ST_PRIV "priv"
+#define ST_RESET_METHOD "reset_method"
+
+#include "stonith_config_xml.h"
+
+#define XML_HOSTNAME_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_HOSTNAME \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOSTNAME_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hostname of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_HOSTNAME_PARM \
+ XML_PARAMETER_BEGIN(ST_HOSTNAME, "string", "1", "1") \
+ XML_HOSTNAME_SHORTDESC \
+ XML_HOSTNAME_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number to where the IPMI message is sent" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_AUTH_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_AUTH \
+ XML_PARM_SHORTDESC_END
+
+#define XML_AUTH_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The authorization type of the IPMI session (\"none\", \"straight\", \"md2\", or \"md5\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_AUTH_PARM \
+ XML_PARAMETER_BEGIN(ST_AUTH, "string", "1", "0") \
+ XML_AUTH_SHORTDESC \
+ XML_AUTH_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PRIV_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PRIV \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PRIV_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The privilege level of the user (\"operator\" or \"admin\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PRIV_PARM \
+ XML_PARAMETER_BEGIN(ST_PRIV, "string", "1", "0") \
+ XML_PRIV_SHORTDESC \
+ XML_PRIV_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_RESET_METHOD_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_RESET_METHOD \
+ XML_PARM_SHORTDESC_END
+
+#define XML_RESET_METHOD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "How to reset the host (\"power_cycle\" or \"hard_reset\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_RESET_METHOD_PARM \
+ XML_PARAMETER_BEGIN(ST_RESET_METHOD, "string", "0", "0") \
+ XML_RESET_METHOD_SHORTDESC \
+ XML_RESET_METHOD_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *ipmilanXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTNAME_PARM
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_AUTH_PARM
+ XML_PRIV_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * Check the status of the IPMI Lan STONITH device.
+ *
+ * NOTE: not sure what we should do here since each host is configured
+ * seperately.
+ *
+ * Two options:
+ * 1) always return S_OK.
+ * 2) using IPMI ping to confirm the status for every host that's
+ * configured.
+ *
+ * For now I choose the option 1 hoping that I can get by. Maybe we should
+ * change it to option 2 later.
+ */
+
+static int
+ipmilan_status(StonithPlugin *s)
+{
+ struct pluginDevice * nd;
+ struct ipmilanHostInfo * node;
+ int ret, rv;
+ int i;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ ret = S_OK;
+
+ nd = (struct pluginDevice *)s;
+ for( i=0, node = nd->hostlist;
+ i < nd->hostcount; i++, node = node->next ) {
+ rv = do_ipmi_cmd(node, ST_IPMI_STATUS);
+ if (rv) {
+ LOG(PIL_INFO, "Host %s ipmilan status failure."
+ , node->hostname);
+ ret = S_ACCESS;
+ } else {
+ LOG(PIL_INFO, "Host %s ipmilan status OK."
+ , node->hostname);
+ }
+
+ }
+
+ return ret;
+}
+
+/*
+ * This function returns the list of hosts that's configured.
+ *
+ * The detailed configuration is disabled because the STONITH command can be
+ * run by anyone so there is a security risk if that to be exposed.
+ */
+
+static char *
+get_config_string(struct pluginDevice * nd, int index)
+{
+ struct ipmilanHostInfo * host;
+ int i;
+
+ if (index >= nd->hostcount || index < 0) {
+ return (NULL);
+ }
+
+ host = nd->hostlist;
+ for (i = 0; i < index; i++) {
+ host = host->next;
+ }
+
+ return STRDUP(host->hostname);
+}
+
+
+/*
+ * Return the list of hosts configured for this ipmilan device
+ *
+ */
+
+static char **
+ipmilan_hostlist(StonithPlugin *s)
+{
+ int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* nd;
+ int j;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in ipmi_hostlist");
+ return(NULL);
+ }
+ numnames = nd->hostcount;
+
+ ret = (char **)MALLOC((numnames + 1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return (ret);
+ }
+
+ memset(ret, 0, (numnames + 1)*sizeof(char*));
+
+ for (j = 0; j < numnames; ++j) {
+ ret[j] = get_config_string(nd, j);
+ if (!ret[j]) {
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ break;
+ }
+ strdown(ret[j]);
+ }
+
+ return(ret);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ *
+ * The buffer for each string is MAX_IPMI_STRING_LEN bytes long.
+ * Right now it is set to 64. Hope this is enough.
+ *
+ */
+
+#define MAX_IPMI_STRING_LEN 64
+
+/*
+ * Reset the given host on this StonithPlugin device.
+ */
+static int
+ipmilan_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ struct pluginDevice * nd;
+ struct ipmilanHostInfo * node;
+ int i;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ nd = (struct pluginDevice *)s;
+ for( i=0, node = nd->hostlist;
+ i < nd->hostcount; i++, node = node->next ) {
+ if (strcasecmp(node->hostname, host) == 0) {
+ break;
+ }
+ }
+
+ if (i >= nd->hostcount) {
+ LOG(PIL_CRIT, "Host %s is not configured in this STONITH "
+ " module. Please check your configuration file.", host);
+ return (S_OOPS);
+ }
+
+ rc = do_ipmi_cmd(node, request);
+ if (!rc) {
+ LOG(PIL_INFO, "Host %s ipmilan-reset.", host);
+ } else {
+ LOG(PIL_INFO, "Host %s ipmilan-reset error. Error = %d."
+ , host, rc);
+ }
+ return rc;
+}
+
+/*
+ * Get configuration parameter names
+ */
+static const char * const *
+ipmilan_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] =
+ { ST_HOSTNAME, ST_IPADDR, ST_PORT, ST_AUTH,
+ ST_PRIV, ST_LOGIN, ST_PASSWD, ST_RESET_METHOD, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters
+ */
+static int
+ipmilan_set_config(StonithPlugin* s, StonithNVpair * list)
+{
+ struct pluginDevice* nd;
+ int rc;
+ struct ipmilanHostInfo * tmp;
+ const char *reset_opt;
+
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTNAME, NULL}
+ , {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_AUTH, NULL}
+ , {ST_PRIV, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice *)s;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (nd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ tmp = ST_MALLOCT(struct ipmilanHostInfo);
+ tmp->hostname = namestocopy[0].s_value;
+ tmp->ipaddr = namestocopy[1].s_value;
+ tmp->portnumber = atoi(namestocopy[2].s_value);
+ FREE(namestocopy[2].s_value);
+ if (namestocopy[3].s_value == NULL) {
+ LOG(PIL_CRIT, "ipmilan auth type is NULL. See "
+ "README.ipmilan for allowed values");
+ return S_OOPS;
+ } else if (strcmp(namestocopy[3].s_value, "none") == 0) {
+ tmp->authtype = 0;
+ } else if (strcmp(namestocopy[3].s_value, "md2") == 0) {
+ tmp->authtype = 1;
+ } else if (strcmp(namestocopy[3].s_value, "md5") == 0) {
+ tmp->authtype = 2;
+ } else if (strcmp(namestocopy[3].s_value, "key") == 0 ||
+ strcmp(namestocopy[3].s_value, "password") == 0 ||
+ strcmp(namestocopy[3].s_value, "straight") == 0) {
+ tmp->authtype = 4;
+ } else {
+ LOG(PIL_CRIT, "ipmilan auth type '%s' invalid. See "
+ "README.ipmilan for allowed values", namestocopy[3].s_value);
+ return S_OOPS;
+ }
+ FREE(namestocopy[3].s_value);
+ if (namestocopy[4].s_value == NULL) {
+ LOG(PIL_CRIT, "ipmilan priv value is NULL. See "
+ "README.ipmilan for allowed values");
+ return S_OOPS;
+ } else if (strcmp(namestocopy[4].s_value, "operator") == 0) {
+ tmp->privilege = 3;
+ } else if (strcmp(namestocopy[4].s_value, "admin") == 0) {
+ tmp->privilege = 4;
+ } else {
+ LOG(PIL_CRIT, "ipmilan priv value '%s' invalid. See "
+ "README.ipmilan for allowed values", namestocopy[4].s_value);
+ return(S_OOPS);
+ }
+ FREE(namestocopy[4].s_value);
+ tmp->username = namestocopy[5].s_value;
+ tmp->password = namestocopy[6].s_value;
+ reset_opt = OurImports->GetValue(list, ST_RESET_METHOD);
+ if (!reset_opt || !strcmp(reset_opt, "power_cycle")) {
+ tmp->reset_method = 0;
+ } else if (!strcmp(reset_opt, "hard_reset")) {
+ tmp->reset_method = 1;
+ } else {
+ LOG(PIL_CRIT, "ipmilan reset_method '%s' invalid", reset_opt);
+ return S_OOPS;
+ }
+
+ if (nd->hostlist == NULL ) {
+ nd->hostlist = tmp;
+ nd->hostlist->prev = tmp;
+ nd->hostlist->next = tmp;
+ } else {
+ tmp->prev = nd->hostlist->prev;
+ tmp->next = nd->hostlist;
+ nd->hostlist->prev->next = tmp;
+ nd->hostlist->prev = tmp;
+ }
+ nd->hostcount++;
+
+ return(S_OK);
+}
+
+static const char *
+ipmilan_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice * nd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = nd->hostlist ? nd->hostlist->hostname : NULL;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "IPMI LAN STONITH device\n";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.intel.com/design/servers/ipmi/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = ipmilanXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * ipmilan StonithPlugin destructor...
+ *
+ * The hostlist is a link list. So have to iterate through.
+ */
+static void
+ipmilan_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+ struct ipmilanHostInfo * host;
+ int i;
+
+ VOIDERRIFWRONGDEV(s);
+
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginid;
+
+ if (nd->hostlist) {
+ host = nd->hostlist->prev;
+ for (i = 0; i < nd->hostcount; i++) {
+ struct ipmilanHostInfo * host_prev = host->prev;
+
+ FREE(host->hostname);
+ FREE(host->ipaddr);
+ FREE(host->username);
+ FREE(host->password);
+
+ FREE(host);
+ host = host_prev;
+ }
+ }
+
+ nd->hostcount = -1;
+ FREE(nd);
+ ipmi_leave();
+}
+
+/* Create a new ipmilan StonithPlugin device. Too bad this function can't be static */
+static StonithPlugin *
+ipmilan_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ LOG(PIL_WARN, "The ipmilan stonith plugin is deprecated! Please use external/ipmi.");
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = 0;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &ipmilanOps;
+ return(&(nd->sp));
+}
diff --git a/lib/plugins/stonith/ipmilan.h b/lib/plugins/stonith/ipmilan.h
new file mode 100644
index 0000000..fb548f0
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.h
@@ -0,0 +1,41 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * 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
+ *
+ */
+
+#define ST_IPMI_STATUS 4
+#include <time.h>
+
+struct ipmilanHostInfo {
+ char * hostname;
+ char * ipaddr;
+ int portnumber;
+ int authtype;
+ int privilege;
+ char * username;
+ char * password;
+ int reset_method;
+
+ struct ipmilanHostInfo * prev;
+ struct ipmilanHostInfo * next;
+};
+
+int do_ipmi_cmd(struct ipmilanHostInfo * host, int request);
+void ipmi_leave(void);
diff --git a/lib/plugins/stonith/ipmilan_command.c b/lib/plugins/stonith/ipmilan_command.c
new file mode 100644
index 0000000..a3de493
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_command.c
@@ -0,0 +1,399 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ *
+ * Copyright Intel Corp.
+ * Yixiong.Zou@intel.com
+ *
+ *
+ * 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
+ */
+#include <stdio.h>
+
+#include <stdlib.h> /* malloc() */
+#include <unistd.h> /* getopt() */
+#include <string.h> /* strerror() */
+#include <netdb.h> /* gethostbyname() */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/selector.h>
+#include <OpenIPMI/ipmi_conn.h>
+#include <OpenIPMI/ipmi_lan.h>
+#include <OpenIPMI/ipmi_smi.h>
+#include <OpenIPMI/ipmi_auth.h>
+#include <OpenIPMI/ipmi_msgbits.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_debug.h>
+
+#include "ipmilan.h"
+#include <stonith/stonith.h>
+#include <clplumbing/cl_log.h>
+
+#include <pils/plugin.h>
+extern const PILPluginImports* PluginImports;
+
+/* #define DUMP_MSG 0 */
+#define OPERATION_TIME_OUT 10
+
+os_handler_t *os_hnd=NULL;
+selector_t *os_sel;
+static ipmi_con_t *con;
+extern os_handler_t ipmi_os_cb_handlers;
+static int reset_method;
+
+static int request_done = 0;
+static int op_done = 0;
+
+typedef enum ipmi_status {
+ /*
+ IPMI_CONNECTION_FAILURE,
+ IPMI_SEND_FAILURE,
+ IPMI_BAD_REQUEST,
+ IPMI_REQUEST_FAILED,
+ IPMI_TIME_OUT,
+ */
+ IPMI_RUNNING = 99,
+} ipmi_status_t;
+
+static ipmi_status_t gstatus;
+
+typedef enum chassis_control_request {
+ POWER_DOWN = 0X00,
+ POWER_UP = 0X01,
+ POWER_CYCLE = 0X02,
+ HARD_RESET = 0X03,
+ PULSE_DIAGNOSTIC_INTERRUPT = 0X04,
+ SOFT_SHUTDOWN = 0X05
+} chassis_control_request_t;
+
+void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type);
+int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi);
+
+void send_ipmi_cmd(ipmi_con_t *con, int request);
+
+void timed_out(selector_t *sel, sel_timer_t *timer, void *data);
+
+void
+timed_out(selector_t *sel, sel_timer_t *timer, void *data)
+{
+ PILCallLog(PluginImports->log,PIL_CRIT, "IPMI operation timed out... :(\n");
+ gstatus = S_TIMEOUT;
+}
+
+void
+dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type)
+{
+ ipmi_system_interface_addr_t *smi_addr = NULL;
+ int i;
+ ipmi_ipmb_addr_t *ipmb_addr = NULL;
+
+ if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
+ smi_addr = (struct ipmi_system_interface_addr *) addr;
+
+ fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
+ addr->channel,
+ msg->netfn,
+ smi_addr->lun,
+ msg->cmd);
+ } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
+ || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) {
+ ipmb_addr = (struct ipmi_ipmb_addr *) addr;
+
+ fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
+ addr->channel,
+ msg->netfn,
+ ipmb_addr->lun,
+ msg->cmd);
+ }
+
+ for (i = 0; i < msg->data_len; i++) {
+ if (((i%16) == 0) && (i != 0)) {
+ printf("\n ");
+ }
+ fprintf(stderr, "%2.2x ", msg->data[i]);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * This function gets called after the response comes back
+ * from the IPMI device.
+ *
+ * Some IPMI device does not return success, 0x00, to the
+ * remote node when the power-reset was issued.
+ *
+ * The host who sent the ipmi cmd might get a 0xc3,
+ * a timeout instead. This creates problems for
+ * STONITH operation, where status is critical. :(
+ *
+ * Right now I am only checking 0xc3 as the return.
+ * If your IPMI device returns some wired code after
+ * reset, you might want to add it in this code block.
+ *
+ */
+
+int
+rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi)
+{
+ int rv;
+ long request;
+
+ /*dump_msg_data(&rspi->msg, &rspi->addr, "response");*/
+ request = (long) rspi->data1;
+
+ op_done = 1;
+ if( !rspi || !(rspi->msg.data) ) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "No data received\n");
+ gstatus = S_RESETFAIL;
+ return IPMI_MSG_ITEM_NOT_USED;
+ }
+ rv = rspi->msg.data[0];
+ /* some IPMI device might not issue 0x00, success, for reset command.
+ instead, a 0xc3, timeout, is returned. */
+ if (rv == 0x00) {
+ gstatus = S_OK;
+ } else if((rv == 0xc3 || rv == 0xff) && request == ST_GENERIC_RESET) {
+ PILCallLog(PluginImports->log,PIL_WARN ,
+ "IPMI reset request failed: %x, but we assume that it succeeded\n", rv);
+ gstatus = S_OK;
+ } else {
+ PILCallLog(PluginImports->log,PIL_INFO
+ , "IPMI request %ld failed: %x\n", request, rv);
+ gstatus = S_RESETFAIL;
+ }
+ return IPMI_MSG_ITEM_NOT_USED;
+}
+
+void
+send_ipmi_cmd(ipmi_con_t *con, int request)
+{
+ ipmi_addr_t addr;
+ unsigned int addr_len;
+ ipmi_msg_t msg;
+ struct ipmi_system_interface_addr *si;
+ int rv;
+ ipmi_msgi_t *rspi;
+ /* chassis control command request is only 1 byte long */
+ unsigned char cc_data = POWER_CYCLE;
+
+ si = (void *) &addr;
+ si->lun = 0x00;
+ si->channel = IPMI_BMC_CHANNEL;
+ si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ addr_len = sizeof(*si);
+
+ msg.netfn = IPMI_CHASSIS_NETFN;
+ msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
+ msg.data = &cc_data;
+ msg.data_len = 1;
+
+ switch (request) {
+ case ST_POWERON:
+ cc_data = POWER_UP;
+ break;
+
+ case ST_POWEROFF:
+ cc_data = POWER_DOWN;
+ break;
+
+ case ST_GENERIC_RESET:
+ cc_data = (reset_method ? POWER_CYCLE : HARD_RESET);
+ break;
+
+ case ST_IPMI_STATUS:
+ msg.netfn = IPMI_APP_NETFN;
+ msg.cmd = IPMI_GET_DEVICE_ID_CMD;
+ msg.data_len = 0;
+ break;
+
+ default:
+ gstatus = S_INVAL;
+ return;
+ }
+
+ gstatus = S_ACCESS;
+ rspi = calloc(1, sizeof(ipmi_msgi_t));
+ if (NULL == rspi) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: Out of memory\n");
+ } else {
+ rspi->data1 = (void *) (long) request;
+ rv = con->send_command(con, &addr, addr_len, &msg, rsp_handler, rspi);
+ if (rv == -1) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv);
+ } else {
+ request_done = 1;
+ }
+ }
+
+ return;
+}
+
+static void
+con_changed_handler(ipmi_con_t *ipmi, int err, unsigned int port_num,
+ int still_connected, void *cb_data)
+{
+ int * request;
+
+ if (err) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Unable to setup connection: %x\n", err);
+ return;
+ }
+
+ if( !request_done ) {
+ request = (int *) cb_data;
+ send_ipmi_cmd(ipmi, *request);
+ }
+}
+
+static int
+setup_ipmi_conn(struct ipmilanHostInfo * host, int *request)
+{
+ int rv;
+
+ struct hostent *ent;
+ struct in_addr lan_addr[2];
+ int lan_port[2];
+ int num_addr = 1;
+ int authtype = 0;
+ int privilege = 0;
+ char username[17];
+ char password[17];
+
+ /*DEBUG_MSG_ENABLE();*/
+
+ os_hnd = ipmi_posix_get_os_handler();
+ if (!os_hnd) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_smi_setup_con: Unable to allocate os handler");
+ return 1;
+ }
+
+ rv = sel_alloc_selector(os_hnd, &os_sel);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Could not allocate selector\n");
+ return rv;
+ }
+
+ ipmi_posix_os_handler_set_sel(os_hnd, os_sel);
+
+ rv = ipmi_init(os_hnd);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_init erro: %d ", rv);
+ return rv;
+ }
+
+ ent = gethostbyname(host->ipaddr);
+ if (!ent) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "gethostbyname failed: %s\n", strerror(h_errno));
+ return 1;
+ }
+
+ memcpy(&lan_addr[0], ent->h_addr_list[0], ent->h_length);
+ lan_port[0] = host->portnumber;
+ lan_port[1] = 0;
+
+ authtype = host->authtype;
+ privilege = host->privilege;
+
+ memcpy(username, host->username, sizeof(username));
+ memcpy(password, host->password, sizeof(password));
+
+ reset_method = host->reset_method;
+
+ rv = ipmi_lan_setup_con(lan_addr, lan_port, num_addr,
+ authtype, privilege,
+ username, strlen(username),
+ password, strlen(password),
+ os_hnd, os_sel,
+ &con);
+
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_lan_setup_con: %s\n", strerror(rv));
+ return S_ACCESS;
+ }
+
+#if OPENIPMI_VERSION_MAJOR < 2
+ con->set_con_change_handler(con, con_changed_handler, request);
+#else
+ con->add_con_change_handler(con, con_changed_handler, request);
+#endif
+
+ gstatus = IPMI_RUNNING;
+
+ rv = con->start_con(con);
+ if (rv) {
+ PILCallLog(PluginImports->log,PIL_CRIT, "Could not start IPMI connection: %x\n", rv);
+ gstatus = S_BADCONFIG;
+ return rv;
+ }
+ return S_OK;
+}
+
+void
+ipmi_leave()
+{
+ if( con && con->close_connection ) {
+ con->close_connection(con);
+ con = NULL;
+ }
+ if( os_sel ) {
+ sel_free_selector(os_sel);
+ os_sel = NULL;
+ }
+}
+
+int
+do_ipmi_cmd(struct ipmilanHostInfo * host, int request)
+{
+ int rv;
+ sel_timer_t * timer;
+ struct timeval timeout;
+
+ request_done = 0;
+ op_done = 0;
+
+ if( !os_hnd ) {
+ rv = setup_ipmi_conn(host, &request);
+ if( rv ) {
+ return rv;
+ }
+ } else {
+ send_ipmi_cmd(con, request);
+ }
+
+ gettimeofday(&timeout, NULL);
+ timeout.tv_sec += OPERATION_TIME_OUT;
+ timeout.tv_usec += 0;
+
+ sel_alloc_timer(os_sel, timed_out, NULL, &timer);
+ sel_start_timer(timer, &timeout);
+
+ while (!op_done) {
+ rv = sel_select(os_sel, NULL, 0, NULL, NULL);
+ if (rv == -1) {
+ break;
+ }
+ }
+
+ sel_free_timer(timer);
+ return gstatus;
+}
+
+#if OPENIPMI_VERSION_MAJOR < 2
+void
+posix_vlog(char *format, enum ipmi_log_type_e log_type, va_list ap)
+{
+}
+#endif
diff --git a/lib/plugins/stonith/ipmilan_test.c b/lib/plugins/stonith/ipmilan_test.c
new file mode 100644
index 0000000..47859a0
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_test.c
@@ -0,0 +1,63 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp.
+ * Yixiong Zou <yixiong.zou@intel.com>
+ *
+ * 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
+ *
+ */
+
+/*
+ * A quick test program to verify that IPMI host is setup correctly.
+ *
+ * You will need to modify the values in user, pass, ip, and port.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "ipmilan.h"
+#include <OpenIPMI/ipmi_auth.h>
+
+int main(int argc, char * argv[])
+{
+ struct ipmilanHostInfo host;
+ int request = 2;
+ int rv;
+
+ char user[] = "joe";
+ char pass[] = "blow";
+ char ip[] = "192.168.1.7";
+
+ host.hostname = NULL;
+ host.portnumber = 999;
+ host.authtype = IPMI_AUTHTYPE_NONE;
+ host.privilege = IPMI_PRIVILEGE_ADMIN;
+
+ host.ipaddr = ip;
+ memcpy(host.username, user, sizeof(user));
+ memcpy(host.password, pass, sizeof(pass));
+ /*
+ memset(host.username, 0, sizeof(host.username));
+ memset(host.password, 0, sizeof(host.password));
+ */
+
+ rv = do_ipmi_cmd(&host, request);
+ if (rv)
+ printf("rv = %d, operation failed. \n", rv);
+ else
+ printf("operation succeeded. \n");
+ return rv;
+}
diff --git a/lib/plugins/stonith/meatware.c b/lib/plugins/stonith/meatware.c
new file mode 100644
index 0000000..029ba35
--- /dev/null
+++ b/lib/plugins/stonith/meatware.c
@@ -0,0 +1,351 @@
+/*
+ * Stonith module for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder@sysfive.com>
+ *
+ * This module is largely based on the "NULL Stonith device", written
+ * by Alan Robertson <alanr@unix.sh>, using code by David C. Teigland
+ * <teigland@sistina.com> originally appeared in the GFS stomith
+ * meatware agent.
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "Meatware STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN meatware
+#define PIL_PLUGIN_S "meatware"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * meatware_new(const char *);
+static void meatware_destroy(StonithPlugin *);
+static int meatware_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * meatware_get_confignames(StonithPlugin *);
+static const char * meatware_getinfo(StonithPlugin * s, int InfoType);
+static int meatware_status(StonithPlugin * );
+static int meatware_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** meatware_hostlist(StonithPlugin *);
+
+static struct stonith_ops meatwareOps ={
+ meatware_new, /* Create new STONITH object */
+ meatware_destroy, /* Destroy STONITH object */
+ meatware_getinfo, /* Return STONITH info string */
+ meatware_get_confignames,/* Return STONITH info string */
+ meatware_set_config, /* Get configuration from NVpairs */
+ meatware_status, /* Return STONITH device status */
+ meatware_reset_req, /* Request a reset */
+ meatware_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &meatwareOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Meatware STONITH device.
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "MeatwareDevice-Stonith";
+static const char * NOTpluginID = "Meatware device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *meatwareXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+meatware_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this Meat device
+ */
+
+static char **
+meatware_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ ERRIFWRONGDEV(s,NULL);
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in Meatware_list_hosts");
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)nd->hostlist);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ */
+
+static int
+Meat_parse_config_info(struct pluginDevice* nd, const char * info)
+{
+ LOG(PIL_INFO , "parse config info info=%s",info);
+ if (nd->hostcount >= 0) {
+ return(S_OOPS);
+ }
+
+ nd->hostlist = OurImports->StringToHostList(info);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return(S_OK);
+}
+
+
+/*
+ * Indicate that host must be power cycled manually.
+ */
+static int
+meatware_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int fd, rc;
+ const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to
+ change this, modify
+ meatclient.c as well */
+
+ char line[256], meatpipe[256];
+ char resp_addr[50], resp_mw[50], resp_result[50];
+
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, host);
+ umask(0);
+ unlink(meatpipe);
+
+ rc = mkfifo(meatpipe, (S_IRUSR | S_IWUSR));
+
+ if (rc < 0) {
+ LOG(PIL_CRIT, "cannot create FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ LOG(PIL_CRIT, "OPERATOR INTERVENTION REQUIRED to reset %s.", host);
+ LOG(PIL_CRIT, "Run \"meatclient -c %s\" AFTER power-cycling the "
+ "machine.", host);
+
+ fd = open(meatpipe, O_RDONLY);
+
+ if (fd < 0) {
+ LOG(PIL_CRIT, "cannot open FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ alarm(600);
+ memset(line, 0, 256);
+ rc = read(fd, line, 256);
+ alarm(0);
+
+ if (rc < 0) {
+ LOG(PIL_CRIT, "read error on FIFO for Meatware_reset_host");
+ return S_OOPS;
+ }
+
+ memset(resp_mw, 0, 50);
+ memset(resp_result, 0, 50);
+ memset(resp_addr, 0, 50);
+
+ if (sscanf(line, "%s %s %s", resp_mw, resp_result, resp_addr) < 3) {
+ LOG(PIL_CRIT, "Format error - failed to Meatware-reset node %s",
+ host);
+ return S_RESETFAIL;
+ }
+
+ strdown(resp_addr);
+
+ if (strncmp(resp_mw, "meatware", 8) ||
+ strncmp(resp_result, "reply", 5) ||
+ strncasecmp(resp_addr, host, strlen(resp_addr))) {
+ LOG(PIL_CRIT, "failed to Meatware-reset node %s", host);
+ return S_RESETFAIL;
+ }else{
+ LOG(PIL_INFO, "node Meatware-reset: %s", host);
+ unlink(meatpipe);
+ return S_OK;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+meatware_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+
+ struct pluginDevice* nd;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ rc = Meat_parse_config_info(nd, namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+meatware_get_confignames(StonithPlugin* p)
+{
+ static const char * MeatwareParams[] = {ST_HOSTLIST, NULL };
+ return MeatwareParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+meatware_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = "Your Name Here";
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Human (meatware) intervention STONITH device.\n"
+ "This STONITH agent prompts a human to reset a machine.\n"
+ "The human tells it when the reset was completed.";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = meatwareXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Meat Stonith destructor...
+ */
+static void
+meatware_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(nd);
+}
+
+/* Create a new Meatware Stonith device. */
+
+static StonithPlugin *
+meatware_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = -1;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &meatwareOps;
+
+ return &(nd->sp);
+}
diff --git a/lib/plugins/stonith/null.c b/lib/plugins/stonith/null.c
new file mode 100644
index 0000000..0d0cf04
--- /dev/null
+++ b/lib/plugins/stonith/null.c
@@ -0,0 +1,260 @@
+/*
+ * Stonith module for NULL Stonith device
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "NULL STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN null
+#define PIL_PLUGIN_S "null"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static StonithPlugin* null_new(const char *);
+static void null_destroy(StonithPlugin *);
+static int null_set_config(StonithPlugin*
+, StonithNVpair*);
+static const char * const * null_get_confignames(StonithPlugin*);
+static const char * null_getinfo(StonithPlugin * s, int InfoType);
+static int null_status(StonithPlugin * );
+static int null_reset_req(StonithPlugin * s
+, int request, const char * host);
+static char ** null_hostlist(StonithPlugin *);
+
+static struct stonith_ops nullOps ={
+ null_new, /* Create new STONITH object */
+ null_destroy, /* Destroy STONITH object */
+ null_getinfo, /* Return STONITH info string */
+ null_get_confignames, /* Return list of config params */
+ null_set_config, /* configure fron NV pairs */
+ null_status, /* Return STONITH device status */
+ null_reset_req, /* Request a reset */
+ null_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &nullOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * Null STONITH device. We are very agreeable, but don't do much :-)
+ */
+
+
+static const char * pluginid = "nullDevice-Stonith";
+static const char * NOTpluginID = "Null device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *nullXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+null_status(StonithPlugin *s)
+{
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this NULL device
+ */
+
+static char **
+null_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd = (struct pluginDevice*)s;
+
+ ERRIFWRONGDEV(s, NULL);
+ return OurImports->CopyHostList((const char * const *)nd->hostlist);
+}
+
+
+/*
+ * Pretend to reset the given host on this Stonith device.
+ * (we don't even error check the "request" type)
+ */
+static int
+null_reset_req(StonithPlugin * s, int request, const char * host)
+{
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* Real devices need to pay attention to the "request" */
+ /* (but we don't care ;-)) */
+
+ LOG(PIL_INFO, "Host null-reset: %s", host);
+ return S_OK;
+}
+
+
+static const char * const *
+null_get_confignames(StonithPlugin* p)
+{
+ static const char * NullParams[] = {ST_HOSTLIST, NULL };
+ return NullParams;
+}
+
+/*
+ * Parse the config information in the given string,
+ * and stash it away...
+ */
+static int
+null_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* nd = (struct pluginDevice*) s;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ nd->hostlist = OurImports->StringToHostList(namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]
+ ; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return nd->hostcount ? S_OK : S_BADCONFIG;
+}
+
+static const char *
+null_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd = (struct pluginDevice*) s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = "(nil)";
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "Dummy (do-nothing) STONITH device\n"
+ "FOR TESTING ONLY!";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = nullXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * NULL Stonith destructor...
+ */
+static void
+null_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTpluginID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(s);
+}
+
+/* Create a new Null Stonith device.
+ * Too bad this function can't be static
+ */
+static StonithPlugin *
+null_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &nullOps;
+ return (StonithPlugin *)nd;
+}
diff --git a/lib/plugins/stonith/nw_rpc100s.c b/lib/plugins/stonith/nw_rpc100s.c
new file mode 100644
index 0000000..5ba0827
--- /dev/null
+++ b/lib/plugins/stonith/nw_rpc100s.c
@@ -0,0 +1,779 @@
+/*
+ * Stonith module for Night/Ware RPC100S
+ *
+ * Original code from baytech.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Modifications for NW RPC100S
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#define DEVICE "NW RPC100S Power Switch"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN nw_rpc100s
+#define PIL_PLUGIN_S "nw_rpc100s"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define MAX_CFGLINE 256
+#include <pils/plugin.h>
+
+static StonithPlugin * nw_rpc100s_new(const char *);
+static void nw_rpc100s_destroy(StonithPlugin *);
+static int nw_rpc100s_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * nw_rpc100s_get_confignames(StonithPlugin *);
+static const char * nw_rpc100s_getinfo(StonithPlugin * s, int InfoType);
+static int nw_rpc100s_status(StonithPlugin * );
+static int nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** nw_rpc100s_hostlist(StonithPlugin *);
+
+static struct stonith_ops nw_rpc100sOps ={
+ nw_rpc100s_new, /* Create new STONITH object */
+ nw_rpc100s_destroy, /* Destroy STONITH object */
+ nw_rpc100s_getinfo, /* Return STONITH info string */
+ nw_rpc100s_get_confignames,/* Return STONITH info string */
+ nw_rpc100s_set_config, /* Get configuration from NVpairs */
+ nw_rpc100s_status, /* Return STONITH device status */
+ nw_rpc100s_reset_req, /* Request a reset */
+ nw_rpc100s_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_signal.h"
+
+#define DOESNT_USE_STONITHKILLCOMM
+#define DOESNT_USE_STONITHSCANLINE
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &nw_rpc100sOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ The Nightware RPS-100S is manufactured by:
+
+ Micro Energetics Corp
+ +1 703 250-3000
+ http://www.nightware.com/
+
+ Thank you to David Hicks of Micro Energetics Corp. for providing
+ a demo unit to write this software.
+
+ This switch has a very simple protocol,
+ You issue a command and it gives a response.
+ Sample commands are conveniently documented on a sticker on the
+ bottom of the device.
+
+ The switch accepts a single command of the form
+
+ //0,yyy,zzz[/m][/h]<CR>
+
+ Where yyy is the wait time before activiting the relay.
+ zzz is the relay time.
+
+ The default is that the relay is in a default state of ON, which
+ means that usually yyy is the number of seconds to wait
+ before shutting off the power and zzz is the number of seconds the
+ power remains off. There is a dip switch to change the default
+ state to 'OFF'. Don't set this switch. It will screw up this code.
+
+ An asterisk can be used for zzz to specify an infinite switch time.
+ The /m /and /h command options will convert the specified wait and
+ switch times to either minutewes or hours.
+
+ A response is either
+ <cr><lf>OK<cr><lf>
+ or
+ <cr><lf>Invalid Entry<cr><lf>
+
+
+ As far as THIS software is concerned, we have to implement 4 commands:
+
+ status --> //0,0,BOGUS; # Not a real command, this is just a
+ # probe to see if switch is alive
+ open(on) --> //0,0,0; # turn power to default state (on)
+ close(off) --> //0,0,*; # leave power off indefinitely
+ reboot --> //0,0,10; # immediately turn power off for 10 seconds.
+
+ and expect the response 'OK' to confirm that the unit is operational.
+*/
+
+
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+
+ int fd; /* FD open to the serial port */
+
+ char * device; /* Serial device name to use to communicate
+ to this RPS10
+ */
+
+ char * node; /* Name of the node that this is controlling */
+
+};
+
+/* This string is used to identify this type of object in the config file */
+static const char * pluginid = "NW_RPC100S";
+static const char * NOTrpcid = "NW RPC100S device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *nw_rpc100sXML =
+ XML_PARAMETERS_BEGIN
+ XML_TTYDEV_PARM
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * Different expect strings that we get from the NW_RPC100S
+ * Remote Power Controllers...
+ */
+
+static struct Etoken NWtokOK[] = { {"OK", 0, 0}, {NULL,0,0}};
+static struct Etoken NWtokInvalidEntry[] = { {"Invalid Entry", 0, 0}, {NULL,0,0}};
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken NWtokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int RPCConnect(struct pluginDevice * ctx);
+static int RPCDisconnect(struct pluginDevice * ctx);
+
+static int RPCReset(struct pluginDevice*, int unitnum, const char * rebootid);
+#if defined(ST_POWERON)
+static int RPCOn(struct pluginDevice*, int unitnum, const char * rebootid);
+#endif
+#if defined(ST_POWEROFF)
+static int RPCOff(struct pluginDevice*, int unitnum, const char * rebootid);
+#endif
+static int RPCNametoOutlet ( struct pluginDevice * ctx, const char * host );
+
+/*static int RPC_parse_config_info(struct pluginDevice* ctx, const char * info);*/
+
+
+#define SENDCMD(cmd, timeout) { \
+ int return_val = RPCSendCommand(ctx, cmd, timeout); \
+ if (return_val != S_OK) { \
+ return return_val; \
+ } \
+ }
+
+/*
+ * RPCSendCommand - send a command to the specified outlet
+ */
+static int
+RPCSendCommand (struct pluginDevice *ctx, const char *command, int timeout)
+{
+ char writebuf[64]; /* All commands are short.
+ They should be WAY LESS
+ than 64 chars long!
+ */
+ int return_val; /* system call result */
+ fd_set rfds, wfds, xfds;
+ /* list of FDs for select() */
+ struct timeval tv; /* */
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+
+ snprintf (writebuf, sizeof(writebuf), "%s\r", command);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Sending %s", writebuf);
+ }
+
+ /* Make sure the serial port won't block on us. use select() */
+ FD_SET(ctx->fd, &wfds);
+ FD_SET(ctx->fd, &xfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv);
+ if (return_val == 0) {
+ /* timeout waiting on serial port */
+ LOG(PIL_CRIT, "%s: Timeout writing to %s"
+ , pluginid, ctx->device);
+ return S_TIMEOUT;
+ } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) {
+ /* an error occured */
+ LOG(PIL_CRIT, "%s: Error before writing to %s: %s"
+ , pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* send the command */
+ if (write(ctx->fd, writebuf, strlen(writebuf)) !=
+ (int)strlen(writebuf)) {
+ LOG(PIL_CRIT, "%s: Error writing to %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* suceeded! */
+ return S_OK;
+
+} /* end RPCSendCommand() */
+
+/*
+ * RPCReset - Reset (power-cycle) the given outlet number
+ *
+ * This device can only control one power outlet - unitnum is ignored.
+ *
+ */
+static int
+RPCReset(struct pluginDevice* ctx, int unitnum, const char * rebootid)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling RPCReset (%s)", pluginid);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "toggle power" command */
+ SENDCMD("//0,0,10;\r\n", 12);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got OK");
+ }
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL");
+ }
+
+ return(S_OK);
+
+} /* end RPCReset() */
+
+
+#if defined(ST_POWERON)
+/*
+ * RPCOn - Turn OFF the given outlet number
+ */
+static int
+RPCOn(struct pluginDevice* ctx, int unitnum, const char * host)
+{
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "On" command */
+ SENDCMD("//0,0,0;\r\n", 10);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPCOn() */
+#endif
+
+
+#if defined(ST_POWEROFF)
+/*
+ * RPCOff - Turn Off the given outlet number
+ */
+static int
+RPCOff(struct pluginDevice* ctx, int unitnum, const char * host)
+{
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid
+ , ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "Off" command */
+ SENDCMD("//0,0,*;\r\n", 10);
+
+ /* Expect "OK" */
+ EXPECT(ctx->fd, NWtokOK, 5);
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPCOff() */
+#endif
+
+
+/*
+ * nw_rpc100s_status - API entry point to probe the status of the stonith device
+ * (basically just "is it reachable and functional?", not the
+ * status of the individual outlets)
+ *
+ * Returns:
+ * S_OOPS - some error occured
+ * S_OK - if the stonith device is reachable and online.
+ */
+static int
+nw_rpc100s_status(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_status (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+ if (RPCConnect(ctx) != S_OK) {
+ return(S_OOPS);
+ }
+
+ /* The "connect" really does enough work to see if the
+ controller is alive... It verifies that it is returning
+ RPS-10 Ready
+ */
+
+ return(RPCDisconnect(ctx));
+}
+
+/*
+ * nw_rpc100s_hostlist - API entry point to return the list of hosts
+ * for the devices on this NW_RPC100S unit
+ *
+ * This type of device is configured from the config file,
+ * so we don't actually have to connect to figure this
+ * out, just peruse the 'ctx' structure.
+ * Returns:
+ * NULL on error
+ * a malloced array, terminated with a NULL,
+ * of null-terminated malloc'ed strings.
+ */
+static char **
+nw_rpc100s_hostlist(StonithPlugin *s)
+{
+ char ** ret = NULL; /* list to return */
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_hostlist (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ctx = (struct pluginDevice*) s;
+
+ ret = OurImports->StringToHostList(ctx->node);
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ } else {
+ strdown(ret[0]);
+ }
+
+ return(ret);
+} /* end si_hostlist() */
+
+/*
+ * Parse the given configuration information, and stash it away...
+ *
+ * <info> contains the parameters specific to this type of object
+ *
+ * The format of <parameters> for this module is:
+ * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
+ *
+ * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through
+ * a device attached to serial port /dev/ttyS0.
+ * A machine named 'nodeb' can kill machines 'nodea' and 'nodec'
+ * through a device attached to serial port /dev/ttyS1 (outlets 0
+ * and 1 respectively)
+ *
+ * stonith nodea NW_RPC100S /dev/ttyS0 nodeb 0
+ * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0 nodec 1
+ *
+ * Another possible configuration is for 2 stonith devices accessible
+ * through 2 different serial ports on nodeb:
+ *
+ * stonith nodeb NW_RPC100S /dev/ttyS0 nodea 0
+ * stonith nodeb NW_RPC100S /dev/ttyS1 nodec 0
+ */
+
+/*static int
+RPC_parse_config_info(struct pluginDevice* ctx, const char * info)
+{
+}*/
+
+
+/*
+ * RPCConnect -
+ *
+ * Connect to the given NW_RPC100S device.
+ * Side Effects
+ * ctx->fd now contains a valid file descriptor to the serial port
+ * ??? LOCK THE SERIAL PORT ???
+ *
+ * Returns
+ * S_OK on success
+ * S_OOPS on error
+ * S_TIMEOUT if the device did not respond
+ *
+ */
+static int
+RPCConnect(struct pluginDevice * ctx)
+{
+
+ /* Open the serial port if it isn't already open */
+ if (ctx->fd < 0) {
+ struct termios tio;
+
+ if (OurImports->TtyLock(ctx->device) < 0) {
+ LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid);
+ return S_OOPS;
+ }
+
+ ctx->fd = open (ctx->device, O_RDWR);
+ if (ctx->fd <0) {
+ LOG(PIL_CRIT, "%s: Can't open %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* set the baudrate to 9600 8 - N - 1 */
+ memset (&tio, 0, sizeof(tio));
+
+ /* ??? ALAN - the -tradtitional flag on gcc causes the
+ CRTSCTS constant to generate a warning, and warnings
+ are treated as errors, so I can't set this flag! - EZA ???
+
+ Hmmm. now that I look at the documentation, RTS
+ is just wired high on this device! we don't need it.
+ */
+ /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/
+ tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ;
+ tio.c_lflag = ICANON;
+
+ if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) {
+ LOG(PIL_CRIT, "%s: Can't set attributes %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+ /* flush all data to and fro the serial port before we start */
+ if (tcflush (ctx->fd, TCIOFLUSH) < 0) {
+ LOG(PIL_CRIT, "%s: Can't flush %s : %s"
+ , pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+
+ }
+
+
+ /* Send a BOGUS string */
+ SENDCMD("//0,0,BOGUS;\r\n", 10);
+
+ /* Should reply with "Invalid Command" */
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waiting for \"Invalid Entry\"");
+ }
+ EXPECT(ctx->fd, NWtokInvalidEntry, 12);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Invalid Entry");
+ }
+ EXPECT(ctx->fd, NWtokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL");
+ }
+
+ return(S_OK);
+}
+
+static int
+RPCDisconnect(struct pluginDevice * ctx)
+{
+
+ if (ctx->fd >= 0) {
+ /* Flush the serial port, we don't care what happens to the characters
+ and failing to do this can cause close to hang.
+ */
+ tcflush(ctx->fd, TCIOFLUSH);
+ close (ctx->fd);
+ if (ctx->device != NULL) {
+ OurImports->TtyUnlock(ctx->device);
+ }
+ }
+ ctx->fd = -1;
+
+ return S_OK;
+}
+
+/*
+ * RPCNametoOutlet - Map a hostname to an outlet number on this stonith device.
+ *
+ * Returns:
+ * 0 on success ( the outlet number on the RPS10 - there is only one )
+ * -1 on failure (host not found in the config file)
+ *
+ */
+static int
+RPCNametoOutlet ( struct pluginDevice * ctx, const char * host )
+{
+ int rc = -1;
+
+ if (!strcasecmp(ctx->node, host)) {
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+ * nw_rpc100s_reset - API call to Reset (reboot) the given host on
+ * this Stonith device. This involves toggling the power off
+ * and then on again, OR just calling the builtin reset command
+ * on the stonith device.
+ */
+static int
+nw_rpc100s_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = S_OK;
+ int outletnum = -1;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Calling nw_rpc100s_reset (%s)", pluginid);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = RPCConnect(ctx)) != S_OK) {
+ return(rc);
+ }
+
+ outletnum = RPCNametoOutlet(ctx, host);
+ LOG(PIL_DEBUG, "zk:outletname=%d", outletnum);
+
+ if (outletnum < 0) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , ctx->device, host);
+ RPCDisconnect(ctx);
+ return(S_BADHOST);
+ }
+
+ switch(request) {
+
+#if defined(ST_POWERON)
+ case ST_POWERON:
+ rc = RPCOn(ctx, outletnum, host);
+ break;
+#endif
+#if defined(ST_POWEROFF)
+ case ST_POWEROFF:
+ rc = RPCOff(ctx, outletnum, host);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = RPCReset(ctx, outletnum, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ lorc = RPCDisconnect(ctx);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+nw_rpc100s_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice* ctx;
+ StonithNamesToGet namestocopy [] =
+ { {ST_TTYDEV, NULL}
+ , {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ if (s->isconfigured) {
+ return S_OOPS;
+ }
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ ctx->device = namestocopy[0].s_value;
+ ctx->node = namestocopy[1].s_value;
+
+ return S_OK;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+nw_rpc100s_get_confignames(StonithPlugin* p)
+{
+ static const char * RpcParams[] = {ST_TTYDEV , ST_HOSTLIST, NULL };
+ return RpcParams;
+}
+
+
+
+/*
+ * nw_rpc100s_getinfo - API entry point to retrieve something from the handle
+ */
+static const char *
+nw_rpc100s_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ctx;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ctx = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ctx->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = ctx->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Micro Energetics Night/Ware RPC100S";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.microenergeticscorp.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = nw_rpc100sXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * nw_rpc100s_destroy - API entry point to destroy a NW_RPC100S Stonith object.
+ */
+static void
+nw_rpc100s_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ctx = (struct pluginDevice *)s;
+
+ ctx->pluginid = NOTrpcid;
+
+ /* close the fd if open and set ctx->fd to invalid */
+ RPCDisconnect(ctx);
+
+ if (ctx->device != NULL) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ if (ctx->node != NULL) {
+ FREE(ctx->node);
+ ctx->node = NULL;
+ }
+ FREE(ctx);
+}
+
+/*
+ * nw_rpc100s_new - API entry point called to create a new NW_RPC100S Stonith
+ * device object.
+ */
+static StonithPlugin *
+nw_rpc100s_new(const char *subplugin)
+{
+ struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
+
+ if (ctx == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->pluginid = pluginid;
+ ctx->fd = -1;
+ ctx->device = NULL;
+ ctx->node = NULL;
+ ctx->idinfo = DEVICE;
+ ctx->sp.s_ops = &nw_rpc100sOps;
+
+ return &(ctx->sp);
+}
diff --git a/lib/plugins/stonith/rcd_serial.c b/lib/plugins/stonith/rcd_serial.c
new file mode 100644
index 0000000..f1396a7
--- /dev/null
+++ b/lib/plugins/stonith/rcd_serial.c
@@ -0,0 +1,602 @@
+/*
+ * Stonith module for RCD_SERIAL Stonith device
+ *
+ * Original code from null.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Copious borrowings from nw_rpc100s.c by
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * and from apcsmart.c by
+ * Copyright (c) 2000 Andreas Piesk <a.piesk@gmx.net>
+ *
+ * Modifications for RC Delayed Serial Ciruit by
+ * Copyright (c) 2002 John Sutton <john@scl.co.uk>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "RC Delayed Serial"
+#include "stonith_plugin_common.h"
+#include "stonith_signal.h"
+
+#define PIL_PLUGIN rcd_serial
+#define PIL_PLUGIN_S "rcd_serial"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#define ST_DTRRTS "dtr_rts"
+#define ST_MSDURATION "msduration"
+#define MAX_RCD_SERIALLINE 512
+
+#include <pils/plugin.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+static StonithPlugin* rcd_serial_new(const char *);
+static void rcd_serial_destroy(StonithPlugin *);
+static int rcd_serial_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rcd_serial_get_confignames(StonithPlugin *);
+static const char * rcd_serial_getinfo(StonithPlugin * s, int InfoType);
+static int rcd_serial_status(StonithPlugin * );
+static int rcd_serial_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rcd_serial_hostlist(StonithPlugin *);
+
+static struct stonith_ops rcd_serialOps ={
+ rcd_serial_new, /* Create new STONITH object */
+ rcd_serial_destroy, /* Destroy STONITH object */
+ rcd_serial_getinfo, /* Return STONITH info string */
+ rcd_serial_get_confignames,/* Return STONITH info string */
+ rcd_serial_set_config, /* Get configuration from NVpairs */
+ rcd_serial_status, /* Return STONITH device status */
+ rcd_serial_reset_req, /* Request a reset */
+ rcd_serial_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rcd_serialOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/* ------------------- RCD specific stuff -------------- */
+
+/*
+ A diagram of a circuit suitable for use with this plugin is in
+ README.rcd_serial which should be somewhere in the distribution (if Alan
+ includes it ;-) and/or at http://www.scl.co.uk/rcd_serial/ (if I remember
+ to put it there ;-).
+
+ Once you've got this built, you can test things using the stonith command
+ as follows:
+
+ stonith -L
+ will show a list of plugin types, including rcd_serial
+
+ stonith -t rcd_serial testhost
+ will show required parameters
+
+ In these 3 you can either pass the params after the -p option or you can
+ put them in a config file and use -F configname instead of -p "param ...".
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -S
+ will show the status of the device
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" -l
+ will list the single host testhost
+
+ stonith -t rcd_serial -p "testhost /dev/ttyS0 rts 1500" testhost
+ will reset testhost (provided testhost has its reset pins
+ suitably wired to the RTS signal coming out of port /dev/ttyS0
+ and that 1.5s is enough time to cause a reset ;-)
+*/
+
+/*
+ Define RCD_NOPAUSE if you are using the serial port for some purpose
+ _in_addition_ to using it as a stonith device. For example, I use one
+ of the input pins on the same serial port for monitoring the state of a
+ power supply. Periodically, a cron job has to open the port to read the
+ state of this input and thus has to clear down the output pins DTR and RTS
+ in order to avoid causing a spurious stonith reset. Now, if it should
+ happen that just at the same time as we are _really_ trying to do a stonith
+ reset, this cron job starts up, then the stonith reset won't occur ;-(.
+ To avoid this (albeit unlikely) outcome, you should #define RCD_NOPAUSE.
+ The effect of this is that instead of setting the line high just once and
+ then falling into a pause until an alarm goes off, rather, the program falls
+ into a loop which is continuously setting the line high. That costs us a bit
+ of CPU as compared with sitting in a pause, but hey, how often is this code
+ going to get exercised! Never, we hope...
+*/
+#undef RCD_NOPAUSE
+
+#ifdef RCD_NOPAUSE
+static int RCD_alarmcaught;
+#endif
+
+/*
+ * own prototypes
+ */
+
+static void RCD_alarm_handler(int sig);
+static int RCD_open_serial_port(char *device);
+static int RCD_close_serial_port(char *device, int fd);
+
+static void
+RCD_alarm_handler(int sig) {
+#if !defined(HAVE_POSIX_SIGNALS)
+ if (sig) {
+ signal(sig, SIG_DFL);
+ }else{
+ signal(sig, RCD_alarm_handler);
+ }
+#else
+ struct sigaction sa;
+ sigset_t sigmask;
+
+ /* Maybe a bit naughty but it works and it saves duplicating all */
+ /* this setup code - if handler called with 0 for sig, we install */
+ /* ourself as handler. */
+ if (sig) {
+ sa.sa_handler = (void (*)(int))SIG_DFL;
+ }else{
+ sa.sa_handler = RCD_alarm_handler;
+ }
+
+ sigemptyset(&sigmask);
+ sa.sa_mask = sigmask;
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL);
+#endif
+
+#ifdef RCD_NOPAUSE
+ RCD_alarmcaught = 1;
+#endif
+ return;
+}
+
+static int
+RCD_open_serial_port(char *device) {
+ int fd;
+ int status;
+ int bothbits;
+
+ if (OurImports->TtyLock(device) < 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: ttylock failed.", __FUNCTION__);
+ }
+ return -1;
+ }
+
+ bothbits = TIOCM_RTS | TIOCM_DTR;
+
+ if ((fd = open(device, O_RDONLY | O_NDELAY)) != -1) {
+ /*
+ Opening the device always sets DTR & CTS high.
+ Clear them down immediately.
+ */
+ status = ioctl(fd, TIOCMBIC, &bothbits);
+ /* If there was an error clearing bits, set the fd to -1
+ * ( indicates error ) */
+ if (status != 0 ) {
+ fd = -1;
+ }
+ }
+
+ return fd;
+}
+
+static int
+RCD_close_serial_port(char *device, int fd) {
+ int rc = close(fd);
+ if (device != NULL) {
+ OurImports->TtyUnlock(device);
+ }
+ return rc;
+}
+
+/*
+ * RCD_Serial STONITH device.
+ */
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist; /* name of single host we can reset */
+ int hostcount; /* i.e. 1 after initialisation */
+ char * device; /* serial device name */
+ char * signal; /* either rts or dtr */
+ long msduration; /* how long (ms) to assert the signal */
+};
+
+static const char * pluginid = "RCD_SerialDevice-Stonith";
+static const char * NOTrcd_serialID = "RCD_Serial device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_DTRRTS_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_DTRRTS \
+ XML_PARM_SHORTDESC_END
+
+#define XML_DTRRTS_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The hardware handshaking technique to use with " ST_TTYDEV "(\"dtr\" or \"rts\")" \
+ XML_PARM_LONGDESC_END
+
+#define XML_DTRRTS_PARM \
+ XML_PARAMETER_BEGIN(ST_DTRRTS, "string", "1", "0") \
+ XML_DTRRTS_SHORTDESC \
+ XML_DTRRTS_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_MSDURATION_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MSDURATION \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MSDURATION_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The delay duration (in milliseconds) between the assertion of the control signal on " ST_TTYDEV " and the closing of the reset switch" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MSDURATION_PARM \
+ XML_PARAMETER_BEGIN(ST_MSDURATION, "string", "1", "0") \
+ XML_MSDURATION_SHORTDESC \
+ XML_MSDURATION_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *rcd_serialXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_TTYDEV_PARM
+ XML_DTRRTS_PARM
+ XML_MSDURATION_PARM
+ XML_PARAMETERS_END;
+
+static int
+rcd_serial_status(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+ int fd;
+ const char * err;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ rcd = (struct pluginDevice*) s;
+
+ /*
+ All we can do is make sure the serial device exists and
+ can be opened and closed without error.
+ */
+
+ if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: open of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ if (RCD_close_serial_port(rcd->device, fd) != 0) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: close of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this RCD_SERIAL device
+ */
+static char **
+rcd_serial_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+
+ ERRIFWRONGDEV(s,NULL);
+ rcd = (struct pluginDevice*) s;
+ if (rcd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in RCD_SERIAL_list_hosts");
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)rcd->hostlist);
+}
+
+/*
+ * At last, we really do it! I don't know what the request argument
+ * is so am just ignoring it...
+ */
+static int
+rcd_serial_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice* rcd;
+ int fd;
+ int sigbit;
+ struct itimerval timer;
+ const char * err;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ rcd = (struct pluginDevice *) s;
+
+ /* check that host matches */
+ if (strcasecmp(host, rcd->hostlist[0])) {
+ LOG(PIL_CRIT, "%s: host '%s' not in hostlist.",
+ __FUNCTION__, host);
+ return(S_BADHOST);
+ }
+
+ /* Set the appropriate bit for the signal */
+ sigbit = *(rcd->signal)=='r' ? TIOCM_RTS : TIOCM_DTR;
+
+ /* Set up the timer */
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_usec = 0;
+ timer.it_value.tv_sec = rcd->msduration / 1000;
+ timer.it_value.tv_usec = (rcd->msduration % 1000) * 1000;
+
+ /* Open the device */
+ if ((fd = RCD_open_serial_port(rcd->device)) == -1) {
+#ifdef HAVE_STRERROR
+ err = strerror(errno);
+#else
+ err = sys_errlist[errno];
+#endif
+ LOG(PIL_CRIT, "%s: open of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ /* Start the timer */
+ RCD_alarm_handler(0);
+#ifdef RCD_NOPAUSE
+ RCD_alarmcaught = 0;
+#endif
+ setitimer(ITIMER_REAL, &timer, 0);
+
+ /* Set the line high */
+ ioctl(fd, TIOCMBIS, &sigbit);
+
+ /* Wait for the alarm signal */
+#ifdef RCD_NOPAUSE
+ while(!RCD_alarmcaught) ioctl(fd, TIOCMBIS, &sigbit);
+#else
+ pause();
+#endif
+
+ /* Clear the line low */
+ ioctl(fd, TIOCMBIC, &sigbit);
+
+ /* Close the port */
+ if (RCD_close_serial_port(rcd->device, fd) != 0) {
+ err = strerror(errno);
+ LOG(PIL_CRIT, "%s: close of %s failed - %s",
+ __FUNCTION__, rcd->device, err);
+ return(S_OOPS);
+ }
+
+ LOG(PIL_INFO,"Host rcd_serial-reset: %s", host);
+ return S_OK;
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+rcd_serial_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice* rcd;
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {ST_TTYDEV, NULL}
+ , {ST_DTRRTS, NULL}
+ , {ST_MSDURATION, NULL}
+ , {NULL, NULL}
+ };
+ char *endptr;
+ int rc = 0;
+
+ LOG(PIL_DEBUG, "%s:called", __FUNCTION__);
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ if (s->isconfigured) {
+ return S_OOPS;
+ }
+
+ rcd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ if ((rcd->hostlist = (char **)MALLOC(2*sizeof(char*))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ FREE(namestocopy[0].s_value);
+ FREE(namestocopy[1].s_value);
+ FREE(namestocopy[2].s_value);
+ FREE(namestocopy[3].s_value);
+ return S_OOPS;
+ }
+ rcd->hostlist[0] = namestocopy[0].s_value;
+ strdown(rcd->hostlist[0]);
+ rcd->hostlist[1] = NULL;
+ rcd->hostcount = 1;
+ rcd->device = namestocopy[1].s_value;
+ rcd->signal = namestocopy[2].s_value;
+ if (strcmp(rcd->signal, "rts") && strcmp(rcd->signal, "dtr")) {
+ LOG(PIL_CRIT, "%s: Invalid signal name '%s'",
+ pluginid, rcd->signal);
+ FREE(namestocopy[3].s_value);
+ return S_BADCONFIG;
+ }
+
+ errno = 0;
+ rcd->msduration = strtol(namestocopy[3].s_value, &endptr, 0);
+ if (((errno == ERANGE)
+ && (rcd->msduration == LONG_MIN || rcd->msduration == LONG_MAX))
+ || *endptr != 0 || rcd->msduration < 1) {
+ LOG(PIL_CRIT, "%s: Invalid msduration '%s'",
+ pluginid, namestocopy[3].s_value);
+ FREE(namestocopy[3].s_value);
+ return S_BADCONFIG;
+ }
+ FREE(namestocopy[3].s_value);
+
+ return S_OK;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+rcd_serial_get_confignames(StonithPlugin* p)
+{
+ static const char * RcdParams[] = {ST_HOSTLIST, ST_TTYDEV
+ , ST_DTRRTS, ST_MSDURATION, NULL };
+ return RcdParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+rcd_serial_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* rcd;
+ const char * ret;
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ rcd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = rcd->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = rcd->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "RC Delayed Serial STONITH Device\n"
+ "This device can be constructed cheaply from"
+ " readily available components,\n"
+ "with sufficient expertise and testing.\n"
+ "See README.rcd_serial for circuit diagram.\n";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.scl.co.uk/rcd_serial/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = rcd_serialXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * RCD_SERIAL Stonith destructor...
+ */
+static void
+rcd_serial_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* rcd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ rcd = (struct pluginDevice *)s;
+
+ rcd->pluginid = NOTrcd_serialID;
+ if (rcd->hostlist) {
+ stonith_free_hostlist(rcd->hostlist);
+ rcd->hostlist = NULL;
+ }
+ rcd->hostcount = -1;
+ if (rcd->device) {
+ FREE(rcd->device);
+ }
+ if (rcd->signal) {
+ FREE(rcd->signal);
+ }
+ FREE(rcd);
+}
+
+/*
+ * Create a new RCD_Serial Stonith device.
+ * Too bad this function can't be static. (Hmm, weird, it _is_ static?)
+ */
+static StonithPlugin *
+rcd_serial_new(const char *subplugin)
+{
+ struct pluginDevice* rcd = ST_MALLOCT(struct pluginDevice);
+
+ if (rcd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(rcd, 0, sizeof(*rcd));
+
+ rcd->pluginid = pluginid;
+ rcd->hostlist = NULL;
+ rcd->hostcount = -1;
+ rcd->device = NULL;
+ rcd->signal = NULL;
+ rcd->msduration = 0;
+ rcd->idinfo = DEVICE;
+ rcd->sp.s_ops = &rcd_serialOps;
+
+ return &(rcd->sp);
+}
diff --git a/lib/plugins/stonith/rhcs.c b/lib/plugins/stonith/rhcs.c
new file mode 100644
index 0000000..293a081
--- /dev/null
+++ b/lib/plugins/stonith/rhcs.c
@@ -0,0 +1,1035 @@
+/*
+ * Stonith module for RedHat Cluster Suite fencing plugins
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ * Portions Copyright (c) 2004, tummy.com, ltd.
+ *
+ * Based on ssh.c, Authors: Joachim Gleissner <jg@suse.de>,
+ * Lars Marowsky-Bree <lmb@suse.de>
+ * Modified for external.c: Scott Kleihege <scott@tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo@tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb@suse.de>, so the circle
+ * closes...
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke
+ * <debltc@us.ibm.com>
+ * Modified for rhcs.c: Dejan Muhamedagic <dejan@suse.de>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <dirent.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlreader.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN rhcs
+#define PIL_PLUGIN_S "rhcs"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+
+#include <pils/plugin.h>
+
+static StonithPlugin * rhcs_new(const char *);
+static void rhcs_destroy(StonithPlugin *);
+static int rhcs_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rhcs_get_confignames(StonithPlugin *);
+static const char * rhcs_getinfo(StonithPlugin * s, int InfoType);
+static int rhcs_status(StonithPlugin * );
+static int rhcs_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rhcs_hostlist(StonithPlugin *);
+
+static struct stonith_ops rhcsOps ={
+ rhcs_new, /* Create new STONITH object */
+ rhcs_destroy, /* Destroy STONITH object */
+ rhcs_getinfo, /* Return STONITH info string */
+ rhcs_get_confignames, /* Return STONITH info string */
+ rhcs_set_config, /* Get configuration from NVpairs */
+ rhcs_status, /* Return STONITH device status */
+ rhcs_reset_req, /* Request a reset */
+ rhcs_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rhcsOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * RHCS STONITH device
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ GHashTable * cmd_opts;
+ char * subplugin;
+ char ** confignames;
+ char * hostlist;
+ char * outputbuf;
+ xmlDoc * metadata;
+};
+
+static const char * pluginid = "RHCSDevice-Stonith";
+static const char * NOTpluginID = "RHCS device has been destroyed";
+
+/* Prototypes */
+
+/* Run the command with op and return the exit status + the output
+ * (NULL -> discard output) */
+static int rhcs_run_cmd(struct pluginDevice *sd, const char *op,
+ const char *host, char **output);
+/* Just free up the configuration and the memory, if any */
+static void rhcs_unconfig(struct pluginDevice *sd);
+
+static int
+rhcs_status(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ const char * op = "monitor";
+ int rc;
+ char * output = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ rc = rhcs_run_cmd(sd, op, NULL, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ }
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ }
+ if (output) {
+ FREE(output);
+ }
+ return rc;
+}
+
+static int
+get_num_tokens(char *str)
+{
+ int namecount = 0;
+
+ if (!str)
+ return namecount;
+ while (*str != EOS) {
+ str += strspn(str, WHITESPACE);
+ if (*str == EOS)
+ break;
+ str += strcspn(str, WHITESPACE);
+ namecount++;
+ }
+ return namecount;
+}
+
+static char **
+rhcs_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+ const char * op = "gethosts";
+ int i, namecount;
+ char ** ret;
+ char * tmp;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ namecount = get_num_tokens(sd->hostlist);
+ ret = MALLOC((namecount+1)*sizeof(char *));
+ if (!ret) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+ memset(ret, 0, (namecount+1)*sizeof(char *));
+
+ /* White-space split the sd->hostlist here */
+ i = 0;
+ tmp = strtok(sd->hostlist, WHITESPACE);
+ while (tmp != NULL) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s host %s",
+ __FUNCTION__, sd->subplugin, tmp);
+ }
+ ret[i] = STRDUP(tmp);
+ if (!ret[i]) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ i++;
+ tmp = strtok(NULL, WHITESPACE);
+ }
+
+ if (i == 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' returned an empty hostlist",
+ __FUNCTION__, sd->subplugin, op);
+ stonith_free_hostlist(ret);
+ ret = NULL;
+ }
+
+ return(ret);
+}
+
+static int
+rhcs_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice * sd;
+ const char * op;
+ int rc;
+ char * output = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Host rhcs-reset initiating on %s", host);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+ switch (request) {
+ case ST_GENERIC_RESET:
+ op = "reboot";
+ break;
+
+ case ST_POWEROFF:
+ op = "off";
+ break;
+
+ case ST_POWERON:
+ op = "on";
+ break;
+
+ default:
+ LOG(PIL_CRIT, "%s: Unknown stonith request %d",
+ __FUNCTION__, request);
+ return S_OOPS;
+ break;
+ }
+
+ rc = rhcs_run_cmd(sd, op, host, &output);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' for host %s failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, host, rc);
+ if (output) {
+ LOG(PIL_CRIT, "plugin output: %s", output);
+ FREE(output);
+ }
+ return S_RESETFAIL;
+ }
+ else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: running '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+ if (output) {
+ LOG(PIL_INFO, "plugin output: %s", output);
+ FREE(output);
+ }
+ return S_OK;
+ }
+
+}
+
+static int
+rhcs_parse_config_info(struct pluginDevice* sd, StonithNVpair * info)
+{
+ char * key;
+ char * value;
+ StonithNVpair * nv;
+
+ sd->hostlist = NULL;
+ sd->cmd_opts = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* TODO: Maybe treat "" as delimeters too so
+ * whitespace can be passed to the plugins... */
+ for (nv = info; nv->s_name; nv++) {
+ if (!nv->s_name || !nv->s_value) {
+ continue;
+ }
+ key = STRDUP(nv->s_name);
+ if (!key) {
+ goto err_mem;
+ }
+ value = STRDUP(nv->s_value);
+ if (!value) {
+ FREE(key);
+ goto err_mem;
+ }
+ if (!strcmp(key,"hostlist")) {
+ sd->hostlist = value;
+ FREE(key);
+ } else {
+ g_hash_table_insert(sd->cmd_opts, key, value);
+ }
+ }
+
+ return(S_OK);
+
+err_mem:
+ LOG(PIL_CRIT, "%s: out of memory!", __FUNCTION__);
+ rhcs_unconfig(sd);
+
+ return(S_OOPS);
+}
+
+static gboolean
+let_remove_eachitem(gpointer key, gpointer value, gpointer user_data)
+{
+ if (key) {
+ FREE(key);
+ }
+ if (value) {
+ FREE(value);
+ }
+ return TRUE;
+}
+
+static void
+rhcs_unconfig(struct pluginDevice *sd) {
+ if (sd->cmd_opts) {
+ g_hash_table_foreach_remove(sd->cmd_opts,
+ let_remove_eachitem, NULL);
+ g_hash_table_destroy(sd->cmd_opts);
+ sd->cmd_opts = NULL;
+ }
+ if (sd->hostlist) {
+ FREE(sd->hostlist);
+ sd->hostlist = NULL;
+ }
+ if (sd->metadata) {
+ xmlFreeDoc(sd->metadata);
+ xmlCleanupParser();
+ sd->metadata = NULL;
+ }
+}
+
+/*
+ * Parse the information in the given string
+ * and stash it away...
+ */
+static int
+rhcs_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ struct pluginDevice * sd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ /* make sure that command has not already been set */
+ if (s->isconfigured) {
+ return(S_OOPS);
+ }
+
+ sd = (struct pluginDevice*) s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(S_OOPS);
+ }
+
+#if 0
+ /* the required parameters may be acquired from the metadata
+ * */
+ if (sd->confignames == NULL) {
+ /* specified by name=value pairs, check required parms */
+ if (rhcs_get_confignames(s) == NULL) {
+ return(S_OOPS);
+ }
+
+ for (p = sd->confignames; *p; p++) {
+ if (OurImports->GetValue(list, *p) == NULL) {
+ LOG(PIL_INFO, "Cannot get parameter %s from "
+ "StonithNVpair", *p);
+ }
+ }
+ }
+#endif
+
+ return rhcs_parse_config_info(sd, list);
+}
+
+
+/* Only interested in regular files starting with fence_ that are also executable */
+static int
+rhcs_exec_select(const struct dirent *dire)
+{
+ struct stat statf;
+ char filename[FILENAME_MAX];
+ int rc;
+
+ rc = snprintf(filename, FILENAME_MAX, "%s/%s",
+ STONITH_RHCS_PLUGINDIR, dire->d_name);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ return 0;
+ }
+
+ if ((stat(filename, &statf) == 0) &&
+ (S_ISREG(statf.st_mode)) &&
+ (statf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ if (statf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_WARN, "Executable file %s ignored "
+ "(writable by group/others)", filename);
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static xmlDoc *
+load_metadata(struct pluginDevice * sd)
+{
+ xmlDoc *doc = NULL;
+ const char *op = "metadata";
+ int rc;
+ char *ret = NULL;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ rc = rhcs_run_cmd(sd, op, NULL, &ret);
+ if (rc != 0) {
+ LOG(PIL_CRIT, "%s: '%s %s' failed with rc %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ if (ret) {
+ LOG(PIL_CRIT, "plugin output: %s", ret);
+ FREE(ret);
+ }
+ goto err;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: '%s %s' returned %d",
+ __FUNCTION__, sd->subplugin, op, rc);
+ }
+
+ doc = xmlParseMemory(ret, strlen(ret));
+ if (!doc) {
+ LOG(PIL_CRIT, "%s: could not parse metadata",
+ __FUNCTION__);
+ goto err;
+ }
+ sd->metadata = doc;
+
+err:
+ if (ret) {
+ FREE(ret);
+ }
+ return doc;
+}
+
+static const char *skip_attrs[] = {
+ "action", "verbose", "debug", "version", "help", "separator",
+ NULL
+};
+/* XML stuff */
+typedef int (*node_proc)
+ (xmlNodeSet *nodes, struct pluginDevice *sd);
+
+static int
+proc_xpath(const char *xpathexp, struct pluginDevice *sd, node_proc fun)
+{
+ xmlXPathObject *xpathObj = NULL;
+ xmlXPathContext *xpathCtx = NULL;
+ int rc = 1;
+
+ if (!sd->metadata && !load_metadata(sd)) {
+ LOG(PIL_INFO, "%s: no metadata", __FUNCTION__);
+ return 1;
+ }
+
+ /* Create xpath evaluation context */
+ xpathCtx = xmlXPathNewContext(sd->metadata);
+ if(xpathCtx == NULL) {
+ LOG(PIL_CRIT, "%s: unable to create new XPath context", __FUNCTION__);
+ return 1;
+ }
+ /* Evaluate xpath expression */
+ xpathObj = xmlXPathEvalExpression((const xmlChar*)xpathexp, xpathCtx);
+ if(xpathObj == NULL) {
+ LOG(PIL_CRIT, "%s: unable to evaluate expression %s",
+ __FUNCTION__, xpathexp);
+ goto err;
+ }
+
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ rc = fun(xpathObj->nodesetval, sd);
+err:
+ if (xpathObj)
+ xmlXPathFreeObject(xpathObj);
+ if (xpathCtx)
+ xmlXPathFreeContext(xpathCtx);
+ return rc;
+}
+
+static int
+load_confignames(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ xmlChar *attr;
+ const char * const*skip;
+ xmlNode *cur;
+ int i, j, namecount;
+
+ namecount = nodes->nodeNr;
+ if (!namecount) {
+ LOG(PIL_INFO, "%s: no configuration parameters", __FUNCTION__);
+ return 1;
+ }
+ sd->confignames = (char **)MALLOC((namecount+1)*sizeof(char *));
+ if (sd->confignames == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return 1;
+ }
+
+ /* now copy over confignames */
+ j = 0;
+ for (i = 0; i < nodes->nodeNr; i++) {
+ cur = nodes->nodeTab[i];
+ attr = xmlGetProp(cur, (const xmlChar*)"name");
+ for (skip = skip_attrs; *skip; skip++) {
+ if (!strcmp(*skip,(char *)attr))
+ goto skip;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s configname %s",
+ __FUNCTION__, sd->subplugin, (char *)attr);
+ }
+ sd->confignames[j++] = strdup((char *)attr);
+ xmlFree(attr);
+ skip:
+ continue;
+ }
+ sd->confignames[j] = NULL;
+
+ return 0;
+}
+
+static int
+dump_content(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ xmlChar *content = NULL;
+ xmlNode *cur;
+ int rc = 1;
+
+ if (!nodes || !nodes->nodeTab || !nodes->nodeTab[0]) {
+ LOG(PIL_WARN, "%s: %s no nodes",
+ __FUNCTION__, sd->subplugin);
+ return 1;
+ }
+ cur = nodes->nodeTab[0];
+ content = xmlNodeGetContent(cur);
+ if (content && strlen((char *)content) > 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s found content for %s",
+ __FUNCTION__, sd->subplugin, cur->name);
+ }
+ sd->outputbuf = STRDUP((char *)content);
+ rc = !(*sd->outputbuf);
+ } else {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %s no content for %s",
+ __FUNCTION__, sd->subplugin, cur->name);
+ }
+ rc = 1;
+ }
+
+ if (content)
+ xmlFree(content);
+ return rc;
+}
+
+static int
+dump_params_xml(xmlNodeSet *nodes, struct pluginDevice *sd)
+{
+ int len = 0;
+ xmlNode *cur;
+ xmlBuffer *xml_buffer = NULL;
+ int rc = 0;
+
+ xml_buffer = xmlBufferCreate();
+ if (!xml_buffer) {
+ LOG(PIL_CRIT, "%s: failed to create xml buffer", __FUNCTION__);
+ return 1;
+ }
+ cur = nodes->nodeTab[0];
+ len = xmlNodeDump(xml_buffer, sd->metadata, cur, 0, TRUE);
+ if (len <= 0) {
+ LOG(PIL_CRIT, "%s: could not dump xml for %s",
+ __FUNCTION__, (char *)xmlGetProp(cur, (const xmlChar*)"name"));
+ rc = 1;
+ goto err;
+ }
+ sd->outputbuf = STRDUP((char *)xml_buffer->content);
+err:
+ xmlBufferFree(xml_buffer);
+ return rc;
+}
+
+/*
+ * Return STONITH config vars
+ */
+static const char * const *
+rhcs_get_confignames(StonithPlugin* p)
+{
+ struct pluginDevice * sd;
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ sd = (struct pluginDevice *)p;
+
+ if (sd->subplugin != NULL) {
+ if (!sd->metadata && !load_metadata(sd)) {
+ return NULL;
+ }
+ proc_xpath("/resource-agent/parameters/parameter", sd, load_confignames);
+ } else {
+ /* return list of subplugins in rhcs directory */
+ struct dirent ** files = NULL;
+ int dircount;
+
+ /* get the rhcs plugin's confignames (list of subplugins) */
+ dircount = scandir(STONITH_RHCS_PLUGINDIR, &files,
+ SCANSEL_CAST rhcs_exec_select, NULL);
+ if (dircount < 0) {
+ return NULL;
+ }
+
+ sd->confignames = (char **)MALLOC((dircount+1)*sizeof(char *));
+ if (!sd->confignames) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < dircount; i++) {
+ sd->confignames[i] = STRDUP(files[i]->d_name+strlen("fence_"));
+ free(files[i]);
+ files[i] = NULL;
+ }
+ free(files);
+ sd->confignames[dircount] = NULL;
+ }
+
+ return (const char * const *)sd->confignames;
+}
+
+/*
+ * Return STONITH info string
+ */
+static const char *
+fake_op(struct pluginDevice * sd, const char *op)
+{
+ const char *pfx = "RHCS plugin ";
+ char *ret = NULL;
+
+ LOG(PIL_INFO, "rhcs plugins don't really support %s", op);
+ ret = MALLOC(strlen(pfx) + strlen(op) + 1);
+ strcpy(ret, pfx);
+ strcat(ret, op);
+ sd->outputbuf = ret;
+ return(ret);
+}
+
+static const char *
+rhcs_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd;
+ const char * op;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ sd = (struct pluginDevice *)s;
+ if (sd->subplugin == NULL) {
+ LOG(PIL_CRIT, "%s: invoked without subplugin", __FUNCTION__);
+ return(NULL);
+ }
+
+ if (!sd->metadata && !load_metadata(sd)) {
+ return NULL;
+ }
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ op = "getinfo-devid";
+ return fake_op(sd, op);
+ break;
+
+ case ST_DEVICENAME:
+ if (!proc_xpath("/resource-agent/shortdesc", sd, dump_content)) {
+ return sd->outputbuf;
+ } else {
+ op = "getinfo-devname";
+ return fake_op(sd, op);
+ }
+ break;
+
+ case ST_DEVICEDESCR:
+ if (!proc_xpath("/resource-agent/longdesc", sd, dump_content)) {
+ return sd->outputbuf;
+ } else {
+ op = "getinfo-devdescr";
+ return fake_op(sd, op);
+ }
+ break;
+
+ case ST_DEVICEURL:
+ op = "getinfo-devurl";
+ return fake_op(sd, op);
+ break;
+
+ case ST_CONF_XML:
+ if (!proc_xpath("/resource-agent/parameters", sd, dump_params_xml)) {
+ return sd->outputbuf;
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+/*
+ * RHCS Stonith destructor...
+ */
+static void
+rhcs_destroy(StonithPlugin *s)
+{
+ struct pluginDevice * sd;
+ char ** p;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginID;
+ rhcs_unconfig(sd);
+ if (sd->confignames != NULL) {
+ for (p = sd->confignames; *p; p++) {
+ FREE(*p);
+ }
+ FREE(sd->confignames);
+ sd->confignames = NULL;
+ }
+ if (sd->subplugin != NULL) {
+ FREE(sd->subplugin);
+ sd->subplugin = NULL;
+ }
+ if (sd->outputbuf != NULL) {
+ FREE(sd->outputbuf);
+ sd->outputbuf = NULL;
+ }
+ FREE(sd);
+}
+
+/* Create a new rhcs Stonith device */
+static StonithPlugin *
+rhcs_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ if (subplugin != NULL) {
+ sd->subplugin = STRDUP(subplugin);
+ if (sd->subplugin == NULL) {
+ FREE(sd);
+ return(NULL);
+ }
+ }
+ sd->sp.s_ops = &rhcsOps;
+ return &(sd->sp);
+}
+
+#define MAXLINE 512
+
+static void
+printparam_to_fd(int fd, const char *key, const char *value)
+{
+ char arg[MAXLINE];
+ int cnt;
+
+ cnt = snprintf(arg, MAXLINE, "%s=%s\n", key, value);
+ if (cnt <= 0 || cnt >= MAXLINE) {
+ LOG(PIL_CRIT, "%s: param/value pair too large", __FUNCTION__);
+ return;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "set rhcs plugin param '%s=%s'", key, value);
+ }
+ if (write(fd, arg, cnt) < 0) {
+ LOG(PIL_CRIT, "%s: write: %m", __FUNCTION__);
+ }
+}
+
+static void
+rhcs_print_var(gpointer key, gpointer value, gpointer user_data)
+{
+ printparam_to_fd(GPOINTER_TO_UINT(user_data), (char *)key, (char *)value);
+}
+
+/* Run the command with op as command line argument(s) and return the exit
+ * status + the output */
+
+static int
+rhcs_run_cmd(struct pluginDevice *sd, const char *op, const char *host, char **output)
+{
+ const int BUFF_LEN=4096;
+ char buff[BUFF_LEN];
+ int read_len = 0;
+ int rc;
+ char * data = NULL;
+ char cmd[FILENAME_MAX+64];
+ struct stat buf;
+ int slen;
+ int pid, status;
+ int fd1[2]; /* our stdout/their stdin */
+ int fd2[2]; /* our stdin/their stdout and stderr */
+
+ rc = snprintf(cmd, FILENAME_MAX, "%s/fence_%s",
+ STONITH_RHCS_PLUGINDIR, sd->subplugin);
+ if (rc <= 0 || rc >= FILENAME_MAX) {
+ LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__);
+ return -1;
+ }
+
+ if (stat(cmd, &buf) != 0) {
+ LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s",
+ __FUNCTION__, cmd, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(buf.st_mode)
+ || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) {
+ LOG(PIL_CRIT, "%s: %s found NOT to be executable.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (buf.st_mode & (S_IWGRP|S_IWOTH)) {
+ LOG(PIL_CRIT, "%s: %s found to be writable by group/others, "
+ "NOT executing for security purposes.",
+ __FUNCTION__, cmd);
+ return -1;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd );
+ }
+
+ if (pipe(fd1) || pipe(fd2))
+ goto err;
+
+ pid = fork();
+ if (pid < 0) {
+ LOG(PIL_CRIT, "%s: fork: %m", __FUNCTION__);
+ goto err;
+ }
+ if (pid) { /* parent */
+ close(fd1[0]);
+ close(fd2[1]);
+
+ if (sd->cmd_opts) {
+ printparam_to_fd(fd1[1], "agent", sd->subplugin);
+ printparam_to_fd(fd1[1], "action", op);
+ if( host )
+ printparam_to_fd(fd1[1], "nodename", host);
+ g_hash_table_foreach(sd->cmd_opts, rhcs_print_var,
+ GUINT_TO_POINTER(fd1[1]));
+ }
+ close(fd1[1]); /* we have nothing more to say */
+
+ fcntl(fd2[0], F_SETFL, fcntl(fd2[0], F_GETFL, 0) | O_NONBLOCK);
+ data = NULL;
+ slen=0;
+ data = MALLOC(1);
+ /* read stdout/stderr from the fence agent */
+ do {
+ data[slen]=EOS;
+ read_len = read(fd2[0], buff, BUFF_LEN);
+ if (read_len > 0) {
+ data=REALLOC(data, slen+read_len+1);
+ if (data == NULL) {
+ goto err;
+ }
+ memcpy(data+slen, buff, read_len);
+ slen += read_len;
+ data[slen] = EOS;
+ } else if (read_len < 0) {
+ if (errno == EAGAIN)
+ continue;
+ LOG(PIL_CRIT, "%s: read from pipe: %m", __FUNCTION__);
+ goto err;
+ }
+ } while (read_len);
+
+ if (!data) {
+ LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__);
+ goto err;
+ }
+ close(fd2[0]);
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status)) {
+ LOG(PIL_CRIT, "%s: fence agent failed: %m", __FUNCTION__);
+ goto err;
+ } else {
+ rc = WEXITSTATUS(status);
+ if (rc) {
+ LOG(PIL_CRIT, "%s: fence agent exit code: %d",
+ __FUNCTION__, rc);
+ goto err;
+ }
+ }
+ } else { /* child */
+ close(fd1[1]);
+ close(fd2[0]);
+ close(STDIN_FILENO);
+ if (dup(fd1[0]) < 0)
+ goto err;
+ close(fd1[0]);
+ close(STDOUT_FILENO);
+ if (dup(fd2[1]) < 0)
+ goto err;
+ close(STDERR_FILENO);
+ if (dup(fd2[1]) < 0)
+ goto err;
+ close(fd2[1]);
+ rc = sd->cmd_opts ?
+ execlp(cmd, cmd, NULL) : execlp(cmd, cmd, "-o", op, NULL);
+ if (rc < 0) {
+ LOG(PIL_CRIT, "%s: Calling '%s' failed: %m",
+ __FUNCTION__, cmd);
+ }
+ goto err;
+ }
+
+ if (Debug && data) {
+ LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data);
+ }
+
+ if (output) {
+ *output = data;
+ } else {
+ FREE(data);
+ }
+
+ return 0;
+
+err:
+ if (data) {
+ FREE(data);
+ }
+ if (output) {
+ *output = NULL;
+ }
+
+ return(-1);
+
+}
diff --git a/lib/plugins/stonith/ribcl.py.in b/lib/plugins/stonith/ribcl.py.in
new file mode 100644
index 0000000..14e070c
--- /dev/null
+++ b/lib/plugins/stonith/ribcl.py.in
@@ -0,0 +1,101 @@
+#!@PYTHON@
+
+
+#
+# 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
+#
+
+import sys
+import socket
+from httplib import *
+from time import sleep
+
+
+argv = sys.argv
+
+
+try:
+ host = argv[1].split('.')[0]+'-rm'
+ cmd = argv[2]
+except IndexError:
+ print "Not enough arguments"
+ sys.exit(1)
+
+
+login = [ '<RIBCL VERSION="1.2">',
+ '<LOGIN USER_LOGIN="Administrator" PASSWORD="********">' ]
+
+
+logout = [ '</LOGIN>', '</RIBCL>' ]
+
+
+status = [ '<SERVER_INFO MODE="read">', '<GET_HOST_POWER_STATUS/>',
+ '</SERVER_INFO>' ]
+
+
+reset = [ '<SERVER_INFO MODE="write">', '<RESET_SERVER/>', '</SERVER_INFO>' ]
+
+
+off = [ '<SERVER_INFO MODE = "write">', '<SET_HOST_POWER HOST_POWER = "N"/>',
+ '</SERVER_INFO>' ]
+
+
+on = [ '<SERVER_INFO MODE = "write">', '<SET_HOST_POWER HOST_POWER = "Y"/>',
+ '</SERVER_INFO>' ]
+
+
+todo = { 'reset':reset, 'on':on, 'off':off, 'status':status }
+
+
+acmds=[]
+try:
+ if cmd == 'reset' and host.startswith('gfxcl'):
+ acmds.append(login + todo['off'] + logout)
+ acmds.append(login + todo['on'] + logout)
+ else:
+ acmds.append(login + todo[cmd] + logout)
+except KeyError:
+ print "Invalid command: "+ cmd
+ sys.exit(1)
+
+
+try:
+ for cmds in acmds:
+
+
+ c=HTTPSConnection(host)
+ c.send('<?xml version="1.0"?>\r\n')
+ c.sock.recv(1024)
+
+
+ for line in cmds:
+ c.send(line+'\r\n')
+ c.sock.recv(1024)
+
+
+ c.close()
+ sleep(1)
+
+
+except socket.gaierror, msg:
+ print msg
+ sys.exit(1)
+except socket.sslerror, msg:
+ print msg
+ sys.exit(1)
+except socket.error, msg:
+ print msg
+ sys.exit(1)
+
diff --git a/lib/plugins/stonith/riloe.c b/lib/plugins/stonith/riloe.c
new file mode 100644
index 0000000..a4a8312
--- /dev/null
+++ b/lib/plugins/stonith/riloe.c
@@ -0,0 +1,338 @@
+/*
+ * Stonith module for RILOE Stonith device
+ *
+ * Copyright (c) 2004 Alain St-Denis <alain.st-denis@ec.gc.ca>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * 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
+ *
+ */
+
+#define DEVICE "Compaq RILOE"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN riloe
+#define PIL_PLUGIN_S "riloe"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * riloe_new(const char *);
+static void riloe_destroy(StonithPlugin *);
+static int riloe_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * riloe_get_confignames(StonithPlugin * );
+static const char * riloe_getinfo(StonithPlugin * s, int InfoType);
+static int riloe_status(StonithPlugin * );
+static int riloe_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** riloe_hostlist(StonithPlugin *);
+
+static struct stonith_ops riloeOps ={
+ riloe_new, /* Create new STONITH object */
+ riloe_destroy, /* Destroy STONITH object */
+ riloe_getinfo, /* Return STONITH info string */
+ riloe_get_confignames, /* Return STONITH info string */
+ riloe_set_config, /* Get configuration from NVpairs */
+ riloe_status, /* Return STONITH device status */
+ riloe_reset_req, /* Request a reset */
+ riloe_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &riloeOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define RILOE_COMMAND STONITH_MODULES "/ribcl.py"
+
+/*
+ * Riloe STONITH device. We are very agreeable, but don't do much :-)
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "RiloeDevice-Stonith";
+static const char * NOTriloeID = "Riloe device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *riloeXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+riloe_status(StonithPlugin *s)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+
+/*
+ * Return the list of hosts configured for this RILOE device
+ */
+
+static char **
+riloe_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+ nd = (struct pluginDevice*) s;
+ if (nd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in %s", __FUNCTION__);
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const*)nd->hostlist);
+}
+
+/*
+ * Parse the config information, and stash it away...
+ */
+
+static int
+RILOE_parse_config_info(struct pluginDevice* nd, const char * info)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (nd->hostcount >= 0) {
+ return(S_OOPS);
+ }
+
+ nd->hostlist = OurImports->StringToHostList(info);
+ if (nd->hostlist == NULL) {
+ LOG(PIL_CRIT,"StringToHostList() failed");
+ return S_OOPS;
+ }
+ for (nd->hostcount = 0; nd->hostlist[nd->hostcount]; nd->hostcount++) {
+ strdown(nd->hostlist[nd->hostcount]);
+ }
+ return(S_OK);
+}
+
+
+/*
+ * Pretend to reset the given host on this Stonith device.
+ * (we don't even error check the "request" type)
+ */
+static int
+riloe_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ char cmd[4096];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ snprintf(cmd, sizeof(cmd), "%s %s reset", RILOE_COMMAND, host);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "command %s will be executed", cmd);
+ }
+
+ if (system(cmd) == 0) {
+ return S_OK;
+ } else {
+ LOG(PIL_CRIT, "command %s failed", cmd);
+ return(S_RESETFAIL);
+ }
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+riloe_set_config(StonithPlugin* s, StonithNVpair *list)
+{
+ StonithNamesToGet namestocopy [] =
+ { {ST_HOSTLIST, NULL}
+ , {NULL, NULL}
+ };
+ struct pluginDevice* nd;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ nd = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+
+ rc = RILOE_parse_config_info(nd , namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return the Stonith plugin configuration parameter
+ */
+static const char* const *
+riloe_get_confignames(StonithPlugin* p)
+{
+ static const char * RiloeParams[] = {ST_HOSTLIST, NULL };
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ return RiloeParams;
+}
+
+/*
+ * Return STONITH info string
+ */
+
+static const char *
+riloe_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nd;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = nd->idinfo;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Compaq RILOE STONITH device\n"
+ "Very early version!";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.hp.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = riloeXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * RILOE Stonith destructor...
+ */
+static void
+riloe_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nd;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+ nd = (struct pluginDevice *)s;
+
+ nd->pluginid = NOTriloeID;
+ if (nd->hostlist) {
+ stonith_free_hostlist(nd->hostlist);
+ nd->hostlist = NULL;
+ }
+ nd->hostcount = -1;
+ FREE(nd);
+}
+
+/* Create a new Riloe Stonith device. Too bad this function can't be static */
+static StonithPlugin *
+riloe_new(const char *subplugin)
+{
+ struct pluginDevice* nd = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (nd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nd, 0, sizeof(*nd));
+ nd->pluginid = pluginid;
+ nd->hostlist = NULL;
+ nd->hostcount = -1;
+ nd->idinfo = DEVICE;
+ nd->sp.s_ops = &riloeOps;
+
+ return &(nd->sp);
+}
diff --git a/lib/plugins/stonith/rps10.c b/lib/plugins/stonith/rps10.c
new file mode 100644
index 0000000..08d9873
--- /dev/null
+++ b/lib/plugins/stonith/rps10.c
@@ -0,0 +1,1070 @@
+/*
+ * Stonith module for WTI Remote Power Controllers (RPS-10M device)
+ *
+ * Original code from baytech.c by
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * Modifications for WTI RPS10
+ * Copyright (c) 2000 Computer Generation Incorporated
+ * Eric Z. Ayers <eric.ayers@compgen.com>
+ *
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#define DEVICE "WTI RPS10 Power Switch"
+#include "stonith_plugin_common.h"
+
+#include <termios.h>
+#define PIL_PLUGIN rps10
+#define PIL_PLUGIN_S "rps10"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define ST_RPS10 "serial_to_targets"
+#define MAX_PRSID 256
+#include <pils/plugin.h>
+
+static StonithPlugin * rps10_new(const char *);
+static void rps10_destroy(StonithPlugin *);
+static int rps10_set_config(StonithPlugin *, StonithNVpair *);
+static const char * const * rps10_get_confignames(StonithPlugin *);
+static const char * rps10_getinfo(StonithPlugin * s, int InfoType);
+static int rps10_status(StonithPlugin * );
+static int rps10_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** rps10_hostlist(StonithPlugin *);
+
+static struct stonith_ops rps10Ops ={
+ rps10_new, /* Create new STONITH object */
+ rps10_destroy, /* Destroy STONITH object */
+ rps10_getinfo, /* Return STONITH info string */
+ rps10_get_confignames, /* Return STONITH info string */
+ rps10_set_config, /* Get configuration from NVpairs */
+ rps10_status, /* Return STONITH device status */
+ rps10_reset_req, /* Request a reset */
+ rps10_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_signal.h"
+#define DOESNT_USE_STONITHKILLCOMM
+#define DOESNT_USE_STONITHSCANLINE
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &rps10Ops
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * This was written for a Western Telematic Inc. (WTI)
+ * Remote Power Switch - RPS-10M.
+ *
+ * It has a DB9 serial port, a Rotary Address Switch,
+ * and a pair of RJ-11 jacks for linking multiple switches
+ * together. The 'M' unit is a master unit which can control
+ * up to 9 additional slave units. (the master unit also has an
+ * A/C outlet, so you can control up to 10 devices)
+ *
+ * There are a set of dip switches. The default shipping configuration
+ * is with all dip switches down. I highly recommend that you flip
+ * switch #3 up, so that when the device is plugged in, the power
+ * to the unit comes on.
+ *
+ * The serial interface is fixed at 9600 BPS (well, you *CAN*
+ * select 2400 BPS with a dip switch, but why?) 8-N-1
+ *
+ * The ASCII command string is:
+ *
+ * ^B^X^X^B^X^Xac^M
+ *
+ * ^B^X^X^B^X^X "fixed password" prefix (CTRL-B CTRL-X ... )
+ * ^M the carriage return character
+ *
+ * a = 0-9 Indicates the address of the module to receive the command
+ * a = * Sends the command to all modules
+ *
+ * c = 0 Switch the AC outlet OFF
+ * Returns:
+ * Plug 0 Off
+ * Complete
+ *
+ * c = 1 Switch the AC outlet ON
+ * Returns:
+ * Plug 0 On
+ * Complete
+ *
+ * c = T Toggle AC OFF (delay) then back ON
+ * Returns:
+ * Plug 0 Off
+ * Plug 0 On
+ * Complete
+ *
+ * c = ? Read and display status of the selected module
+ * Returns:
+ * Plug 0 On # or Plug 0 Off
+ * Complete
+ *
+ * e.g. ^B^X^X^B^X^X0T^M toggles the power on plug 0 OFF and then ON
+ *
+ * 21 September 2000
+ * Eric Z. Ayers
+ * Computer Generation, Inc.
+ */
+
+struct cntrlr_str {
+ char outlet_id; /* value 0-9, '*' */
+ char * node; /* name of the node attached to this outlet */
+};
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+
+ int fd; /* FD open to the serial port */
+
+ char * device; /* Serial device name to use to communicate
+ to this RPS10
+ */
+
+#define WTI_NUM_CONTROLLERS 10
+ struct cntrlr_str
+ controllers[WTI_NUM_CONTROLLERS];
+ /* one master switch can address 10 controllers */
+
+ /* Number of actually configured units */
+ int unit_count;
+
+};
+
+/* This string is used to identify this type of object in the config file */
+static const char * pluginid = "WTI_RPS10";
+static const char * NOTwtiid = "OBJECT DESTROYED: (WTI RPS-10)";
+
+#include "stonith_config_xml.h"
+
+#define XML_RPS10_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Value in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_RPS10_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The RPS-10 STONITH device configuration information in the format \"serial_device remotenode outlet [remotenode outlet]...\"" \
+ XML_PARM_LONGDESC_END
+
+#define XML_RPS10_PARM \
+ XML_PARAMETER_BEGIN(ST_RPS10, "string", "1", "1") \
+ XML_RPS10_SHORTDESC \
+ XML_RPS10_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *rps10XML =
+ XML_PARAMETERS_BEGIN
+ XML_RPS10_PARM
+ XML_PARAMETERS_END;
+
+/* WTIpassword - The fixed string ^B^X^X^B^X^X */
+static const char WTIpassword[7] = {2,24,24,2,24,24,0};
+
+/*
+ * Different expect strings that we get from the WTI_RPS10
+ * Remote Power Controllers...
+ */
+
+static struct Etoken WTItokReady[] = { {"RPS-10 Ready", 0, 0}, {NULL,0,0}};
+static struct Etoken WTItokComplete[] = { {"Complete", 0, 0} ,{NULL,0,0}};
+static struct Etoken WTItokPlug[] = { {"Plug", 0, 0}, {NULL,0,0}};
+static struct Etoken WTItokOutlet[] = { {"0", 0, 0},
+ {"1", 0, 0},
+ {"2", 0, 0},
+ {"3", 0, 0},
+ {"4", 0, 0},
+ {"5", 0, 0},
+ {"6", 0, 0},
+ {"7", 0, 0},
+ {"8", 0, 0},
+ {"9", 0, 0},
+ {NULL,0,0}};
+
+static struct Etoken WTItokOff[] = { {"Off", 0, 0}, {NULL,0,0}};
+
+/*
+ * Tokens currently not used because they don't show up on all RPS10 units:
+ *
+ */
+static struct Etoken WTItokOn[] = { {"On", 0, 0}, {NULL,0,0}};
+
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken WTItokCRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int RPSConnect(struct pluginDevice * ctx);
+static int RPSDisconnect(struct pluginDevice * ctx);
+
+static int RPSReset(struct pluginDevice*, char unit_id, const char * rebootid);
+#if defined(ST_POWERON)
+static int RPSOn(struct pluginDevice*, char unit_id, const char * rebootid);
+#endif
+#if defined(ST_POWEROFF)
+static int RPSOff(struct pluginDevice*, char unit_id, const char * rebootid);
+#endif
+static signed char RPSNametoOutlet ( struct pluginDevice * ctx, const char * host );
+
+static int RPS_parse_config_info(struct pluginDevice* ctx, const char * info);
+
+#define SENDCMD(outlet, cmd, timeout) { \
+ int ret_val = RPSSendCommand(ctx, outlet, cmd, timeout);\
+ if (ret_val != S_OK) { \
+ return ret_val; \
+ } \
+ }
+
+/*
+ * RPSSendCommand - send a command to the specified outlet
+ */
+static int
+RPSSendCommand (struct pluginDevice *ctx, char outlet, char command, int timeout)
+{
+ char writebuf[10]; /* all commands are 9 chars long! */
+ int return_val; /* system call result */
+ fd_set rfds, wfds, xfds;
+ struct timeval tv; /* */
+
+ /* list of FDs for select() */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+
+ snprintf (writebuf, sizeof(writebuf), "%s%c%c\r",
+ WTIpassword, outlet, command);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "Sending %s\n", writebuf);
+ }
+
+ /* Make sure the serial port won't block on us. use select() */
+ FD_SET(ctx->fd, &wfds);
+ FD_SET(ctx->fd, &xfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ return_val = select(ctx->fd+1, NULL, &wfds,&xfds, &tv);
+ if (return_val == 0) {
+ /* timeout waiting on serial port */
+ LOG(PIL_CRIT, "%s: Timeout writing to %s",
+ pluginid, ctx->device);
+ return S_TIMEOUT;
+ } else if ((return_val == -1) || FD_ISSET(ctx->fd, &xfds)) {
+ /* an error occured */
+ LOG(PIL_CRIT, "%s: Error before writing to %s: %s",
+ pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* send the command */
+ if (write(ctx->fd, writebuf, strlen(writebuf)) !=
+ (int)strlen(writebuf)) {
+ LOG(PIL_CRIT, "%s: Error writing to %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* suceeded! */
+ return S_OK;
+
+} /* end RPSSendCommand() */
+
+/*
+ * RPSReset - Reset (power-cycle) the given outlet id
+ */
+static int
+RPSReset(struct pluginDevice* ctx, char unit_id, const char * rebootid)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
+ ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "toggle power" command */
+ SENDCMD(unit_id, 'T', 10);
+
+ /* Expect "Plug 0 Off" */
+ /* Note: If asked to control "*", the RPS10 will report all units it
+ * separately; however, we don't know how many, so we can only wait
+ * for the first unit to report something and then wait until the
+ * "Complete" */
+ EXPECT(ctx->fd, WTItokPlug, 5);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Plug\n");
+ }
+ EXPECT(ctx->fd, WTItokOutlet, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Outlet #\n");
+ }
+ EXPECT(ctx->fd, WTItokOff, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Off\n");
+ }
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ LOG(PIL_INFO, "Host is being rebooted: %s", rebootid);
+
+ /* Expect "Complete" */
+ EXPECT(ctx->fd, WTItokComplete, 14);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got Complete\n");
+ }
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL\n");
+ }
+
+ return(S_OK);
+
+} /* end RPSReset() */
+
+
+#if defined(ST_POWERON)
+/*
+ * RPSOn - Turn OFF the given outlet id
+ */
+static int
+RPSOn(struct pluginDevice* ctx, char unit_id, const char * host)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
+ ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "On" command */
+ SENDCMD(unit_id, '1', 10);
+
+ /* Expect "Plug 0 On" */
+ EXPECT(ctx->fd, WTItokPlug, 5);
+ EXPECT(ctx->fd, WTItokOutlet, 2);
+ EXPECT(ctx->fd, WTItokOn, 2);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ LOG(PIL_INFO, "Host is being turned on: %s", host);
+
+ /* Expect "Complete" */
+ EXPECT(ctx->fd, WTItokComplete, 5);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPSOn() */
+#endif
+
+
+#if defined(ST_POWEROFF)
+/*
+ * RPSOff - Turn Off the given outlet id
+ */
+static int
+RPSOff(struct pluginDevice* ctx, char unit_id, const char * host)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd < 0) {
+ LOG(PIL_CRIT, "%s: device %s is not open!", pluginid,
+ ctx->device);
+ return S_OOPS;
+ }
+
+ /* send the "Off" command */
+ SENDCMD(unit_id, '0', 10);
+
+ /* Expect "Plug 0 Off" */
+ EXPECT(ctx->fd, WTItokPlug, 5);
+ EXPECT(ctx->fd, WTItokOutlet, 2);
+ EXPECT(ctx->fd, WTItokOff, 2);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ LOG(PIL_INFO, "Host is being turned on: %s", host);
+
+ /* Expect "Complete" */
+ EXPECT(ctx->fd, WTItokComplete, 5);
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+
+ return(S_OK);
+
+} /* end RPSOff() */
+#endif
+
+
+/*
+ * rps10_status - API entry point to probe the status of the stonith device
+ * (basically just "is it reachable and functional?", not the
+ * status of the individual outlets)
+ *
+ * Returns:
+ * S_OOPS - some error occured
+ * S_OK - if the stonith device is reachable and online.
+ */
+static int
+rps10_status(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+ if (RPSConnect(ctx) != S_OK) {
+ return(S_OOPS);
+ }
+
+ /* The "connect" really does enough work to see if the
+ controller is alive... It verifies that it is returning
+ RPS-10 Ready
+ */
+
+ return(RPSDisconnect(ctx));
+}
+
+/*
+ * rps10_hostlist - API entry point to return the list of hosts
+ * for the devices on this WTI_RPS10 unit
+ *
+ * This type of device is configured from the config file,
+ * so we don't actually have to connect to figure this
+ * out, just peruse the 'ctx' structure.
+ * Returns:
+ * NULL on error
+ * a malloced array, terminated with a NULL,
+ * of null-terminated malloc'ed strings.
+ */
+static char **
+rps10_hostlist(StonithPlugin *s)
+{
+ char ** ret = NULL; /* list to return */
+ int i;
+ int j;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ ctx = (struct pluginDevice*) s;
+
+ if (ctx->unit_count >= 1) {
+ ret = (char **)MALLOC((ctx->unit_count+1)*sizeof(char*));
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+ ret[ctx->unit_count]=NULL; /* null terminate the array */
+ for (i=0; i < ctx->unit_count; i++) {
+ ret[i] = STRDUP(ctx->controllers[i].node);
+ if (ret[i] == NULL) {
+ for(j=0; j<i; j++) {
+ FREE(ret[j]);
+ }
+ FREE(ret); ret = NULL;
+ break;
+ }
+ } /* end for each possible outlet */
+ } /* end if any outlets are configured */
+ return(ret);
+} /* end si_hostlist() */
+
+/*
+ * Parse the given configuration information, and stash
+ * it away...
+ *
+ * The format of <info> for this module is:
+ * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
+ *
+ * e.g. A machine named 'nodea' can kill a machine named 'nodeb' through
+ * a device attached to serial port /dev/ttyS0.
+ * A machine named 'nodeb' can kill machines 'nodea' and 'nodec'
+ * through a device attached to serial port /dev/ttyS1 (outlets 0
+ * and 1 respectively)
+ *
+ * <assuming this is the heartbeat configuration syntax:>
+ *
+ * stonith nodea rps10 /dev/ttyS0 nodeb 0
+ * stonith nodeb rps10 /dev/ttyS0 nodea 0 nodec 1
+ *
+ * Another possible configuration is for 2 stonith devices
+ * accessible through 2 different serial ports on nodeb:
+ *
+ * stonith nodeb rps10 /dev/ttyS0 nodea 0
+ * stonith nodeb rps10 /dev/ttyS1 nodec 0
+ */
+
+/*
+ * OOPS!
+ *
+ * Most of the large block of comments above is incorrect as far as this
+ * module is concerned. It is somewhat applicable to the heartbeat code,
+ * but not to this Stonith module.
+ *
+ * The format of parameter string for this module is:
+ * <serial device> <remotenode> <outlet> [<remotenode> <outlet>] ...
+ */
+
+static int
+RPS_parse_config_info(struct pluginDevice* ctx, const char * info)
+{
+ char *copy;
+ char *token;
+ char *outlet, *node;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* strtok() is nice to use to parse a string with
+ (other than it isn't threadsafe), but it is destructive, so
+ we're going to alloc our own private little copy for the
+ duration of this function.
+ */
+
+ copy = STRDUP(info);
+ if (!copy) {
+ LOG(PIL_CRIT, "out of memory");
+ return S_OOPS;
+ }
+
+ /* Grab the serial device */
+ token = strtok (copy, " \t");
+
+ if (!token) {
+ LOG(PIL_CRIT, "%s: Can't find serial device on config line '%s'",
+ pluginid, info);
+ goto token_error;
+ }
+
+ ctx->device = STRDUP(token);
+ if (!ctx->device) {
+ LOG(PIL_CRIT, "out of memory");
+ goto token_error;
+ }
+
+ /* Loop through the rest of the command line which should consist of */
+ /* <nodename> <outlet> pairs */
+ while ((node = strtok (NULL, " \t"))
+ && (outlet = strtok (NULL, " \t\n"))) {
+ char outlet_id;
+
+ /* validate the outlet token */
+ if ((sscanf (outlet, "%c", &outlet_id) != 1)
+ || !( ((outlet_id >= '0') && (outlet_id <= '9'))
+ || (outlet_id == '*') || (outlet_id == 'A') )
+ ) {
+ LOG(PIL_CRIT
+ , "%s: the outlet_id %s must be between"
+ " 0 and 9 or '*' / 'A'",
+ pluginid, outlet);
+ goto token_error;
+ }
+
+ if (outlet_id == 'A') {
+ /* Remap 'A' to '*'; in some configurations,
+ * a '*' can't be configured because it breaks
+ * scripts -- lmb */
+ outlet_id = '*';
+ }
+
+ if (ctx->unit_count >= WTI_NUM_CONTROLLERS) {
+ LOG(PIL_CRIT,
+ "%s: Tried to configure too many controllers",
+ pluginid);
+ goto token_error;
+ }
+
+ ctx->controllers[ctx->unit_count].node = STRDUP(node);
+ strdown(ctx->controllers[ctx->unit_count].node);
+ ctx->controllers[ctx->unit_count].outlet_id = outlet_id;
+ ctx->unit_count++;
+
+ }
+
+ /* free our private copy of the string we've been destructively
+ * parsing with strtok()
+ */
+ FREE(copy);
+ return ((ctx->unit_count > 0) ? S_OK : S_BADCONFIG);
+
+token_error:
+ FREE(copy);
+ if (ctx->device) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ return(S_BADCONFIG);
+}
+
+
+/*
+ * dtrtoggle - toggle DTR on the serial port
+ *
+ * snarfed from minicom, sysdep1.c, a well known POSIX trick.
+ *
+ */
+static void dtrtoggle(int fd) {
+ struct termios tty, old;
+ int sec = 2;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ tcgetattr(fd, &tty);
+ tcgetattr(fd, &old);
+ cfsetospeed(&tty, B0);
+ cfsetispeed(&tty, B0);
+ tcsetattr(fd, TCSANOW, &tty);
+ if (sec>0) {
+ sleep(sec);
+ tcsetattr(fd, TCSANOW, &old);
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "dtrtoggle Complete (%s)\n", pluginid);
+ }
+}
+
+/*
+ * RPSConnect -
+ *
+ * Connect to the given WTI_RPS10 device.
+ * Side Effects
+ * DTR on the serial port is toggled
+ * ctx->fd now contains a valid file descriptor to the serial port
+ * ??? LOCK THE SERIAL PORT ???
+ *
+ * Returns
+ * S_OK on success
+ * S_OOPS on error
+ * S_TIMEOUT if the device did not respond
+ *
+ */
+static int
+RPSConnect(struct pluginDevice * ctx)
+{
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Open the serial port if it isn't already open */
+ if (ctx->fd < 0) {
+ struct termios tio;
+
+ if (OurImports->TtyLock(ctx->device) < 0) {
+ LOG(PIL_CRIT, "%s: TtyLock failed.", pluginid);
+ return S_OOPS;
+ }
+
+ ctx->fd = open (ctx->device, O_RDWR);
+ if (ctx->fd <0) {
+ LOG(PIL_CRIT, "%s: Can't open %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ return S_OOPS;
+ }
+
+ /* set the baudrate to 9600 8 - N - 1 */
+ memset (&tio, 0, sizeof(tio));
+
+ /* ??? ALAN - the -tradtitional flag on gcc causes the
+ CRTSCTS constant to generate a warning, and warnings
+ are treated as errors, so I can't set this flag! - EZA ???
+
+ Hmmm. now that I look at the documentation, RTS
+ is just wired high on this device! we don't need it.
+ */
+ /* tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS ;*/
+ tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD ;
+ tio.c_lflag = ICANON;
+
+ if (tcsetattr (ctx->fd, TCSANOW, &tio) < 0) {
+ LOG(PIL_CRIT, "%s: Can't set attributes %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+ /* flush all data to and fro the serial port before we start */
+ if (tcflush (ctx->fd, TCIOFLUSH) < 0) {
+ LOG(PIL_CRIT, "%s: Can't flush %s : %s",
+ pluginid, ctx->device, strerror(errno));
+ close (ctx->fd);
+ OurImports->TtyUnlock(ctx->device);
+ ctx->fd=-1;
+ return S_OOPS;
+ }
+
+ }
+
+ /* Toggle DTR - this 'resets' the controller serial port interface
+ In minicom, try CTRL-A H to hangup and you can see this behavior.
+ */
+ dtrtoggle(ctx->fd);
+
+ /* Wait for the switch to respond with "RPS-10 Ready".
+ Emperically, this usually takes 5-10 seconds...
+ ... If this fails, this may be a hint that you got
+ a broken serial cable, which doesn't connect hardware
+ flow control.
+ */
+ if (Debug) {
+ LOG(PIL_DEBUG, "Waiting for READY\n");
+ }
+ EXPECT(ctx->fd, WTItokReady, 12);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got READY\n");
+ }
+ EXPECT(ctx->fd, WTItokCRNL, 2);
+ if (Debug) {
+ LOG(PIL_DEBUG, "Got NL\n");
+ }
+
+ return(S_OK);
+}
+
+static int
+RPSDisconnect(struct pluginDevice * ctx)
+{
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx->fd >= 0) {
+ /* Flush the serial port, we don't care what happens to the
+ * characters and failing to do this can cause close to hang.
+ */
+ tcflush(ctx->fd, TCIOFLUSH);
+ close (ctx->fd);
+ if (ctx->device != NULL) {
+ OurImports->TtyUnlock(ctx->device);
+ }
+ }
+ ctx->fd = -1;
+
+ return S_OK;
+}
+
+/*
+ * RPSNametoOutlet - Map a hostname to an outlet on this stonith device.
+ *
+ * Returns:
+ * 0-9, * on success ( the outlet id on the RPS10 )
+ * -1 on failure (host not found in the config file)
+ *
+ */
+static signed char
+RPSNametoOutlet ( struct pluginDevice * ctx, const char * host )
+{
+ int i=0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* scan the controllers[] array to see if this host is there */
+ for (i=0;i<ctx->unit_count;i++) {
+ /* return the outlet id */
+ if ( ctx->controllers[i].node
+ && !strcasecmp(host, ctx->controllers[i].node)) {
+ /* found it! */
+ break;
+ }
+ }
+
+ if (i == ctx->unit_count) {
+ return -1;
+ } else {
+ return ctx->controllers[i].outlet_id;
+ }
+}
+
+
+/*
+ * rps10_reset - API call to Reset (reboot) the given host on
+ * this Stonith device. This involves toggling the power off
+ * and then on again, OR just calling the builtin reset command
+ * on the stonith device.
+ */
+static int
+rps10_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = S_OK;
+ int lorc = S_OK;
+ signed char outlet_id = -1;
+ struct pluginDevice* ctx;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ ctx = (struct pluginDevice*) s;
+
+ if ((rc = RPSConnect(ctx)) != S_OK) {
+ return(rc);
+ }
+
+ outlet_id = RPSNametoOutlet(ctx, host);
+
+ if (outlet_id < 0) {
+ LOG(PIL_WARN, "%s: %s doesn't control host [%s]"
+ , pluginid, ctx->device, host );
+ RPSDisconnect(ctx);
+ return(S_BADHOST);
+ }
+
+ switch(request) {
+
+#if defined(ST_POWERON)
+ case ST_POWERON:
+ rc = RPSOn(ctx, outlet_id, host);
+ break;
+#endif
+#if defined(ST_POWEROFF)
+ case ST_POWEROFF:
+ rc = RPSOff(ctx, outlet_id, host);
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = RPSReset(ctx, outlet_id, host);
+ break;
+ default:
+ rc = S_INVAL;
+ break;
+ }
+
+ lorc = RPSDisconnect(ctx);
+
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+rps10_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* ctx;
+ StonithNamesToGet namestocopy [] =
+ { {ST_RPS10, NULL}
+ , {NULL, NULL}
+ };
+ int rc=0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if (s->isconfigured) {
+ /* The module is already configured. */
+ return(S_OOPS);
+ }
+
+ ctx = (struct pluginDevice*) s;
+
+ if((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK){
+ LOG(PIL_DEBUG , "get all calues failed");
+ return rc;
+ }
+
+ rc = RPS_parse_config_info(ctx, namestocopy[0].s_value);
+ FREE(namestocopy[0].s_value);
+ return rc;
+}
+
+/*
+ * Return the Stonith plugin configuration parameter
+ *
+ */
+static const char * const *
+rps10_get_confignames(StonithPlugin* p)
+{
+ static const char * Rps10Params[] = {ST_RPS10 ,NULL };
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ return Rps10Params;
+}
+
+/*
+ * rps10_getinfo - API entry point to retrieve something from the handle
+ */
+static const char *
+rps10_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* ctx;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ ctx = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ctx->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = ctx->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Western Telematic Inc. (WTI) "
+ "Remote Power Switch - RPS-10M.\n";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = rps10XML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * rps10_destroy - API entry point to destroy a WTI_RPS10 Stonith object.
+ */
+static void
+rps10_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* ctx;
+ int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ ctx = (struct pluginDevice *)s;
+
+ ctx->pluginid = NOTwtiid;
+
+ /* close the fd if open and set ctx->fd to invalid */
+ RPSDisconnect(ctx);
+
+ if (ctx->device != NULL) {
+ FREE(ctx->device);
+ ctx->device = NULL;
+ }
+ if (ctx->unit_count > 0) {
+ for (i = 0; i < ctx->unit_count; i++) {
+ if (ctx->controllers[i].node != NULL) {
+ FREE(ctx->controllers[i].node);
+ ctx->controllers[i].node = NULL;
+ }
+ }
+ }
+ FREE(ctx);
+}
+
+/*
+ * rps10_new - API entry point called to create a new WTI_RPS10 Stonith device
+ * object.
+ */
+static StonithPlugin *
+rps10_new(const char *subplugin)
+{
+ struct pluginDevice* ctx = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if (ctx == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->pluginid = pluginid;
+ ctx->fd = -1;
+ ctx->unit_count = 0;
+ ctx->device = NULL;
+ ctx->idinfo = DEVICE;
+ ctx->sp.s_ops = &rps10Ops;
+
+ return &(ctx->sp);
+}
diff --git a/lib/plugins/stonith/ssh.c b/lib/plugins/stonith/ssh.c
new file mode 100644
index 0000000..e90c199
--- /dev/null
+++ b/lib/plugins/stonith/ssh.c
@@ -0,0 +1,351 @@
+/*
+ * Stonith module for SSH Stonith device
+ *
+ * Copyright (c) 2001 SuSE Linux AG
+ *
+ * Authors: Joachim Gleissner <jg@suse.de>, Lars Marowsky-Brée <lmb@suse.de>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+#include <config.h>
+
+#define DEVICE "SSH STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN ssh
+#define PIL_PLUGIN_S "ssh"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * ssh_new(const char *);
+static void ssh_destroy(StonithPlugin *);
+static const char * const * ssh_get_confignames(StonithPlugin *);
+static int ssh_set_config(StonithPlugin *, StonithNVpair*);
+static const char * ssh_get_info(StonithPlugin * s, int InfoType);
+static int ssh_status(StonithPlugin * );
+static int ssh_reset_req(StonithPlugin * s, int request
+, const char * host);
+static char ** ssh_hostlist(StonithPlugin *);
+
+static struct stonith_ops sshOps ={
+ ssh_new, /* Create new STONITH object */
+ ssh_destroy, /* Destroy STONITH object */
+ ssh_get_info, /* Return STONITH info string */
+ ssh_get_confignames, /* Return configuration parameters */
+ ssh_set_config, /* set configuration */
+ ssh_status, /* Return STONITH device status */
+ ssh_reset_req, /* Request a reset */
+ ssh_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &sshOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/* uncomment this if you have an ssh that can do what it claims
+#define SSH_COMMAND "ssh -q -x -o PasswordAuthentication=no StrictHostKeyChecking=no"
+*/
+/* use this if you have the (broken) OpenSSH 2.1.1 */
+/* sunjd@cn.ibm.com added the option -f to temporily work around the block issue
+ * in which the child process always stay in 'system' call. Please FIX this.
+ * Additonally, this issue seems related to both of 2.6 kernel and stonithd.
+ */
+#define SSH_COMMAND "ssh -q -x -n -l root"
+
+/* We need to do a real hard reboot without syncing anything to simulate a
+ * power cut.
+ * We have to do it in the background, otherwise this command will not
+ * return.
+ */
+#define REBOOT_COMMAND "nohup sh -c '(sleep 2; nohup " REBOOT " " REBOOT_OPTIONS ") </dev/null >/dev/null 2>&1' &"
+#undef REBOOT_COMMAND
+#define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+#define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+
+#define MAX_PING_ATTEMPTS 15
+
+/*
+ * SSH STONITH device
+ *
+ * I used the null device as template, so I guess there is missing
+ * some functionality.
+ *
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ char ** hostlist;
+ int hostcount;
+};
+
+static const char * pluginid = "SSHDevice-Stonith";
+static const char * NOTpluginid = "SSH device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *sshXML =
+ XML_PARAMETERS_BEGIN
+ XML_HOSTLIST_PARM
+ XML_PARAMETERS_END;
+
+static int
+ssh_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ return system(NULL) ? S_OK : S_OOPS;
+}
+
+
+/*
+ * Return the list of hosts configured for this SSH device
+ */
+
+static char **
+ssh_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice* sd = (struct pluginDevice*)s;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ if (sd->hostcount < 0) {
+ LOG(PIL_CRIT
+ , "unconfigured stonith object in %s", __FUNCTION__);
+ return(NULL);
+ }
+
+ return OurImports->CopyHostList((const char * const *)sd->hostlist);
+}
+
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+ssh_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ char cmd[4096];
+ int i, status = -1;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (request == ST_POWERON) {
+ LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE);
+ return S_INVAL;
+ } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) {
+ return S_INVAL;
+ }
+
+ for (i = 0; i < sd->hostcount; i++) {
+ if (strcasecmp(host, sd->hostlist[i]) == 0) {
+ break;
+ }
+ }
+
+ if (i >= sd->hostcount) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , sd->idinfo, host);
+ return(S_BADHOST);
+ }
+
+ LOG(PIL_INFO, "Initiating ssh-%s on host: %s"
+ , request == ST_POWEROFF ? "poweroff" : "reset", host);
+
+ snprintf(cmd, sizeof(cmd)-1, "%s \"%s\" \"%s\"", SSH_COMMAND
+ , host
+ , request == ST_POWEROFF ? POWEROFF_COMMAND : REBOOT_COMMAND);
+
+ status = system(cmd);
+ if (WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "checking whether %s stonith'd", host);
+ }
+
+ snprintf(cmd, sizeof(cmd)-1
+ , "ping -w1 -c1 %s >/dev/null 2>&1", host);
+
+ for (i = 0; i < MAX_PING_ATTEMPTS; i++) {
+ status = system(cmd);
+ if (WIFEXITED(status) && 1 == WEXITSTATUS(status)) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "unable to ping %s"
+ " after %d tries, stonith did work"
+ , host, i);
+ }
+ return S_OK;
+ }
+ sleep(1);
+ }
+
+ LOG(PIL_CRIT, "still able to ping %s after %d tries, stonith"
+ " did not work", host, MAX_PING_ATTEMPTS);
+ return S_RESETFAIL;
+ }else{
+ LOG(PIL_CRIT, "command %s failed", cmd);
+ return S_RESETFAIL;
+ }
+}
+
+static const char * const *
+ssh_get_confignames(StonithPlugin* p)
+{
+ static const char * SshParams[] = {ST_HOSTLIST, NULL };
+ return SshParams;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+ssh_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * hlist;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ if ((hlist = OurImports->GetValue(list, ST_HOSTLIST)) == NULL) {
+ return S_OOPS;
+ }
+ sd->hostlist = OurImports->StringToHostList(hlist);
+ if (sd->hostlist == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ sd->hostcount = 0;
+ }else{
+ for (sd->hostcount = 0; sd->hostlist[sd->hostcount]
+ ; sd->hostcount++) {
+ strdown(sd->hostlist[sd->hostcount]);
+ }
+ }
+
+ return sd->hostcount ? S_OK : S_OOPS;
+}
+
+
+static const char *
+ssh_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = sd->idinfo;
+ break;
+
+
+ case ST_DEVICENAME:
+ ret = "ssh STONITH device";
+ break;
+
+
+ case ST_DEVICEDESCR: /* Description of device type */
+ ret = "SSH-based host reset\n"
+ "Fine for testing, but not suitable for production!";
+ break;
+
+
+ case ST_DEVICEURL:
+ ret = "http://openssh.org";
+ break;
+
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = sshXML;
+ break;
+
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * SSH Stonith destructor...
+ */
+static void
+ssh_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd->pluginid = NOTpluginid;
+ if (sd->hostlist) {
+ stonith_free_hostlist(sd->hostlist);
+ sd->hostlist = NULL;
+ }
+ sd->hostcount = -1;
+ FREE(sd);
+}
+
+/* Create a new ssh Stonith device */
+static StonithPlugin*
+ssh_new(const char *subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->hostlist = NULL;
+ sd->hostcount = -1;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &sshOps;
+ return &(sd->sp);
+}
diff --git a/lib/plugins/stonith/stonith_config_xml.h b/lib/plugins/stonith/stonith_config_xml.h
new file mode 100644
index 0000000..ff04ae9
--- /dev/null
+++ b/lib/plugins/stonith/stonith_config_xml.h
@@ -0,0 +1,157 @@
+/*
+ * stonith_config_xml.h: common macros easing the writing of config
+ * XML for STONITH plugins. Only a STONITH
+ * plugin should include this header!
+ *
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author: Dave Blaschke <debltc@us.ibm.com>
+ * Support: linux-ha@lists.linux-ha.org
+ *
+ * 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
+ *
+ */
+#ifndef _STONITH_CONFIG_XML_H
+#define _STONITH_CONFIG_XML_H
+
+/*
+ * The generic constants for XML
+ */
+
+/* <parameters>?</parameters> */
+#define XML_PARAMETERS_BEGIN "<parameters>"
+#define XML_PARAMETERS_END "</parameters>"
+
+/* <parameter name="ipaddr" unique="?">?<content type="string" /></parameter> */
+#define XML_PARAMETER_BEGIN(name,type,req,uniq) \
+ "<parameter name=\"" name "\" unique=\"" uniq "\" required=\"" req "\">" \
+ "<content type=\"" type "\" />\n"
+#define XML_PARAMETER_END "</parameter>\n"
+
+/* <shortdesc lang="en">?</shortdesc> */
+#define XML_PARM_SHORTDESC_BEGIN(lang) \
+ "<shortdesc lang=\"" lang "\">\n"
+#define XML_PARM_SHORTDESC_END "</shortdesc>\n"
+
+/* <longdesc lang="en">?</longdesc> */
+#define XML_PARM_LONGDESC_BEGIN(lang) \
+ "<longdesc lang=\"" lang "\">\n"
+#define XML_PARM_LONGDESC_END "</longdesc>\n"
+
+/*
+ * The short and long descriptions for the few standardized parameter names;
+ * these can be translated by appending different languages to these constants
+ * (must include XML_PARM_****DESC_BEGIN(), the translated description, and
+ * XML_PARM_****DESC_END for each language)
+ */
+#define XML_HOSTLIST_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Hostlist" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_HOSTLIST_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The list of hosts that the STONITH device controls" \
+ XML_PARM_LONGDESC_END
+
+#define XML_IPADDR_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "IP Address" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_IPADDR_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The IP address of the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_LOGIN_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Login" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_LOGIN_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The username used for logging in to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PASSWD_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "Password" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PASSWD_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The password used for logging in to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_COMMUNITY_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "SNMP Community" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_COMMUNITY_LONGDESC "" \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The SNMP community string associated with the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_TTYDEV_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ "TTY Device" \
+ XML_PARM_SHORTDESC_END
+
+#define XML_TTYDEV_LONGDESC "" \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The TTY device used for connecting to the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+/*
+ * Complete parameter descriptions for the few standardized parameter names
+ */
+#define XML_HOSTLIST_PARM \
+ XML_PARAMETER_BEGIN(ST_HOSTLIST, "string", "1", "0") \
+ XML_HOSTLIST_SHORTDESC \
+ XML_HOSTLIST_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_IPADDR_PARM \
+ XML_PARAMETER_BEGIN(ST_IPADDR, "string", "1", "0") \
+ XML_IPADDR_SHORTDESC \
+ XML_IPADDR_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_LOGIN_PARM \
+ XML_PARAMETER_BEGIN(ST_LOGIN, "string", "1", "0") \
+ XML_LOGIN_SHORTDESC \
+ XML_LOGIN_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_PASSWD_PARM \
+ XML_PARAMETER_BEGIN(ST_PASSWD, "string", "1", "0") \
+ XML_PASSWD_SHORTDESC \
+ XML_PASSWD_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_COMMUNITY_PARM \
+ XML_PARAMETER_BEGIN(ST_COMMUNITY, "string", "1", "0") \
+ XML_COMMUNITY_SHORTDESC \
+ XML_COMMUNITY_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_TTYDEV_PARM \
+ XML_PARAMETER_BEGIN(ST_TTYDEV, "string", "1", "0") \
+ XML_TTYDEV_SHORTDESC \
+ XML_TTYDEV_LONGDESC \
+ XML_PARAMETER_END
+
+#endif
diff --git a/lib/plugins/stonith/stonith_expect_helpers.h b/lib/plugins/stonith/stonith_expect_helpers.h
new file mode 100644
index 0000000..f9eaa19
--- /dev/null
+++ b/lib/plugins/stonith/stonith_expect_helpers.h
@@ -0,0 +1,120 @@
+/*
+ * stonith_expect_helpers.h: Some common expect defines.
+ *
+ * Copyright (C) 2004 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * 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
+ *
+ */
+
+/* This is still somewhat ugly. It needs to be included after the PILS
+ * definitions so that it can access them, but the code reduction seemed
+ * to justify this. Hopefully it can be made somewhat more elegant
+ * eventually. */
+
+/*
+ * Many expect/telnet plugins use these defines and functions.
+ */
+
+#define SEND(fd,s) { \
+ size_t slen = strlen(s); \
+ if (Debug) { \
+ LOG(PIL_DEBUG \
+ , "Sending [%s] (len %d)" \
+ , (s) \
+ , (int)slen); \
+ } \
+ if (write((fd), (s), slen) != slen) { \
+ LOG(PIL_CRIT \
+ , "%s: write failed" \
+ , __FUNCTION__); \
+ } \
+ }
+
+#define EXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(errno == ETIMEDOUT \
+ ? S_TIMEOUT : S_OOPS); \
+ }
+
+#define NULLEXPECT(fd,p,t) { \
+ if (StonithLookFor(fd, p, t) < 0) \
+ return(NULL); \
+ }
+
+#define SNARF(fd,s, to) { \
+ if (StonithScanLine(fd,to,(s),sizeof(s))\
+ != S_OK){ \
+ return(S_OOPS); \
+ } \
+ }
+
+#define NULLSNARF(fd,s, to){ \
+ if (StonithScanLine(fd,to,(s),sizeof(s))\
+ != S_OK) { \
+ return(NULL); \
+ } \
+ }
+
+/* Look for any of the given patterns. We don't care which */
+static int
+StonithLookFor(int fd, struct Etoken * tlist, int timeout)
+{
+ int rc;
+ char savebuf[512];
+
+ if ((rc = EXPECT_TOK(fd, tlist, timeout, savebuf, sizeof(savebuf)
+ , Debug)) < 0) {
+ LOG(PIL_CRIT, "Did not find string %s from " DEVICE "."
+ , tlist[0].string);
+ LOG(PIL_CRIT, "Received [%s]", savebuf);
+ }
+ return(rc);
+}
+
+#ifndef DOESNT_USE_STONITHSCANLINE
+/* Accept either a CR/NL or an NL/CR */
+static struct Etoken CRNL[] = { {"\n\r",0,0},{"\r\n",0,0},{NULL,0,0}};
+
+static int
+StonithScanLine(int fd, int timeout, char * buf, int max)
+{
+ if (EXPECT_TOK(fd, CRNL, timeout, buf, max, Debug) < 0) {
+ LOG(PIL_CRIT, "Could not read line from" DEVICE ".");
+ return(S_OOPS);
+ }
+ return(S_OK);
+}
+#endif
+
+#ifndef DOESNT_USE_STONITHKILLCOMM
+static void
+Stonithkillcomm(int *rdfd, int *wrfd, int *pid)
+{
+ if ((rdfd != NULL) && (*rdfd >= 0)) {
+ close(*rdfd);
+ *rdfd = -1;
+ }
+ if ((wrfd != NULL) && (*wrfd >= 0)) {
+ close(*wrfd);
+ *wrfd = -1;
+ }
+ if ((pid != NULL) && (*pid > 0)) {
+ STONITH_KILL(*pid, SIGKILL);
+ (void)waitpid(*pid, NULL, 0);
+ *pid = -1;
+ }
+}
+#endif
diff --git a/lib/plugins/stonith/stonith_plugin_common.h b/lib/plugins/stonith/stonith_plugin_common.h
new file mode 100644
index 0000000..dcdd7c8
--- /dev/null
+++ b/lib/plugins/stonith/stonith_plugin_common.h
@@ -0,0 +1,127 @@
+/*
+ * stonith_plugin_common.h: common macros easing the writing of STONITH
+ * plugins. Only a STONITH plugin should
+ * include this header!
+ *
+ * Copyright (C) 2004 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * 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
+ *
+ */
+#ifndef _STONITH_PLUGIN_COMMON_H
+#define _STONITH_PLUGIN_COMMON_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libintl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#ifdef HAVE_TERMIO_H
+# include <termio.h>
+#endif
+#ifdef HAVE_SYS_TERMIOS_H
+#include <sys/termios.h>
+#else
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#endif
+#include <glib.h>
+
+
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+#define LOG(w...) PILCallLog(PluginImports->log, w)
+
+#define MALLOC PluginImports->alloc
+#define REALLOC PluginImports->mrealloc
+#define STRDUP PluginImports->mstrdup
+#define FREE PluginImports->mfree
+#define EXPECT_TOK OurImports->ExpectToken
+#define STARTPROC OurImports->StartProcess
+
+#ifdef MALLOCT
+# undef MALLOCT
+#endif
+#define ST_MALLOCT(t) ((t *)(MALLOC(sizeof(t))))
+
+#define N_(text) (text)
+#define _(text) dgettext(ST_TEXTDOMAIN, text)
+
+#define WHITESPACE " \t\n\r\f"
+
+#ifndef MIN
+/* some macros */
+# define MIN( i, j ) ( i > j ? j : i )
+#endif
+
+#define REPLSTR(s,v) { \
+ if ((s) != NULL) { \
+ FREE(s); \
+ (s)=NULL; \
+ } \
+ (s) = STRDUP(v); \
+ if ((s) == NULL) { \
+ PILCallLog(PluginImports->log, \
+ PIL_CRIT, "out of memory"); \
+ } \
+ }
+
+#ifndef DEVICE
+#define DEVICE "Dummy"
+#endif
+
+#define PIL_PLUGINTYPE STONITH_TYPE
+#define PIL_PLUGINTYPE_S STONITH_TYPE_S
+
+#define ISCORRECTDEV(i) ((i)!= NULL \
+ && ((struct pluginDevice *)(i))->pluginid == pluginid)
+
+#define ERRIFWRONGDEV(s, retval) if (!ISCORRECTDEV(s)) { \
+ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \
+ return(retval); \
+ }
+
+#define VOIDERRIFWRONGDEV(s) if (!ISCORRECTDEV(s)) { \
+ LOG(PIL_CRIT, "%s: invalid argument", __FUNCTION__); \
+ return; \
+ }
+
+#define ISCONFIGED(i) (i->isconfigured)
+
+#define ERRIFNOTCONFIGED(s,retval) ERRIFWRONGDEV(s,retval); \
+ if (!ISCONFIGED(s)) { \
+ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \
+ return(retval); \
+ }
+
+#define VOIDERRIFNOTCONFIGED(s) VOIDERRIFWRONGDEV(s); \
+ if (!ISCONFIGED(s)) { \
+ LOG(PIL_CRIT, "%s: not configured", __FUNCTION__); \
+ return; \
+ }
+
+#endif
+
diff --git a/lib/plugins/stonith/stonith_signal.h b/lib/plugins/stonith/stonith_signal.h
new file mode 100644
index 0000000..99513f5
--- /dev/null
+++ b/lib/plugins/stonith/stonith_signal.h
@@ -0,0 +1,68 @@
+/*
+ * stonith_signal.h: signal handling routines to be used by stonith
+ * plugin libraries
+ *
+ * Copyright (C) 2002 Horms <horms@verge.net.au>
+ *
+ * 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
+ *
+ */
+#ifndef _STONITH_SIGNAL_H
+#define _STONITH_SIGNAL_H
+
+#include <signal.h>
+#include <sys/signal.h>
+
+int
+stonith_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact);
+
+int
+stonith_signal_set_simple_handler(int sig, void (*handler)(int)
+, struct sigaction *oldact)
+{
+ struct sigaction sa;
+ sigset_t mask;
+
+ (void)stonith_signal_set_simple_handler;
+ if(sigemptyset(&mask) < 0) {
+ return(-1);
+ }
+
+ sa.sa_handler = handler;
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+
+ if(sigaction(sig, &sa, oldact) < 0) {
+ return(-1);
+ }
+
+ return(0);
+}
+
+#define STONITH_SIGNAL(_sig, _handler) \
+ stonith_signal_set_simple_handler((_sig), (_handler), NULL)
+#ifdef HAVE_SIGIGNORE
+#define STONITH_IGNORE_SIG(_sig) \
+ sigignore((_sig))
+#else
+#define STONITH_IGNORE_SIG(_sig) \
+ STONITH_SIGNAL((_sig), SIG_IGN)
+#endif
+#define STONITH_DEFAULT_SIG(_sig) STONITH_SIGNAL((_sig), SIG_DFL)
+
+#define STONITH_KILL(_pid, _sig) kill((_pid), (_sig))
+
+#endif /* _STONITH_SIGNAL_H */
diff --git a/lib/plugins/stonith/suicide.c b/lib/plugins/stonith/suicide.c
new file mode 100644
index 0000000..b9d1db4
--- /dev/null
+++ b/lib/plugins/stonith/suicide.c
@@ -0,0 +1,274 @@
+/* File: suicide.c
+ * Description: Stonith module for suicide
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * 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
+ */
+
+#include <lha_internal.h>
+#include <config.h>
+#include <sys/utsname.h>
+
+#define DEVICE "Suicide STONITH device"
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN suicide
+#define PIL_PLUGIN_S "suicide"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * suicide_new(const char *);
+static void suicide_destroy(StonithPlugin *);
+static const char * const * suicide_get_confignames(StonithPlugin *);
+static int suicide_set_config(StonithPlugin *, StonithNVpair*);
+static const char * suicide_get_info(StonithPlugin * s, int InfoType);
+static int suicide_status(StonithPlugin * );
+static int suicide_reset_req(StonithPlugin * s, int request
+ , const char * host);
+static char ** suicide_hostlist(StonithPlugin *);
+
+static struct stonith_ops suicideOps ={
+ suicide_new, /* Create new STONITH object */
+ suicide_destroy, /* Destroy STONITH object */
+ suicide_get_info, /* Return STONITH info string */
+ suicide_get_confignames, /* Return configuration parameters */
+ suicide_set_config, /* Set configuration */
+ suicide_status, /* Return STONITH device status */
+ suicide_reset_req, /* Request a reset */
+ suicide_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &suicideOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+#define REBOOT_COMMAND "nohup sh -c 'sleep 2; " REBOOT " " REBOOT_OPTIONS " </dev/null >/dev/null 2>&1' &"
+#define POWEROFF_COMMAND "nohup sh -c 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS " </dev/null >/dev/null 2>&1' &"
+/*
+#define REBOOT_COMMAND "echo 'sleep 2; " REBOOT " " REBOOT_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+#define POWEROFF_COMMAND "echo 'sleep 2; " POWEROFF_CMD " " POWEROFF_OPTIONS "' | SHELL=/bin/sh at now >/dev/null 2>&1"
+*/
+
+/*
+ * Suicide STONITH device
+ */
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+};
+
+static const char * pluginid = "SuicideDevice-Stonith";
+static const char * NOTpluginid = "Suicide device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *suicideXML =
+ XML_PARAMETERS_BEGIN
+ XML_PARAMETERS_END;
+
+static int
+suicide_status(StonithPlugin *s)
+{
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ return S_OK;
+}
+
+/*
+ * Return the list of hosts configured for this Suicide device
+ */
+static char **
+suicide_hostlist(StonithPlugin *s)
+{
+ char** ret = NULL;
+ struct utsname name;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ if (uname(&name) == -1) {
+ LOG(PIL_CRIT, "uname error %d", errno);
+ return ret;
+ }
+
+ ret = OurImports->StringToHostList(name.nodename);
+ if (ret == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return ret;
+ }
+ strdown(ret[0]);
+
+ return ret;
+}
+
+/*
+ * Suicide - reset or poweroff itself.
+ */
+static int
+suicide_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = -1;
+ struct utsname name;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+
+ if (request == ST_POWERON) {
+ LOG(PIL_CRIT, "%s not capable of power-on operation", DEVICE);
+ return S_INVAL;
+ } else if (request != ST_POWEROFF && request != ST_GENERIC_RESET) {
+ LOG(PIL_CRIT, "As for suicide virtual stonith device, "
+ "reset request=%d is not supported", request);
+ return S_INVAL;
+ }
+
+ if (uname(&name) == -1) {
+ LOG(PIL_CRIT, "uname error %d", errno);
+ return S_RESETFAIL ;
+ }
+
+ if (strcmp(name.nodename, host)) {
+ LOG(PIL_CRIT, "%s doesn't control host [%s]"
+ , name.nodename, host);
+ return S_RESETFAIL ;
+ }
+
+ LOG(PIL_INFO, "Initiating suicide on host %s", host);
+
+ rc = system(
+ request == ST_GENERIC_RESET ? REBOOT_COMMAND : POWEROFF_COMMAND);
+
+ if (rc == 0) {
+ LOG(PIL_INFO, "Suicide stonith succeeded.");
+ return S_OK;
+ } else {
+ LOG(PIL_CRIT, "Suicide stonith failed.");
+ return S_RESETFAIL ;
+ }
+}
+
+static const char * const *
+suicide_get_confignames(StonithPlugin* p)
+{
+ /* Donnot need to initialize from external. */
+ static const char * SuicideParams[] = { NULL };
+ return SuicideParams;
+}
+
+/*
+ * Parse the config information in the given string, and stash it away...
+ */
+static int
+suicide_set_config(StonithPlugin* s, StonithNVpair* list)
+{
+ ERRIFWRONGDEV(s,S_OOPS);
+ return S_OK;
+}
+
+static const char *
+suicide_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+ sd = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = "suicide STONITH device";
+ break;
+
+ case ST_DEVICEDESCR: /* Description of device type */
+ ret = "Virtual device to reboot/powerdown itself.\n";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = suicideXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Suicide Stonith destructor...
+ */
+static void
+suicide_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* sd;
+
+ VOIDERRIFWRONGDEV(s);
+
+ sd = (struct pluginDevice *)s;
+
+ sd->pluginid = NOTpluginid;
+ FREE(sd);
+}
+
+/* Create a new suicide Stonith device */
+static StonithPlugin*
+suicide_new(const char * subplugin)
+{
+ struct pluginDevice* sd = ST_MALLOCT(struct pluginDevice);
+
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->pluginid = pluginid;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &suicideOps;
+ return &(sd->sp);
+}
diff --git a/lib/plugins/stonith/vacm.c b/lib/plugins/stonith/vacm.c
new file mode 100644
index 0000000..ce6d041
--- /dev/null
+++ b/lib/plugins/stonith/vacm.c
@@ -0,0 +1,485 @@
+
+/******************************************************************************
+*
+* Copyright 2000 Sistina Software, Inc.
+* Tiny bits Copyright 2000 Alan Robertson <alanr@unix.sh>
+* Tiny bits Copyright 2000 Zac Sprackett, VA Linux Systems
+* Tiny bits Copyright 2005 International Business Machines
+* Significantly Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+*
+* This is free software released under the GNU General Public License.
+* There is no warranty for this software. See the file COPYING for
+* details.
+*
+* See the file CONTRIBUTORS for a list of contributors.
+*
+* This file is maintained by:
+* Michael C Tilstra <conrad@sistina.com>
+*
+* Becasue I have no device to test, now I just make it pass the compiling
+* with vacm-2.0.5a. Please review before using.
+* Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+*
+* This module provides a driver for the VA Linux Cluster Manager.
+* For more information on VACM, see http://vacm.sourceforge.net/
+*
+* This module is rather poorly commented. But if you've read the
+* VACM Manual, and looked at the code example they have, this
+* should make pretty clean sense. (You obiviously should have
+* looked at the other stonith source too)
+*
+*/
+
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define DEVICE "VA Linux Cluster Manager"
+
+#include "stonith_plugin_common.h"
+#include "vacmclient_api.h"
+
+#define PIL_PLUGIN vacm
+#define PIL_PLUGIN_S "vacm"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+static StonithPlugin * vacm_new(const char *);
+static void vacm_destroy(StonithPlugin *);
+static const char * const * vacm_get_confignames(StonithPlugin *);
+static int vacm_set_config(StonithPlugin *, StonithNVpair *);
+static const char * vacm_getinfo(StonithPlugin * s, int InfoType);
+static int vacm_status(StonithPlugin * );
+static int vacm_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** vacm_hostlist(StonithPlugin *);
+
+static struct stonith_ops vacmOps ={
+ vacm_new, /* Create new STONITH object */
+ vacm_destroy, /* Destroy STONITH object */
+ vacm_getinfo, /* Return STONITH info string */
+ vacm_get_confignames, /* Return configuration parameters */
+ vacm_set_config, /* Set configuration */
+ vacm_status, /* Return STONITH device status */
+ vacm_reset_req, /* Request a reset */
+ vacm_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug);
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &vacmOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*structs*/
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ void *h; /* a handle to the nexxus. */
+ char * nexxus;
+ char * user;
+ char * passwd;
+};
+
+#define ST_NEXXUS "nexxus"
+
+static const char * pluginid = "VACMDevice-Stonith";
+static const char * NOTpluginid = "VACM device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_NEXXUS_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_NEXXUS \
+ XML_PARM_SHORTDESC_END
+
+#define XML_NEXXUS_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The Nexxus component of the VA Cluster Manager" \
+ XML_PARM_LONGDESC_END
+
+#define XML_NEXXUS_PARM \
+ XML_PARAMETER_BEGIN(ST_NEXXUS, "string", "1", "1") \
+ XML_NEXXUS_SHORTDESC \
+ XML_NEXXUS_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *vacmXML =
+ XML_PARAMETERS_BEGIN
+ XML_NEXXUS_PARM
+ XML_LOGIN_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+/*funcs*/
+int
+vacm_status(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char snd[] = "NEXXUS:VERSION";
+ char *rcv, *tk;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ sd = (struct pluginDevice*)s;
+
+ /* If grabbing the nexxus version works, then the status must be ok.
+ * right?
+ */
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ break;
+ }
+ if (!(tk = strtok(rcv,":"))) { /*NEXXUS*/
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* Job ID */
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* one of the below */
+ break;
+ } else if ( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return S_OK; /* YEAH!! */
+ }else if(!strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ }else if(!strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ break;
+ }else if(!strcmp(tk, "VERSION")) {
+ free(rcv);
+ continue;
+ } else {
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n"
+ , tk, rcv);
+ break;
+ }
+ }
+
+ return S_OOPS;
+}
+
+/* Better make sure the current group is correct.
+ * Can't think of a good way to do this.
+ */
+char **
+vacm_hostlist(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+ char snd[] = "NEXXUS:NODE_LIST";
+ char *rcv,*tk;
+ int rcvlen;
+ char ** hlst=NULL;
+ int hacnt=0, hrcnt=0;
+#define MSTEP 20
+
+ ERRIFWRONGDEV(s, NULL);
+ sd = (struct pluginDevice*)s;
+
+ hlst = (char **)MALLOC(MSTEP * sizeof(char*));
+ if (hlst == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return NULL;
+ }
+ hacnt=MSTEP;
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if(api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ goto HL_cleanup;
+ }
+ if(!(tk=strtok(rcv, ":"))) { /* NEXXUS */
+ goto HL_cleanup;
+ }else if(!(tk=strtok(NULL,":"))) { /* Job ID */
+ goto HL_cleanup;
+ }else if(!(tk=strtok(NULL,":"))) { /* JOB_* or NODELIST */
+ goto HL_cleanup;
+ }else if( !strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ }else if( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return hlst;
+ }else if( !strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ break;
+ }else if( !strcmp(tk, "NODELIST")) {
+ if(!(tk = strtok(NULL,":"))) { /* group */
+ goto HL_cleanup;
+ }else if((tk = strtok(NULL," \t\n\r"))) { /*Finally, a machine name.*/
+ if( hrcnt >= (hacnt-1)) { /* grow array. */
+ char **oldhlst = hlst;
+ hlst = (char **)REALLOC(hlst, (hacnt +MSTEP)*sizeof(char*));
+ if( !hlst ) {
+ stonith_free_hostlist(oldhlst);
+ return NULL;
+ }
+ hacnt += MSTEP;
+ }
+ hlst[hrcnt] = STRDUP(tk); /* stuff the name. */
+ hlst[hrcnt+1] = NULL; /* set next to NULL for looping */
+ if (hlst[hrcnt] == NULL) {
+ stonith_free_hostlist(hlst);
+ return NULL;
+ }
+ strdown(hlst[hrcnt]);
+ hrcnt++;
+ }
+ }else {
+ /* WTF?! */
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n",tk,rcv);
+ break;
+ }
+ }
+
+HL_cleanup:
+ stonith_free_hostlist(hlst); /* give the mem back */
+ return NULL;
+}
+
+#define SND_SIZE 256
+int
+vacm_reset_req(StonithPlugin *s, int request, const char *host)
+{
+ struct pluginDevice *sd;
+ char snd[SND_SIZE]; /* god forbid its bigger than this */
+ char *rcv, *tk;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s,S_OOPS);
+ sd = (struct pluginDevice*)s;
+
+ switch(request) {
+#ifdef ST_POWERON
+ case ST_POWERON:
+ snprintf(snd, SND_SIZE, "EMP:POWER_ON:%s", host);
+ break;
+#endif /*ST_POWERON*/
+#ifdef ST_POWEROFF
+ case ST_POWEROFF:
+ snprintf(snd, SND_SIZE, "EMP:POWER_OFF:%s", host);
+ break;
+#endif /*ST_POWEROFF*/
+ case ST_GENERIC_RESET:
+ snprintf(snd, SND_SIZE, "EMP:POWER_CYCLE:%s", host);
+ break;
+ default:
+ return S_INVAL;
+ }
+
+ api_nexxus_send_ipc(sd->h, snd, strlen(snd)+1);
+ while(1) {
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ return S_RESETFAIL;
+ }
+ if (!(tk = strtok(rcv,":"))) { /*EMP*/
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* Job ID */
+ break;
+ }else if (!(tk=strtok(NULL,":"))) { /* one of teh below */
+ break;
+ } else if ( !strcmp(tk, "JOB_COMPLETED")) {
+ free(rcv);
+ return S_OK;
+ } else if(!strcmp(tk, "JOB_STARTED")) {
+ free(rcv);
+ continue;
+ } else if(!strcmp(tk, "JOB_ERROR")) {
+ free(rcv);
+ return S_RESETFAIL;
+ } else {
+ /* WTF?! */
+ LOG(PIL_CRIT, "Unexpected token \"%s\" in line \"%s\"\n"
+ , tk, rcv);
+ break;
+ }
+ }
+
+ return S_RESETFAIL;
+}
+
+/* list => "nexxus:username:password" */
+static const char * const *
+vacm_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_NEXXUS, ST_LOGIN, ST_PASSWD, NULL};
+ return ret;
+}
+
+static int
+vacm_set_config(StonithPlugin *s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ StonithNamesToGet namestocopy [] =
+ { {ST_NEXXUS, NULL}
+ , {ST_LOGIN, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+ char *rcv;
+ int rcvlen;
+
+ ERRIFWRONGDEV(s, S_OOPS);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->nexxus = namestocopy[0].s_value;
+ sd->user = namestocopy[1].s_value;
+ sd->passwd = namestocopy[2].s_value;
+ /* When to initialize the sd->h */
+
+ if (api_nexxus_connect(sd->nexxus, sd->user, sd->passwd, &sd->h)<0){
+ return S_OOPS;
+ }
+ if (api_nexxus_wait_for_data(sd->h, &rcv, &rcvlen, 20)<0) {
+ return S_OOPS;
+ }
+ if (strcmp(rcv, "NEXXUS_READY")) {
+ rc = S_BADCONFIG;
+ }else{
+ rc = S_OK;
+ }
+ free(rcv);
+
+ return(rc);
+}
+
+/*
+ * The "vacmconf:" is in the conffile so that one file could be used for
+ * multiple device configs. This module will only look at the first line
+ * that starts with this token. All other line are ignored. (and thus
+ * could contain configs for other modules.)
+ *
+ * I don't think any other stonith modules do this currently.
+ */
+const char *
+vacm_getinfo(StonithPlugin *s, int reqtype)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ const char * ret;
+
+ ERRIFWRONGDEV(s, NULL);
+ switch (reqtype) {
+
+ case ST_DEVICEID: /* What type of device? */
+ ret = sd->idinfo;
+ break;
+
+ case ST_DEVICENAME: /* Which particular device? */
+ ret = dgettext(ST_TEXTDOMAIN, "VACM");
+ break;
+
+ case ST_DEVICEDESCR: /* Description of dev type */
+ ret = "A driver for the VA Linux Cluster Manager.";
+ break;
+
+ case ST_DEVICEURL: /* VACM's web site */
+ ret = "http://vacm.sourceforge.net/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = vacmXML;
+ break;
+
+ default:
+ ret = NULL;
+ break;
+ }
+
+ return ret;
+}
+
+void
+vacm_destroy(StonithPlugin *s)
+{
+ struct pluginDevice *sd;
+
+ VOIDERRIFWRONGDEV(s);
+ sd = (struct pluginDevice*)s;
+
+ if( sd->h ) {
+ api_nexxus_disconnect(sd->h);
+ }
+
+ sd->pluginid = NOTpluginid;
+ if (sd->nexxus != NULL) {
+ FREE(sd->nexxus);
+ sd->nexxus = NULL;
+ }
+ if (sd->user != NULL) {
+ FREE(sd->user);
+ sd->user = NULL;
+ }
+ if (sd->passwd != NULL) {
+ FREE(sd->passwd);
+ sd->passwd = NULL;
+ }
+
+ FREE(sd);
+}
+
+static StonithPlugin *
+vacm_new(const char *subplugin)
+{
+ struct pluginDevice *sd;
+
+ sd = MALLOC(sizeof(struct pluginDevice));
+ if (sd == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(sd, 0, sizeof(*sd));
+ sd->h = NULL;
+ sd->pluginid = pluginid;
+ sd->nexxus = NULL;
+ sd->user = NULL;
+ sd->passwd = NULL;
+ sd->idinfo = DEVICE;
+ sd->sp.s_ops = &vacmOps;
+ return &(sd->sp); /* same as "sd" */
+}
diff --git a/lib/plugins/stonith/wti_mpc.c b/lib/plugins/stonith/wti_mpc.c
new file mode 100644
index 0000000..548f91c
--- /dev/null
+++ b/lib/plugins/stonith/wti_mpc.c
@@ -0,0 +1,856 @@
+/*
+ * Stonith module for WTI MPC (SNMP)
+ * Copyright (c) 2001 Andreas Piesk <a.piesk@gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd@cn.ibm.com>, IBM, 2005
+ *
+ * Modified for WTI MPC by Denis Chapligin <chollya@satgate.net>, SatGate, 2009
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+
+/* device ID */
+#define DEVICE "WTI MPC"
+
+#include "stonith_plugin_common.h"
+#undef FREE /* defined by snmp stuff */
+
+#ifdef PACKAGE_BUGREPORT
+#undef PACKAGE_BUGREPORT
+#endif
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
+# include <net-snmp/net-snmp-config.h>
+# include <net-snmp/net-snmp-includes.h>
+# include <net-snmp/agent/net-snmp-agent-includes.h>
+# define INIT_AGENT() init_master_agent()
+#else
+# include <ucd-snmp/ucd-snmp-config.h>
+# include <ucd-snmp/ucd-snmp-includes.h>
+# include <ucd-snmp/ucd-snmp-agent-includes.h>
+# ifndef NETSNMP_DS_APPLICATION_ID
+# define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID
+# endif
+# ifndef NETSNMP_DS_AGENT_ROLE
+# define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE
+# endif
+# define netsnmp_ds_set_boolean ds_set_boolean
+# define INIT_AGENT() init_master_agent(161, NULL, NULL)
+#endif
+
+#define PIL_PLUGIN wti_mpc
+#define PIL_PLUGIN_S "wti_mpc"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#include <pils/plugin.h>
+
+#define DEBUGCALL \
+ if (Debug) { \
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__); \
+ }
+
+static StonithPlugin * wti_mpc_new(const char *);
+static void wti_mpc_destroy(StonithPlugin *);
+static const char * const * wti_mpc_get_confignames(StonithPlugin *);
+static int wti_mpc_set_config(StonithPlugin *, StonithNVpair *);
+static const char * wti_mpc_getinfo(StonithPlugin * s, int InfoType);
+static int wti_mpc_status(StonithPlugin * );
+static int wti_mpc_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** wti_mpc_hostlist(StonithPlugin *);
+
+static struct stonith_ops wti_mpcOps ={
+ wti_mpc_new, /* Create new STONITH object */
+ wti_mpc_destroy, /* Destroy STONITH object */
+ wti_mpc_getinfo, /* Return STONITH info string */
+ wti_mpc_get_confignames, /* Get configuration parameters */
+ wti_mpc_set_config, /* Set configuration */
+ wti_mpc_status, /* Return STONITH device status */
+ wti_mpc_reset_req, /* Request a reset */
+ wti_mpc_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+ DEBUGCALL;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &wti_mpcOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * APCMaster tested with APC Masterswitch 9212
+ */
+
+/* outlet commands / status codes */
+#define OUTLET_ON 5
+#define OUTLET_OFF 6
+#define OUTLET_REBOOT 7
+
+/* oids */
+#define OID_IDENT ".1.3.6.1.2.1.1.5.0"
+
+#define OID_GROUP_NAMES_V1 ".1.3.6.1.4.1.2634.3.1.3.1.2.%u"
+#define OID_GROUP_STATE_V1 ".1.3.6.1.4.1.2634.3.1.3.1.3.%i"
+
+#define OID_GROUP_NAMES_V3 ".1.3.6.1.4.1.2634.3.100.300.1.2.%u"
+#define OID_GROUP_STATE_V3 ".1.3.6.1.4.1.2634.3.100.300.1.3.%i"
+
+#define MAX_OUTLETS 128
+
+/*
+ snmpset -c private -v1 172.16.0.32:161
+ ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1" i 1
+ The last octet in the OID is the plug number. The value can
+ be 1 thru 8 because there are 8 power plugs on this device.
+ The integer that can be set is as follows: 1=on, 2=off, and
+ 3=reset
+*/
+
+/* own defines */
+#define MAX_STRING 128
+#define ST_PORT "port"
+#define ST_MIBVERSION "mib-version"
+
+/* structur of stonith object */
+struct pluginDevice {
+ StonithPlugin sp; /* StonithPlugin object */
+ const char* pluginid; /* id of object */
+ const char* idinfo; /* type of device */
+ struct snmp_session* sptr; /* != NULL->session created */
+ char * hostname; /* masterswitch's hostname */
+ /* or ip addr */
+ int port; /* snmp port */
+ int mib_version; /* mib version to use */
+ char * community; /* snmp community (r/w) */
+ int num_outlets; /* number of outlets */
+};
+
+/* constant strings */
+static const char *pluginid = "WTI-MPC-Stonith";
+static const char *NOTpluginID = "WTI MPC device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+#define XML_PORT_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_PORT \
+ XML_PARM_SHORTDESC_END
+
+#define XML_PORT_LONGDESC \
+ XML_PARM_LONGDESC_BEGIN("en") \
+ "The port number on which the SNMP server is running on the STONITH device" \
+ XML_PARM_LONGDESC_END
+
+#define XML_PORT_PARM \
+ XML_PARAMETER_BEGIN(ST_PORT, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+#define XML_MIBVERSION_SHORTDESC \
+ XML_PARM_SHORTDESC_BEGIN("en") \
+ ST_MIBVERSION \
+ XML_PARM_SHORTDESC_END
+
+#define XML_MIBVERSION_LONGDESC \
+ XML_MIBVERSION_LONGDESC_BEGIN("en") \
+ "Version number of MPC MIB that we should use. Valid values are 1 (for 1.44 firmware) and 3 (for 1.62 firmware and later)" \
+ XML_PARM_LONGDESC_END
+
+#define XML_MIBVERSION_PARM \
+ XML_PARAMETER_BEGIN(ST_MIBVERSION, "string", "1", "0") \
+ XML_PORT_SHORTDESC \
+ XML_PORT_LONGDESC \
+ XML_PARAMETER_END
+
+static const char *apcmastersnmpXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PORT_PARM
+ XML_COMMUNITY_PARM
+ XML_MIBVERSION_PARM
+ XML_PARAMETERS_END;
+
+/*
+ * own prototypes
+ */
+
+static void MPC_error(struct snmp_session *sptr, const char *fn
+, const char *msg);
+static struct snmp_session *MPC_open(char *hostname, int port
+, char *community);
+static void *MPC_read(struct snmp_session *sptr, const char *objname
+, int type);
+static int MPC_write(struct snmp_session *sptr, const char *objname
+, char type, char *value);
+
+static void
+MPC_error(struct snmp_session *sptr, const char *fn, const char *msg)
+{
+ int snmperr = 0;
+ int cliberr = 0;
+ char *errstr;
+
+ snmp_error(sptr, &cliberr, &snmperr, &errstr);
+ LOG(PIL_CRIT
+ , "%s: %s (cliberr: %i / snmperr: %i / error: %s)."
+ , fn, msg, cliberr, snmperr, errstr);
+ free(errstr);
+}
+
+
+/*
+ * creates a snmp session
+ */
+static struct snmp_session *
+MPC_open(char *hostname, int port, char *community)
+{
+ static struct snmp_session session;
+ struct snmp_session *sptr;
+
+ DEBUGCALL;
+
+ /* create session */
+ snmp_sess_init(&session);
+
+ /* fill session */
+ session.peername = hostname;
+ session.version = SNMP_VERSION_1;
+ session.remote_port = port;
+ session.community = (u_char *)community;
+ session.community_len = strlen(community);
+ session.retries = 5;
+ session.timeout = 1000000;
+
+ /* open session */
+ sptr = snmp_open(&session);
+
+ if (sptr == NULL) {
+ MPC_error(&session, __FUNCTION__, "cannot open snmp session");
+ }
+
+ /* return pointer to opened session */
+ return (sptr);
+}
+
+/*
+ * parse config
+ */
+
+/*
+ * read value of given oid and return it as string
+ */
+static void *
+MPC_read(struct snmp_session *sptr, const char *objname, int type)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct variable_list *vars;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+ static char response_str[MAX_STRING];
+ static int response_int;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return NULL if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (NULL);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_GET)) != NULL) {
+
+ /* get-request have no values */
+ snmp_add_null_var(pdu, name, namelen);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == SNMPERR_SUCCESS) {
+
+ /* request succeed, got valid response ? */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* go through the returned vars */
+ for (vars = resp->variables; vars;
+ vars = vars->next_variable) {
+
+ /* return response as string */
+ if ((vars->type == type) && (type == ASN_OCTET_STR)) {
+ memset(response_str, 0, MAX_STRING);
+ strncpy(response_str, (char *)vars->val.string,
+ MIN(vars->val_len, MAX_STRING));
+ snmp_free_pdu(resp);
+ return ((void *) response_str);
+ }
+ /* return response as integer */
+ if ((vars->type == type) && (type == ASN_INTEGER)) {
+ response_int = *vars->val.integer;
+ snmp_free_pdu(resp);
+ return ((void *) &response_int);
+ }
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free repsonse pdu (necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ MPC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error: return nothing */
+ return (NULL);
+}
+
+/*
+ * write value of given oid
+ */
+static int
+MPC_write(struct snmp_session *sptr, const char *objname, char type,
+ char *value)
+{
+ oid name[MAX_OID_LEN];
+ size_t namelen = MAX_OID_LEN;
+ struct snmp_pdu *pdu;
+ struct snmp_pdu *resp;
+
+ DEBUGCALL;
+
+ /* convert objname into oid; return FALSE if invalid */
+ if (!read_objid(objname, name, &namelen)) {
+ LOG(PIL_CRIT, "%s: cannot convert %s to oid.", __FUNCTION__, objname);
+ return (FALSE);
+ }
+
+ /* create pdu */
+ if ((pdu = snmp_pdu_create(SNMP_MSG_SET)) != NULL) {
+
+ /* add to be written value to pdu */
+ snmp_add_var(pdu, name, namelen, type, value);
+
+ /* send pdu and get response; return NULL if error */
+ if (snmp_synch_response(sptr, pdu, &resp) == STAT_SUCCESS) {
+
+ /* go through the returned vars */
+ if (resp->errstat == SNMP_ERR_NOERROR) {
+
+ /* request successful done */
+ snmp_free_pdu(resp);
+ return (TRUE);
+
+ }else{
+ LOG(PIL_CRIT, "%s: error in response packet, reason %ld [%s]."
+ , __FUNCTION__, resp->errstat, snmp_errstring(resp->errstat));
+ }
+ }else{
+ MPC_error(sptr, __FUNCTION__, "error sending/receiving pdu");
+ }
+ /* free pdu (again: necessary?) */
+ snmp_free_pdu(resp);
+ }else{
+ MPC_error(sptr, __FUNCTION__, "cannot create pdu");
+ }
+ /* error */
+ return (FALSE);
+}
+
+/*
+ * return the status for this device
+ */
+
+static int
+wti_mpc_status(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+ char *ident;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ if ((ident = MPC_read(ad->sptr, OID_IDENT, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read ident.", __FUNCTION__);
+ return (S_ACCESS);
+ }
+
+ /* status ok */
+ return (S_OK);
+}
+
+/*
+ * return the list of hosts configured for this device
+ */
+
+static char **
+wti_mpc_hostlist(StonithPlugin * s)
+{
+ char **hl;
+ struct pluginDevice *ad;
+ int j, h, num_outlets;
+ char *outlet_name;
+ char objname[MAX_STRING];
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ /* allocate memory for array of up to NUM_OUTLETS strings */
+ if ((hl = (char **)MALLOC((ad->num_outlets+1) * sizeof(char *))) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+ /* clear hostlist array */
+ memset(hl, 0, (ad->num_outlets + 1) * sizeof(char *));
+ num_outlets = 0;
+
+ /* read NUM_OUTLETS values and put them into hostlist array */
+ for (j = 0; j < ad->num_outlets; ++j) {
+
+ /* prepare objname */
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,j+1);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,j+1);
+ break;
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: using %s as group names oid", __FUNCTION__, objname);
+ }
+
+ /* read outlet name */
+ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR)) ==
+ NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, j+1);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+
+ /* Check whether the host is already listed */
+ for (h = 0; h < num_outlets; ++h) {
+ if (strcasecmp(hl[h],outlet_name) == 0)
+ break;
+ }
+
+ if (h >= num_outlets) {
+ /* put outletname in hostlist */
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: added %s to hostlist."
+ , __FUNCTION__, outlet_name);
+ }
+
+ if ((hl[num_outlets] = STRDUP(outlet_name)) == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ stonith_free_hostlist(hl);
+ hl = NULL;
+ return (hl);
+ }
+ strdown(hl[num_outlets]);
+ num_outlets++;
+ }
+ }
+
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: %d unique hosts connected to %d outlets."
+ , __FUNCTION__, num_outlets, j);
+ }
+ /* return list */
+ return (hl);
+}
+
+/*
+ * reset the host
+ */
+
+static int
+wti_mpc_reset_req(StonithPlugin * s, int request, const char *host)
+{
+ struct pluginDevice *ad;
+ char objname[MAX_STRING];
+ char value[MAX_STRING];
+ char *outlet_name;
+ int req_oid = OUTLET_REBOOT;
+ int outlet;
+ int found_outlet=-1;
+
+ DEBUGCALL;
+
+ ERRIFNOTCONFIGED(s, S_OOPS);
+
+ ad = (struct pluginDevice *) s;
+
+ /* read max. as->num_outlets values */
+ for (outlet = 1; outlet <= ad->num_outlets; outlet++) {
+
+ /* prepare objname */
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,outlet);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,outlet);
+ break;
+ }
+
+ /* read outlet name */
+ if ((outlet_name = MPC_read(ad->sptr, objname, ASN_OCTET_STR))
+ == NULL) {
+ LOG(PIL_CRIT, "%s: cannot read name for outlet %d."
+ , __FUNCTION__, outlet);
+ return (S_ACCESS);
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found outlet: %s.", __FUNCTION__, outlet_name);
+ }
+
+ /* found one */
+ if (strcasecmp(outlet_name, host) == 0) {
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: found %s at outlet %d."
+ , __FUNCTION__, host, outlet);
+ }
+
+ /* Ok, stop iterating over host list */
+ found_outlet=outlet;
+ break;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: outlet: %i.", __FUNCTION__, outlet);
+ }
+
+ /* host not found in outlet names */
+ if (found_outlet == -1) {
+ LOG(PIL_CRIT, "%s: no active outlet for '%s'.", __FUNCTION__, host);
+ return (S_BADHOST);
+ }
+
+
+ /* choose the OID for the stonith request */
+ switch (request) {
+ case ST_POWERON:
+ req_oid = OUTLET_ON;
+ break;
+ case ST_POWEROFF:
+ req_oid = OUTLET_OFF;
+ break;
+ case ST_GENERIC_RESET:
+ req_oid = OUTLET_REBOOT;
+ break;
+ default: break;
+ }
+
+ /* Turn them all off */
+
+ /* prepare objnames */
+
+ switch (ad->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_STATE_V3,found_outlet);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_STATE_V1,found_outlet);
+ break;
+ }
+
+ snprintf(value, MAX_STRING, "%i", req_oid);
+
+ /* send reboot cmd */
+ if (!MPC_write(ad->sptr, objname, 'i', value)) {
+ LOG(PIL_CRIT
+ , "%s: cannot send reboot command for outlet %d."
+ , __FUNCTION__, found_outlet);
+ return (S_RESETFAIL);
+ }
+
+ return (S_OK);
+}
+
+/*
+ * Get the configuration parameter names.
+ */
+
+static const char * const *
+wti_mpc_get_confignames(StonithPlugin * s)
+{
+ static const char * ret[] = {ST_IPADDR, ST_PORT, ST_COMMUNITY, ST_MIBVERSION, NULL};
+ return ret;
+}
+
+/*
+ * Set the configuration parameters.
+ */
+
+static int
+wti_mpc_set_config(StonithPlugin * s, StonithNVpair * list)
+{
+ struct pluginDevice* sd = (struct pluginDevice *)s;
+ int rc;
+ char * i;
+ int mo;
+ char objname[MAX_STRING];
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PORT, NULL}
+ , {ST_COMMUNITY, NULL}
+ , {ST_MIBVERSION, NULL}
+ , {NULL, NULL}
+ };
+
+ DEBUGCALL;
+ ERRIFWRONGDEV(s,S_INVAL);
+ if (sd->sp.isconfigured) {
+ return S_OOPS;
+ }
+
+ if ((rc=OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ sd->hostname = namestocopy[0].s_value;
+ sd->port = atoi(namestocopy[1].s_value);
+ PluginImports->mfree(namestocopy[1].s_value);
+ sd->community = namestocopy[2].s_value;
+ sd->mib_version = atoi(namestocopy[3].s_value);
+ PluginImports->mfree(namestocopy[3].s_value);
+
+ /* try to resolve the hostname/ip-address */
+ if (gethostbyname(sd->hostname) != NULL) {
+ /* init snmp library */
+ init_snmp("wti_mpc");
+
+ /* now try to get a snmp session */
+ if ((sd->sptr = MPC_open(sd->hostname, sd->port, sd->community)) != NULL) {
+
+ /* ok, get the number of groups from the mpc */
+ sd->num_outlets=0;
+ /* We scan goup names table starting from 1 to MAX_OUTLETS */
+ /* and increase num_outlet counter on every group entry with name */
+ /* first entry without name is the mark of the end of the group table */
+ for (mo=1;mo<MAX_OUTLETS;mo++) {
+ switch (sd->mib_version) {
+ case 3:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V3,mo);
+ break;
+ case 1:
+ default:
+ snprintf(objname,MAX_STRING,OID_GROUP_NAMES_V1,mo);
+ break;
+ }
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: used for groupTable retrieval: %s."
+ , __FUNCTION__, objname);
+ }
+
+ if ((i = MPC_read(sd->sptr, objname, ASN_OCTET_STR)) == NULL) {
+ LOG(PIL_CRIT
+ , "%s: cannot read number of outlets."
+ , __FUNCTION__);
+ return (S_ACCESS);
+ }
+ if (strlen(i)) {
+ /* store the number of outlets */
+ sd->num_outlets++;
+ } else {
+ break;
+ }
+ }
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: number of outlets: %i."
+ , __FUNCTION__, sd->num_outlets );
+ }
+
+ /* Everything went well */
+ return (S_OK);
+ }else{
+ LOG(PIL_CRIT, "%s: cannot create snmp session."
+ , __FUNCTION__);
+ }
+ }else{
+ LOG(PIL_CRIT, "%s: cannot resolve hostname '%s', h_errno %d."
+ , __FUNCTION__, sd->hostname, h_errno);
+ }
+
+ /* not a valid config */
+ return (S_BADCONFIG);
+}
+
+/*
+ * get info about the stonith device
+ */
+
+static const char *
+wti_mpc_getinfo(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice *ad;
+ const char *ret = NULL;
+
+ DEBUGCALL;
+
+ ERRIFWRONGDEV(s, NULL);
+
+ ad = (struct pluginDevice *) s;
+
+ switch (reqtype) {
+ case ST_DEVICEID:
+ ret = ad->idinfo;
+ break;
+
+ case ST_DEVICENAME:
+ ret = ad->hostname;
+ break;
+
+ case ST_DEVICEDESCR:
+ ret = "WTI MPC (via SNMP)\n"
+ "The WTI MPC can accept multiple simultaneous SNMP clients";
+ break;
+
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+
+ case ST_CONF_XML: /* XML metadata */
+ ret = apcmastersnmpXML;
+ break;
+
+ }
+ return ret;
+}
+
+
+/*
+ * APC StonithPlugin destructor...
+ */
+
+static void
+wti_mpc_destroy(StonithPlugin * s)
+{
+ struct pluginDevice *ad;
+
+ DEBUGCALL;
+
+ VOIDERRIFWRONGDEV(s);
+
+ ad = (struct pluginDevice *) s;
+
+ ad->pluginid = NOTpluginID;
+
+ /* release snmp session */
+ if (ad->sptr != NULL) {
+ snmp_close(ad->sptr);
+ ad->sptr = NULL;
+ }
+
+ /* reset defaults */
+ if (ad->hostname != NULL) {
+ PluginImports->mfree(ad->hostname);
+ ad->hostname = NULL;
+ }
+ if (ad->community != NULL) {
+ PluginImports->mfree(ad->community);
+ ad->community = NULL;
+ }
+ ad->num_outlets = 0;
+
+ PluginImports->mfree(ad);
+}
+
+/*
+ * Create a new APC StonithPlugin device. Too bad this function can't be
+ * static
+ */
+
+static StonithPlugin *
+wti_mpc_new(const char *subplugin)
+{
+ struct pluginDevice *ad = ST_MALLOCT(struct pluginDevice);
+
+ DEBUGCALL;
+
+ /* no memory for stonith-object */
+ if (ad == NULL) {
+ LOG(PIL_CRIT, "%s: out of memory.", __FUNCTION__);
+ return (NULL);
+ }
+
+ /* clear stonith-object */
+ memset(ad, 0, sizeof(*ad));
+
+ /* set defaults */
+ ad->pluginid = pluginid;
+ ad->sptr = NULL;
+ ad->hostname = NULL;
+ ad->community = NULL;
+ ad->mib_version=1;
+ ad->idinfo = DEVICE;
+ ad->sp.s_ops = &wti_mpcOps;
+
+ /* return the object */
+ return (&(ad->sp));
+}
diff --git a/lib/plugins/stonith/wti_nps.c b/lib/plugins/stonith/wti_nps.c
new file mode 100644
index 0000000..f0b81f7
--- /dev/null
+++ b/lib/plugins/stonith/wti_nps.c
@@ -0,0 +1,813 @@
+/*
+ *
+ * Copyright 2001 Mission Critical Linux, Inc.
+ *
+ * All Rights Reserved.
+ */
+/*
+ * Stonith module for WTI Network Power Switch Devices (NPS-xxx)
+ * Also supports the WTI Telnet Power Switch Devices (TPS-xxx)
+ *
+ * Copyright 2001 Mission Critical Linux, Inc.
+ * author: mike ledoux <mwl@mclinux.com>
+ * author: Todd Wheeling <wheeling@mclinux.com>
+ * Mangled by Zhaokai <zhaokai@cn.ibm.com>, IBM, 2005
+ * Further hurt by Lon <lhh@redhat.com>, Red Hat, 2005
+ *
+ * Supported WTI devices:
+ * NPS-115
+ * NPS-230
+ * IPS-15
+ * IPS-800
+ * IPS-800-CE
+ * NBB-1600
+ * NBB-1600-CE
+ * TPS-2
+ *
+ * Based strongly on original code from baytech.c by Alan Robertson.
+ *
+ * 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
+ *
+ */
+
+/* Observations/Notes
+ *
+ * 1. The WTI Network Power Switch, unlike the BayTech network power switch,
+ * accpets only one (telnet) connection/session at a time. When one
+ * session is active, any subsequent attempt to connect to the NPS will
+ * result in a connection refused/closed failure. In a cluster environment
+ * or other environment utilizing polling/monitoring of the NPS
+ * (from multiple nodes), this can clearly cause problems. Obviously the
+ * more nodes and the shorter the polling interval, the more frequently such
+ * errors/collisions may occur.
+ *
+ * 2. We observed that on busy networks where there may be high occurances
+ * of broadcasts, the NPS became unresponsive. In some
+ * configurations this necessitated placing the power switch onto a
+ * private subnet.
+ */
+
+#include <lha_internal.h>
+#define DEVICE "WTI Network Power Switch"
+
+#define DOESNT_USE_STONITHKILLCOMM 1
+
+#include "stonith_plugin_common.h"
+
+#define PIL_PLUGIN wti_nps
+#define PIL_PLUGIN_S "wti_nps"
+#define PIL_PLUGINLICENSE LICENSE_LGPL
+#define PIL_PLUGINLICENSEURL URL_LGPL
+#define MAX_WTIPLUGINID 256
+
+#include <pils/plugin.h>
+
+#include "stonith_signal.h"
+
+static StonithPlugin * wti_nps_new(const char *);
+static void wti_nps_destroy(StonithPlugin *);
+static const char * const * wti_nps_get_confignames(StonithPlugin *);
+static int wti_nps_set_config(StonithPlugin * , StonithNVpair * );
+static const char * wti_nps_get_info(StonithPlugin * s, int InfoType);
+static int wti_nps_status(StonithPlugin * );
+static int wti_nps_reset_req(StonithPlugin * s, int request, const char * host);
+static char ** wti_nps_hostlist(StonithPlugin *);
+
+static struct stonith_ops wti_npsOps ={
+ wti_nps_new, /* Create new STONITH object */
+ wti_nps_destroy, /* Destroy STONITH object */
+ wti_nps_get_info, /* Return STONITH info string */
+ wti_nps_get_confignames,/* Return configration parameters */
+ wti_nps_set_config, /* set configration */
+ wti_nps_status, /* Return STONITH device status */
+ wti_nps_reset_req, /* Request a reset */
+ wti_nps_hostlist, /* Return list of supported hosts */
+};
+
+PIL_PLUGIN_BOILERPLATE2("1.0", Debug)
+static const PILPluginImports* PluginImports;
+static PILPlugin* OurPlugin;
+static PILInterface* OurInterface;
+static StonithImports* OurImports;
+static void* interfprivate;
+
+#include "stonith_expect_helpers.h"
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports);
+
+PIL_rc
+PIL_PLUGIN_INIT(PILPlugin*us, const PILPluginImports* imports)
+{
+ /* Force the compiler to do a little type checking */
+ (void)(PILPluginInitFun)PIL_PLUGIN_INIT;
+
+ PluginImports = imports;
+ OurPlugin = us;
+
+ /* Register ourself as a plugin */
+ imports->register_plugin(us, &OurPIExports);
+
+ /* Register our interface implementation */
+ return imports->register_interface(us, PIL_PLUGINTYPE_S
+ , PIL_PLUGIN_S
+ , &wti_npsOps
+ , NULL /*close */
+ , &OurInterface
+ , (void*)&OurImports
+ , &interfprivate);
+}
+
+/*
+ * I have a NPS-110. This code has been tested with this switch.
+ * (Tested with NPS-230 and TPS-2 by lmb)
+ */
+
+struct pluginDevice {
+ StonithPlugin sp;
+ const char * pluginid;
+ const char * idinfo;
+ pid_t pid;
+ int rdfd;
+ int wrfd;
+ char * device;
+ char * passwd;
+};
+
+static const char * pluginid = "WTINPS-Stonith";
+static const char * NOTnpsid = "WTINPS device has been destroyed";
+
+#include "stonith_config_xml.h"
+
+static const char *wti_npsXML =
+ XML_PARAMETERS_BEGIN
+ XML_IPADDR_PARM
+ XML_PASSWD_PARM
+ XML_PARAMETERS_END;
+
+
+/*
+ * Different expect strings that we get from the WTI
+ * Network Power Switch
+ */
+
+#define WTINPSSTR " Power Switch"
+#define WTINBBSTR "Boot Bar"
+
+static struct Etoken password[] = { {"Password:", 0, 0}, {NULL,0,0}};
+static struct Etoken Prompt[] = { {"PS>", 0, 0}
+ , {"IPS>", 0, 0}
+ , {"BB>", 0, 0}
+ , {NULL,0,0}};
+static struct Etoken LoginOK[] = { {WTINPSSTR, 0, 0}
+ , {WTINBBSTR, 0, 0}
+ , {"Invalid password", 1, 0}
+ , {NULL,0,0} };
+static struct Etoken Separator[] = { {"-----+", 0, 0} ,{NULL,0,0}};
+
+/* We may get a notice about rebooting, or a request for confirmation */
+static struct Etoken Processing[] = { {"rocessing - please wait", 0, 0}
+ , {"(Y/N):", 1, 0}
+ , {NULL,0,0}};
+
+static int NPS_connect_device(struct pluginDevice * nps);
+static int NPSLogin(struct pluginDevice * nps);
+static int NPSNametoOutlet(struct pluginDevice*, const char * name, char **outlets);
+static int NPSReset(struct pluginDevice*, char * outlets, const char * rebootid);
+static int NPSLogout(struct pluginDevice * nps);
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int NPS_onoff(struct pluginDevice*, const char * outlets, const char * unitid
+, int request);
+#endif
+
+/* Attempt to login up to 20 times... */
+static int
+NPSRobustLogin(struct pluginDevice * nps)
+{
+ int rc = S_OOPS;
+ int j = 0;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ for ( ; ; ) {
+ if (NPS_connect_device(nps) == S_OK) {
+ rc = NPSLogin(nps);
+ if (rc == S_OK) {
+ break;
+ }
+ }
+ if ((++j) == 20) {
+ break;
+ }
+ else {
+ sleep(1);
+ }
+ }
+
+ return rc;
+}
+
+/* Login to the WTI Network Power Switch (NPS) */
+static int
+NPSLogin(struct pluginDevice * nps)
+{
+ char IDinfo[128];
+ char * idptr = IDinfo;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Look for the unit type info */
+ if (EXPECT_TOK(nps->rdfd, password, 2, IDinfo
+ , sizeof(IDinfo), Debug) < 0) {
+ LOG(PIL_CRIT, "No initial response from %s.", nps->idinfo);
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ idptr += strspn(idptr, WHITESPACE);
+ /*
+ * We should be looking at something like this:
+ * Enter Password:
+ */
+
+ SEND(nps->wrfd, nps->passwd);
+ SEND(nps->wrfd, "\r");
+ /* Expect "Network Power Switch vX.YY" */
+
+ switch (StonithLookFor(nps->rdfd, LoginOK, 5)) {
+
+ case 0: /* Good! */
+ LOG(PIL_INFO, "Successful login to %s.", nps->idinfo);
+ break;
+
+ case 1: /* Uh-oh - bad password */
+ LOG(PIL_CRIT, "Invalid password for %s.", nps->idinfo);
+ return(S_ACCESS);
+
+ default:
+ return(errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS);
+ }
+ return(S_OK);
+}
+
+/* Log out of the WTI NPS */
+
+static int
+NPSLogout(struct pluginDevice* nps)
+{
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ /*
+ SEND(nps->wrfd, "/h\r");
+ */
+ /* Expect "PS>" */
+ rc = StonithLookFor(nps->rdfd, Prompt, 5);
+
+ /* "/x" is Logout, "/x,y" auto-confirms */
+ SEND(nps->wrfd, "/x,y\r");
+
+ close(nps->wrfd);
+ close(nps->rdfd);
+ nps->wrfd = nps->rdfd = -1;
+
+ return(rc >= 0 ? S_OK : (errno == ETIMEDOUT ? S_TIMEOUT : S_OOPS));
+}
+
+/* Reset (power-cycle) the given outlets */
+static int
+NPSReset(struct pluginDevice* nps, char * outlets, const char * rebootid)
+{
+ char unum[32];
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* Send REBOOT command for given outlets */
+ snprintf(unum, sizeof(unum), "/BOOT %s,y\r", outlets);
+ SEND(nps->wrfd, unum);
+
+ /* Expect "Processing "... or "(Y/N)" (if confirmation turned on) */
+
+ retry:
+ switch (StonithLookFor(nps->rdfd, Processing, 5)) {
+ case 0: /* Got "Processing" Do nothing */
+ break;
+
+ case 1: /* Got that annoying command confirmation :-( */
+ SEND(nps->wrfd, "Y\r");
+ goto retry;
+
+ default:
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+ LOG(PIL_INFO, "Host is being rebooted: %s", rebootid);
+
+ /* Expect "PS>" */
+ if (StonithLookFor(nps->rdfd, Prompt, 60) < 0) {
+ return(errno == ETIMEDOUT ? S_RESETFAIL : S_OOPS);
+ }
+
+ /* All Right! Power is back on. Life is Good! */
+
+ LOG(PIL_INFO, "Power restored to host: %s", rebootid);
+ SEND(nps->wrfd, "/h\r");
+ return(S_OK);
+}
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+static int
+NPS_onoff(struct pluginDevice* nps, const char * outlets, const char * unitid, int req)
+{
+ char unum[32];
+
+ const char * onoff = (req == ST_POWERON ? "/On" : "/Off");
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ /* Send "/h" help command and expect prompt back */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* Send ON/OFF command for given outlet */
+ snprintf(unum, sizeof(unum), "%s %s,y\r", onoff, outlets);
+ SEND(nps->wrfd, unum);
+
+ /* Expect "Processing"... or "(Y/N)" (if confirmation turned on) */
+
+ if (StonithLookFor(nps->rdfd, Processing, 5) == 1) {
+ /* They've turned on that annoying command confirmation :-( */
+ SEND(nps->wrfd, "Y\r");
+ }
+ EXPECT(nps->rdfd, Prompt, 60);
+
+ /* All Right! Command done. Life is Good! */
+ LOG(PIL_INFO, "Power to NPS outlet(s) %s turned %s", outlets, onoff);
+
+ SEND(nps->wrfd, "/h\r");
+ return(S_OK);
+}
+#endif /* defined(ST_POWERON) && defined(ST_POWEROFF) */
+
+/*
+ * Map the given host name into an (AC) Outlet number on the power strip
+ */
+
+static int
+NPSNametoOutlet(struct pluginDevice* nps, const char * name, char **outlets)
+{
+ char NameMapping[128];
+ int sockno;
+ char sockname[32];
+ char buf[32];
+ int left = 17;
+ int ret = -1;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ if ((*outlets = (char *)MALLOC(left*sizeof(char))) == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(-1);
+ }
+
+ strncpy(*outlets, "", left);
+ left = left - 1; /* ensure terminating '\0' */
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(nps->wrfd, "/s\r");
+
+ /* Expect: "-----+" so we can skip over it... */
+ EXPECT(nps->rdfd, Separator, 5);
+
+ do {
+ NameMapping[0] = EOS;
+ SNARF(nps->rdfd, NameMapping, 5);
+
+ if (sscanf(NameMapping
+ , "%d | %16c",&sockno, sockname) == 2) {
+
+ char * last = sockname+16;
+ *last = EOS;
+ --last;
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (strncasecmp(name, sockname, 16) == 0) {
+ ret = sockno;
+ snprintf(buf, sizeof(buf), "%d ", sockno);
+ strncat(*outlets, buf, left);
+ left = left - strlen(buf);
+ }
+ }
+ } while (strlen(NameMapping) > 2 && left > 0);
+
+ return(ret);
+}
+
+static int
+wti_nps_status(StonithPlugin *s)
+{
+ struct pluginDevice* nps;
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = NPSRobustLogin(nps) != S_OK)) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ return(rc);
+ }
+
+ /* Send "/h" help command and expect back prompt */
+ SEND(nps->wrfd, "/h\r");
+ /* Expect "PS>" */
+ EXPECT(nps->rdfd, Prompt, 5);
+
+ return(NPSLogout(nps));
+}
+
+/*
+ * Return the list of hosts (outlet names) for the devices on this NPS unit
+ */
+
+static char **
+wti_nps_hostlist(StonithPlugin *s)
+{
+ char NameMapping[128];
+ char* NameList[64];
+ unsigned int numnames = 0;
+ char ** ret = NULL;
+ struct pluginDevice* nps;
+ unsigned int i;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,NULL);
+
+ nps = (struct pluginDevice*) s;
+ if (NPSRobustLogin(nps) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ return(NULL);
+ }
+
+ /* Expect "PS>" */
+ NULLEXPECT(nps->rdfd, Prompt, 5);
+
+ /* The status command output contains mapping of hosts to outlets */
+ SEND(nps->wrfd, "/s\r");
+
+ /* Expect: "-----" so we can skip over it... */
+ NULLEXPECT(nps->rdfd, Separator, 5);
+ NULLEXPECT(nps->rdfd, CRNL, 5);
+
+ /* Looks Good! Parse the status output */
+
+ do {
+ int sockno;
+ char sockname[64];
+ NameMapping[0] = EOS;
+ NULLSNARF(nps->rdfd, NameMapping, 5);
+ if (sscanf(NameMapping
+ , "%d | %16c",&sockno, sockname) == 2) {
+
+ char * last = sockname+16;
+ char * nm;
+ *last = EOS;
+ --last;
+
+ /* Strip off trailing blanks */
+ for(; last > sockname; --last) {
+ if (*last == ' ') {
+ *last = EOS;
+ }else{
+ break;
+ }
+ }
+ if (numnames >= DIMOF(NameList)-1) {
+ break;
+ }
+ if (!strcmp(sockname,"(undefined)") ||
+ !strcmp(sockname,"---")) {
+ /* lhh - skip undefined */
+ continue;
+ }
+ if ((nm = STRDUP(sockname)) == NULL) {
+ goto out_of_memory;
+ }
+ strdown(nm);
+ NameList[numnames] = nm;
+ ++numnames;
+ NameList[numnames] = NULL;
+ }
+ } while (strlen(NameMapping) > 2);
+
+ if (numnames >= 1) {
+ ret = (char **)MALLOC((numnames+1)*sizeof(char*));
+ if (ret == NULL) {
+ goto out_of_memory;
+ }else{
+ memset(ret, 0, (numnames+1)*sizeof(char*));
+ memcpy(ret, NameList, (numnames+1)*sizeof(char*));
+ }
+ }
+ (void)NPSLogout(nps);
+
+ return(ret);
+
+out_of_memory:
+ LOG(PIL_CRIT, "out of memory");
+ for (i=0; i<numnames; i++) {
+ FREE(NameList[i]);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Connect to the given NPS device. We should add serial support here
+ * eventually...
+ */
+static int
+NPS_connect_device(struct pluginDevice * nps)
+{
+ int fd = OurImports->OpenStreamSocket(nps->device
+ , TELNET_PORT, TELNET_SERVICE);
+
+ if (fd < 0) {
+ return(S_OOPS);
+ }
+ nps->rdfd = nps->wrfd = fd;
+ return(S_OK);
+}
+
+/*
+ * Reset the given host on this Stonith device.
+ */
+static int
+wti_nps_reset_req(StonithPlugin * s, int request, const char * host)
+{
+ int rc = 0;
+ int lorc = 0;
+ struct pluginDevice* nps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s:called.", __FUNCTION__);
+ }
+
+ ERRIFNOTCONFIGED(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = NPSRobustLogin(nps)) != S_OK) {
+ LOG(PIL_CRIT, "Cannot log into %s.", nps->idinfo);
+ }else{
+ char *outlets;
+ int noutlet;
+
+ outlets = NULL;
+ noutlet = NPSNametoOutlet(nps, host, &outlets);
+
+ if (noutlet < 1) {
+ LOG(PIL_WARN, "%s doesn't control host [%s]"
+ , nps->device, host);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ return(S_BADHOST);
+ }
+ switch(request) {
+
+#if defined(ST_POWERON) && defined(ST_POWEROFF)
+ case ST_POWERON:
+ case ST_POWEROFF:
+ rc = NPS_onoff(nps, outlets, host, request);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+#endif
+ case ST_GENERIC_RESET:
+ rc = NPSReset(nps, outlets, host);
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+ default:
+ rc = S_INVAL;
+ if (outlets != NULL) {
+ FREE(outlets);
+ outlets = NULL;
+ }
+ break;
+ }
+ }
+
+ lorc = NPSLogout(nps);
+ return(rc != S_OK ? rc : lorc);
+}
+
+/*
+ * Parse the information in the given string,
+ * and stash it away...
+ */
+static int
+wti_nps_set_config(StonithPlugin * s, StonithNVpair *list)
+{
+ struct pluginDevice* nps;
+ StonithNamesToGet namestocopy [] =
+ { {ST_IPADDR, NULL}
+ , {ST_PASSWD, NULL}
+ , {NULL, NULL}
+ };
+ int rc;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.\n", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,S_OOPS);
+
+ nps = (struct pluginDevice*) s;
+
+ if ((rc = OurImports->CopyAllValues(namestocopy, list)) != S_OK) {
+ return rc;
+ }
+ nps->device = namestocopy[0].s_value;
+ nps->passwd = namestocopy[1].s_value;
+ return S_OK;
+}
+
+
+/*
+ * Return the Stonith plugin configuration parameter
+ *
+ */
+static const char * const *
+wti_nps_get_confignames(StonithPlugin * p)
+{
+ static const char * names[] = { ST_IPADDR , ST_PASSWD , NULL};
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+ return names;
+}
+
+/*
+ * Get info about the stonith device
+ *
+ */
+static const char *
+wti_nps_get_info(StonithPlugin * s, int reqtype)
+{
+ struct pluginDevice* nps;
+ const char * ret;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ ERRIFWRONGDEV(s,NULL);
+
+ /*
+ * We look in the ST_TEXTDOMAIN catalog for our messages
+ */
+ nps = (struct pluginDevice *)s;
+
+ switch (reqtype) {
+
+ case ST_DEVICEID:
+ ret = nps->idinfo;
+ break;
+ case ST_DEVICENAME:
+ ret = nps->device;
+ break;
+ case ST_DEVICEDESCR:
+ ret = "Western Telematic (WTI) Network Power Switch Devices (NPS-xxx)\n"
+ "Also supports the WTI Telnet Power Switch Devices (TPS-xxx)\n"
+ "NOTE: The WTI Network Power Switch, accepts only "
+ "one (telnet) connection/session at a time.";
+ break;
+ case ST_DEVICEURL:
+ ret = "http://www.wti.com/";
+ break;
+ case ST_CONF_XML: /* XML metadata */
+ ret = wti_npsXML;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * WTI NPS Stonith destructor...
+ */
+static void
+wti_nps_destroy(StonithPlugin *s)
+{
+ struct pluginDevice* nps;
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ VOIDERRIFWRONGDEV(s);
+
+ nps = (struct pluginDevice *)s;
+
+ nps->pluginid = NOTnpsid;
+ if (nps->rdfd >= 0) {
+ close(nps->rdfd);
+ nps->rdfd = -1;
+ }
+ if (nps->wrfd >= 0) {
+ close(nps->wrfd);
+ nps->wrfd = -1;
+ }
+ if (nps->device != NULL) {
+ FREE(nps->device);
+ nps->device = NULL;
+ }
+ if (nps->passwd != NULL) {
+ FREE(nps->passwd);
+ nps->passwd = NULL;
+ }
+ FREE(nps);
+}
+
+/* Create a new BayTech Stonith device. */
+
+static StonithPlugin *
+wti_nps_new(const char *subplugin)
+{
+ struct pluginDevice* nps = ST_MALLOCT(struct pluginDevice);
+
+ if (Debug) {
+ LOG(PIL_DEBUG, "%s: called.", __FUNCTION__);
+ }
+
+ if (nps == NULL) {
+ LOG(PIL_CRIT, "out of memory");
+ return(NULL);
+ }
+ memset(nps, 0, sizeof(*nps));
+ nps->pluginid = pluginid;
+ nps->pid = -1;
+ nps->rdfd = -1;
+ nps->wrfd = -1;
+ nps->device = NULL;
+ nps->passwd = NULL;
+ nps->idinfo = DEVICE;
+ nps->sp.s_ops = &wti_npsOps;
+
+ return &(nps->sp);
+}
+
diff --git a/lib/stonith/Makefile.am b/lib/stonith/Makefile.am
new file mode 100644
index 0000000..429e1d3
--- /dev/null
+++ b/lib/stonith/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Stonith: Shoot The Node In The Head
+#
+# Copyright (C) 2001 Alan Robertson
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+## include files
+
+## binaries
+sbin_PROGRAMS = stonith meatclient
+
+stonith_SOURCES = main.c
+
+stonith_LDADD = libstonith.la $(top_builddir)/lib/pils/libpils.la $(GLIBLIB) \
+ $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/clplumbing/libplumbgpl.la
+stonith_LDFLAGS = @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@ @LIBADD_INTL@
+
+meatclient_SOURCES = meatclient.c
+meatclient_LDADD = $(GLIBLIB) libstonith.la
+
+## libraries
+
+lib_LTLIBRARIES = libstonith.la
+
+libstonith_la_SOURCES = expect.c stonith.c st_ttylock.c
+libstonith_la_LDFLAGS = -version-info 1:0:0
+libstonith_la_LIBADD = $(top_builddir)/lib/pils/libpils.la \
+ $(top_builddir)/replace/libreplace.la \
+ $(GLIBLIB)
+
+helperdir = $(datadir)/$(PACKAGE_NAME)
+helper_SCRIPTS = ha_log.sh
+
+EXTRA_DIST = $(helper_SCRIPTS)
diff --git a/lib/stonith/README b/lib/stonith/README
new file mode 100644
index 0000000..6b98ef9
--- /dev/null
+++ b/lib/stonith/README
@@ -0,0 +1,31 @@
+The STONITH module (a.k.a. STOMITH) provides an extensible interface
+for remotely powering down a node in the cluster. The idea is quite simple:
+When the software running on one machine wants to make sure another
+machine in the cluster is not using a resource, pull the plug on the other
+machine. It's simple and reliable, albiet admittedly brutal.
+
+Here's an example command line invocation used to power off a machine
+named 'nodeb'. The parameters are dependent on the type of device you
+are using for this capability.
+
+stonith -t rps10 -p "/dev/ttyS5 nodeb 0 " nodeb
+
+Currently supported devices:
+
+ apcsmart: APCSmart (tested with 2 old 900XLI)
+ baytech: Baytech RPC5
+ meatware: Alerts an operator to manually turn off a device.
+ nw_rpc100s: Micro Energetics Night/Ware RPC100S
+ rps10: Western Telematics RPS10
+ vacm_stonith: VA Linux Cluster Manager (see README.vacm)
+
+
+To see the parameter syntax for a module, run the 'stonith' command and omit the
+-p parameter. For example:
+
+$ /usr/sbin/stonith -t rps10 test
+
+stonith: Invalid config file for rps10 device.
+stonith: Config file syntax: <serial_device> <node> <outlet> [ <node> <outlet> [...] ]
+All tokens are white-space delimited.
+Blank lines and lines beginning with # are ignored
diff --git a/lib/stonith/expect.c b/lib/stonith/expect.c
new file mode 100644
index 0000000..bb1f818
--- /dev/null
+++ b/lib/stonith/expect.c
@@ -0,0 +1,539 @@
+/*
+ * Simple expect module for the STONITH library
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stonith/st_ttylock.h>
+#include <clplumbing/longclock.h>
+#define ENABLE_PIL_DEFS_PRIVATE
+#include <pils/plugin.h>
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+extern PILPluginUniv* StonithPIsys;
+
+#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
+#define DEBUG(args...) LOG(PIL_DEBUG, args)
+#undef DEBUG
+#define DEBUG(args...) PILCallLog(StonithPIsys->imports->log, PIL_DEBUG, args)
+#define MALLOC StonithPIsys->imports->alloc
+#define REALLOC StonithPIsys->imports->mrealloc
+#define STRDUP StonithPIsys->imports->mstrdup
+#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
+
+#ifdef TIMES_ALLOWS_NULL_PARAM
+# define TIMES_PARAM NULL
+#else
+ static struct tms dummy_longclock_tms_struct;
+# define TIMES_PARAM &dummy_longclock_tms_struct
+#endif
+
+static unsigned long
+our_times(void) /* Make times(2) behave rationally on Linux */
+{
+ clock_t ret;
+#ifndef DISABLE_TIMES_KLUDGE
+ int save_errno = errno;
+
+ /*
+ * This code copied from clplumbing/longclock.c to avoid
+ * making STONITH depend on clplumbing. See it for an explanation
+ */
+
+ errno = 0;
+#endif /* DISABLE_TIMES_KLUDGE */
+
+ ret = times(TIMES_PARAM);
+
+#ifndef DISABLE_TIMES_KLUDGE
+ if (errno != 0) {
+ ret = (clock_t) (-errno);
+ }
+ errno = save_errno;
+#endif /* DISABLE_TIMES_KLUDGE */
+ return (unsigned long)ret;
+}
+
+/*
+ * Look for ('expect') any of a series of tokens in the input
+ * Return the token type for the given token or -1 on error.
+ */
+
+static int
+ExpectToken(int fd, struct Etoken * toklist, int to_secs, char * savebuf
+, int maxline, int Debug)
+{
+ unsigned long starttime;
+ unsigned long endtime;
+ int wraparound=0;
+ unsigned Hertz = sysconf(_SC_CLK_TCK);
+ int tickstousec = (1000000/Hertz);
+ unsigned long now;
+ unsigned long ticks;
+ int nchars = 1; /* reserve space for an EOS */
+ struct timeval tv;
+ char * buf = savebuf;
+
+ struct Etoken * this;
+
+ /* Figure out when to give up. Handle lbolt wraparound */
+
+ starttime = our_times();
+ ticks = (to_secs*Hertz);
+ endtime = starttime + ticks;
+
+ if (endtime < starttime) {
+ wraparound = 1;
+ }
+
+ if (buf) {
+ *buf = EOS;
+ }
+
+ for (this=toklist; this->string; ++this) {
+ this->matchto = 0;
+ }
+
+
+ while (now = our_times(),
+ (wraparound && (now > starttime || now <= endtime))
+ || (!wraparound && now <= endtime)) {
+
+ fd_set infds;
+ char ch;
+ unsigned long timeleft;
+ int retval;
+
+ timeleft = endtime - now;
+
+ tv.tv_sec = timeleft / Hertz;
+ tv.tv_usec = (timeleft % Hertz) * tickstousec;
+
+ if (tv.tv_sec == 0 && tv.tv_usec < tickstousec) {
+ /* Give 'em a little chance */
+ tv.tv_usec = tickstousec;
+ }
+
+ /* Watch our FD to see when it has input. */
+ FD_ZERO(&infds);
+ FD_SET(fd, &infds);
+
+ retval = select(fd+1, &infds, NULL, NULL, &tv);
+ if (retval <= 0) {
+ errno = ETIMEDOUT;
+ return(-1);
+ }
+ /* Whew! All that work just to read one character! */
+
+ if (read(fd, &ch, sizeof(ch)) <= 0) {
+ return(-1);
+ }
+ /* Save the text, if we can */
+ if (buf && nchars < maxline-1) {
+ *buf = ch;
+ ++buf;
+ *buf = EOS;
+ ++nchars;
+ }
+ if (Debug > 1) {
+ DEBUG("Got '%c'", ch);
+ }
+
+ /* See how this character matches our expect strings */
+
+ for (this=toklist; this->string; ++this) {
+
+ if (ch == this->string[this->matchto]) {
+
+ /* It matches the current token */
+
+ ++this->matchto;
+ if (this->string[this->matchto] == EOS){
+ /* Hallelujah! We matched */
+ if (Debug) {
+ DEBUG("Matched [%s] [%d]"
+ , this->string
+ , this->toktype);
+ if (savebuf) {
+ DEBUG("Saved [%s]"
+ , savebuf);
+ }
+ }
+ return(this->toktype);
+ }
+ }else{
+
+ /* It doesn't appear to match this token */
+
+ int curlen;
+ int nomatch=1;
+ /*
+ * If we already had a match (matchto is
+ * greater than zero), we look for a match
+ * of the tail of the pattern matched so far
+ * (with the current character) against the
+ * head of the pattern.
+ */
+
+ /*
+ * This is to make the string "aab" match
+ * the pattern "ab" correctly
+ * Painful, but nice to do it right.
+ */
+
+ for (curlen = (this->matchto)
+ ; nomatch && curlen >= 0
+ ; --curlen) {
+ const char * tail;
+ tail=(this->string)
+ + this->matchto
+ - curlen;
+
+ if (strncmp(this->string, tail
+ , curlen) == 0
+ && this->string[curlen] == ch) {
+
+ if (this->string[curlen+1]==EOS){
+ /* We matched! */
+ /* (can't happen?) */
+ return(this->toktype);
+ }
+ this->matchto = curlen+1;
+ nomatch=0;
+ }
+ }
+ if (nomatch) {
+ this->matchto = 0;
+ }
+ }
+ }
+ }
+ errno = ETIMEDOUT;
+ return(-1);
+}
+
+/*
+ * Start a process with its stdin and stdout redirected to pipes
+ * so the parent process can talk to it.
+ */
+static int
+StartProcess(const char * cmd, int * readfd, int * writefd)
+{
+ pid_t pid;
+ int wrpipe[2]; /* The pipe the parent process writes to */
+ /* (which the child process reads from) */
+ int rdpipe[2]; /* The pipe the parent process reads from */
+ /* (which the child process writes to) */
+
+ if (pipe(wrpipe) < 0) {
+ perror("cannot create pipe\n");
+ return(-1);
+ }
+ if (pipe(rdpipe) < 0) {
+ perror("cannot create pipe\n");
+ close(wrpipe[0]);
+ close(wrpipe[1]);
+ return(-1);
+ }
+ switch(pid=fork()) {
+
+ case -1: perror("cannot StartProcess cmd");
+ close(rdpipe[0]);
+ close(wrpipe[1]);
+ close(wrpipe[0]);
+ close(rdpipe[1]);
+ return(-1);
+
+ case 0: /* We are the child */
+
+ /* Redirect stdin */
+ close(0);
+ dup2(wrpipe[0], 0);
+ close(wrpipe[0]);
+ close(wrpipe[1]);
+
+ /* Redirect stdout */
+ close(1);
+ dup2(rdpipe[1], 1);
+ close(rdpipe[0]);
+ close(rdpipe[1]);
+#if defined(SCHED_OTHER) && !defined(ON_DARWIN)
+ {
+ /*
+ * Try and (re)set our scheduling to "normal"
+ * Sometimes our callers run in soft
+ * real-time mode. The program we exec might
+ * not be very well behaved - this is bad for
+ * operation in high-priority (soft real-time)
+ * mode. In particular, telnet is prone to
+ * going into infinite loops when killed.
+ */
+ struct sched_param sp;
+ memset(&sp, 0, sizeof(sp));
+ sp.sched_priority = 0;
+ sched_setscheduler(0, SCHED_OTHER, &sp);
+ }
+#endif
+ execlp("/bin/sh", "sh", "-c", cmd, (const char *)NULL);
+ perror("cannot exec shell!");
+ exit(1);
+
+ default: /* We are the parent */
+ *readfd = rdpipe[0];
+ close(rdpipe[1]);
+
+ *writefd = wrpipe[1];
+ close(wrpipe[0]);
+ return(pid);
+ }
+ /*NOTREACHED*/
+ return(-1);
+}
+
+static char **
+stonith_copy_hostlist(const char * const * hostlist)
+{
+ int hlleng = 1;
+ const char * const * here = hostlist;
+ char ** hret;
+ char ** ret;
+
+ for (here = hostlist; *here; ++here) {
+ ++hlleng;
+ }
+ ret = (char**)MALLOC(hlleng * sizeof(char *));
+ if (ret == NULL) {
+ return ret;
+ }
+
+ hret = ret;
+ for (here = hostlist; *here; ++here,++hret) {
+ *hret = STRDUP(*here);
+ if (*hret == NULL) {
+ stonith_free_hostlist(ret);
+ return NULL;
+ }
+ }
+ *hret = NULL;
+ return ret;
+}
+
+static char **
+StringToHostList(const char * s)
+{
+ const char * here;
+ int hlleng = 0;
+ char ** ret;
+ char ** hret;
+ const char * delims = " \t\n\f\r,";
+
+ /* Count the number of strings (words) in the result */
+ here = s;
+ while (*here != EOS) {
+ /* skip delimiters */
+ here += strspn(here, delims);
+ if (*here == EOS) {
+ break;
+ }
+ /* skip over substring proper... */
+ here += strcspn(here, delims);
+ ++hlleng;
+ }
+
+
+ /* Malloc space for the result string pointers */
+ ret = (char**)MALLOC((hlleng+1) * sizeof(char *));
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ hret = ret;
+ here = s;
+
+ /* Copy each substring into a separate string */
+ while (*here != EOS) {
+ int slen; /* substring length */
+
+ /* skip delimiters */
+ here += strspn(here, delims);
+ if (*here == EOS) {
+ break;
+ }
+ /* Compute substring length */
+ slen = strcspn(here, delims);
+ *hret = MALLOC((slen+1) * sizeof(char));
+ if (*hret == NULL) {
+ stonith_free_hostlist(hret);
+ return NULL;
+ }
+ /* Copy string (w/o EOS) */
+ memcpy(*hret, here, slen);
+ /* Add EOS to result string */
+ (*hret)[slen] = EOS;
+ strdown(*hret);
+ here += slen;
+ ++hret;
+ }
+ *hret = NULL;
+ return ret;
+}
+
+
+static const char *
+GetValue(StonithNVpair* parameters, const char * name)
+{
+ while (parameters->s_name) {
+ if (strcmp(name, parameters->s_name) == 0) {
+ return parameters->s_value;
+ }
+ ++parameters;
+ }
+ return NULL;
+}
+
+static int
+CopyAllValues(StonithNamesToGet* output, StonithNVpair * input)
+{
+ int j;
+ int rc;
+
+ for (j=0; output[j].s_name; ++j) {
+ const char * value = GetValue(input, output[j].s_name);
+ if (value == NULL) {
+ rc = S_INVAL;
+ output[j].s_value = NULL;
+ goto fail;
+ }
+ if ((output[j].s_value = STRDUP(value)) == NULL) {
+ rc = S_OOPS;
+ goto fail;
+ }
+ }
+ return S_OK;
+
+fail:
+ for (j=0; output[j].s_value; ++j) {
+ FREE(output[j].s_value);
+ }
+ return rc;
+}
+
+
+static int
+OpenStreamSocket(const char * host, int port, const char * service)
+{
+ union s_un {
+ struct sockaddr_in si4;
+ struct sockaddr_in6 si6;
+ }sockun;
+ int sock;
+ int addrlen = -1;
+
+
+ memset(&sockun, 0, sizeof(sockun));
+
+ if (inet_pton(AF_INET, host, (void*)&sockun.si4.sin_addr) < 0) {
+ sockun.si4.sin_family = AF_INET;
+ }else if (inet_pton(AF_INET6, host, (void*)&sockun.si6.sin6_addr)<0){
+ sockun.si6.sin6_family = AF_INET6;
+ }else{
+ struct hostent* hostp = gethostbyname(host);
+ if (hostp == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ sockun.si4.sin_family = hostp->h_addrtype;
+ memcpy(&sockun.si4.sin_addr, hostp->h_addr, hostp->h_length);
+ }
+ if ((sock = socket(sockun.si4.sin_family, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+ if (service != NULL) {
+ struct servent* se = getservbyname(service, "tcp");
+ if (se != NULL) {
+ /* We convert it back later... */
+ port = ntohs(se->s_port);
+ }
+ }
+ if (port <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ port = htons(port);
+ if (sockun.si6.sin6_family == AF_INET6) {
+ sockun.si6.sin6_port = port;
+ addrlen = sizeof(sockun.si6);
+ }else if (sockun.si4.sin_family == AF_INET) {
+ sockun.si4.sin_port = port;
+ addrlen = sizeof(sockun.si4);
+ }else{
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr*)(&sockun), addrlen)< 0){
+ int save = errno;
+ perror("connect() failed");
+ close(sock);
+ errno = save;
+ return -1;
+ }
+ return sock;
+}
+
+StonithImports stonithimports = {
+ ExpectToken,
+ StartProcess,
+ OpenStreamSocket,
+ GetValue,
+ CopyAllValues,
+ StringToHostList,
+ stonith_copy_hostlist,
+ stonith_free_hostlist,
+ st_ttylock,
+ st_ttyunlock
+};
diff --git a/lib/stonith/ha_log.sh b/lib/stonith/ha_log.sh
new file mode 100755
index 0000000..73093f0
--- /dev/null
+++ b/lib/stonith/ha_log.sh
@@ -0,0 +1,114 @@
+#!/bin/sh
+#
+#
+# ha_log.sh for stonith external plugins
+# (equivalent to ocf_log in ocf-shellfuncs in resource-agents)
+#
+# 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: @GLUE_BUILD_VERSION@
+
+PROG=`basename $0`
+
+: ${HA_DATEFMT=+"%b %d %T"}
+: ${HA_LOGD=yes}
+: ${HA_LOGTAG=""}
+: ${HA_LOGFACILITY=daemon}
+: ${HA_LOGFILE=""}
+: ${HA_DEBUGLOG=""}
+: ${HA_debug="0"}
+
+hadate() {
+ date "+$HA_DATEFMT"
+}
+
+level_pres() {
+ case "$1" in
+ crit) echo "CRIT";;
+ err|error) echo "ERROR";;
+ warn|warning) echo "WARN";;
+ notice) echo "notice";;
+ info) echo "info";;
+ debug) echo "debug";;
+ *)
+ ha_log err "$PROG: unrecognized loglevel: $1"
+ exit 1
+ ;;
+ esac
+}
+
+set_logtag() {
+ # add parent pid to the logtag
+ if [ "$HA_LOGTAG" ]; then
+ if [ -n "$CRM_meta_st_device_id" ]; then
+ HA_LOGTAG="$HA_LOGTAG($CRM_meta_st_device_id)[$PPID]"
+ else
+ HA_LOGTAG="$HA_LOGTAG[$PPID]"
+ fi
+ fi
+}
+
+ha_log() {
+ loglevel=$1
+ shift
+ prn_level=`level_pres $loglevel`
+ msg="$prn_level: $@"
+
+ if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
+ return 0
+ fi
+
+ set_logtag
+
+ # if we're connected to a tty, then output to stderr
+ if tty >/dev/null; then
+ if [ "$HA_LOGTAG" ]; then
+ echo "$HA_LOGTAG: $msg"
+ else
+ echo "$msg"
+ fi >&2
+ return 0
+ fi
+
+ [ "x$HA_LOGD" = "xyes" ] &&
+ cat<<EOF | ha_logger -t "$HA_LOGTAG" && return 0
+$msg
+EOF
+
+ if [ -n "$HA_LOGFACILITY" -a "$HA_LOGFACILITY" != none ]; then
+ logger -t "$HA_LOGTAG" -p $HA_LOGFACILITY.$loglevel "$msg"
+ fi
+ dest=${HA_LOGFILE:-$HA_DEBUGLOG}
+ if [ -n "$dest" ]; then
+ msg="$prn_level: `hadate` $@"
+ echo "$HA_LOGTAG: $msg" >> $dest
+ fi
+}
+
+if [ $# -lt 2 ]; then
+ ha_log err "$PROG: not enough arguments [$#]"
+ exit 1
+fi
+
+loglevel="$1"
+shift 1
+msg="$*"
+
+ha_log "$loglevel" "$msg"
diff --git a/lib/stonith/main.c b/lib/stonith/main.c
new file mode 100644
index 0000000..44e099f
--- /dev/null
+++ b/lib/stonith/main.c
@@ -0,0 +1,727 @@
+/*
+ * Stonith: simple test program for exercising the Stonith API code
+ *
+ * Copyright (C) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stonith/stonith.h>
+#include <pils/plugin.h>
+#include <clplumbing/cl_log.h>
+#include <glib.h>
+#include <libxml/entities.h>
+
+#define OPTIONS "c:F:p:t:T:EsnSlLmvhVd"
+#define EQUAL '='
+
+extern char * optarg;
+extern int optind, opterr, optopt;
+
+static int debug = 0;
+
+#define LOG_TERMINAL 0
+#define LOG_CLLOG 1
+static int log_destination = LOG_TERMINAL;
+
+static const char META_TEMPLATE[] =
+"<?xml version=\"1.0\"?>\n"
+"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
+"<resource-agent name=\"%s\">\n"
+"<version>1.0</version>\n"
+"<longdesc lang=\"en\">\n"
+"%s\n"
+"</longdesc>\n"
+"<shortdesc lang=\"en\">%s</shortdesc>\n"
+"%s\n"
+"<actions>\n"
+"<action name=\"start\" timeout=\"20\" />\n"
+"<action name=\"stop\" timeout=\"15\" />\n"
+"<action name=\"status\" timeout=\"20\" />\n"
+"<action name=\"monitor\" timeout=\"20\" interval=\"3600\" />\n"
+"<action name=\"meta-data\" timeout=\"15\" />\n"
+"</actions>\n"
+"<special tag=\"heartbeat\">\n"
+"<version>2.0</version>\n"
+"</special>\n"
+"</resource-agent>\n";
+
+void version(void);
+void usage(const char * cmd, int exit_status, const char * devtype);
+void confhelp(const char * cmd, FILE* stream, const char * devtype);
+void print_stonith_meta(Stonith * stonith_obj, const char *rsc_type);
+void print_types(void);
+void print_confignames(Stonith *s);
+
+void log_buf(int severity, char *buf);
+void log_msg(int severity, const char * fmt, ...)G_GNUC_PRINTF(2,3);
+void trans_log(int priority, const char * fmt, ...)G_GNUC_PRINTF(2,3);
+
+static int pil_loglevel_to_syslog_severity[] = {
+ /* Indices: <none>=0, PIL_FATAL=1, PIL_CRIT=2, PIL_WARN=3,
+ PIL_INFO=4, PIL_DEBUG=5
+ */
+ LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_WARNING, LOG_INFO, LOG_DEBUG
+ };
+
+/*
+ * Note that we don't use the cl_log logging code because the STONITH
+ * command is intended to be shipped without the clplumbing libraries.
+ *
+ * :-(
+ *
+ * The stonith command has so far always been shipped along with
+ * the clplumbing library, so we'll use cl_log
+ * If that ever changes, we'll use something else
+ */
+
+void
+version()
+{
+ printf("stonith: %s (%s)\n", GLUE_VERSION, GLUE_BUILD_VERSION);
+ exit(0);
+}
+
+void
+usage(const char * cmd, int exit_status, const char * devtype)
+{
+ FILE *stream;
+
+ stream = exit_status ? stderr : stdout;
+
+ /* non-NULL devtype indicates help for specific device, so no usage */
+ if (devtype == NULL) {
+ fprintf(stream, "usage:\n");
+ fprintf(stream, "\t %s [-svh] "
+ "-L\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "-n\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "-m\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "{-p stonith-device-parameters | "
+ "-F stonith-device-parameters-file | "
+ "-E | "
+ "name=value...} "
+ "[-c count] "
+ "-lS\n"
+ , cmd);
+
+ fprintf(stream, "\t %s [-svh] "
+ "-t stonith-device-type "
+ "{-p stonith-device-parameters | "
+ "-F stonith-device-parameters-file | "
+ "-E | "
+ "name=value...} "
+ "[-c count] "
+ "-T {reset|on|off} nodename\n"
+ , cmd);
+
+ fprintf(stream, "\nwhere:\n");
+ fprintf(stream, "\t-L\tlist supported stonith device types\n");
+ fprintf(stream, "\t-l\tlist hosts controlled by this stonith device\n");
+ fprintf(stream, "\t-S\treport stonith device status\n");
+ fprintf(stream, "\t-s\tsilent\n");
+ fprintf(stream, "\t-v\tverbose\n");
+ fprintf(stream, "\t-n\toutput the config names of stonith-device-parameters\n");
+ fprintf(stream, "\t-m\tdisplay meta-data of the stonith device type\n");
+ fprintf(stream, "\t-h\tdisplay detailed help message with stonith device description(s)\n");
+ }
+
+ if (exit_status == 0) {
+ confhelp(cmd, stream, devtype);
+ }
+
+ exit(exit_status);
+}
+
+/* Thanks to Lorn Kay <lorn_kay@hotmail.com> for the confhelp code */
+void
+confhelp(const char * cmd, FILE* stream, const char * devtype)
+{
+ char ** typelist;
+ char ** this;
+ Stonith * s;
+ int devfound = 0;
+
+
+ /* non-NULL devtype indicates help for specific device, so no header */
+ if (devtype == NULL) {
+ fprintf(stream
+ , "\nSTONITH -t device types and"
+ " associated configuration details:\n");
+ }
+
+ typelist = stonith_types();
+
+ if (typelist == NULL) {
+ fprintf(stderr,
+ "Failed to retrieve list of STONITH modules!\n");
+ return;
+ }
+ for(this=typelist; *this && !devfound; ++this) {
+ const char * SwitchType = *this;
+ const char * cres;
+ const char * const * pnames;
+
+
+ if ((s = stonith_new(SwitchType)) == NULL) {
+ fprintf(stderr, "Invalid STONITH type %s(!)\n"
+ , SwitchType);
+ continue;
+ }
+
+ if (devtype) {
+ if (strcmp(devtype, SwitchType)) {
+ continue;
+ } else {
+ devfound = 1;
+ }
+ }
+
+ fprintf(stream, "\n\nSTONITH Device: %s - ", SwitchType);
+
+ if ((cres = stonith_get_info(s, ST_DEVICEDESCR)) != NULL){
+ fprintf(stream, "%s\n"
+ , cres);
+ }
+
+ if ((cres = stonith_get_info(s, ST_DEVICEURL)) != NULL){
+ fprintf(stream
+ , "For more information see %s\n"
+ , cres);
+ }
+ if (NULL == (pnames = stonith_get_confignames(s))) {
+ continue;
+ }
+ fprintf(stream
+ , "List of valid parameter names for %s STONITH device:\n"
+ , SwitchType);
+ for (;*pnames; ++pnames) {
+ fprintf(stream
+ , "\t%s\n", *pnames);
+ }
+
+#ifdef ST_CONFI_INFO_SYNTAX
+ fprintf(stream, "\nConfig info [-p] syntax for %s:\n\t%s\n"
+ , SwitchType, stonith_get_info(s, ST_CONF_INFO_SYNTAX));
+#else
+ fprintf(stream, "For Config info [-p] syntax"
+ ", give each of the above parameters in order as"
+ "\nthe -p value.\n"
+ "Arguments are separated by white space.");
+#endif
+#ifdef ST_CONFI_FILE_SYNTAX
+ fprintf(stream, "\nConfig file [-F] syntax for %s:\n\t%s\n"
+ , SwitchType, stonith->get_info(s, ST_CONF_FILE_SYNTAX));
+#else
+ fprintf(stream
+ , "\nConfig file [-F] syntax is the same as -p"
+ ", except # at the start of a line"
+ "\ndenotes a comment\n");
+#endif
+
+ stonith_delete(s); s = NULL;
+ }
+ /* Note that the type list can't/shouldn't be freed */
+ if (devtype && !devfound) {
+ fprintf(stderr, "Invalid device type: '%s'\n", devtype);
+ }
+
+}
+
+void
+print_stonith_meta(Stonith * stonith_obj, const char *rsc_type)
+{
+ const char * meta_param = NULL;
+ const char * meta_longdesc = NULL;
+ const char * meta_shortdesc = NULL;
+ char *xml_meta_longdesc = NULL;
+ char *xml_meta_shortdesc = NULL;
+ static const char * no_parameter_info = "<!-- no value -->";
+
+ meta_longdesc = stonith_get_info(stonith_obj, ST_DEVICEDESCR);
+ if (meta_longdesc == NULL) {
+ fprintf(stderr, "stonithRA plugin: no long description");
+ meta_longdesc = no_parameter_info;
+ }
+ xml_meta_longdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc);
+
+ meta_shortdesc = stonith_get_info(stonith_obj, ST_DEVICEID);
+ if (meta_shortdesc == NULL) {
+ fprintf(stderr, "stonithRA plugin: no short description");
+ meta_shortdesc = no_parameter_info;
+ }
+ xml_meta_shortdesc = (char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc);
+
+ meta_param = stonith_get_info(stonith_obj, ST_CONF_XML);
+ if (meta_param == NULL) {
+ fprintf(stderr, "stonithRA plugin: no list of parameters");
+ meta_param = no_parameter_info;
+ }
+
+ printf(META_TEMPLATE,
+ rsc_type, xml_meta_longdesc, xml_meta_shortdesc, meta_param);
+
+ xmlFree(xml_meta_longdesc);
+ xmlFree(xml_meta_shortdesc);
+}
+
+#define MAXNVARG 50
+
+void
+print_types()
+{
+ char ** typelist;
+
+ typelist = stonith_types();
+ if (typelist == NULL) {
+ log_msg(LOG_ERR, "Could not list Stonith types.");
+ }else{
+ char ** this;
+
+ for(this=typelist; *this; ++this) {
+ printf("%s\n", *this);
+ }
+ }
+}
+
+void
+print_confignames(Stonith *s)
+{
+ const char * const * names;
+ int i;
+
+ names = stonith_get_confignames(s);
+
+ if (names != NULL) {
+ for (i=0; names[i]; ++i) {
+ printf("%s ", names[i]);
+ }
+ }
+ printf("\n");
+}
+
+void
+log_buf(int severity, char *buf)
+{
+ if (severity == LOG_DEBUG && !debug)
+ return;
+ if (log_destination == LOG_TERMINAL) {
+ fprintf(stderr, "%s: %s\n", prio2str(severity),buf);
+ } else {
+ cl_log(severity, "%s", buf);
+ }
+}
+
+void
+log_msg(int severity, const char * fmt, ...)
+{
+ va_list ap;
+ char buf[MAXLINE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ va_end(ap);
+ log_buf(severity, buf);
+}
+
+void
+trans_log(int priority, const char * fmt, ...)
+{
+ int severity;
+ va_list ap;
+ char buf[MAXLINE];
+
+ severity = pil_loglevel_to_syslog_severity[ priority % sizeof
+ (pil_loglevel_to_syslog_severity) ];
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ va_end(ap);
+ log_buf(severity, buf);
+}
+
+int
+main(int argc, char** argv)
+{
+ char * cmdname;
+ int rc;
+ Stonith * s;
+ const char * SwitchType = NULL;
+ const char * optfile = NULL;
+ const char * parameters = NULL;
+ int reset_type = ST_GENERIC_RESET;
+ int verbose = 0;
+ int status = 0;
+ int silent = 0;
+ int listhosts = 0;
+ int listtypes = 0;
+ int listparanames = 0;
+ int params_from_env = 0;
+
+ int c;
+ int errors = 0;
+ int argcount;
+ StonithNVpair nvargs[MAXNVARG];
+ int nvcount=0;
+ int j;
+ int count = 1;
+ int help = 0;
+ int metadata = 0;
+
+ /* The bladehpi stonith plugin makes use of openhpi which is
+ * threaded. The mix of memory allocation without thread
+ * initialization followed by g_thread_init followed by
+ * deallocating that memory results in segfault. Hence the
+ * following G_SLICE setting; see
+ * http://library.gnome.org/devel/glib/stable/glib-Memory-Slices.html#g-slice-alloc
+ */
+
+ setenv("G_SLICE", "always-malloc", 1);
+
+ if ((cmdname = strrchr(argv[0], '/')) == NULL) {
+ cmdname = argv[0];
+ }else{
+ ++cmdname;
+ }
+
+
+ while ((c = getopt(argc, argv, OPTIONS)) != -1) {
+ switch(c) {
+
+ case 'c': count = atoi(optarg);
+ if (count < 1) {
+ fprintf(stderr
+ , "bad count [%s]\n"
+ , optarg);
+ usage(cmdname, 1, NULL);
+ }
+ break;
+
+ case 'd': debug++;
+ break;
+
+ case 'F': optfile = optarg;
+ break;
+
+ case 'E': params_from_env = 1;
+ break;
+
+ case 'h': help++;
+ break;
+
+ case 'm': metadata++;
+ break;
+
+ case 'l': ++listhosts;
+ break;
+
+ case 'L': ++listtypes;
+ break;
+
+ case 'p': parameters = optarg;
+ break;
+
+ case 's': ++silent;
+ break;
+
+ case 'S': ++status;
+ break;
+
+ case 't': SwitchType = optarg;
+ break;
+
+ case 'T': if (strcmp(optarg, "on")== 0) {
+ reset_type = ST_POWERON;
+ }else if (strcmp(optarg, "off")== 0) {
+ reset_type = ST_POWEROFF;
+ }else if (strcmp(optarg, "reset")== 0) {
+ reset_type = ST_GENERIC_RESET;
+ }else{
+ fprintf(stderr
+ , "bad reset type [%s]\n"
+ , optarg);
+ usage(cmdname, 1, NULL);
+ }
+ break;
+
+ case 'n': ++listparanames;
+ break;
+
+ case 'v': ++verbose;
+ break;
+
+ case 'V': version();
+ break;
+
+ default: ++errors;
+ break;
+ }
+ }
+
+ /* if we're invoked by stonithd, log through cl_log */
+ if (!isatty(fileno(stdin))) {
+ log_destination = LOG_CLLOG;
+ cl_log_set_entity("stonith");
+ cl_log_enable_stderr(debug?TRUE:FALSE);
+ cl_log_set_facility(HA_LOG_FACILITY);
+
+ /* Use logd if it's enabled by heartbeat */
+ cl_inherit_logging_environment(0);
+ }
+
+ if (help && !errors) {
+ usage(cmdname, 0, SwitchType);
+ }
+ if (debug) {
+ PILpisysSetDebugLevel(debug);
+ setenv("HA_debug","2",0);
+ }
+ if ((optfile && parameters) || (optfile && params_from_env)
+ || (params_from_env && parameters)) {
+ fprintf(stderr
+ , "Please use just one of -F, -p, and -E options\n");
+ usage(cmdname, 1, NULL);
+ }
+
+ /*
+ * Process name=value arguments on command line...
+ */
+ for (;optind < argc; ++optind) {
+ char * eqpos;
+ if ((eqpos=strchr(argv[optind], EQUAL)) == NULL) {
+ break;
+ }
+ if (parameters || optfile || params_from_env) {
+ fprintf(stderr
+ , "Cannot mix name=value and -p, -F, or -E "
+ "style arguments\n");
+ usage(cmdname, 1, NULL);
+ }
+ if (nvcount >= MAXNVARG) {
+ fprintf(stderr
+ , "Too many name=value style arguments\n");
+ exit(1);
+ }
+ nvargs[nvcount].s_name = argv[optind];
+ *eqpos = EOS;
+ nvargs[nvcount].s_value = eqpos+1;
+ nvcount++;
+ }
+ nvargs[nvcount].s_name = NULL;
+ nvargs[nvcount].s_value = NULL;
+
+ argcount = argc - optind;
+
+ if (!(argcount == 1 || (argcount < 1
+ && (status||listhosts||listtypes||listparanames||metadata)))) {
+ ++errors;
+ }
+
+ if (errors) {
+ usage(cmdname, 1, NULL);
+ }
+
+ if (listtypes) {
+ print_types();
+ exit(0);
+ }
+
+ if (SwitchType == NULL) {
+ log_msg(LOG_ERR,"Must specify device type (-t option)");
+ usage(cmdname, 1, NULL);
+ }
+ s = stonith_new(SwitchType);
+ if (s == NULL) {
+ log_msg(LOG_ERR,"Invalid device type: '%s'", SwitchType);
+ exit(S_OOPS);
+ }
+ if (debug) {
+ stonith_set_debug(s, debug);
+ }
+ stonith_set_log(s, (PILLogFun)trans_log);
+
+ if (!listparanames && !metadata && optfile == NULL &&
+ parameters == NULL && !params_from_env && nvcount == 0) {
+ const char * const * names;
+ int needs_parms = 1;
+
+ if (s != NULL && (names = stonith_get_confignames(s)) != NULL && names[0] == NULL) {
+ needs_parms = 0;
+ }
+
+ if (needs_parms) {
+ fprintf(stderr
+ , "Must specify either -p option, -F option, -E option, or "
+ "name=value style arguments\n");
+ if (s != NULL) {
+ stonith_delete(s);
+ }
+ usage(cmdname, 1, NULL);
+ }
+ }
+
+ if (listparanames) {
+ print_confignames(s);
+ stonith_delete(s);
+ s=NULL;
+ exit(0);
+ }
+
+ if (metadata) {
+ print_stonith_meta(s,SwitchType);
+ stonith_delete(s);
+ s=NULL;
+ exit(0);
+ }
+
+ /* Old STONITH version 1 stuff... */
+ if (optfile) {
+ /* Configure the Stonith object from a file */
+ if ((rc=stonith_set_config_file(s, optfile)) != S_OK) {
+ log_msg(LOG_ERR
+ , "Invalid config file for %s device."
+ , SwitchType);
+#if 0
+ log_msg(LOG_INFO, "Config file syntax: %s"
+ , s->s_ops->getinfo(s, ST_CONF_FILE_SYNTAX));
+#endif
+ stonith_delete(s); s=NULL;
+ exit(S_BADCONFIG);
+ }
+ }else if (params_from_env) {
+ /* Configure Stonith object from the environment */
+ StonithNVpair * pairs;
+ if ((pairs = stonith_env_to_NVpair(s)) == NULL) {
+ fprintf(stderr
+ , "Invalid config info for %s device.\n"
+ , SwitchType);
+ stonith_delete(s); s=NULL;
+ exit(1);
+ }
+ if ((rc = stonith_set_config(s, pairs)) != S_OK) {
+ fprintf(stderr
+ , "Invalid config info for %s device\n"
+ , SwitchType);
+ }
+ }else if (parameters) {
+ /* Configure Stonith object from the -p argument */
+ StonithNVpair * pairs;
+ if ((pairs = stonith1_compat_string_to_NVpair
+ ( s, parameters)) == NULL) {
+ fprintf(stderr
+ , "Invalid STONITH -p parameter [%s]\n"
+ , parameters);
+ stonith_delete(s); s=NULL;
+ exit(1);
+ }
+ if ((rc = stonith_set_config(s, pairs)) != S_OK) {
+ fprintf(stderr
+ , "Invalid config info for %s device\n"
+ , SwitchType);
+ }
+ }else{
+ /*
+ * Configure STONITH device using cmdline arguments...
+ */
+ if ((rc = stonith_set_config(s, nvargs)) != S_OK) {
+ const char * const * names;
+ int j;
+ fprintf(stderr
+ , "Invalid config info for %s device\n"
+ , SwitchType);
+
+ names = stonith_get_confignames(s);
+
+ if (names != NULL) {
+ fprintf(stderr
+ , "Valid config names are:\n");
+
+ for (j=0; names[j]; ++j) {
+ fprintf(stderr
+ , "\t%s\n", names[j]);
+ }
+ }
+ stonith_delete(s); s=NULL;
+ exit(rc);
+ }
+ }
+
+
+ for (j=0; j < count; ++j) {
+ rc = S_OK;
+
+ if (status) {
+ rc = stonith_get_status(s);
+
+ if (!silent) {
+ if (rc == S_OK) {
+ log_msg((log_destination == LOG_TERMINAL) ?
+ LOG_INFO : LOG_DEBUG,
+ "%s device OK.", SwitchType);
+ }else{
+ /* Uh-Oh */
+ log_msg(LOG_ERR, "%s device not accessible."
+ , SwitchType);
+ }
+ }
+ }
+
+ if (listhosts) {
+ char ** hostlist;
+
+ hostlist = stonith_get_hostlist(s);
+ if (hostlist == NULL) {
+ log_msg(LOG_ERR, "Could not list hosts for %s."
+ , SwitchType);
+ rc = -1;
+ }else{
+ char ** this;
+
+ for(this=hostlist; *this; ++this) {
+ printf("%s\n", *this);
+ }
+ stonith_free_hostlist(hostlist);
+ }
+ }
+
+ if (optind < argc) {
+ char *nodename;
+ nodename = g_strdup(argv[optind]);
+ strdown(nodename);
+ rc = stonith_req_reset(s, reset_type, nodename);
+ g_free(nodename);
+ }
+ }
+ stonith_delete(s); s = NULL;
+ return(rc);
+}
diff --git a/lib/stonith/meatclient.c b/lib/stonith/meatclient.c
new file mode 100644
index 0000000..e95dc0e
--- /dev/null
+++ b/lib/stonith/meatclient.c
@@ -0,0 +1,152 @@
+/*
+ * Stonith client for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder@sysfive.com>
+ *
+ * This program is a rewrite of the "do_meatware" program by
+ * David C. Teigland <teigland@sistina.com> originally appeared
+ * in the GFS stomith meatware agent.
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stonith/stonith.h>
+#include <glib.h>
+
+#define OPTIONS "c:w"
+
+void usage(const char * cmd);
+
+void
+usage(const char * cmd)
+{
+ fprintf(stderr, "usage: %s -c node [-w]\n", cmd);
+ exit(S_INVAL);
+}
+
+extern char * optarg;
+extern int optind, opterr, optopt;
+int
+main(int argc, char** argv)
+{
+ char * cmdname;
+ const char * meatpipe_pr = HA_VARRUNDIR "/meatware"; /* if you intend to
+ change this, modify
+ meatware.c as well */
+ char * opthost = NULL;
+ int clearhost = 0;
+
+ int c, argcount, waitmode = 0;
+ int errors = 0;
+
+ if ((cmdname = strrchr(argv[0], '/')) == NULL) {
+ cmdname = argv[0];
+ }else{
+ ++cmdname;
+ }
+
+ while ((c = getopt(argc, argv, OPTIONS)) != -1) {
+ switch(c) {
+ case 'c': opthost = optarg;
+ ++clearhost;
+ break;
+ case 'w': ++waitmode;
+ break;
+ default: ++errors;
+ break;
+ }
+ }
+ argcount = argc - optind;
+ if (!(argcount == 0) || !opthost) {
+ errors++;
+ }
+
+ if (errors) {
+ usage(cmdname);
+ }
+
+ strdown(opthost);
+
+ if (clearhost) {
+
+ int rc, fd;
+ char resp[3];
+
+ char line[256];
+ char meatpipe[256];
+
+ gboolean waited=FALSE;
+
+ snprintf(meatpipe, 256, "%s.%s", meatpipe_pr, opthost);
+
+ while(1) {
+ fd = open(meatpipe, O_WRONLY | O_NONBLOCK);
+ if (fd >= 0)
+ break;
+ if (!waitmode || (errno != ENOENT && errno != ENXIO)) {
+ if (waited) printf("\n");
+ snprintf(line, sizeof(line)
+ , "Meatware_IPC failed: %s", meatpipe);
+ perror(line);
+ exit(S_BADHOST);
+ }
+ printf("."); fflush(stdout); waited=TRUE;
+ sleep(1);
+ }
+ if (waited) printf("\n");
+
+ printf("\nWARNING!\n\n"
+ "If node \"%s\" has not been manually power-cycled or "
+ "disconnected from all shared resources and networks, "
+ "data on shared disks may become corrupted and "
+ "migrated services might not work as expected.\n"
+ "Please verify that the name or address above "
+ "corresponds to the node you just rebooted.\n\n"
+ "PROCEED? [yN] ", opthost);
+
+ rc = scanf("%s", resp);
+
+ if (rc == 0 || rc == EOF || tolower(resp[0] != 'y')) {
+ printf("Meatware_client: operation canceled.\n");
+ exit(S_INVAL);
+ }
+
+ sprintf(line, "meatware reply %s", opthost);
+
+ rc = write(fd, line, 256);
+
+ if (rc < 0) {
+ sprintf(line, "Meatware_IPC failed: %s", meatpipe);
+ perror(line);
+ exit(S_OOPS);
+ }
+
+ printf("Meatware_client: reset confirmed.\n");
+ }
+
+ exit(S_OK);
+}
diff --git a/lib/stonith/st_ttylock.c b/lib/stonith/st_ttylock.c
new file mode 100644
index 0000000..adc918d
--- /dev/null
+++ b/lib/stonith/st_ttylock.c
@@ -0,0 +1,225 @@
+/*
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <clplumbing/cl_signal.h>
+#include <stonith/st_ttylock.h>
+
+/*
+ * The following information is from the Filesystem Hierarchy Standard
+ * version 2.1 dated 12 April, 2000.
+ *
+ * 5.6 /var/lock : Lock files
+ * Lock files should be stored within the /var/lock directory structure.
+ * Device lock files, such as the serial device lock files that were originally
+ * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
+ * /var/lock. The naming convention which must be used is LCK.. followed by
+ * the base name of the device file. For example, to lock /dev/cua0 the file
+ * LCK..cua0 would be created.
+ *
+ * The format used for device lock files must be the HDB UUCP lock file format.
+ * The HDB format is to store the process identifier (PID) as a ten byte
+ * ASCII decimal number, with a trailing newline. For example, if process 1230
+ * holds a lock file, it would contain the eleven characters: space, space,
+ * space, space, space, space, one, two, three, zero, and newline.
+ * Then, anything wishing to use /dev/cua0 can read the lock file and act
+ * accordingly (all locks in /var/lock should be world-readable).
+ *
+ *
+ * PERMISSIONS NOTE:
+ * Different linux distributions set the mode of the lock directory differently
+ * Any process which wants to create lock files must have write permissions
+ * on HA_VARLOCKDIR (probably /var/lock). For things like the heartbeat API
+ * code, this may mean allowing the uid of the processes that use this API
+ * to join group uucp, or making the binaries setgid to uucp.
+ */
+
+#define DEVDIR "/dev/"
+#define DEVLEN (sizeof(DEVDIR)-1)
+
+static void raw_device (const char *dev, char *dest_name, size_t size);
+static int DoLock(const char * prefix, const char *lockname);
+static int DoUnlock(const char * prefix, const char *lockname);
+
+/* The code in this file originally written by Guenther Thomsen */
+/* Somewhat mangled by Alan Robertson */
+
+/*
+ * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL)
+ * serial_device has to be _the complete path_, i.e. including '/dev/' to the
+ * special file, which denotes the tty to lock -tho
+ * return 0 on success,
+ * -1 if device is locked (lockfile exists and isn't stale),
+ * -2 for temporarily failure, try again,
+ * other negative value, if something unexpected happend (failure anyway)
+ */
+
+static void
+raw_device (const char *serial_device, char *dest_name, size_t size)
+{
+ char* dp = dest_name;
+ const char* sp = serial_device+DEVLEN;
+ const char* dpend = dp + size - 1;
+
+ while (*sp != '\0' && dp < dpend) {
+ if (isalnum((unsigned int)*sp))
+ *dp++ = *sp;
+ sp++;
+ }
+ *dp = EOS;
+}
+
+int
+st_ttylock(const char *serial_device)
+{
+ char rawname[64];
+
+ if (serial_device == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+ raw_device (serial_device, rawname, sizeof(rawname));
+ return(DoLock("LCK..", rawname));
+}
+
+/*
+ * Unlock a tty (remove its lockfile)
+ * do we need to check, if its (still) ours? No, IMHO, if someone else
+ * locked our line, it's his fault -tho
+ * returns 0 on success
+ * <0 if some failure occured
+ */
+
+int
+st_ttyunlock(const char *serial_device)
+{
+ char rawname[64];
+
+ if (serial_device == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+
+ raw_device (serial_device, rawname, sizeof(rawname));
+ return(DoUnlock("LCK..", rawname));
+}
+
+/* This is what the FHS standard specifies for the size of our lock file */
+#define LOCKSTRLEN 11
+
+static int
+DoLock(const char * prefix, const char *lockname)
+{
+ char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
+ int fd;
+ long pid, mypid;
+ int rc;
+ struct stat sbuf;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(lf_name, sizeof(lf_name), "%s/%s%s"
+ , HA_VARLOCKDIR, prefix, lockname);
+
+ snprintf(tf_name, sizeof(tf_name), "%s/tmp%lu-%s"
+ , HA_VARLOCKDIR, mypid, lockname);
+
+ if ((fd = open(lf_name, O_RDONLY)) >= 0) {
+ if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
+ sleep(1); /* if someone was about to create one,
+ * give'm a sec to do so
+ * Though if they follow our protocol,
+ * this won't happen. They should really
+ * put the pid in, then link, not the
+ * other way around.
+ */
+ }
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ /* lockfile empty -> rm it and go on */;
+ } else {
+ if (sscanf(buf, "%lu", &pid) < 1) {
+ /* lockfile screwed up -> rm it and go on */
+ } else {
+ if (pid > 1 && ((long)getpid() != pid)
+ && ((CL_KILL((pid_t)pid, 0) >= 0)
+ || errno != ESRCH)) {
+ /* tty is locked by existing (not
+ * necessarily running) process
+ * -> give up */
+ close(fd);
+ return -1;
+ } else {
+ /* stale lockfile -> rm it and go on */
+ }
+ }
+ }
+ unlink(lf_name);
+ }
+ if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
+ /* Hmmh, why did we fail? Anyway, nothing we can do about it */
+ return -3;
+ }
+
+ /* Slight overkill with the %*d format ;-) */
+ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
+
+ if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
+ /* Again, nothing we can do about this */
+ return -3;
+ }
+ close(fd);
+
+ switch (link(tf_name, lf_name)) {
+ case 0:
+ if (stat(tf_name, &sbuf) < 0) {
+ /* something weird happened */
+ rc = -3;
+ break;
+ }
+ if (sbuf.st_nlink < 2) {
+ /* somehow, it didn't get through - NFS trouble? */
+ rc = -2;
+ break;
+ }
+ rc = 0;
+ break;
+ case EEXIST:
+ rc = -1;
+ break;
+ default:
+ rc = -3;
+ }
+ unlink(tf_name);
+ return rc;
+}
+
+static int
+DoUnlock(const char * prefix, const char *lockname)
+{
+ char lf_name[256];
+
+ snprintf(lf_name, sizeof(lf_name), "%s/%s%s", HA_VARLOCKDIR
+ , prefix, lockname);
+ return unlink(lf_name);
+}
diff --git a/lib/stonith/stonith.c b/lib/stonith/stonith.c
new file mode 100644
index 0000000..4ced8c7
--- /dev/null
+++ b/lib/stonith/stonith.c
@@ -0,0 +1,636 @@
+/*
+ * Stonith API infrastructure.
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr@unix.sh>
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <glib.h>
+#define ENABLE_PIL_DEFS_PRIVATE
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <stonith/stonith.h>
+#include <stonith/stonith_plugin.h>
+
+
+#define MALLOC StonithPIsys->imports->alloc
+#ifdef MALLOCT
+# undef MALLOCT
+#endif
+#define MALLOCT(t) (t*)(MALLOC(sizeof(t)))
+#define REALLOC StonithPIsys->imports->mrealloc
+#define STRDUP StonithPIsys->imports->mstrdup
+#define FREE(p) {StonithPIsys->imports->mfree(p); (p) = NULL;}
+
+#define LOG(args...) PILCallLog(StonithPIsys->imports->log, args)
+
+#define EXTPINAME_S "external"
+#define RHCSPINAME_S "rhcs"
+
+PILPluginUniv* StonithPIsys = NULL;
+static GHashTable* Splugins = NULL;
+static int init_pluginsys(void);
+extern StonithImports stonithimports;
+
+static PILGenericIfMgmtRqst Reqs[] =
+{
+ {STONITH_TYPE_S, &Splugins, &stonithimports, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}
+};
+
+void PILpisysSetDebugLevel(int);
+/* Initialize the plugin system... */
+static int
+init_pluginsys(void) {
+
+ if (StonithPIsys) {
+ return TRUE;
+ }
+
+
+ /* PILpisysSetDebugLevel(10); */
+ StonithPIsys = NewPILPluginUniv(STONITH_MODULES);
+
+ if (StonithPIsys) {
+ int rc = PILLoadPlugin(StonithPIsys, PI_IFMANAGER, "generic", Reqs);
+ if (rc != PIL_OK) {
+ fprintf(stderr, "generic plugin load failed: %d\n", rc);
+ DelPILPluginUniv(StonithPIsys);
+ StonithPIsys = NULL;
+ }
+ /*PILSetDebugLevel(StonithPIsys, PI_IFMANAGER, "generic", 10);*/
+ }else{
+ fprintf(stderr, "pi univ creation failed\n");
+ }
+ return StonithPIsys != NULL;
+}
+
+/*
+ * Create a new Stonith object of the requested type.
+ */
+
+Stonith *
+stonith_new(const char * type)
+{
+ StonithPlugin * sp = NULL;
+ struct stonith_ops* ops = NULL;
+ char * key;
+ char * subplugin;
+ char * typecopy;
+
+
+ if (!init_pluginsys()) {
+ return NULL;
+ }
+
+ if ((typecopy = STRDUP(type)) == NULL) {
+ return NULL;
+ }
+
+ if (((subplugin = strchr(typecopy, '/')) != NULL) &&
+ (strncmp(EXTPINAME_S, typecopy, strlen(EXTPINAME_S)) == 0 ||
+ strncmp(RHCSPINAME_S, typecopy, strlen(RHCSPINAME_S)) == 0)) {
+ *subplugin++ = 0; /* make two strings */
+ }
+
+ /* Look and see if it's already loaded... */
+
+ if (g_hash_table_lookup_extended(Splugins, typecopy
+ , (gpointer)&key, (gpointer)&ops)) {
+ /* Yes! Increment reference count */
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, typecopy, 1);
+
+ }else{ /* No. Try and load it... */
+ if (PILLoadPlugin(StonithPIsys, STONITH_TYPE_S, typecopy, NULL)
+ != PIL_OK) {
+ FREE(typecopy);
+ return NULL;
+ }
+
+ /* Look up the plugin in the Splugins table */
+ if (!g_hash_table_lookup_extended(Splugins, typecopy
+ , (void*)&key, (void*)&ops)) {
+ /* OOPS! didn't find it(!?!)... */
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S
+ , typecopy, -1);
+ FREE(typecopy);
+ return NULL;
+ }
+ }
+
+ if (ops != NULL) {
+ sp = ops->new((const char *)(subplugin));
+ if (sp != NULL) {
+ sp->s.stype = STRDUP(typecopy);
+ }
+ }
+
+ FREE(typecopy);
+ return sp ? (&sp->s) : NULL;
+}
+
+static int
+qsort_string_cmp(const void *a, const void *b)
+{
+ return(strcmp(*(const char * const *)a, *(const char * const *)b));
+}
+
+/*
+ * Return list of STONITH types valid in stonith_new()
+ */
+
+static char **
+get_plugin_list(const char *pltype)
+{
+ char ** typelist = NULL;
+ const char * const *extPI;
+ const char * const *p;
+ int numextPI, i;
+ Stonith * ext;
+
+ /* let the external plugin return a list */
+ if ((ext = stonith_new(pltype)) == NULL) {
+ LOG(PIL_CRIT, "Cannot create new external "
+ "plugin object");
+ return NULL;
+ }
+ if ((extPI = stonith_get_confignames(ext)) == NULL) {
+ /* don't complain if rhcs plugins are not installed */
+ if (strcmp(pltype, "rhcs"))
+ LOG(PIL_INFO, "Cannot get %s plugin subplugins", pltype);
+ stonith_delete(ext);
+ return NULL;
+ }
+
+ /* count the external plugins */
+ for (numextPI = 0, p = extPI; *p; p++, numextPI++);
+
+ typelist = (char **)
+ MALLOC((numextPI+1)*sizeof(char *));
+ if (typelist == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ stonith_delete(ext);
+ return NULL;
+ }
+
+ memset(typelist, 0, (numextPI + 1)*sizeof(char *));
+
+ /* copy external plugins */
+ for (i = 0; i < numextPI; i++) {
+ int len = strlen(pltype) +
+ strlen(extPI[i]) + 2;
+ typelist[i] = MALLOC(len);
+ if (typelist[i] == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ stonith_delete(ext);
+ goto err;
+ }
+ snprintf(typelist[i], len, "%s/%s"
+ , pltype, extPI[i]);
+ }
+
+ stonith_delete(ext);
+
+ /* sort the list of plugin names */
+ qsort(typelist, numextPI, sizeof(char *), qsort_string_cmp);
+
+ return typelist;
+err:
+ stonith_free_hostlist(typelist);
+ return NULL;
+}
+
+char **
+stonith_types(void)
+{
+ int i, j, cur=0, rl_size, sub_pl = 0;
+ static char ** rl = NULL;
+ char ** new_list, **sub_list = NULL;
+
+ if (!init_pluginsys()) {
+ return NULL;
+ }
+
+ new_list = PILListPlugins(StonithPIsys, STONITH_TYPE_S, NULL);
+ if (new_list == NULL) {
+ return NULL;
+ }
+ for (i=0; new_list[i]; ++i)
+ ; /* count */
+ rl_size = i+1;
+
+ rl = (char**)MALLOC(rl_size * sizeof(char *));
+ if (rl == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ goto types_exit;
+ }
+
+ for (i=0; new_list[i]; ++i) {
+ /* look for 'external' and 'rhcs' plugins */
+ if (strcmp(new_list[i], EXTPINAME_S) == 0) {
+ sub_list = get_plugin_list(EXTPINAME_S);
+ sub_pl = 1;
+ } else if (strcmp(new_list[i], RHCSPINAME_S) == 0) {
+ sub_list = get_plugin_list(RHCSPINAME_S);
+ sub_pl = 1;
+ }
+ if (sub_pl) {
+ if (sub_list) {
+ for (j=0; sub_list[j]; ++j)
+ ; /* count */
+ rl_size += j;
+ rl = (char**)REALLOC(rl, rl_size*sizeof(char *));
+ for (j=0; sub_list[j]; ++j) {
+ rl[cur++] = sub_list[j];
+ }
+ FREE(sub_list);
+ sub_list = NULL;
+ }
+ sub_pl = 0;
+ } else {
+ rl[cur] = STRDUP(new_list[i]);
+ if (rl[cur] == NULL) {
+ LOG(PIL_CRIT, "Out of memory");
+ goto types_exit_mem;
+ }
+ cur++;
+ }
+ }
+
+ rl[cur] = NULL;
+ goto types_exit;
+
+types_exit_mem:
+ stonith_free_hostlist(rl);
+ rl = NULL;
+types_exit:
+ PILFreePluginList(new_list);
+ return rl;
+}
+
+/* Destroy the STONITH object... */
+
+void
+stonith_delete(Stonith *s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ char * st = sp->s.stype;
+ sp->s_ops->destroy(sp);
+ PILIncrIFRefCount(StonithPIsys, STONITH_TYPE_S, st, -1);
+ /* destroy should not free it */
+ FREE(st);
+ }
+}
+
+const char * const *
+stonith_get_confignames(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ return sp->s_ops->get_confignames(sp);
+ }
+ return NULL;
+}
+
+const char*
+stonith_get_info(Stonith* s, int infotype)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ return sp->s_ops->get_info(sp, infotype);
+ }
+ return NULL;
+
+}
+
+void
+stonith_set_debug (Stonith* s, int debuglevel)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (StonithPIsys == NULL) {
+ return;
+ }
+ PILSetDebugLevel(StonithPIsys, STONITH_TYPE_S, sp->s.stype, debuglevel);
+}
+
+void
+stonith_set_log(Stonith* s, PILLogFun logfun)
+{
+ if (StonithPIsys == NULL) {
+ return;
+ }
+ PilPluginUnivSetLog(StonithPIsys, logfun);
+}
+
+int
+stonith_set_config(Stonith* s, StonithNVpair* list)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+
+ if (sp && sp->s_ops) {
+ int rc = sp->s_ops->set_config(sp, list);
+ if (rc == S_OK) {
+ sp->isconfigured = TRUE;
+ }
+ return rc;
+ }
+ return S_INVAL;
+}
+
+/*
+ * FIXME: We really ought to support files with name=value type syntax
+ * on each line...
+ *
+ */
+int
+stonith_set_config_file(Stonith* s, const char * configname)
+{
+ FILE * cfgfile;
+
+ char line[1024];
+
+ if ((cfgfile = fopen(configname, "r")) == NULL) {
+ LOG(PIL_CRIT, "Cannot open %s", configname);
+ return(S_BADCONFIG);
+ }
+ while (fgets(line, sizeof(line), cfgfile) != NULL){
+ int len;
+
+ if (*line == '#' || *line == '\n' || *line == EOS) {
+ continue;
+ }
+
+ /*remove the new line in the end*/
+ len = strnlen(line, sizeof(line)-1);
+ if (line[len-1] == '\n'){
+ line[len-1] = '\0';
+ }else{
+ line[len] = '\0';
+ }
+
+ fclose(cfgfile);
+ return stonith_set_config_info(s, line);
+ }
+ fclose(cfgfile);
+ return S_BADCONFIG;
+}
+
+int
+stonith_set_config_info(Stonith* s, const char * info)
+{
+ StonithNVpair* cinfo;
+ int rc;
+ cinfo = stonith1_compat_string_to_NVpair(s, info);
+ if (cinfo == NULL) {
+ return S_BADCONFIG;
+ }
+ rc = stonith_set_config(s, cinfo);
+ free_NVpair(cinfo); cinfo = NULL;
+ return rc;
+}
+
+char**
+stonith_get_hostlist(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ return sp->s_ops->get_hostlist(sp);
+ }
+ return NULL;
+}
+
+void
+stonith_free_hostlist(char** hostlist)
+{
+ char ** here;
+
+ for (here=hostlist; *here; ++here) {
+ FREE(*here);
+ }
+ FREE(hostlist);
+}
+
+int
+stonith_get_status(Stonith* s)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ return sp->s_ops->get_status(sp);
+ }
+ return S_INVAL;
+}
+
+void
+strdown(char *str)
+{
+ while( *str ) {
+ if( isupper(*str) )
+ *str = tolower(*str);
+ str++;
+ }
+}
+
+int
+stonith_req_reset(Stonith* s, int operation, const char* node)
+{
+ StonithPlugin* sp = (StonithPlugin*)s;
+ if (sp && sp->s_ops && sp->isconfigured) {
+ char* nodecopy = STRDUP(node);
+ int rc;
+ if (nodecopy == NULL) {
+ return S_OOPS;
+ }
+ strdown(nodecopy);
+
+ rc = sp->s_ops->req_reset(sp, operation, nodecopy);
+ FREE(nodecopy);
+ return rc;
+ }
+ return S_INVAL;
+}
+/* Stonith 1 compatibility: Convert a string to an NVpair set */
+StonithNVpair*
+stonith1_compat_string_to_NVpair(Stonith* s, const char * str)
+{
+ /* We make some assumptions that the order of parameters in the
+ * result from stonith_get_confignames() matches that which
+ * was required from a Stonith1 module.
+ * Everything after the last delimiter is passed along as part of
+ * the final argument - white space and all...
+ */
+ const char * const * config_names;
+ int n_names;
+ int j;
+ const char * delims = " \t\n\r\f";
+ StonithNVpair* ret;
+
+ if ((config_names = stonith_get_confignames(s)) == NULL) {
+ return NULL;
+ }
+ for (n_names=0; config_names[n_names] != NULL; ++n_names) {
+ /* Just count */;
+ }
+ ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
+ for (j=0; j < n_names; ++j) {
+ size_t len;
+ if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
+ goto freeandexit;
+ }
+ ret[j].s_value = NULL;
+ str += strspn(str, delims);
+ if (*str == EOS) {
+ goto freeandexit;
+ }
+ if (j == (n_names -1)) {
+ len = strlen(str);
+ }else{
+ len = strcspn(str, delims);
+ }
+ if ((ret[j].s_value = MALLOC((len+1)*sizeof(char))) == NULL) {
+ goto freeandexit;
+ }
+ memcpy(ret[j].s_value, str, len);
+ ret[j].s_value[len] = EOS;
+ str += len;
+ }
+ ret[j].s_name = NULL;
+ return ret;
+freeandexit:
+ free_NVpair(ret); ret = NULL;
+ return NULL;
+}
+
+StonithNVpair*
+stonith_env_to_NVpair(Stonith* s)
+{
+ /* Read the config names values from the environment */
+ const char * const * config_names;
+ int n_names;
+ int j;
+ StonithNVpair* ret;
+
+ if ((config_names = stonith_get_confignames(s)) == NULL) {
+ return NULL;
+ }
+ for (n_names=0; config_names[n_names] != NULL; ++n_names) {
+ /* Just count */;
+ }
+ ret = (StonithNVpair*) (MALLOC((n_names+1)*sizeof(StonithNVpair)));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, (n_names+1)*sizeof(StonithNVpair));
+ for (j=0; j < n_names; ++j) {
+ char *env_value;
+ if ((ret[j].s_name = STRDUP(config_names[j])) == NULL) {
+ goto freeandexit;
+ }
+ env_value = getenv(config_names[j]);
+ if (env_value) {
+ if ((ret[j].s_value = STRDUP(env_value)) == NULL) {
+ goto freeandexit;
+ }
+ } else {
+ ret[j].s_value = NULL;
+ }
+ }
+ ret[j].s_name = NULL;
+ return ret;
+freeandexit:
+ free_NVpair(ret); ret = NULL;
+ return NULL;
+}
+
+static int NVcur = -1;
+static int NVmax = -1;
+static gboolean NVerr = FALSE;
+
+static void
+stonith_walk_ghash(gpointer key, gpointer value, gpointer user_data)
+{
+ StonithNVpair* u = user_data;
+
+ if (NVcur <= NVmax && !NVerr) {
+ u[NVcur].s_name = STRDUP(key);
+ u[NVcur].s_value = STRDUP(value);
+ if (u[NVcur].s_name == NULL || u[NVcur].s_value == NULL) {
+ /* Memory allocation error */
+ NVerr = TRUE;
+ return;
+ }
+ ++NVcur;
+ }else{
+ NVerr = TRUE;
+ }
+}
+
+
+StonithNVpair*
+stonith_ghash_to_NVpair(GHashTable* stringtable)
+{
+ int hsize = g_hash_table_size(stringtable);
+ StonithNVpair* ret;
+
+ if ((ret = (StonithNVpair*)MALLOC(sizeof(StonithNVpair)*(hsize+1))) == NULL) {
+ return NULL;
+ }
+ NVmax = hsize;
+ NVcur = 0;
+ ret[hsize].s_name = NULL;
+ ret[hsize].s_value = NULL;
+ g_hash_table_foreach(stringtable, stonith_walk_ghash, ret);
+ NVmax = NVcur = -1;
+ if (NVerr) {
+ free_NVpair(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+void
+free_NVpair(StonithNVpair* nv)
+{
+ StonithNVpair* this;
+
+ if (nv == NULL) {
+ return;
+ }
+ for (this=nv; this->s_name; ++this) {
+ FREE(this->s_name);
+ if (this->s_value) {
+ FREE(this->s_value);
+ }
+ }
+ FREE(nv);
+}
diff --git a/logd/Makefile.am b/logd/Makefile.am
new file mode 100644
index 0000000..7ac75e2
--- /dev/null
+++ b/logd/Makefile.am
@@ -0,0 +1,56 @@
+#
+# hbclient library: Linux-HA heartbeat code
+#
+# Copyright (C) 2001 Michael Moerz
+# Copyright (C) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+halibdir = $(libdir)/@HB_PKG@
+ha_sbindir = $(sbindir)
+
+LIBRT = @LIBRT@
+AM_CFLAGS = @CFLAGS@
+
+initddir = @INITDIR@
+
+## binary progs
+ha_sbin_PROGRAMS = ha_logger
+halib_PROGRAMS = ha_logd logtest
+
+ha_logd_SOURCES = ha_logd.c
+ha_logd_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(top_builddir)/lib/clplumbing/libplumbgpl.la
+
+# $(top_builddir)/lib/apphb/libapphb.la
+
+ha_logger_SOURCES = ha_logger.c
+ha_logger_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la
+
+logtest_SOURCES = logtest.c
+logtest_LDADD = $(top_builddir)/lib/clplumbing/libplumb.la
+
+if HAVE_SYSTEMD
+systemdsystemunit_DATA = \
+ logd.service
+else
+initd_SCRIPTS = logd
+endif
diff --git a/logd/ha_logd.c b/logd/ha_logd.c
new file mode 100644
index 0000000..c98b9d7
--- /dev/null
+++ b/logd/ha_logd.c
@@ -0,0 +1,1085 @@
+/*
+ * ha_logd.c logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <lha_internal.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/GSource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <clplumbing/loggingdaemon.h>
+#include <netinet/in.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/coredumps.h>
+#include <clplumbing/setproctitle.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_misc.h>
+#include <sys/wait.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/cl_syslog.h>
+
+/*two processes involved
+ 1. parent process which reads messages from all client channels
+ and writes them to the child process
+
+ 2. the child process which reads messages from the parent process through IPC
+ and writes them to syslog/disk
+
+ I call the parent process READ process, and the child process WRITE one,
+ for convenience.
+
+*/
+
+
+
+#define DEFAULT_CFG_FILE HA_SYSCONFDIR "/logd.cf"
+#define LOGD_PIDFILE HA_VARRUNDIR "/logd.pid"
+
+#define FD_STDIN 0
+#define FD_STDOUT 1
+
+#define FD_STDERR 2
+
+
+#define WRITE_PROC_CHAN 0
+#define READ_PROC_CHAN 1
+#define LOGD_QUEUE_LEN 128
+
+#define EOS '\0'
+#define nullchk(a) ((a) ? (a) : "<null>")
+
+static const int logd_keepalive_ms = 1000;
+static const int logd_warntime_ms = 5000;
+static const int logd_deadtime_ms = 10000;
+static gboolean verbose = FALSE;
+static pid_t write_process_pid;
+static IPC_Channel *chanspair[2];
+static gboolean stop_reading = FALSE;
+static gboolean needs_shutdown = FALSE;
+
+static struct {
+ char debugfile[MAXLINE];
+ char logfile[MAXLINE];
+ char entity[MAXENTITY];
+ char syslogprefix[MAXENTITY];
+ int log_facility;
+ mode_t logmode;
+ gboolean syslogfmtmsgs;
+} logd_config =
+ {
+ .debugfile = "",
+ .logfile = "",
+ .entity = "logd",
+ .syslogprefix = "",
+ .log_facility = HA_LOG_FACILITY,
+ .logmode = 0644,
+ .syslogfmtmsgs = FALSE
+ };
+
+static void logd_log(const char * fmt, ...) G_GNUC_PRINTF(1,2);
+static int set_debugfile(const char* option);
+static int set_logfile(const char* option);
+static int set_facility(const char * value);
+static int set_entity(const char * option);
+static int set_syslogprefix(const char * option);
+static int set_sendqlen(const char * option);
+static int set_recvqlen(const char * option);
+static int set_logmode(const char * option);
+static int set_syslogfmtmsgs(const char * option);
+
+
+static char* cmdname = NULL;
+
+
+static struct directive {
+ const char* name;
+ int (*add_func)(const char*);
+} Directives[] = {
+ {"debugfile", set_debugfile},
+ {"logfile", set_logfile},
+ {"logfacility", set_facility},
+ {"entity", set_entity},
+ {"syslogprefix",set_syslogprefix},
+ {"sendqlen", set_sendqlen},
+ {"recvqlen", set_recvqlen},
+ {"logmode", set_logmode},
+ {"syslogmsgfmt",set_syslogfmtmsgs}
+};
+
+static void
+logd_log( const char * fmt, ...)
+{
+ char buf[MAXLINE];
+ va_list ap;
+
+ buf[MAXLINE-1] = EOS;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s", buf);
+
+ return;
+}
+
+static int
+set_debugfile(const char* option)
+{
+ if (!option){
+ logd_config.debugfile[0] = EOS;
+ return FALSE;
+ }
+
+ cl_log(LOG_INFO, "setting debug file to %s", option);
+ strncpy(logd_config.debugfile, option, MAXLINE);
+ return TRUE;
+}
+static int
+set_logfile(const char* option)
+{
+ if (!option){
+ logd_config.logfile[0] = EOS;
+ return FALSE;
+ }
+ cl_log(LOG_INFO, "setting log file to %s", option);
+ strncpy(logd_config.logfile, option, MAXLINE);
+ return TRUE;
+}
+
+/* set syslog facility config variable */
+static int
+set_facility(const char * value)
+{
+ int i;
+
+ i = cl_syslogfac_str2int(value);
+ if (i >= 0) {
+ cl_log(LOG_INFO, "setting log facility to %s", value);
+ logd_config.log_facility = i;
+ return(TRUE);
+ }
+ else {
+ return(FALSE);
+ }
+}
+
+static int
+set_entity(const char * option)
+{
+ if (!option){
+ logd_config.entity[0] = EOS;
+ return FALSE;
+ }
+ strncpy(logd_config.entity, option, MAXENTITY);
+ logd_config.entity[MAXENTITY-1] = '\0';
+ if (strlen(option) >= MAXENTITY)
+ cl_log(LOG_WARNING, "setting entity to %s (truncated from %s)",
+ logd_config.entity, option);
+ else
+ cl_log(LOG_INFO, "setting entity to %s", logd_config.entity);
+ return TRUE;
+
+}
+
+static int
+set_syslogprefix(const char * option)
+{
+ if (!option){
+ logd_config.syslogprefix[0] = EOS;
+ return FALSE;
+ }
+ strncpy(logd_config.syslogprefix, option, MAXENTITY);
+ logd_config.syslogprefix[MAXENTITY-1] = '\0';
+ if (strlen(option) >= MAXENTITY)
+ cl_log(LOG_WARNING,
+ "setting syslogprefix to %s (truncated from %s)",
+ logd_config.syslogprefix, option);
+ else
+ cl_log(LOG_INFO,
+ "setting syslogprefix to %s",
+ logd_config.syslogprefix);
+ return TRUE;
+
+}
+
+static int
+set_sendqlen(const char * option)
+{
+ int length;
+
+ if (!option){
+ cl_log(LOG_ERR, "NULL send queue length");
+ return FALSE;
+ }
+
+ length = atoi(option);
+ if (length < 0){
+ cl_log(LOG_ERR, "negative send queue length");
+ return FALSE;
+ }
+
+ cl_log(LOG_INFO, "setting send queue length to %d", length);
+ chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN],
+ length);
+
+ return TRUE;
+
+}
+
+static int
+set_recvqlen(const char * option)
+{
+ int length;
+
+ if (!option){
+ cl_log(LOG_ERR, "NULL recv queue length");
+ return FALSE;
+ }
+
+ length = atoi(option);
+ if (length < 0){
+ cl_log(LOG_ERR, "negative recv queue length");
+ return FALSE;
+ }
+
+ cl_log(LOG_INFO, "setting recv queue length to %d", length);
+ chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN],
+ length);
+
+ return TRUE;
+
+}
+
+static int
+set_logmode(const char * option)
+{
+ unsigned long mode;
+ char * endptr;
+ if (!option){
+ cl_log(LOG_ERR, "NULL logmode parameter");
+ return FALSE;
+ }
+ mode = strtoul(option, &endptr, 8);
+ if (*endptr != EOS) {
+ cl_log(LOG_ERR, "Invalid log mode [%s]", option);
+ return FALSE;
+ }
+ if (*option != '0') {
+ /* Whine if mode doesn't start with '0' */
+ cl_log(LOG_WARNING, "Log mode [%s] assumed to be octal"
+ , option);
+ }
+ logd_config.logmode = (mode_t)mode;
+ return TRUE;
+}
+static int
+set_syslogfmtmsgs(const char * option)
+{
+ gboolean dosyslogfmt;
+
+ if (cl_str_to_boolean(option, &dosyslogfmt) == HA_OK) {
+ cl_log_enable_syslog_filefmt(dosyslogfmt);
+ }else{
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+typedef struct {
+ char app_name[MAXENTITY];
+ pid_t pid;
+ gid_t gid;
+ uid_t uid;
+
+ IPC_Channel* chan;
+ IPC_Channel* logchan;
+ GCHSource* g_src;
+}ha_logd_client_t;
+
+static GList* logd_client_list = NULL;
+
+static IPC_Message*
+getIPCmsg(IPC_Channel* ch)
+{
+
+ int rc;
+ IPC_Message* ipcmsg;
+
+ /* FIXME: Should we block here?? */
+ rc = ch->ops->waitin(ch);
+
+ switch(rc) {
+ default:
+ case IPC_FAIL:
+ cl_log(LOG_ERR, "getIPCmsg: waitin failure\n");
+ return NULL;
+
+ case IPC_BROKEN:
+ sleep(1);
+ return NULL;
+
+ case IPC_INTR:
+ return NULL;
+
+ case IPC_OK:
+ break;
+ }
+
+ ipcmsg = NULL;
+ rc = ch->ops->recv(ch, &ipcmsg);
+ if (rc != IPC_OK) {
+ return NULL;
+ }
+
+ return ipcmsg;
+
+}
+
+/* Flow control all clients off */
+static void
+logd_suspend_clients(IPC_Channel* notused1, gpointer notused2)
+{
+ GList * gl;
+
+ stop_reading = TRUE;
+ for (gl=g_list_first(logd_client_list); gl != NULL
+ ; gl = g_list_next(gl)) {
+ ha_logd_client_t* client = gl->data;
+ if (client && client->g_src) {
+ G_main_IPC_Channel_pause(client->g_src);
+ }else if (client) {
+ cl_log(LOG_ERR, "Could not suspend client [%s] pid %d"
+ , nullchk(client->app_name), client->pid);
+ }else{
+ cl_log(LOG_ERR, "%s: Could not suspend NULL client",
+ __FUNCTION__);
+ }
+ }
+}
+
+/* Resume input from clients - Flow control all clients back on */
+static void
+logd_resume_clients(IPC_Channel* notused1, gpointer notused2)
+{
+ GList * gl;
+
+ stop_reading = FALSE;
+ for (gl=g_list_first(logd_client_list); gl != NULL
+ ; gl = g_list_next(gl)) {
+ ha_logd_client_t* client = gl->data;
+ if (client && client->g_src) {
+ G_main_IPC_Channel_resume(client->g_src);
+ }else if (client) {
+ cl_log(LOG_ERR, "Could not resume client [%s] pid %d"
+ , nullchk(client->app_name), client->pid);
+ }else{
+ cl_log(LOG_ERR, "%s: Could not suspend NULL client",
+ __FUNCTION__);
+ }
+ }
+}
+
+static gboolean
+on_receive_cmd (IPC_Channel* ch, gpointer user_data)
+{
+ IPC_Message* ipcmsg;
+ ha_logd_client_t* client = (ha_logd_client_t*)user_data;
+ IPC_Channel* logchan= client->logchan;
+
+
+ if (!ch->ops->is_message_pending(ch)) {
+ goto getout;
+ }
+
+ ipcmsg = getIPCmsg(ch);
+ if (ipcmsg == NULL){
+ if (IPC_ISRCONN(ch)) {
+ cl_log(LOG_ERR, "%s: read error on connected channel [%s:%d]"
+ , __FUNCTION__, client->app_name, client->pid);
+ }
+ return FALSE;
+ }
+
+ if( ipcmsg->msg_body && ipcmsg->msg_len > 0 ){
+
+ if (client->app_name[0] == '\0'){
+ LogDaemonMsgHdr* logmsghdr;
+ logmsghdr = (LogDaemonMsgHdr*) ipcmsg->msg_body;
+ strncpy(client->app_name, logmsghdr->entity, MAXENTITY);
+ }
+
+ if (!IPC_ISWCONN(logchan)){
+ cl_log(LOG_ERR
+ , "%s: channel to write process disconnected"
+ , __FUNCTION__);
+ return FALSE;
+ }
+ if (logchan->ops->send(logchan, ipcmsg) != IPC_OK){
+ cl_log(LOG_ERR
+ , "%s: forwarding msg from [%s:%d] to"
+ " write process failed"
+ , __FUNCTION__
+ , client->app_name, client->pid);
+ cl_log(LOG_ERR, "queue too small? (max=%ld, current len =%ld)",
+ (long)logchan->send_queue->max_qlen,
+ (long)logchan->send_queue->current_qlen);
+ return TRUE;
+ }
+
+ }else {
+ cl_log(LOG_ERR, "on_receive_cmd:"
+ " invalid ipcmsg\n");
+ }
+
+ getout:
+ return TRUE;
+}
+
+static void
+on_remove_client (gpointer user_data)
+{
+
+ logd_client_list = g_list_remove(logd_client_list, user_data);
+ if (user_data){
+ free(user_data);
+ }
+ return;
+}
+
+
+
+/*
+ *GLoop Message Handlers
+ */
+static gboolean
+on_connect_cmd (IPC_Channel* ch, gpointer user_data)
+{
+ ha_logd_client_t* client = NULL;
+
+ /* check paremeters */
+ if (NULL == ch) {
+ cl_log(LOG_ERR, "on_connect_cmd: channel is null");
+ return TRUE;
+ }
+ /* create new client */
+ if (NULL == (client = malloc(sizeof(ha_logd_client_t)))) {
+ return FALSE;
+ }
+ memset(client, 0, sizeof(ha_logd_client_t));
+ client->pid = ch->farside_pid;
+ client->chan = ch;
+ client->logchan = (IPC_Channel*)user_data;
+ client->g_src = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
+ ch, FALSE, on_receive_cmd,
+ (gpointer)client,
+ on_remove_client);
+ if (client->g_src == NULL){
+ cl_log(LOG_ERR, "add the client to main loop failed");
+ free(client);
+ return TRUE;
+ }
+ if (stop_reading){
+ G_main_IPC_Channel_pause(client->g_src);
+ }
+
+ logd_client_list = g_list_append(logd_client_list, client);
+
+
+ return TRUE;
+}
+
+
+
+static void
+logd_make_daemon(gboolean daemonize)
+{
+ long pid;
+
+ if (daemonize) {
+ if (daemon(0,0)) {
+ fprintf(stderr, "%s: could not start daemon\n"
+ , cmdname);
+ perror("fork");
+ exit(LSB_EXIT_GENERIC);
+ }
+ }
+
+ if (cl_lock_pidfile(LOGD_PIDFILE) < 0 ){
+ pid = cl_read_pidfile(LOGD_PIDFILE);
+ if (pid > 0)
+ fprintf(stderr, "%s: already running [pid %ld].\n",
+ cmdname, pid);
+ else
+ fprintf(stderr, "%s: problem creating pid file %s\n",
+ cmdname, LOGD_PIDFILE);
+ exit(LSB_EXIT_OK);
+ }
+
+ if (daemonize || !verbose){
+ cl_log_enable_stderr(FALSE);
+ }
+
+ if (daemonize){
+ mode_t mask;
+ /*
+ * Some sample umask calculations:
+ *
+ * logmode = 0644
+ *
+ * (~0644)&0777 = 0133
+ * (0133 & ~0111) = 0022
+ * => umask will be 022 (the expected result)
+ *
+ * logmode = 0600
+ * (~0600)&0777 = 0177
+ * (0177 & ~0111) = 0066
+ */
+ mask = (mode_t)(((~logd_config.logmode) & 0777) & (~0111));
+ umask(mask);
+ }
+}
+
+
+
+static void
+logd_stop(void)
+{
+
+ long running_logd_pid = cl_read_pidfile(LOGD_PIDFILE);
+ int err;
+
+ if (running_logd_pid < 0) {
+ fprintf(stderr, "ha_logd already stopped.\n");
+ cl_log(LOG_INFO, "ha_logd already stopped.");
+ exit(LSB_EXIT_OK);
+ }
+
+ cl_log(LOG_DEBUG, "Stopping ha_logd with pid %ld", running_logd_pid);
+ if (kill((pid_t)running_logd_pid, SIGTERM) >= 0) {
+ /* Wait for the running logd to die */
+ cl_log(LOG_INFO, "Waiting for pid=%ld to exit",
+ running_logd_pid);
+ alarm(0);
+ do {
+ sleep(1);
+ }while (kill((pid_t)running_logd_pid, 0) >= 0);
+ }
+ err = errno;
+
+ if(errno == ESRCH) {
+ cl_log(LOG_INFO, "Pid %ld exited", running_logd_pid);
+ exit(LSB_EXIT_OK);
+ } else {
+ cl_perror("Pid %ld not killed", running_logd_pid);
+ exit((err == EPERM || err == EACCES)
+ ? LSB_EXIT_EPERM
+ : LSB_EXIT_GENERIC);
+ }
+
+}
+
+
+static int
+get_dir_index(const char* directive)
+{
+ int j;
+ for(j=0; j < DIMOF(Directives); j++){
+ if (0 == strcasecmp(directive, Directives[j].name)){
+ return j;
+ }
+ }
+ return -1;
+}
+
+
+/* Adapted from parse_config in config.c */
+static gboolean
+parse_config(const char* cfgfile)
+{
+ FILE* f;
+ char buf[MAXLINE];
+ char* bp;
+ char* cp;
+ char directive[MAXLINE];
+ int dirlength;
+ int optionlength;
+ char option[MAXLINE];
+ int dir_index;
+
+ gboolean ret = TRUE;
+
+ if ((f = fopen(cfgfile, "r")) == NULL){
+ cl_log(LOG_WARNING, "Cannot open config file [%s]", cfgfile);
+ return(FALSE);
+ }
+
+ while(fgets(buf, MAXLINE, f) != NULL){
+ bp = buf;
+ /* Skip over white space*/
+ bp += strspn(bp, " \t\n\r\f");
+
+ /* comments */
+ if ((cp = strchr(bp, '#')) != NULL){
+ *cp = EOS;
+ }
+
+ if (*bp == EOS){
+ continue;
+ }
+
+ dirlength = strcspn(bp, " \t\n\f\r");
+ strncpy(directive, bp, dirlength);
+ directive[dirlength] = EOS;
+
+ if ((dir_index = get_dir_index(directive)) == -1){
+ fprintf(stderr, "Illegal directive [%s] in %s\n"
+ , directive, cfgfile);
+ ret = FALSE;
+ continue;
+ }
+
+ bp += dirlength;
+
+ /* skip delimiters */
+ bp += strspn(bp, " ,\t\n\f\r");
+
+ /* Set option */
+ optionlength = strcspn(bp, " ,\t\n\f\r");
+ strncpy(option, bp, optionlength);
+ option[optionlength] = EOS;
+ if (!(*Directives[dir_index].add_func)(option)) {
+ ret = FALSE;
+ }
+ }/*while*/
+ fclose(f);
+ return ret;
+}
+
+static gboolean
+logd_term_action(int sig, gpointer userdata)
+{
+ GList *log_iter = logd_client_list;
+ GMainLoop *mainloop = (GMainLoop*)userdata;
+ ha_logd_client_t *client = NULL;
+
+ cl_log(LOG_DEBUG, "logd_term_action: received SIGTERM");
+ if (mainloop == NULL){
+ cl_log(LOG_ERR, "logd_term_action: invalid arguments");
+ return FALSE;
+ }
+
+ stop_reading = TRUE;
+
+ while(log_iter != NULL) {
+ client = log_iter->data;
+ log_iter = log_iter->next;
+
+ cl_log(LOG_DEBUG, "logd_term_action:"
+ " waiting for %d messages to be read for process %s",
+ (int)client->logchan->send_queue->current_qlen,
+ client->app_name);
+
+ client->logchan->ops->waitout(client->logchan);
+ }
+
+ cl_log(LOG_DEBUG, "logd_term_action"
+ ": waiting for %d messages to be read by write process"
+ , (int)chanspair[WRITE_PROC_CHAN]->send_queue->current_qlen);
+ chanspair[WRITE_PROC_CHAN]->ops->waitout(chanspair[WRITE_PROC_CHAN]);
+
+ cl_log(LOG_DEBUG, "logd_term_action: sending SIGTERM to write process");
+ if (CL_KILL(write_process_pid, SIGTERM) >= 0){
+
+ pid_t pid;
+ pid = wait4(write_process_pid, NULL, 0, NULL);
+ if (pid < 0){
+ cl_log(LOG_ERR, "wait4 for write process failed");
+ }
+
+ }
+
+ g_main_quit(mainloop);
+
+ return TRUE;
+}
+
+/*
+ * Handle SIGHUP to re-open log files
+ */
+static gboolean
+logd_hup_action(int sig, gpointer userdata)
+{
+ cl_log_close_log_files();
+ if (write_process_pid)
+ /* do we want to propagate the HUP,
+ * or do we assume that it was a killall anyways? */
+ CL_KILL(write_process_pid, SIGHUP);
+ else
+ cl_log(LOG_INFO, "SIGHUP received, re-opened log files");
+ return TRUE;
+}
+
+static void
+read_msg_process(IPC_Channel* chan)
+{
+ GHashTable* conn_cmd_attrs;
+ IPC_WaitConnection* conn_cmd = NULL;
+ char path[] = "path";
+ char socketpath[] = HA_LOGDAEMON_IPC;
+ GMainLoop* mainloop;
+
+
+
+ mainloop = g_main_new(FALSE);
+
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM,
+ logd_term_action,mainloop, NULL);
+
+ conn_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+
+ g_hash_table_insert(conn_cmd_attrs, path, socketpath);
+
+ conn_cmd = ipc_wait_conn_constructor(IPC_ANYTYPE, conn_cmd_attrs);
+ g_hash_table_destroy(conn_cmd_attrs);
+
+ if (conn_cmd == NULL){
+ fprintf(stderr, "ERROR: create waiting connection failed");
+ exit(1);
+ }
+
+ /*Create a source to handle new connect rquests for command*/
+ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cmd, NULL, FALSE
+ , on_connect_cmd, chan, NULL);
+ chan->ops->set_high_flow_callback(chan, logd_suspend_clients, NULL);
+ chan->ops->set_low_flow_callback(chan, logd_resume_clients, NULL);
+ chan->high_flow_mark = chan->send_queue->max_qlen;
+ chan->low_flow_mark = (chan->send_queue->max_qlen*3)/4;
+
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan, FALSE,NULL,NULL,NULL);
+
+ G_main_add_SignalHandler(G_PRIORITY_DEFAULT, SIGHUP,
+ logd_hup_action, mainloop, NULL);
+ g_main_run(mainloop);
+
+ return;
+}
+
+static gboolean
+direct_log(IPC_Channel* ch, gpointer user_data)
+{
+ IPC_Message* ipcmsg;
+ GMainLoop* loop;
+ int pri = LOG_DEBUG + 1;
+
+ loop =(GMainLoop*)user_data;
+
+ while(ch->ops->is_message_pending(ch)){
+ if (ch->ch_status == IPC_DISCONNECT){
+ cl_log(LOG_ERR, "read channel is disconnected:"
+ "something very wrong happened");
+ return FALSE;
+ }
+
+ ipcmsg = getIPCmsg(ch);
+ if (ipcmsg == NULL){
+ return TRUE;
+ }
+
+ if( ipcmsg->msg_body
+ && ipcmsg->msg_len > 0 ){
+ LogDaemonMsgHdr *logmsghdr;
+ LogDaemonMsgHdr copy;
+ char *msgtext;
+
+ logmsghdr = (LogDaemonMsgHdr*) ipcmsg->msg_body;
+ /* this copy nonsense is here because apparently ia64
+ * complained about "unaligned memory access. */
+#define COPYFIELD(copy, msg, field) memcpy(((u_char*)&copy.field), ((u_char*)&msg->field), sizeof(copy.field))
+ COPYFIELD(copy, logmsghdr, use_pri_str);
+ COPYFIELD(copy, logmsghdr, entity);
+ COPYFIELD(copy, logmsghdr, entity_pid);
+ COPYFIELD(copy, logmsghdr, timestamp);
+ COPYFIELD(copy, logmsghdr, priority);
+ /* Don't want to copy the following message text */
+
+ msgtext = (char *)logmsghdr + sizeof(LogDaemonMsgHdr);
+ cl_direct_log(copy.priority, msgtext
+ , copy.use_pri_str
+ , copy.entity, copy.entity_pid
+ , copy.timestamp);
+
+ if (copy.priority < pri)
+ pri = copy.priority;
+
+ (void)logd_log;
+/*
+ if (verbose){
+ logd_log("%s[%d]: %s %s\n",
+ logmsg->entity[0]=='\0'?
+ "unknown": copy.entity,
+ copy.entity_pid,
+ ha_timestamp(copy.timestamp),
+ msgtext);
+ }
+ */
+ if (ipcmsg->msg_done){
+ ipcmsg->msg_done(ipcmsg);
+ }
+ }
+ }
+ /* current message backlog processed,
+ * about to return to mainloop,
+ * fflush and potentially fsync stuff */
+ cl_log_do_fflush(pri <= LOG_ERR);
+
+ if(needs_shutdown) {
+ cl_log(LOG_INFO, "Exiting write process");
+ g_main_quit(loop);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+logd_term_write_action(int sig, gpointer userdata)
+{
+ /* as a side-effect, the log message makes sure we enter direct_log()
+ * one last time (so we always exit)
+ */
+ needs_shutdown = TRUE;
+ cl_log(LOG_INFO, "logd_term_write_action: received SIGTERM");
+ cl_log(LOG_DEBUG, "Writing out %d messages then quitting",
+ (int)chanspair[WRITE_PROC_CHAN]->recv_queue->current_qlen);
+
+ direct_log(chanspair[WRITE_PROC_CHAN], userdata);
+
+ return TRUE;
+}
+
+static void
+write_msg_process(IPC_Channel* readchan)
+{
+
+ GMainLoop* mainloop;
+ IPC_Channel* ch = readchan;
+
+
+ mainloop = g_main_new(FALSE);
+
+ G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
+ ch, FALSE,
+ direct_log, mainloop, NULL);
+
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM,
+ logd_term_write_action, mainloop, NULL);
+
+ G_main_add_SignalHandler(G_PRIORITY_DEFAULT, SIGHUP,
+ logd_hup_action, mainloop, NULL);
+
+ g_main_run(mainloop);
+
+}
+
+
+
+
+
+
+static void
+usage(void)
+{
+ printf("usage: \n"
+ "%s [options]\n\n"
+ "options: \n"
+ "-d make the program a daemon\n"
+ "-k stop the logging daemon if it is already running\n"
+ "-s return logging daemon status \n"
+ "-c use this config file\n"
+ "-v verbosely print debug messages"
+ "-h print out this message\n\n",
+ cmdname);
+
+ return;
+}
+int
+main(int argc, char** argv, char** envp)
+{
+
+ int c;
+ gboolean daemonize = FALSE;
+ gboolean stop_logd = FALSE;
+ gboolean ask_status= FALSE;
+ const char* cfgfile = NULL;
+ pid_t pid;
+
+ cmdname = argv[0];
+ while ((c = getopt(argc, argv, "c:dksvh")) != -1){
+
+ switch(c){
+
+ case 'd': /* daemonize */
+ daemonize = TRUE;
+ break;
+ case 'k': /* stop */
+ stop_logd = TRUE;
+ break;
+ case 's': /* status */
+ ask_status = TRUE;
+ break;
+ case 'c': /* config file*/
+ cfgfile = optarg;
+ break;
+ case 'v':
+ verbose = TRUE;
+ break;
+ case 'h': /*help message */
+ default:
+ usage();
+ exit(1);
+ }
+
+ }
+
+ set_ipc_time_debug_flag(FALSE);
+ cl_log_set_uselogd(FALSE);
+
+ if (!cfgfile && access(DEFAULT_CFG_FILE, F_OK) == 0) {
+ cfgfile = DEFAULT_CFG_FILE;
+ }
+
+
+ /* default one set to "logd"
+ * by setting facility, we enable syslog
+ */
+ cl_log_enable_stderr(TRUE);
+ cl_log_set_entity(logd_config.entity);
+ cl_log_set_facility(logd_config.log_facility);
+
+
+ if (ask_status){
+ long pid;
+
+ if( (pid = cl_read_pidfile(LOGD_PIDFILE)) > 0 ){
+ printf("logging daemon is running [pid = %ld].\n", pid);
+ exit(LSB_EXIT_OK);
+ }else{
+ if (pid == - LSB_STATUS_VAR_PID) {
+ printf("logging daemon is stopped: %s exists.\n"
+ , LOGD_PIDFILE);
+ }else{
+ printf("logging daemon is stopped.\n");
+ }
+ }
+ exit(-pid);
+
+ }
+ if (stop_logd){
+ logd_stop();
+ exit(LSB_EXIT_OK);
+ }
+
+ logd_make_daemon(daemonize);
+
+
+ if (ipc_channel_pair(chanspair) != IPC_OK){
+ cl_perror("cannot create channel pair IPC");
+ return -1;
+ }
+
+
+ if (cfgfile && !parse_config(cfgfile)) {
+ FILE* f;
+ if ((f = fopen(cfgfile, "r")) != NULL){
+ fclose(f);
+ cl_log(LOG_ERR, "Config file [%s] is incorrect."
+ , cfgfile);
+ exit(LSB_EXIT_NOTCONFIGED);
+ }
+ }
+
+ if (strlen(logd_config.debugfile) > 0) {
+ cl_log_set_debugfile(logd_config.debugfile);
+ }
+ if (strlen(logd_config.logfile) > 0) {
+ cl_log_set_logfile(logd_config.logfile);
+ }
+ cl_log_set_syslogprefix(logd_config.syslogprefix);
+ cl_log_set_entity(logd_config.entity);
+ cl_log_set_facility(logd_config.log_facility);
+
+ cl_log(LOG_INFO, "logd started with %s.",
+ cfgfile ? cfgfile : "default configuration");
+
+ if (cl_enable_coredumps(TRUE) < 0){
+ cl_log(LOG_ERR, "enabling core dump failed");
+ }
+ cl_cdtocoredir();
+
+
+
+
+ chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN],
+ LOGD_QUEUE_LEN);
+
+ chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN],
+ LOGD_QUEUE_LEN);
+
+ if (init_set_proc_title(argc, argv, envp) < 0) {
+ cl_log(LOG_ERR, "Allocation of proc title failed.");
+ return -1;
+ }
+
+ switch(pid = fork()){
+ case -1:
+ cl_perror("Can't fork child process!");
+ return -1;
+ case 0:
+ /*child*/
+ cl_log_use_buffered_io(1);
+ set_proc_title("ha_logd: write process");
+ write_msg_process(chanspair[WRITE_PROC_CHAN]);
+ break;
+ default:
+ /*parent*/
+ set_proc_title("ha_logd: read process");
+ write_process_pid = pid;
+ /* we don't expect to log anything in the parent. */
+ cl_log_close_log_files();
+
+ read_msg_process(chanspair[READ_PROC_CHAN]);
+ break;
+ }
+ return 0;
+}
+
+
+
+
diff --git a/logd/ha_logger.1 b/logd/ha_logger.1
new file mode 100644
index 0000000..db7f939
--- /dev/null
+++ b/logd/ha_logger.1
@@ -0,0 +1,38 @@
+.TH HA_LOGGER 1 "5th Nov 2004"
+.SH NAME
+.B ha_logger
+\- Log a message to files/syslog through the HA Logging Daemon
+.SH SYNOPSIS
+.B ha_logger
+.RI "[-D ha-log/ha-debug] [-t tag] [message ...]"
+.P
+.SH DESCRIPTION
+.B ha_logger
+is used to log a message to files/syslog through the HA Logging Daemon
+.PP
+.IP "\fB-D\fP \fIha-log/ha-debug\fP"
+Log the message to different files. Ha-log will log the message to
+the log file and the debug file, while ha-debug will log the message
+to the debug file only.
+.IP "\fB-t\fP \fItag\fP"
+Mark every line in the log with the specified
+.IP \fBmessage\fP
+The message you want to log on.
+.PP
+.SH SEE ALSO
+ha_logd(8) heartbeat(8)
+
+.SH DOCUMENTATION
+More information may be found at
+.UR http://linux-ha.org/wiki
+http://linux-ha.org/wiki
+.UE
+
+.SH AUTHORS
+.nf
+ Guochun Shi <gshi@ncsa.uiuc.edu>
+ Alan Robertson <alanr@unix.sh>
+ Lars Marowsky-Bree <lmb@suse.de>
+.fi
+
+
diff --git a/logd/ha_logger.c b/logd/ha_logger.c
new file mode 100644
index 0000000..5e2f9ef
--- /dev/null
+++ b/logd/ha_logger.c
@@ -0,0 +1,142 @@
+/*
+ * ha_logger.c utility to log a message to the logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <lha_internal.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/GSource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <clplumbing/loggingdaemon.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#define EXIT_OK 0
+#define EXIT_FAIL 1
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+void cl_log(int priority, const char * fmt, ...) G_GNUC_PRINTF(2,3);
+static void
+usage(void)
+{
+ printf("usage: "
+ "ha_logger [-t tag] [-D <ha-log/ha-debug>] [message]\n");
+ return;
+}
+#define BUFSIZE 1024
+int
+main(int argc, char** argv)
+{
+ int priority;
+ char* entity = NULL;
+ int c;
+ char buf[BUFSIZE];
+ const char* logtype = "ha-log";
+
+
+ while (( c =getopt(argc, argv,"t:D:h")) != -1){
+ switch(c){
+
+ case 't':
+ entity = optarg;
+ break;
+ case 'D':
+ logtype=optarg;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ usage();
+ exit(1);
+ }
+
+ }
+
+ if(!cl_log_test_logd()){
+ fprintf(stderr, "logd is not running");
+ return EXIT_FAIL;
+ }
+
+ argc -=optind;
+ argv += optind;
+
+ if (entity != NULL){
+ cl_log_set_entity(entity);
+ }
+
+ if (strcmp(logtype, "ha-log") == 0){
+ priority = LOG_INFO;
+ } else if (strcmp(logtype, "ha-debug") == 0){
+ priority = LOG_DEBUG;
+ }else{
+ goto err_exit;
+ }
+
+ if (argc > 0){
+ register char *p;
+
+ for (p = *argv; *argv; argv++, p = *argv) {
+ while (strlen(p) > BUFSIZE-1) {
+ memcpy(buf, p, BUFSIZE-1);
+ *(buf+BUFSIZE-1) = '\0';
+ if (LogToDaemon(priority,buf,
+ BUFSIZE,FALSE) != HA_OK){
+ return EXIT_FAIL;
+ }
+ p += BUFSIZE-1;
+ }
+ if (LogToDaemon(priority,p,
+ strnlen(p, BUFSIZE),FALSE) != HA_OK){
+ return EXIT_FAIL;
+ }
+ }
+ return EXIT_OK;
+ }else {
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ /* glibc is buggy and adds an additional newline,
+ so we have to remove it here until glibc is fixed */
+ int len = strlen(buf);
+
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ if (LogToDaemon(priority, buf,strlen(buf), FALSE) == HA_OK){
+ continue;
+ }else {
+ return EXIT_FAIL;
+ }
+ }
+
+ return EXIT_OK;
+ }
+
+ err_exit:
+ usage();
+ return(1);
+
+}
+
diff --git a/logd/logd.cf b/logd/logd.cf
new file mode 100644
index 0000000..cd8ccc3
--- /dev/null
+++ b/logd/logd.cf
@@ -0,0 +1,66 @@
+# File to write debug messages to
+# Default: /var/log/ha-debug
+#debugfile /var/log/ha-debug
+
+#
+#
+# File to write other messages to
+# Default: /var/log/ha-log
+#logfile /var/log/ha-log
+
+#
+#
+# Octal file permission to create the log files with
+# Default: 0644
+#logmode 0640
+
+
+#
+#
+# Facility to use for syslog()/logger
+# (set to 'none' to disable syslog logging)
+# Default: daemon
+#logfacility daemon
+
+
+# Entity to be shown at beginning of a message
+# generated by the logging daemon itself
+# Default: "logd"
+#entity logd
+
+
+# Entity to be shown at beginning of _every_ message
+# passed to syslog (not to log files).
+#
+# Intended for easier filtering, or safe blacklisting.
+# You can filter on logfacility and this prefix.
+#
+# Message format changes like this:
+# -Nov 18 11:30:31 soda logtest: [21366]: info: total message dropped: 0
+# +Nov 18 11:30:31 soda common-prefix: logtest[21366]: info: total message dropped: 0
+#
+# Default: none (disabled)
+#syslogprefix linux-ha
+
+
+# Do we register to apphbd
+# Default: no
+#useapphbd no
+
+# There are two processes running for logging daemon
+# 1. parent process which reads messages from all client channels
+# and writes them to the child process
+#
+# 2. the child process which reads messages from the parent process through IPC
+# and writes them to syslog/disk
+
+
+# set the send queue length from the parent process to the child process
+#
+#sendqlen 256
+
+# set the recv queue length in child process
+#
+#recvqlen 256
+
+
diff --git a/logd/logd.in b/logd/logd.in
new file mode 100755
index 0000000..41d60c5
--- /dev/null
+++ b/logd/logd.in
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+#
+# logd Start logd (non-blocking log service)
+#
+# Author: Dejan Muhamedagic <dmuhamedagic@suse.de>
+# (After the heartbeat init script)
+# License: GNU General Public License (GPL)
+#
+# This script works correctly under SuSE, Debian,
+# Conectiva, Red Hat and a few others. Please let me know if it
+# doesn't work under your distribution, and we'll fix it.
+# We don't hate anyone, and like for everyone to use
+# our software, no matter what OS or distribution you're using.
+#
+# chkconfig: 2345 19 21
+# description: Startup script logd service.
+# processname: ha_logd
+# pidfile: @localstatedir@/run/logd.pid
+# config: @sysconfdir@/logd.cf
+#
+### BEGIN INIT INFO
+# Description: ha_logd is a non-blocking logging daemon.
+# It can log messages either to a file or through syslog
+# daemon.
+# Short-Description: ha_logd logging daemon
+# Provides: ha_logd
+# Required-Start: $network $syslog $remote_fs
+# Required-Stop: $network $syslog $remote_fs
+# X-Start-Before: heartbeat openais corosync
+# Default-Start: 3 5
+# Default-Stop: 0 1 6
+### END INIT INFO
+
+LOGD_CFG=@sysconfdir@/logd.cf
+LOGD_OPT=""
+[ -f "$LOGD_CFG" ] && LOGD_OPT="-c $LOGD_CFG"
+LOGD_BIN="@libdir@/@HB_PKG@/ha_logd"
+
+if [ ! -f $LOGD_BIN ]; then
+ echo -n "ha_logd not installed."
+ exit 5
+fi
+
+StartLogd() {
+ echo -n "Starting ha_logd: "
+ $LOGD_BIN -s >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ echo "logd is already running"
+ return 0
+ fi
+
+ $LOGD_BIN -d $LOGD_OPT >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "starting logd failed"
+ exit 1
+ fi
+ echo "ok"
+ exit 0
+}
+
+StopLogd() {
+ echo -n "Stopping ha_logd: "
+
+ $LOGD_BIN -s >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "logd is already stopped"
+ return 0
+ fi
+
+ $LOGD_BIN -k >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "stopping logd failed"
+ exit 1
+ fi
+ echo "stopped"
+ exit 0
+}
+
+StatusLogd() {
+ $LOGD_BIN -s
+ exit $?
+}
+
+case "$1" in
+ start) StartLogd ;;
+ status) StatusLogd ;;
+ stop) StopLogd ;;
+ restart)
+ sleeptime=1
+ $0 stop && sleep $sleeptime && $0 start
+ echo
+ ;;
+ try-restart)
+ $0 status && $0 restart
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|status|restart}"
+ exit 1
+esac
+
diff --git a/logd/logd.service.in b/logd/logd.service.in
new file mode 100644
index 0000000..9783547
--- /dev/null
+++ b/logd/logd.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=ha_logd logging daemon
+Before=pacemaker.service
+PartOf=pacemaker.service
+
+[Service]
+ExecStart=@libdir@/@HB_PKG@/ha_logd -c @sysconfdir@/logd.cf
+ExecStartPre=/bin/rm -f @HA_VARRUNDIR@/logd.pid
+ExecStopPost=/bin/rm -f @HA_VARRUNDIR@/logd.pid
+PIDFile=@HA_VARRUNDIR@/logd.pid
+
+[Install]
+WantedBy=multi-user.target
diff --git a/logd/logtest.c b/logd/logtest.c
new file mode 100644
index 0000000..11e4014
--- /dev/null
+++ b/logd/logtest.c
@@ -0,0 +1,129 @@
+/*
+ * ha_logger.c utility to log a message to the logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <lha_internal.h>
+#include <glib.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/ipc.h>
+#include <clplumbing/GSource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <clplumbing/loggingdaemon.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#define EXIT_OK 0
+#define EXIT_FAIL 1
+#define MAXMSGSIZE (1048*4)
+
+int LogToDaemon(int priority, const char * buf, int bstrlen, gboolean use_pri_str);
+extern IPC_Channel * get_log_chan(void);
+
+static GMainLoop* loop;
+
+static gboolean
+send_log_msg(gpointer data)
+{
+ static int count = 0;
+ char msgstring[MAXMSGSIZE];
+ int priority;
+ static int dropmsg = 0;
+ long maxcount = (long) data;
+ IPC_Channel* chan = get_log_chan();
+
+
+ if (chan == NULL){
+ cl_log(LOG_ERR, "logging channel is NULL");
+ g_main_quit(loop);
+ return FALSE;
+
+ }
+ if (count >= maxcount){
+ cl_log(LOG_INFO, "total message dropped: %d", dropmsg);
+ g_main_quit(loop);
+ return FALSE;
+ }
+
+ if (chan->send_queue->current_qlen
+ == chan->send_queue->max_qlen){
+ return TRUE;
+ }
+
+
+ priority = LOG_INFO;
+ msgstring[0]=0;
+
+ snprintf(msgstring, sizeof(msgstring),"Message %d", count++);
+ fprintf(stderr, "sending %s\n", msgstring);
+ if (LogToDaemon(priority, msgstring,MAXMSGSIZE, FALSE) != HA_OK){
+ printf("sending out messge %d failed\n", count);
+ dropmsg++;
+ }
+
+
+
+ return TRUE;
+}
+
+
+static void
+usage(char* prog)
+{
+ printf("Usage:%s <num_of_messages>\n", prog);
+ return;
+}
+
+int
+main(int argc, char** argv)
+{
+
+ long maxcount;
+
+ if (argc < 2){
+ usage(argv[0]);
+ return 1;
+ }
+
+ maxcount = atoi(argv[1]);
+
+ cl_log_set_entity("logtest");
+ cl_log_set_facility(HA_LOG_FACILITY);
+ cl_log_set_uselogd(TRUE);
+
+ if(!cl_log_test_logd()){
+ return EXIT_FAIL;
+ }
+
+ cl_log_set_logd_channel_source(NULL, NULL);
+
+ g_idle_add(send_log_msg, (gpointer)maxcount);
+
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_run(loop);
+ return(1);
+
+}
+
diff --git a/lrm/Makefile.am b/lrm/Makefile.am
new file mode 100644
index 0000000..78a92c4
--- /dev/null
+++ b/lrm/Makefile.am
@@ -0,0 +1,20 @@
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = lrmd admin test
diff --git a/lrm/admin/Makefile.am b/lrm/admin/Makefile.am
new file mode 100644
index 0000000..a92cd72
--- /dev/null
+++ b/lrm/admin/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+halibdir = $(libdir)/@HB_PKG@
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la $(GLIBLIB)
+LRM_DIR = lrm
+sbin_PROGRAMS = lrmadmin
+sbin_SCRIPTS = cibsecret
+lrmadmin_SOURCES = lrmadmin.c
+lrmadmin_LDFLAGS = $(COMMONLIBS)
+lrmadmin_LDADD = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la
+lrmadmin_DEPENDENCIES = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la
+
+if BUILD_HELP
+man8_MANS = $(sbin_PROGRAMS:%=%.8)
+%.8: %
+ echo Creating $@
+ chmod a+x $<
+ help2man --output $@ --no-info --section 8 --name "Part of the Linux-HA project" $(top_builddir)/lrm/admin/$<
+endif
diff --git a/lrm/admin/cibsecret.in b/lrm/admin/cibsecret.in
new file mode 100755
index 0000000..5255cdd
--- /dev/null
+++ b/lrm/admin/cibsecret.in
@@ -0,0 +1,350 @@
+#!/bin/sh
+
+# Copyright (C) 2011 Dejan Muhamedagic <dmuhamedagic@suse.de>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU 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 software 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU 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
+#
+
+# WARNING:
+#
+# The CIB secrets interface and implementation is still being
+# discussed, it may change
+
+#
+# cibsecret: manage the secrets directory /var/lib/heartbeat/lrm/secrets
+#
+# secrets are ascii files, holding just one value per file:
+# /var/lib/heartbeat/lrm/secrets/<rsc>/<param>
+#
+# NB: this program depends on utillib.sh
+#
+
+. @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs
+
+HA_NOARCHBIN=@datadir@/@PACKAGE_NAME@
+
+. $HA_NOARCHBIN/utillib.sh
+
+LRM_CIBSECRETS=$HA_VARLIB/lrm/secrets
+
+PROG=`basename $0`
+SSH_OPTS="-o StrictHostKeyChecking=no"
+
+usage() {
+ cat<<EOF
+usage: $PROG [-C] <command> <parameters>
+
+-C: don't read/write the CIB
+
+command: set | delete | stash | unstash | get | check | sync
+
+ set <rsc> <param> <value>
+ get <rsc> <param>
+ check <rsc> <param>
+ stash <rsc> <param> (if not -C)
+ unstash <rsc> <param> (if not -C)
+ delete <rsc> <param>
+ sync
+
+stash/unstash: move the parameter from/to the CIB (if you already
+ have the parameter set in the CIB).
+
+set/delete: add/remove a parameter from the local file.
+
+get: display the parameter from the local file.
+
+check: verify MD5 hash of the parameter from the local file and the CIB.
+
+sync: copy $LRM_CIBSECRETS to other nodes.
+
+Examples:
+
+ $PROG set ipmi_node1 passwd SecreT_PASS
+ $PROG stash ipmi_node1 passwd
+ $PROG get ipmi_node1 passwd
+ $PROG check ipmi_node1 passwd
+ $PROG sync
+EOF
+ exit $1
+}
+fatal() {
+ echo "ERROR: $*"
+ exit 1
+}
+warn() {
+ echo "WARNING: $*"
+}
+info() {
+ echo "INFO: $*"
+}
+
+check_env() {
+ which md5sum >/dev/null 2>&1 ||
+ fatal "please install md5sum to run $PROG"
+ if which pssh >/dev/null 2>&1; then
+ rsh=pssh_fun
+ rcp=pscp_fun
+ elif which pdsh >/dev/null 2>&1; then
+ rsh=pdsh_fun
+ rcp=pdcp_fun
+ elif which ssh >/dev/null 2>&1; then
+ rsh=ssh_fun
+ rcp=scp_fun
+ else
+ fatal "please install pssh, pdsh, or ssh to run $PROG"
+ fi
+ ps -ef | grep '[c]rmd' >/dev/null ||
+ fatal "pacemaker not running? $PROG needs pacemaker"
+}
+
+get_other_nodes() {
+ crm_node -l | awk '{print $2}' | grep -v `uname -n`
+}
+check_down_nodes() {
+ local n down_nodes
+ down_nodes=`(for n; do echo $n; done) | sort | uniq -u`
+ if [ -n "$down_nodes" ]; then
+ if [ `echo $down_nodes | wc -w` = 1 ]; then
+ warn "node $down_nodes is down"
+ warn "you'll need to update it using $PROG sync later"
+ else
+ warn "nodes `echo $down_nodes` are down"
+ warn "you'll need to update them using $PROG sync later"
+ fi
+ fi
+}
+
+pssh_fun() {
+ pssh -qi -H "$nodes" -x "$SSH_OPTS" $*
+}
+pscp_fun() {
+ pscp -q -H "$nodes" -x "-pr" -x "$SSH_OPTS" $*
+}
+pdsh_fun() {
+ local pdsh_nodes=`echo $nodes | tr ' ' ','`
+ export PDSH_SSH_ARGS_APPEND="$SSH_OPTS"
+ pdsh -w $pdsh_nodes $*
+}
+pdcp_fun() {
+ local pdsh_nodes=`echo $nodes | tr ' ' ','`
+ export PDSH_SSH_ARGS_APPEND="$SSH_OPTS"
+ pdcp -pr -w $pdsh_nodes $*
+}
+ssh_fun() {
+ local h
+ for h in $nodes; do
+ ssh $SSH_OPTS $h $* || return
+ done
+}
+scp_fun() {
+ local h src="$1" dest=$2
+ for h in $nodes; do
+ scp -pr -q $SSH_OPTS $src $h:$dest || return
+ done
+}
+# TODO: this procedure should be replaced with csync2
+# provided that csync2 has already been configured
+sync_files() {
+ local crm_nodes=`get_other_nodes`
+ local nodes=`get_live_nodes $crm_nodes`
+ check_down_nodes $nodes $crm_nodes
+ [ "$nodes" = "" ] && {
+ info "no other nodes live"
+ return
+ }
+ info "syncing $LRM_CIBSECRETS to `echo $nodes` ..."
+ $rsh rm -rf $LRM_CIBSECRETS &&
+ $rsh mkdir -p `dirname $LRM_CIBSECRETS` &&
+ $rcp $LRM_CIBSECRETS `dirname $LRM_CIBSECRETS`
+}
+sync_one() {
+ local f=$1 f_all="$1 $1.sign"
+ local crm_nodes=`get_other_nodes`
+ local nodes=`get_live_nodes $crm_nodes`
+ check_down_nodes $nodes $crm_nodes
+ [ "$nodes" = "" ] && {
+ info "no other nodes live"
+ return
+ }
+ info "syncing $f to `echo $nodes` ..."
+ $rsh mkdir -p `dirname $f` &&
+ if [ -f "$f" ]; then
+ $rcp "$f_all" `dirname $f`
+ else
+ $rsh rm -f $f_all
+ fi
+}
+
+is_secret() {
+ # assume that the secret is in the CIB if we cannot talk to
+ # cib
+ [ "$NO_CRM" ] ||
+ test "$1" = "$MAGIC"
+}
+check_cib_rsc() {
+ local rsc=$1 output
+ output=`$NO_CRM crm_resource -r $rsc -W >/dev/null 2>&1` ||
+ fatal "resource $rsc doesn't exist: $output"
+}
+get_cib_param() {
+ local rsc=$1 param=$2
+ check_cib_rsc $rsc
+ $NO_CRM crm_resource -r $rsc -g $param 2>/dev/null
+}
+set_cib_param() {
+ local rsc=$1 param=$2 value=$3
+ check_cib_rsc $rsc
+ $NO_CRM crm_resource -r $rsc -p $param -v "$value" 2>/dev/null
+}
+remove_cib_param() {
+ local rsc=$1 param=$2
+ check_cib_rsc $rsc
+ $NO_CRM crm_resource -r $rsc -d $param 2>/dev/null
+}
+
+localfiles() {
+ local cmd=$1
+ local rsc=$2 param=$3 value=$4
+ local local_file=$LRM_CIBSECRETS/$rsc/$param
+ case $cmd in
+ "get")
+ cat $local_file 2>/dev/null
+ true
+ ;;
+ "getsum")
+ cat $local_file.sign 2>/dev/null
+ true
+ ;;
+ "set")
+ local md5sum
+ md5sum=`printf $value | md5sum` ||
+ fatal "md5sum failed to produce hash for resource $rsc parameter $param"
+ md5sum=`echo $md5sum | awk '{print $1}'`
+ mkdir -p `dirname $local_file` &&
+ echo $value > $local_file &&
+ echo $md5sum > $local_file.sign &&
+ sync_one $local_file
+ ;;
+ "remove")
+ rm -f $local_file
+ sync_one $local_file
+ ;;
+ *)
+ # not reached, this is local interface
+ ;;
+ esac
+}
+get_local_param() {
+ local rsc=$1 param=$2
+ localfiles get $rsc $param
+}
+set_local_param() {
+ local rsc=$1 param=$2 value=$3
+ localfiles set $rsc $param $value
+}
+remove_local_param() {
+ local rsc=$1 param=$2
+ localfiles remove $rsc $param
+}
+
+cibsecret_set() {
+ local value=$1
+
+ if [ -z "$NO_CRM" ]; then
+ [ "$current" -a "$current" != "$MAGIC" -a "$current" != "$value" ] &&
+ fatal "CIB value <$current> different for $rsc parameter $param; please delete it first"
+ fi
+ set_local_param $rsc $param $value &&
+ set_cib_param $rsc $param "$MAGIC"
+}
+
+cibsecret_check() {
+ local md5sum local_md5sum
+ is_secret "$current" ||
+ fatal "resource $rsc parameter $param not set as secret, nothing to check"
+ local_md5sum=`localfiles getsum $rsc $param`
+ [ "$local_md5sum" ] ||
+ fatal "no MD5 hash for resource $rsc parameter $param"
+ md5sum=`printf "$current_local" | md5sum | awk '{print $1}'`
+ [ "$md5sum" = "$local_md5sum" ] ||
+ fatal "MD5 hash mismatch for resource $rsc parameter $param"
+}
+
+cibsecret_get() {
+ cibsecret_check
+ echo "$current_local"
+}
+
+cibsecret_delete() {
+ remove_local_param $rsc $param &&
+ remove_cib_param $rsc $param
+}
+
+cibsecret_stash() {
+ [ "$NO_CRM" ] &&
+ fatal "no access to Pacemaker, stash not supported"
+ [ "$current" = "" ] &&
+ fatal "nothing to stash for resource $rsc parameter $param"
+ is_secret "$current" &&
+ fatal "resource $rsc parameter $param already set as secret, nothing to stash"
+ cibsecret_set "$current"
+}
+
+cibsecret_unstash() {
+ [ "$NO_CRM" ] &&
+ fatal "no access to Pacemaker, unstash not supported"
+ [ "$current_local" = "" ] &&
+ fatal "nothing to unstash for resource $rsc parameter $param"
+ is_secret "$current" ||
+ warn "resource $rsc parameter $param not set as secret, but we have local value so proceeding anyway"
+ remove_local_param $rsc $param &&
+ set_cib_param $rsc $param $current_local
+}
+
+cibsecret_sync() {
+ sync_files
+}
+
+check_env
+
+MAGIC="lrm://"
+umask 0077
+
+if [ "$1" = "-C" ]; then
+ NO_CRM=':'
+ shift 1
+fi
+
+cmd=$1
+rsc=$2
+param=$3
+value=$4
+
+case "$cmd" in
+ set) [ $# -ne 4 ] && usage 1;;
+ get) [ $# -ne 3 ] && usage 1;;
+ check) [ $# -ne 3 ] && usage 1;;
+ stash) [ $# -ne 3 ] && usage 1;;
+ unstash) [ $# -ne 3 ] && usage 1;;
+ delete) [ $# -ne 3 ] && usage 1;;
+ sync) [ $# -ne 1 ] && usage 1;;
+ *) usage 1;
+esac
+
+# we'll need these two often
+current=`get_cib_param $rsc $param`
+current_local=`get_local_param $rsc $param`
+
+cibsecret_$cmd $value
diff --git a/lrm/admin/lrmadmin.c b/lrm/admin/lrmadmin.c
new file mode 100644
index 0000000..27f37bf
--- /dev/null
+++ b/lrm/admin/lrmadmin.c
@@ -0,0 +1,1129 @@
+/* File: lrmadmin.c
+ * Description: A adminstration tool for Local Resource Manager
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * Todo: security verification
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#ifndef __USE_GNU
+#define __USE_GNU
+/* For strnlen protype */
+#include <string.h>
+#undef __USE_GNU
+#else
+#include <string.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif /* HAVE_GETOPT_H */
+#include <clplumbing/cl_log.h>
+#include <lrm/lrm_api.h>
+#include <lrm/lrm_msg.h>
+#include <lrm/raexec.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/Gmain_timeout.h>
+
+static const char *optstring = "A:D:X:dE:F:dg:p:M:O:P:c:S:LI:CT:n:hv";
+
+#ifdef HAVE_GETOPT_H
+static struct option long_options[] = {
+ {"daemon", 0, NULL, 'd'},
+ {"executera", 1, NULL, 'E'},
+ {"flush", 1, NULL, 'F'},
+ {"state", 1, NULL, 'S'},
+ {"listall", 0, NULL, 'L'},
+ {"information", 1, NULL, 'I'},
+ {"add", 1, NULL, 'A'},
+ {"delete", 1, NULL, 'D'},
+ {"fail", 1, NULL, 'X'},
+ {"raclass_supported", 1, NULL, 'C'},
+ {"ratype_supported", 1, NULL, 'T'},
+ {"all_type_metadata", 1, NULL, 'O'},
+ {"metadata", 1, NULL, 'M'},
+ {"provider", 1, NULL, 'P'},
+ {"set_lrmd_param", 1, NULL, 'p'},
+ {"get_lrmd_param", 1, NULL, 'g'},
+ {"help", 0, NULL, 'h'},
+ {"version", 0, NULL, 'v'},
+ {NULL, 0, NULL, 0}
+};
+#endif /* HAVE_GETOPT_H */
+
+static GMainLoop *mainloop;
+static const char *lrmadmin_name = "lrmadmin";
+static const char *fake_name;
+/* 20 is the length limit for a argv[x] */
+static const int ARGVI_MAX_LEN = 48;
+
+typedef enum {
+ ERROR_OPTION = -1,
+ NULL_OP,
+ DAEMON_OP,
+ EXECUTE_RA,
+ FLUSH,
+ RSC_STATE,
+ LIST_ALLRSC,
+ INF_RSC,
+ SET_PARAM,
+ GET_PARAM,
+ ADD_RSC,
+ DEL_RSC,
+ FAIL_RSC,
+ RACLASS_SUPPORTED,
+ RATYPE_SUPPORTED,
+ RA_METADATA,
+ RA_PROVIDER,
+ ALL_RA_METADATA,
+ HELP
+} lrmadmin_cmd_t;
+
+#define nullcheck(p) ((p) ? (p) : "<null>")
+static const char * status_msg[6] = {
+ "pending", /* LRM_OP_PENDING */
+ "succeed", /* LRM_OP_DONE */
+ "cancelled", /* LRM_OP_CANCELLED */
+ "timeout", /* LRM_OP_TIMEOUT */
+ "not Supported", /* LRM_OP_NOTSUPPORTED */
+ "failed due to an error" /* LRM_OP_ERROR */
+};
+
+static const char * rc_msg[] = {
+ "unknown error",
+ "no ra",
+ "ok",
+ "unknown error",
+ "invalid parameter",
+ "unimplement feature",
+ "insufficient priority",
+ "not installed",
+ "not configured",
+ "not running",
+ "running master",
+ "failed master",
+ "invalid rc",
+ /* For status command only */
+ "daemon dead1",
+ "daemon dead2",
+ "daemon stopped",
+ "status unknow"
+};
+
+
+static gboolean QUIT_GETOPT = FALSE;
+static lrmadmin_cmd_t lrmadmin_cmd = NULL_OP;
+static gboolean ASYN_OPS = FALSE;
+static int call_id = 0;
+static int TIMEOUT = -1; /* the unit is ms */
+
+static const char *simple_help_screen =
+"lrmadmin -d,--daemon\n"
+" -A,--add <rscid> <raclass> <ratype> <provider|NULL> [<rsc_params_list>]\n"
+" -D,--delete <rscid>\n"
+" -F,--flush <rscid>\n"
+" -X,--fail <rscid> [<fail_rc> [<fail_reason>]]\n"
+" -E,--execute <rscid> <operator> <timeout> <interval> <target_rc|EVERYTIME|CHANGED> [<operator_parameters_list>]\n"
+" -S,--state <rscid> [-n <fake_name>]\n"
+" -L,--listall\n"
+" -I,--information <rsc_id>\n"
+" -C,--raclass_supported\n"
+" -T,--ratype_supported <raclass>\n"
+" -O,--all metadata of this class <raclass>\n"
+" -M,--metadata <raclass> <ratype> <provider|NULL>\n"
+" -P,--provider <raclass> <ratype>\n"
+" -p,--set_lrmd_param <name> <value>\n"
+" -g,--get_lrmd_param <name>\n"
+" -v,--version\n"
+" -h,--help\n";
+
+#define OPTION_OBSCURE_CHECK \
+ if ( lrmadmin_cmd != NULL_OP ) { \
+ cl_log(LOG_ERR,"Obscure options."); \
+ return -1; \
+ }
+
+/* the begin of the internal used function list */
+static int resource_operation(ll_lrm_t * lrmd, char *rsc_id,
+ int argc, int optind, char * argv[]);
+static int add_resource(ll_lrm_t * lrmd, char *rsc_id,
+ int argc, int optind, char * argv[]);
+static int fail_resource(ll_lrm_t * lrmd, char *rsc_id, int optc, char *opts[]);
+static int get_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[]);
+static int set_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[]);
+static int transfer_cmd_params(int amount, int start, char * argv[],
+ const char * class, GHashTable ** params_ht);
+static void g_print_stringitem_and_free(gpointer data, gpointer user_data);
+static void g_print_rainfo_item_and_free(gpointer data, gpointer user_data);
+static void g_print_ops(gpointer data, gpointer user_data);
+static void g_get_rsc_description(gpointer data, gpointer user_data);
+static void g_print_meta(gpointer key, gpointer value, gpointer user_data);
+
+static void print_rsc_inf(lrm_rsc_t * lrmrsc);
+static char * params_hashtable_to_str(const char * class, GHashTable * ht);
+static void free_stritem_of_hashtable(gpointer key, gpointer value,
+ gpointer user_data);
+static void ocf_params_hash_to_str(gpointer key, gpointer value,
+ gpointer user_data);
+static void normal_params_hash_to_str(gpointer key, gpointer value,
+ gpointer user_data);
+static lrm_rsc_t * get_lrm_rsc(ll_lrm_t * lrmd, char * rscid);
+
+static int ra_metadata(ll_lrm_t * lrmd, int argc, int optind, char * argv[]);
+static int ra_provider(ll_lrm_t * lrmd, int argc, int optind, char * argv[]);
+static gboolean lrmd_output_dispatch(IPC_Channel* notused, gpointer user_data);
+static gboolean lrm_op_timeout(gpointer data);
+
+/* the end of the internal used function list */
+
+static void lrm_op_done_callback(lrm_op_t* op);
+
+static int ret_value;
+int main(int argc, char **argv)
+{
+ int option_char;
+ char rscid_arg_tmp[RID_LEN];
+ ll_lrm_t* lrmd;
+ lrm_rsc_t * lrm_rsc;
+ GList *raclass_list = NULL,
+ *ratype_list = NULL,
+ *rscid_list;
+ GHashTable *all_meta = NULL;
+ char raclass[20];
+ const char * login_name = lrmadmin_name;
+
+ /* Prevent getopt_long to print error message on stderr itself */
+ /*opterr = 0; */
+
+ if (argc == 1) {
+ printf("%s",simple_help_screen);
+ return 0;
+ }
+
+ cl_log_set_entity(lrmadmin_name);
+ cl_log_enable_stderr(TRUE);
+ cl_log_set_facility(LOG_USER);
+
+ memset(rscid_arg_tmp, '\0', RID_LEN);
+ memset(raclass, '\0', 20);
+ do {
+#ifdef HAVE_GETOPT_H
+ option_char = getopt_long (argc, argv, optstring,
+ long_options, NULL);
+#else
+ option_char = getopt (argc, argv, optstring);
+#endif
+
+ if (option_char == -1) {
+ break;
+ }
+
+ switch (option_char) {
+ case 'd':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = DAEMON_OP;
+ QUIT_GETOPT = TRUE;
+ break;
+
+ case 'A':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = ADD_RSC;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'D':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = DEL_RSC;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'X':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = FAIL_RSC;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'C':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = RACLASS_SUPPORTED;
+ break;
+
+ case 'T':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = RATYPE_SUPPORTED;
+ if (optarg) {
+ strncpy(raclass, optarg, 19);
+ }
+ break;
+
+ case 'O':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = ALL_RA_METADATA;
+ if (optarg) {
+ strncpy(raclass, optarg, 19);
+ }
+ break;
+
+ case 'F':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = FLUSH;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'E':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = EXECUTE_RA;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'M':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = RA_METADATA;
+ break;
+
+ case 'P':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = RA_PROVIDER;
+ break;
+
+ case 'S':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = RSC_STATE;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'L':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = LIST_ALLRSC;
+ break;
+
+ case 'I':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = INF_RSC;
+ strncpy(rscid_arg_tmp, optarg, RID_LEN-1);
+ break;
+
+ case 'p':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = SET_PARAM;
+ break;
+
+ case 'g':
+ OPTION_OBSCURE_CHECK
+ lrmadmin_cmd = GET_PARAM;
+ break;
+
+ case 'n':
+ if (optarg) {
+ fake_name = optarg;
+ }
+ break;
+
+ case 'v':
+ printf("%s\n",GLUE_VERSION);
+ return 0;
+ case 'h':
+ OPTION_OBSCURE_CHECK
+ printf("%s",simple_help_screen);
+ return 0;
+
+ case '?':
+ /* cl_log(LOG_ERR,"There is a unrecognized
+ option %s", optarg);
+ */
+ printf("%s", simple_help_screen);
+ return -1;
+
+ default:
+ cl_log(LOG_ERR,"getopt returned character"
+ " code %c.", option_char);
+ return -1;
+ }
+ } while (!QUIT_GETOPT);
+
+ lrmd = ll_lrm_new("lrm");
+
+ if (NULL == lrmd) {
+ cl_log(LOG_ERR,"ll_lrm_new returned NULL.");
+ return -2;
+ }
+
+ lrmd->lrm_ops->set_lrm_callback(lrmd, lrm_op_done_callback);
+
+ if (fake_name != NULL) {
+ login_name = fake_name;
+ }
+ if (lrmd->lrm_ops->signon(lrmd, login_name) != 1) { /* != HA_OK */
+ printf("lrmd is not running.\n");
+ if (lrmadmin_cmd == DAEMON_OP) {
+ return LSB_STATUS_STOPPED;
+ } else {
+ cl_log(LOG_WARNING,"Can't connect to lrmd!");
+ return -2;
+ }
+ }
+
+ if (lrmadmin_cmd == DAEMON_OP) {
+ printf("lrmd is stopped.\n");
+ lrmd->lrm_ops->signoff(lrmd);
+ return 0;
+ }
+
+ switch (lrmadmin_cmd) {
+ case EXECUTE_RA:
+ call_id = resource_operation(lrmd, rscid_arg_tmp, argc, optind, argv);
+ if (call_id < 0) {
+ if ( call_id == -2 ) {
+ cl_log(LOG_ERR, "Failed to operate "
+ "resource %s due to parameter error."
+ , argv[optind]);
+ ret_value = -3;
+ }
+ if ( call_id == -1 ) {
+ cl_log(LOG_WARNING, "Failed! No such "
+ "resource %s.", argv[optind]);
+ ret_value = -2;
+ }
+ else {
+ cl_log(LOG_ERR, "Failed to operate "
+ "resource %s due to unknown error."
+ , argv[optind]);
+ ret_value = -3;
+ }
+ ASYN_OPS = FALSE;
+ } else {
+ /* Return value: HA_OK = 1 Or HA_FAIL = 0 */
+ if ( call_id == 0 ) {
+ cl_log(LOG_ERR, "Resource operation "
+ "failed." );
+ ret_value = -3;
+ ASYN_OPS = FALSE;
+ } else {
+ ASYN_OPS = TRUE;
+ }
+ }
+ break;
+
+ case RA_METADATA:
+ ra_metadata(lrmd, argc, optind, argv);
+ ASYN_OPS = FALSE;
+ break;
+ case RA_PROVIDER:
+ ra_provider(lrmd, argc, optind, argv);
+ ASYN_OPS = FALSE;
+ break;
+
+ case SET_PARAM:
+ set_param(lrmd, argc, optind, argv);
+ ASYN_OPS = FALSE;
+ break;
+
+ case GET_PARAM:
+ get_param(lrmd, argc, optind, argv);
+ ASYN_OPS = FALSE;
+ break;
+
+ case ADD_RSC:
+ if (add_resource(lrmd, rscid_arg_tmp, argc, optind, argv) == 0) {
+ printf("Succeeded in adding this resource.\n");
+ } else {
+ printf("Failed to add this resource.\n");
+ ret_value = -3;
+ }
+ ASYN_OPS = FALSE;
+ break;
+
+ case DEL_RSC:
+ /* Return value: HA_OK = 1 Or HA_FAIL = 0 */
+ if (lrmd->lrm_ops->delete_rsc(lrmd, rscid_arg_tmp)==1) {
+ printf("Succeeded in deleting this resource.\n");
+ } else {
+ printf("Failed to delete this resource.\n");
+ ret_value = -3;
+ }
+ ASYN_OPS = FALSE;
+ break;
+
+ case FAIL_RSC:
+ /* Return value: HA_OK = 1 Or HA_FAIL = 0 */
+ if (fail_resource(lrmd, rscid_arg_tmp,
+ argc-optind, argv+optind) == 1)
+ {
+ printf("Succeeded in failing the resource.\n");
+ } else {
+ printf("Failed to fail the resource.\n");
+ ret_value = -3;
+ }
+ ASYN_OPS = FALSE;
+ break;
+
+ case FLUSH:
+ lrm_rsc = get_lrm_rsc(lrmd, rscid_arg_tmp);
+ if (!(lrm_rsc)) {
+ ret_value = -3;
+ } else {
+ /* Return value: HA_OK = 1 Or HA_FAIL = 0 */
+ if (lrm_rsc->ops->flush_ops(lrm_rsc) == 1 ) {
+ printf("Succeeded in flushing.\n");
+ } else {
+ printf("Failed to flush.\n");
+ ret_value = -3;
+ }
+ lrm_free_rsc(lrm_rsc);
+ }
+
+ ASYN_OPS = FALSE;
+ break;
+
+ case RACLASS_SUPPORTED:
+ raclass_list = lrmd->lrm_ops->
+ get_rsc_class_supported(lrmd);
+ printf("There are %d RA classes supported:\n",
+ g_list_length(raclass_list));
+ if (raclass_list) {
+ g_list_foreach(raclass_list, g_print_stringitem_and_free,
+ NULL);
+ g_list_free(raclass_list);
+ ret_value = LSB_EXIT_OK;
+ } else {
+ printf("No RA classes found!\n");
+ ret_value = -3;
+ }
+
+ ASYN_OPS = FALSE;
+ break;
+
+ case RATYPE_SUPPORTED:
+ ratype_list = lrmd->lrm_ops->
+ get_rsc_type_supported(lrmd, raclass);
+ printf("There are %d RAs:\n", g_list_length(ratype_list));
+ if (ratype_list) {
+ g_list_foreach(ratype_list, g_print_rainfo_item_and_free,
+ NULL);
+ g_list_free(ratype_list);
+ }
+
+ ASYN_OPS = FALSE;
+ break;
+ case ALL_RA_METADATA:
+ all_meta = lrmd->lrm_ops->get_all_type_metadata(lrmd, raclass);
+ if (all_meta) {
+ g_hash_table_foreach(all_meta, g_print_meta, NULL);
+ g_hash_table_destroy(all_meta);
+ }
+ ASYN_OPS = FALSE;
+ break;
+ case LIST_ALLRSC:
+ rscid_list = lrmd->lrm_ops->get_all_rscs(lrmd);
+ if (rscid_list) {
+ g_list_foreach(rscid_list, g_get_rsc_description
+ , lrmd);
+ g_list_free(rscid_list);
+ } else
+ printf("Currently no resources are managed by "
+ "LRM.\n");
+
+ ASYN_OPS = FALSE;
+ break;
+
+ case INF_RSC:
+ lrm_rsc = get_lrm_rsc(lrmd, rscid_arg_tmp);
+ if (!(lrm_rsc)) {
+ ret_value = -3;
+ } else {
+ print_rsc_inf(lrm_rsc);
+ lrm_free_rsc(lrm_rsc);
+ }
+
+ ASYN_OPS = FALSE;
+ break;
+
+ case RSC_STATE:
+ lrm_rsc = get_lrm_rsc(lrmd, rscid_arg_tmp);
+ if (!(lrm_rsc)) {
+ ret_value = -3;
+ } else {
+ state_flag_t cur_state = LRM_RSC_IDLE;
+ GList * ops_queue;
+ ops_queue = lrm_rsc->ops->get_cur_state(lrm_rsc,
+ &cur_state);
+ printf("resource state:%s\n",
+ cur_state==LRM_RSC_IDLE?
+ "LRM_RSC_IDLE":"LRM_RSC_BUSY");
+
+ printf("The resource %d operations' "
+ "information:\n"
+ , g_list_length(ops_queue));
+ if (ops_queue) {
+ g_list_foreach(ops_queue,
+ g_print_ops,
+ NULL);
+ lrm_free_op_list(ops_queue);
+ }
+ lrm_free_rsc(lrm_rsc);
+ }
+
+ ASYN_OPS = FALSE;
+ break;
+
+
+ default:
+ fprintf(stderr, "Option %c is not supported yet.\n",
+ option_char);
+ ret_value = -1;
+ ASYN_OPS = FALSE;
+ break;
+ }
+
+ if (ASYN_OPS) {
+ G_main_add_IPC_Channel(G_PRIORITY_LOW, lrmd->lrm_ops->ipcchan(lrmd),
+ FALSE, lrmd_output_dispatch, lrmd, NULL);
+ if (TIMEOUT > 0) {
+ Gmain_timeout_add(TIMEOUT, lrm_op_timeout, &ret_value);
+ }
+
+ mainloop = g_main_new(FALSE);
+ printf( "Waiting for lrmd to callback...\n");
+ g_main_run(mainloop);
+ }
+
+ lrmd->lrm_ops->signoff(lrmd);
+ return ret_value;
+}
+
+static gboolean
+lrm_op_timeout(gpointer data)
+{
+ int * idata = data;
+
+ printf("ERROR: This operation has timed out - no result from lrmd.\n");
+
+ *idata = -5;
+ g_main_quit(mainloop);
+ return FALSE;
+}
+
+static gboolean
+lrmd_output_dispatch(IPC_Channel* notused, gpointer user_data)
+{
+ ll_lrm_t *lrm = (ll_lrm_t*)user_data;
+ lrm->lrm_ops->rcvmsg(lrm, FALSE);
+
+ g_main_quit(mainloop);
+ return TRUE;
+}
+
+static void
+lrm_op_done_callback(lrm_op_t* op)
+{
+ if (!op) {
+ cl_log(LOG_ERR, "In callback function, op is NULL pointer.");
+ ret_value = -3;
+ return;
+ }
+
+ printf("----------------operation--------------\n");
+ printf("type:%s\n", op->op_type);
+ if ( (0 == STRNCMP_CONST(op->op_type, "status")
+ || 0 == STRNCMP_CONST(op->op_type, "monitor")) && (op->rc == 7) ) {
+ printf("operation status:%s\n", status_msg[LRM_OP_DONE-LRM_OP_PENDING]);
+ printf("op_status: %d\n", LRM_OP_DONE);
+ } else {
+ printf("operation status:%s\n", status_msg[(op->op_status
+ - LRM_OP_PENDING) % DIMOF(status_msg)]);
+ printf("op_status: %d\n", op->op_status);
+ }
+ printf("return code: %d\n", op->rc);
+ printf("output data: \n%s\n", (op->output ? op->output : "[null]"));
+ printf("---------------------------------------\n\n");
+ ret_value = op->rc;
+}
+
+static int
+resource_operation(ll_lrm_t * lrmd, char *rsc_id, int argc, int optind, char * argv[])
+{
+ GHashTable * params_ht = NULL;
+ lrm_op_t op = lrm_zero_op;
+ lrm_rsc_t * lrm_rsc;
+ int call_id;
+
+ if ((argc - optind) < 3) {
+ cl_log(LOG_ERR,"Not enough parameters.");
+ return -2;
+ }
+
+ lrm_rsc = lrmd->lrm_ops->get_rsc(lrmd, rsc_id);
+ if (!lrm_rsc) {
+ return -1;
+ }
+
+ op.op_type = argv[optind];
+ op.timeout = atoi(argv[optind+1]);
+
+ /* When op.timeout!=0, plus additional 1s. Or lrmadmin may time out before
+ the normal operation result returned from lrmd. This may be redudant,
+ but harmless. */
+ if (0 < op.timeout ) {
+ TIMEOUT = op.timeout + 1000;
+ }
+ op.interval = atoi(argv[optind+2]);
+ op.user_data = NULL;
+ op.user_data_len = 0;
+ if (0 == strcmp(argv[optind+3], "EVERYTIME")) {
+ op.target_rc = EVERYTIME;
+ }
+ else
+ if (0 == strcmp(argv[optind+3], "CHANGED")) {
+ op.target_rc = CHANGED;
+ }
+ else {
+ op.target_rc = atoi(argv[optind+3]);
+ }
+
+ if ((argc - optind) > 3) {
+ if (0 > transfer_cmd_params(argc, optind+4, argv,
+ lrm_rsc->class, &params_ht) ) {
+ return -2;
+ }
+ }
+ op.params = params_ht;
+
+ call_id = lrm_rsc->ops->perform_op(lrm_rsc, &op);
+ lrm_free_rsc(lrm_rsc);
+ if (params_ht) {
+ g_hash_table_foreach(params_ht, free_stritem_of_hashtable, NULL);
+ g_hash_table_destroy(params_ht);
+ }
+ return call_id;
+}
+static int
+ra_metadata(ll_lrm_t * lrmd, int argc, int optind, char * argv[])
+{
+ const char * class = argv[optind-1];
+ const char * type = argv[optind];
+ const char * provider = argv[optind+1];
+ char* metadata;
+
+ if(argc < 5) {
+ cl_log(LOG_ERR,"Not enough parameters.");
+ return -2;
+ }
+
+ if (0 == strncmp(provider,"NULL",strlen("NULL"))) {
+ provider=NULL;
+ }
+
+ metadata = lrmd->lrm_ops->get_rsc_type_metadata(lrmd, class, type, provider);
+ if (NULL!=metadata) {
+ printf ("%s\n", metadata);
+ g_free (metadata);
+ }
+ return 0;
+}
+
+static int
+ra_provider(ll_lrm_t * lrmd, int argc, int optind, char * argv[])
+{
+ const char * class = argv[optind-1];
+ const char * type = argv[optind];
+ GList* providers = NULL;
+ GList* provider = NULL;
+
+ if(argc < 4) {
+ cl_log(LOG_ERR,"Not enough parameters.");
+ return -2;
+ }
+
+ providers = lrmd->lrm_ops->get_rsc_provider_supported(lrmd,class,type);
+
+ while (NULL != (provider = g_list_first(providers))) {
+ printf("%s\n",(char*)provider->data);
+ g_free(provider->data);
+ providers = g_list_remove(providers, provider->data);
+ }
+ g_list_free(providers);
+ return 0;
+}
+
+static int
+add_resource(ll_lrm_t * lrmd, char *rsc_id, int argc, int optind, char * argv[])
+{
+ const char * class = argv[optind];
+ const char * type = argv[optind+1];
+ const char * provider = argv[optind+2];
+ GHashTable * params_ht = NULL;
+ int tmp_ret;
+
+ if ((argc - optind) < 3) {
+ cl_log(LOG_ERR,"Not enough parameters.");
+ return -2;
+ }
+
+ if (0 == strncmp(provider, "NULL", strlen("NULL"))) {
+ provider=NULL;
+ }
+
+ /* delete Hashtable */
+ if ((argc - optind) > 3) {
+ if ( 0 > transfer_cmd_params(argc, optind+3, argv, class,
+ &params_ht) ) {
+ return -1;
+ }
+ }
+
+ tmp_ret = lrmd->lrm_ops->add_rsc(lrmd, rsc_id, class,
+ type, provider, params_ht);
+
+ /*delete params_ht*/
+ if (params_ht) {
+ g_hash_table_foreach(params_ht, free_stritem_of_hashtable, NULL);
+ g_hash_table_destroy(params_ht);
+ }
+
+ return (tmp_ret ? 0 : -1); /* tmp_ret is HA_OK=1 or HA_FAIL=0 */
+}
+
+static int
+fail_resource(ll_lrm_t * lrmd, char *rsc_id, int optc, char *opts[])
+{
+ int fail_rc = 0;
+ const char * reason = NULL;
+
+ if (optc > 2) {
+ cl_log(LOG_ERR,"Bad usage.");
+ return -2;
+ }
+
+ if (optc >= 1)
+ fail_rc = atoi(opts[0]);
+ if (optc == 2)
+ reason = opts[1];
+
+ return lrmd->lrm_ops->fail_rsc(lrmd, rsc_id, fail_rc, reason);
+}
+
+static int
+get_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[])
+{
+ const char *name = argv[optind-1];
+ char *value;
+
+ if ((argc - optind) != 0) {
+ cl_log(LOG_ERR,"Bad usage.");
+ return -2;
+ }
+ value = lrmd->lrm_ops->get_lrmd_param(lrmd, name);
+ printf("%s: %s\n", name, value);
+ return 0;
+}
+
+static int
+set_param(ll_lrm_t * lrmd, int argc, int optind, char * argv[])
+{
+ const char *name = argv[optind-1];
+ const char *value = argv[optind];
+
+ if ((argc - optind) != 1) {
+ cl_log(LOG_ERR,"Bad usage.");
+ return -2;
+ }
+ return lrmd->lrm_ops->set_lrmd_param(lrmd, name, value);
+}
+
+static int
+transfer_cmd_params(int amount, int start, char * argv[], const char * class,
+GHashTable ** params_ht)
+{
+ int i, len_tmp;
+ char * delimit, * key, * value;
+ char buffer[21];
+
+ if (amount < start) {
+ return -1;
+ }
+
+ if ( strncmp("ocf", class, strlen("ocf"))==0
+ || strncmp("stonith", class, strlen("stonith"))==0) {
+ *params_ht = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i=start; i<amount; i++) {
+ delimit = strchr(argv[i], '=');
+ if (!delimit) {
+ cl_log(LOG_ERR, "Parameter %s is invalid for "
+ "the OCF standard.", argv[i]);
+ goto error_return; /* Have to */
+ }
+
+ len_tmp = strnlen(delimit+1, MAX_PARAM_LEN) + 1;
+ value = g_new(gchar, len_tmp);
+ strncpy(value, delimit+1, len_tmp);
+
+ len_tmp = strnlen(argv[i], MAX_PARAM_LEN) - strnlen(delimit, MAX_PARAM_LEN);
+ key = g_new(gchar, len_tmp+1);
+ key[len_tmp] = '\0';
+ strncpy(key, argv[i], len_tmp);
+
+ g_hash_table_insert(*params_ht, key, value);
+ }
+ } else if ( strncmp("lsb", class, strlen("lsb")) == 0
+ || strncmp("heartbeat", class, strlen("heartbeat")) == 0 ) {
+
+ /* Pay attention: for parameter ordring issue */
+ *params_ht = g_hash_table_new(g_str_hash, g_str_equal);
+
+ memset(buffer, '0', 21);
+ for (i=start; i<amount; i++) {
+ snprintf(buffer, 20, "%d", i-start+1);
+ g_hash_table_insert( *params_ht, g_strdup(buffer),
+ g_strdup(argv[i]));
+ /* printf("index: %d value: %s \n", i-start+1, argv[i]); */
+ }
+ } else {
+ fprintf(stderr, "Not supported resource agent class.\n");
+ return -1;
+ }
+
+ return 0;
+
+error_return:
+ if (*params_ht) {
+ g_hash_table_foreach(*params_ht, free_stritem_of_hashtable, NULL);
+ g_hash_table_destroy(*params_ht);
+ *params_ht = NULL;
+ }
+ return -1;
+}
+
+static char *
+params_hashtable_to_str(const char * class, GHashTable * ht)
+{
+ int i,ht_size;
+ gchar * params_str = NULL;
+ GString * gstr_tmp;
+ gchar * tmp_str;
+
+ if (!ht) {
+ return NULL;
+ }
+
+ if ( strncmp("ocf", class, strlen("ocf")) == 0
+ || strncmp("stonith", class, strlen("stonith")) == 0) {
+ gstr_tmp = g_string_new("");
+ g_hash_table_foreach(ht, ocf_params_hash_to_str, &gstr_tmp);
+ params_str = g_new(gchar, gstr_tmp->len+1);
+ strncpy(params_str, gstr_tmp->str, gstr_tmp->len+1);
+ g_string_free(gstr_tmp, TRUE);
+ } else if ( strncmp("lsb", class, strlen("lsb")) == 0
+ || strncmp("heartbeat", class, strlen("heartbeat")) == 0 ) {
+ ht_size = g_hash_table_size(ht);
+ if (ht_size == 0) {
+ return NULL;
+ }
+ tmp_str = g_new(gchar, ht_size*ARGVI_MAX_LEN);
+ memset(tmp_str, ' ', ht_size*ARGVI_MAX_LEN);
+ tmp_str[ht_size*ARGVI_MAX_LEN-1] = '\0';
+ g_hash_table_foreach(ht, normal_params_hash_to_str, &tmp_str);
+ gstr_tmp = g_string_new("");
+ for (i=0; i< ht_size; i++) {
+ gstr_tmp = g_string_append(gstr_tmp
+ , tmp_str + i*ARGVI_MAX_LEN );
+ gstr_tmp = g_string_append(gstr_tmp, " ");
+ }
+ params_str = g_new(gchar, gstr_tmp->len+1);
+ strncpy(params_str, gstr_tmp->str, gstr_tmp->len+1);
+ g_string_free(gstr_tmp, TRUE);
+ } else {
+ fprintf(stderr, "Not supported resource agent class.\n");
+ }
+
+ return params_str;
+}
+
+static void
+g_print_stringitem_and_free(gpointer data, gpointer user_data)
+{
+ printf("%s\n", (char*)data);
+ g_free(data);
+}
+
+static void
+g_print_rainfo_item_and_free(gpointer data, gpointer user_data)
+{
+ printf("%s\n", (char *)data);
+ g_free(data);
+}
+
+
+static void
+g_print_ops(gpointer data, gpointer user_data)
+{
+ lrm_op_t* op = (lrm_op_t*)data;
+ GString * param_gstr;
+ time_t run_at=0, rcchange_at=0;
+
+ if (NULL == op) {
+ cl_log(LOG_ERR, "%s:%d: op==NULL"
+ , __FUNCTION__, __LINE__);
+ return;
+ }
+
+ param_gstr = g_string_new("");
+ g_hash_table_foreach(op->params, ocf_params_hash_to_str, &param_gstr);
+
+ if( op->t_run )
+ run_at=(time_t)op->t_run;
+ if( op->t_rcchange )
+ rcchange_at=(time_t)op->t_rcchange;
+ printf(" operation '%s' [call_id=%d]:\n"
+ " start_delay=%d, interval=%d, timeout=%d, app_name=%s\n"
+ " rc=%d (%s), op_status=%d (%s)\n"
+ , nullcheck(op->op_type), op->call_id
+ , op->start_delay, op->interval, op->timeout
+ , nullcheck(op->app_name), op->rc
+ , rc_msg[(op->rc-EXECRA_EXEC_UNKNOWN_ERROR) % DIMOF(rc_msg)]
+ , op->op_status
+ , status_msg[(op->op_status-LRM_OP_PENDING) % DIMOF(status_msg)]
+ );
+ if( op->t_run || op->t_rcchange )
+ printf(" run at: %s"
+ " last rc change at: %s"
+ " queue time: %lums, exec time: %lums\n"
+ , op->t_run ? ctime(&run_at) : "N/A\n"
+ , op->t_rcchange ? ctime(&rcchange_at) : "N/A\n"
+ , op->queue_time, op->exec_time
+ );
+ printf(" parameters: %s\n", param_gstr->str);
+ g_string_free(param_gstr, TRUE);
+}
+
+static void
+g_get_rsc_description(gpointer data, gpointer user_data)
+{
+ ll_lrm_t* lrmd = (ll_lrm_t *)user_data;
+ lrm_rsc_t * lrm_rsc;
+ char rsc_id_tmp[RID_LEN];
+
+ if (!(user_data)) {
+ return;
+ }
+
+ memset(rsc_id_tmp, '\0', RID_LEN);
+ strncpy(rsc_id_tmp, data, RID_LEN-1);
+
+ lrm_rsc = lrmd->lrm_ops->get_rsc(lrmd, rsc_id_tmp);
+ if (lrm_rsc) {
+ print_rsc_inf(lrm_rsc);
+ lrm_free_rsc(lrm_rsc);
+ } else
+ cl_log(LOG_ERR, "Invalid resource id: %s.",
+ rsc_id_tmp);
+
+ g_free(data);
+}
+static void
+g_print_meta(gpointer key, gpointer value, gpointer user_data)
+{
+ printf("%s\n", (const char*)key);
+ printf("%s\n", (const char*)value);
+}
+static void
+print_rsc_inf(lrm_rsc_t * lrm_rsc)
+{
+ char rscid_str_tmp[RID_LEN];
+ char * tmp = NULL;
+
+ if (!lrm_rsc) {
+ return;
+ }
+
+ rscid_str_tmp[RID_LEN-1] = '\0';
+ strncpy(rscid_str_tmp, lrm_rsc->id, RID_LEN-1);
+ printf("\nResource ID:%s\n", rscid_str_tmp);
+ printf("Resource agent class:%s\n", lrm_rsc->class);
+ printf("Resource agent type:%s\n", lrm_rsc->type);
+ printf("Resource agent provider:%s\n"
+ , lrm_rsc->provider?lrm_rsc->provider:"default");
+
+ if (lrm_rsc->params) {
+ tmp = params_hashtable_to_str(lrm_rsc->class,
+ lrm_rsc->params);
+ printf("Resource agent parameters:%s\n"
+ , (tmp == NULL) ? "No parameter" : tmp);
+ if (tmp != NULL) {
+ g_free(tmp);
+ }
+ }
+}
+
+static void
+free_stritem_of_hashtable(gpointer key, gpointer value, gpointer user_data)
+{
+ /*printf("key=%s value=%s\n", (char *)key, (char *)value);*/
+ g_free(key);
+ g_free(value);
+}
+
+static void
+ocf_params_hash_to_str(gpointer key, gpointer value, gpointer user_data)
+{
+ GString * gstr_tmp = *(GString **)user_data;
+ gstr_tmp = g_string_append(gstr_tmp, (char*)key);
+ gstr_tmp = g_string_append(gstr_tmp, "=");
+ gstr_tmp = g_string_append(gstr_tmp, (char *)value);
+ gstr_tmp = g_string_append(gstr_tmp, " ");
+}
+
+static void
+normal_params_hash_to_str(gpointer key, gpointer value, gpointer user_data)
+{
+ gint key_int;
+
+ gchar * str_tmp = *(gchar **) user_data;
+ if (str_tmp == NULL ) {
+ return;
+ }
+
+ key_int = atoi((char *)key) - 1;
+ if( key_int < 0 ) {
+ return;
+ }
+ strncpy(str_tmp + key_int * ARGVI_MAX_LEN, (char*)value,
+ ARGVI_MAX_LEN - 1);
+}
+
+static lrm_rsc_t *
+get_lrm_rsc(ll_lrm_t * lrmd, char * rscid)
+{
+ char uuid_str_tmp[RID_LEN];
+ lrm_rsc_t * lrm_rsc;
+ lrm_rsc = lrmd->lrm_ops->get_rsc(lrmd, rscid);
+ if (!(lrm_rsc)) {
+ uuid_str_tmp[RID_LEN-1] = '\0';
+ strncpy(uuid_str_tmp, rscid, RID_LEN-1);
+ cl_log(LOG_ERR,"Resource %s does not exist.", uuid_str_tmp);
+ }
+ return lrm_rsc;
+}
+
diff --git a/lrm/admin/lrmadmin.txt b/lrm/admin/lrmadmin.txt
new file mode 100644
index 0000000..739aa70
--- /dev/null
+++ b/lrm/admin/lrmadmin.txt
@@ -0,0 +1,60 @@
+# LRM Admin Command-line Interface
+# It's a draft
+NAME
+ lrmadmin - Local Resource Manager Commander-line Daministrator Tools
+
+SYNOPSIS
+lrmadmin {-d|--daemon}
+ {-A|--add} <rscid> <rsc_class> <rsc_type> [<rsc_params_list>]
+ {-D|--delete} <rscid>
+ {-F|--flush} <rscid>
+ {-E|--execute} <rscid> <operator> <timeout> [<operator_parameters_list>]
+ {-M|--monitor} -s <rscid> <operator> <timeout> <interval>
+ [<operator_parameters_list>]
+ {-M|--monitor} {-g|-c} <rscid>
+ {-S|--status} <rscid>
+ {-L|--listall}
+ {-I|--information} <rsc_id>
+ {-R|--rasupported}
+ {-h|--help}
+
+Detailed Explanation for Options
+
+Lrmd daemon options
+ {-d|--daemon}
+# -s The status of lrmd: running or not running
+# -r Reset lrmd (?)
+
+Resource options
+ {-A|--add} <rscid> <rsc_class> <rsc_type> [<rsc_params_list>]
+ Add a resource.
+
+ {-D|--delete} <rscid>
+ Delete a resource
+
+ {-E|--execute} <rscid> <operator> <timeout> [<operator_parameters_list>]
+ Let resource agent to performance the operation
+
+ {-F|--flush} <rscid>
+ Clear all pending operation on the resource agnency
+
+ {-M|--monitor} {-s|-g} <rscid> <interval>
+ -s rscname Set a monitors on the resource agent
+ -g Get the information about the monitors on the
+ resource agent
+
+ {-S|--status} <rscid>
+ Get the status of current resource agent
+
+ {-L|--listall}
+ List all available resource agent
+
+ {-I|--information} <rsc_id>
+ List the information about a resource
+
+ {-R|--rasupported}
+ List the support types of resource agent such as OCF and etc.
+
+Other options
+ {-h|--help}
+ Display the help screen
diff --git a/lrm/lrmd/Makefile.am b/lrm/lrmd/Makefile.am
new file mode 100644
index 0000000..3680928
--- /dev/null
+++ b/lrm/lrmd/Makefile.am
@@ -0,0 +1,42 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2002 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \
+ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \
+ -I$(top_builddir) -I$(top_srcdir)
+
+halibdir = $(libdir)/@HB_PKG@
+
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la \
+ $(GLIBLIB)
+# $(top_builddir)/lib/apphb/libapphb.la
+
+halib_PROGRAMS = lrmd
+
+lrmd_SOURCES = lrmd.c audit.c cib_secrets.c lrmd_fdecl.h lrmd.h
+
+lrmd_LDFLAGS = $(top_builddir)/lib/lrm/liblrm.la \
+ $(COMMONLIBS) @LIBLTDL@ \
+ $(top_builddir)/lib/pils/libpils.la
+
+noinst_HEADERS = lrmd_fdecl.h lrmd.h
+
+# make lrmd's owner as hacluster:haclient?
diff --git a/lrm/lrmd/audit.c b/lrm/lrmd/audit.c
new file mode 100644
index 0000000..ec92dad
--- /dev/null
+++ b/lrm/lrmd/audit.c
@@ -0,0 +1,191 @@
+/*
+ * Audit lrmd global data structures
+ *
+ * Author: Dejan Muhamedagic <dejan@suse.de>
+ * Copyright (c) 2007 Novell GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <time.h>
+
+#include <glib.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/coredumps.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/cl_pidfile.h>
+#include <ha_msg.h>
+#ifdef ENABLE_APPHB
+# include <apphb.h>
+#endif
+
+#include <lrm/lrm_api.h>
+#include <lrm/lrm_msg.h>
+#include <lrm/raexec.h>
+#include <lrmd.h>
+
+#ifdef DOLRMAUDITS
+
+extern GHashTable* clients;
+extern GHashTable* resources;
+
+#define ptr_bad(level,p,item,text) \
+ lrmd_log(level,"LRMAUDIT: 0x%lx unallocated pointer for: %s(%s)", \
+ (unsigned long)p,item,text);
+#define ptr_null(level,item,text) \
+ lrmd_log(level,"LRMAUDIT: pointer null for: %s(%s)", \
+ item,text);
+
+/* NB: this macro contains return */
+#define ret_on_null(p,item,text) do { \
+ if( !p ) { \
+ ptr_bad(LOG_INFO,p,item,text); \
+ return; \
+ } \
+} while(0)
+#define log_on_null(p,item,text) do { \
+ if( !p ) { \
+ ptr_null(LOG_INFO,item,text); \
+ } \
+} while(0)
+
+void
+lrmd_audit(const char *function, int line)
+{
+ lrmd_log(LOG_DEBUG, "LRMAUDIT: in %s:%d",function,line);
+#ifdef LRMAUDIT_CLIENTS
+ audit_clients();
+#endif
+#ifdef LRMAUDIT_RESOURCES
+ audit_resources();
+#endif
+}
+
+void
+audit_clients()
+{
+ g_hash_table_foreach(clients, on_client, NULL);
+}
+
+void
+audit_resources()
+{
+ g_hash_table_foreach(resources, on_resource, NULL);
+}
+
+void
+audit_ops(GList* rsc_ops, lrmd_rsc_t* rsc, const char *desc)
+{
+ GList *oplist;
+
+ for( oplist = g_list_first(rsc_ops);
+ oplist; oplist = g_list_next(oplist) )
+ {
+ on_op(oplist->data, rsc, desc);
+ }
+}
+
+void
+on_client(gpointer key, gpointer value, gpointer user_data)
+{
+ lrmd_client_t * client = (lrmd_client_t*)value;
+
+ ret_on_null(client,"","client");
+ log_on_null(client->app_name,"","app_name");
+ log_on_null(client->ch_cmd,client->app_name,"ch_cmd");
+ log_on_null(client->ch_cbk,client->app_name,"ch_cbk");
+ log_on_null(client->g_src,client->app_name,"g_src");
+ log_on_null(client->g_src_cbk,client->app_name,"g_src_cbk");
+}
+
+void
+on_resource(gpointer key, gpointer value, gpointer user_data)
+{
+ lrmd_rsc_t* rsc = (lrmd_rsc_t*)value;
+
+ ret_on_null(rsc,"","rsc");
+ ret_on_null(rsc->id,"","id");
+ log_on_null(rsc->type,rsc->id,"type");
+ log_on_null(rsc->class,rsc->id,"class");
+ log_on_null(rsc->provider,rsc->id,"provider");
+ /*log_on_null(rsc->params,rsc->id,"params");*/
+ log_on_null(rsc->last_op_table,rsc->id,"last_op_table");
+ log_on_null(rsc->last_op_done,rsc->id,"last_op_done");
+ audit_ops(rsc->op_list,rsc,"op_list");
+ audit_ops(rsc->repeat_op_list,rsc,"repeat_op_list");
+}
+
+void
+on_op(lrmd_op_t *op, lrmd_rsc_t* rsc, const char *desc)
+{
+ ret_on_null(op,rsc->id,desc);
+ log_on_null(op->rsc_id,rsc->id,"rsc_id");
+ if( strcmp(op->rsc_id,rsc->id) ) {
+ lrmd_log(LOG_ERR,"LRMAUDIT: rsc %s, op %s "
+ "op->rsc_id does not match rsc->id",
+ rsc->id,small_op_info(op));
+ }
+ log_on_null(op->msg,small_op_info(op),"msg");
+ if( op->rapop ) {
+ if( op->rapop->lrmd_op != op ) {
+ lrmd_log(LOG_ERR,
+ "LRMAUDIT: rsc %s, op %s: rapop->lrmd_op does not match op",
+ rsc->id,small_op_info(op));
+ }
+ if( strcmp(op->rapop->rsc_id,op->rsc_id) ) {
+ lrmd_log(LOG_ERR,
+ "LRMAUDIT: rsc %s, op %s rapop->rsc_id does not match op->rsc_id",
+ rsc->id,small_op_info(op));
+ }
+ on_ra_pipe_op(op->rapop,op,"rapop");
+ }
+}
+
+void
+on_ra_pipe_op(ra_pipe_op_t *rapop, lrmd_op_t *op, const char *desc)
+{
+ ret_on_null(rapop,small_op_info(op),desc);
+ log_on_null(rapop->ra_stdout_gsource,small_op_info(op),"ra_stdout_gsource");
+ log_on_null(rapop->ra_stderr_gsource,small_op_info(op),"ra_stderr_gsource");
+ log_on_null(rapop->rsc_id,small_op_info(op),"rsc_id");
+ log_on_null(rapop->op_type,small_op_info(op),"op_type");
+ log_on_null(rapop->rsc_class,small_op_info(op),"rsc_class");
+ if( strcmp(op->rsc_id,rapop->rsc_id) ) {
+ lrmd_log(LOG_ERR,"LRMAUDIT: %s: rapop->rsc_id "
+ "does not match op_rsc->id",
+ small_op_info(op));
+ }
+}
+
+#endif /*DOLRMAUDITS*/
diff --git a/lrm/lrmd/cib_secrets.c b/lrm/lrmd/cib_secrets.c
new file mode 100644
index 0000000..612ffdb
--- /dev/null
+++ b/lrm/lrmd/cib_secrets.c
@@ -0,0 +1,205 @@
+/*
+ * cib_secrets.c
+ *
+ * Author: Dejan Muhamedagic <dejan@suse.de>
+ * Copyright (c) 2011 SUSE, Attachmate
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <glib.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/coredumps.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/md5.h>
+#include <ha_msg.h>
+
+#include <lrm/lrm_api.h>
+#include <lrm/lrm_msg.h>
+
+#include <lrmd.h>
+
+int replace_secret_params(char *rsc_id, GHashTable* params);
+static int is_magic_value(char *p);
+static int check_md5_hash(char *hash, char *value);
+static void add_secret_params(gpointer key, gpointer value, gpointer user_data);
+static char *read_local_file(char *local_file);
+
+#define MAGIC "lrm://"
+
+static int
+is_magic_value(char *p)
+{
+ return !strcmp(p, MAGIC);
+}
+
+#define MD5LEN 16
+static int
+check_md5_hash(char *hash, char *value)
+{
+ int i;
+ char hash2[2*MD5LEN+1];
+ unsigned char binary[MD5LEN+1];
+
+ MD5((unsigned char *)value, strlen(value), binary);
+ for (i = 0; i < MD5LEN; i++)
+ sprintf(hash2+2*i, "%02x", binary[i]);
+ hash2[2*i] = '\0';
+ lrmd_debug2(LOG_DEBUG
+ , "%s:%d: hash: %s, calculated hash: %s"
+ , __FUNCTION__, __LINE__, hash, hash2);
+ return !strcmp(hash, hash2);
+}
+
+static char *
+read_local_file(char *local_file)
+{
+ FILE *fp = fopen(local_file, "r");
+ char buf[MAX_VALUE_LEN+1];
+ char *p;
+
+ if (!fp) {
+ if (errno != ENOENT) {
+ cl_perror("%s:%d: cannot open %s"
+ , __FUNCTION__, __LINE__, local_file);
+ }
+ return NULL;
+ }
+ if (!fgets(buf, MAX_VALUE_LEN, fp)) {
+ cl_perror("%s:%d: cannot read %s"
+ , __FUNCTION__, __LINE__, local_file);
+ return NULL;
+ }
+ /* strip white space */
+ for (p = buf+strlen(buf)-1; p >= buf && isspace(*p); p--)
+ ;
+ *(p+1) = '\0';
+ return g_strdup(buf);
+}
+
+/*
+ * returns 0 on success or no replacements necessary
+ * returns -1 if replacement failed for whatever reasone
+ */
+
+int
+replace_secret_params(char *rsc_id, GHashTable* params)
+{
+ char local_file[FILENAME_MAX+1], *start_pname;
+ char hash_file[FILENAME_MAX+1], *hash;
+ GList *secret_params = NULL, *l;
+ char *key, *pvalue, *secret_value;
+ int rc = 0;
+
+ /* secret_params could be cached with the resource;
+ * there are also parameters sent with operations
+ * which cannot be cached
+ */
+ g_hash_table_foreach(params, add_secret_params, &secret_params);
+ if (!secret_params) /* none found? */
+ return 0;
+
+ lrmd_debug(LOG_DEBUG
+ , "%s:%d: replace secret parameters for resource %s"
+ , __FUNCTION__, __LINE__, rsc_id);
+ if (snprintf(local_file, FILENAME_MAX,
+ LRM_CIBSECRETS "/%s/", rsc_id) > FILENAME_MAX) {
+ lrmd_log(LOG_ERR
+ , "%s:%d: filename size exceeded for resource %s"
+ , __FUNCTION__, __LINE__, rsc_id);
+ return -1;
+ }
+ start_pname = local_file + strlen(local_file);
+
+ for (l = g_list_first(secret_params); l; l = g_list_next(l)) {
+ key = (char *)(l->data);
+ pvalue = g_hash_table_lookup(params, key);
+ if (!pvalue) { /* this cannot really happen */
+ lrmd_log(LOG_ERR
+ , "%s:%d: odd, no parameter %s for rsc %s found now"
+ , __FUNCTION__, __LINE__, key, rsc_id);
+ continue;
+ }
+ if ((strlen(key) + strlen(local_file)) >= FILENAME_MAX-2) {
+ lrmd_log(LOG_ERR
+ , "%s:%d: parameter name %s too big"
+ , __FUNCTION__, __LINE__, key);
+ rc = -1;
+ continue;
+ }
+ strcpy(start_pname, key);
+ secret_value = read_local_file(local_file);
+ if (!secret_value) {
+ lrmd_log(LOG_ERR
+ , "%s:%d: secret for rsc %s parameter %s "
+ "not found in " LRM_CIBSECRETS
+ , __FUNCTION__, __LINE__, rsc_id, key);
+ rc = -1;
+ continue;
+ }
+ strcpy(hash_file, local_file);
+ if (strlen(hash_file) + 5 > FILENAME_MAX) {
+ lrmd_log(LOG_ERR
+ , "%s:%d: cannot build such a long name "
+ "for the sign file: %s.sign"
+ , __FUNCTION__, __LINE__, hash_file);
+ } else {
+ strncat(hash_file, ".sign", 5);
+ hash = read_local_file(hash_file);
+ if (!check_md5_hash(hash, secret_value)) {
+ lrmd_log(LOG_ERR
+ , "%s:%d: md5 sum for rsc %s parameter %s "
+ "does not match"
+ , __FUNCTION__, __LINE__, rsc_id, key);
+ g_free(secret_value);
+ g_free(hash);
+ rc = -1;
+ continue;
+ }
+ g_free(hash);
+ }
+ g_hash_table_replace(params, g_strdup(key), secret_value);
+ }
+ g_list_free(secret_params);
+ return rc;
+}
+
+static void
+add_secret_params(gpointer key, gpointer value, gpointer user_data)
+{
+ GList **lp = (GList **)user_data;
+
+ if (is_magic_value((char *)value))
+ *lp = g_list_append(*lp, (char *)key);
+}
diff --git a/lrm/lrmd/lrmd.c b/lrm/lrmd/lrmd.c
new file mode 100644
index 0000000..385096b
--- /dev/null
+++ b/lrm/lrmd/lrmd.c
@@ -0,0 +1,4053 @@
+/*
+ * Local Resource Manager Daemon
+ *
+ * Author: Huang Zhen <zhenhltc@cn.ibm.com>
+ * Partly contributed by Andrew Beekhof <andrew@beekhof.net>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <time.h>
+#include <sched.h>
+
+#include <glib.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <clplumbing/GSource.h>
+#include <clplumbing/lsb_exitcodes.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/proctrack.h>
+#include <clplumbing/coredumps.h>
+#include <clplumbing/uids.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/realtime.h>
+#include <ha_msg.h>
+#ifdef ENABLE_APPHB
+# include <apphb.h>
+#endif
+/* #include <hb_api.h> */
+
+#include <lrm/lrm_api.h>
+#include <lrm/lrm_msg.h>
+#include <lrm/raexec.h>
+
+#include <lrmd.h>
+#include <lrmd_fdecl.h>
+
+static gboolean in_alloc_dump = FALSE;
+
+ProcTrack_ops ManagedChildTrackOps = {
+ on_ra_proc_finished,
+ on_ra_proc_registered,
+ on_ra_proc_query_name
+};
+
+/* msg dispatch table */
+typedef int (*msg_handler)(lrmd_client_t* client, struct ha_msg* msg);
+struct msg_map
+{
+ const char *msg_type;
+ int reply_time;
+ msg_handler handler;
+ int min_priv; /* minimum privileges required */
+};
+
+/*
+ * two ways to handle replies:
+ * REPLY_NOW: pack whatever the handler returned and send it
+ * NO_MSG: the handler will send the reply itself
+ */
+#define REPLY_NOW 0
+#define NO_MSG 1
+#define send_msg_now(p) \
+ (p->reply_time==REPLY_NOW)
+
+struct msg_map msg_maps[] = {
+ {REGISTER, REPLY_NOW, on_msg_register, 0},
+ {GETRSCCLASSES, NO_MSG, on_msg_get_rsc_classes, 0},
+ {GETRSCTYPES, NO_MSG, on_msg_get_rsc_types, 0},
+ {GETPROVIDERS, NO_MSG, on_msg_get_rsc_providers, 0},
+ {ADDRSC, REPLY_NOW, on_msg_add_rsc, PRIV_ADMIN},
+ {GETRSC, NO_MSG, on_msg_get_rsc, PRIV_ADMIN},
+ {GETLASTOP, NO_MSG, on_msg_get_last_op, PRIV_ADMIN},
+ {GETALLRCSES, NO_MSG, on_msg_get_all, PRIV_ADMIN},
+ {DELRSC, REPLY_NOW, on_msg_del_rsc, PRIV_ADMIN},
+ {FAILRSC, REPLY_NOW, on_msg_fail_rsc, PRIV_ADMIN},
+ {PERFORMOP, REPLY_NOW, on_msg_perform_op, PRIV_ADMIN},
+ {FLUSHOPS, REPLY_NOW, on_msg_flush_all, PRIV_ADMIN},
+ {CANCELOP, REPLY_NOW, on_msg_cancel_op, PRIV_ADMIN},
+ {GETRSCSTATE, NO_MSG, on_msg_get_state, PRIV_ADMIN},
+ {GETRSCMETA, NO_MSG, on_msg_get_metadata, 0},
+ {SETLRMDPARAM, REPLY_NOW, on_msg_set_lrmd_param, PRIV_ADMIN},
+ {GETLRMDPARAM, NO_MSG, on_msg_get_lrmd_param, 0},
+};
+#define MSG_NR sizeof(msg_maps)/sizeof(struct msg_map)
+
+GHashTable* clients = NULL; /* a GHashTable indexed by pid */
+GHashTable* resources = NULL; /* a GHashTable indexed by rsc_id */
+
+static GMainLoop* mainloop = NULL;
+static int call_id = 1;
+static const char* lrm_system_name = "lrmd";
+static GHashTable * RAExecFuncs = NULL;
+static GList* ra_class_list = NULL;
+static gboolean shutdown_in_progress = FALSE;
+static unsigned long apphb_interval = 2000; /* Millisecond */
+static gboolean reg_to_apphbd = FALSE;
+static int max_child_count = 4;
+static int retry_interval = 1000; /* Millisecond */
+static int child_count = 0;
+static IPC_Auth * auth = NULL;
+
+static struct {
+ int opcount;
+ int clientcount;
+ int rsccount;
+}lrm_objectstats;
+
+/* define indexes into logmsg_ctrl_defs */
+#define OP_STAYED_TOO_LONG 0
+static struct logspam logmsg_ctrl_defs[] = {
+ { "operation stayed too long in the queue",
+ 10, 60, 120, /* max 10 messages in 60s, then delay for 120s */
+ "configuration advice: reduce operation contention "
+ "either by increasing lrmd max_children or by increasing intervals "
+ "of monitor operations"
+ },
+};
+
+#define set_fd_opts(fd,opts) do { \
+ int flag; \
+ if ((flag = fcntl(fd, F_GETFL)) >= 0) { \
+ if (fcntl(fd, F_SETFL, flag|opts) < 0) { \
+ cl_perror("%s::%d: fcntl", __FUNCTION__ \
+ , __LINE__); \
+ } \
+ } else { \
+ cl_perror("%s::%d: fcntl", __FUNCTION__, __LINE__); \
+ } \
+ } while(0)
+
+static ra_pipe_op_t *
+ra_pipe_op_new(int child_stdout, int child_stderr, lrmd_op_t * lrmd_op)
+{
+ ra_pipe_op_t * rapop;
+ lrmd_rsc_t* rsc = NULL;
+
+ if ( NULL == lrmd_op ) {
+ lrmd_log(LOG_WARNING
+ , "%s:%d: lrmd_op==NULL, no need to malloc ra_pipe_op"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ rapop = calloc(sizeof(ra_pipe_op_t), 1);
+ if ( rapop == NULL) {
+ lrmd_log(LOG_ERR, "%s:%d out of memory"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ rapop->first_line_read = FALSE;
+
+ /*
+ * No any obviouse proof of lrmd hang in pipe read yet.
+ * Bug 475 may be a duplicate of bug 499.
+ * Anyway, via test, it's proved that NOBLOCK read will
+ * obviously reduce the RA execution time (bug 553).
+ */
+ /* Let the read operation be NONBLOCK */
+ set_fd_opts(child_stdout,O_NONBLOCK);
+ set_fd_opts(child_stderr,O_NONBLOCK);
+
+ /* there's so much code duplication here */
+ rapop->ra_stdout_fd = child_stdout;
+ if (rapop->ra_stdout_fd <= STDERR_FILENO) {
+ lrmd_log(LOG_ERR, "%s: invalid stdout fd [%d]"
+ , __FUNCTION__, rapop->ra_stdout_fd);
+ }
+ rapop->ra_stdout_gsource = G_main_add_fd(G_PRIORITY_HIGH
+ , child_stdout, FALSE, handle_pipe_ra_stdout
+ , rapop, destroy_pipe_ra_stdout);
+
+ rapop->ra_stderr_fd = child_stderr;
+ if (rapop->ra_stderr_fd <= STDERR_FILENO) {
+ lrmd_log(LOG_ERR, "%s: invalid stderr fd [%d]"
+ , __FUNCTION__, rapop->ra_stderr_fd);
+ }
+ rapop->ra_stderr_gsource = G_main_add_fd(G_PRIORITY_HIGH
+ , child_stderr, FALSE, handle_pipe_ra_stderr
+ , rapop, destroy_pipe_ra_stderr);
+
+ rapop->lrmd_op = lrmd_op;
+
+ rapop->op_type = strdup(ha_msg_value(lrmd_op->msg, F_LRM_OP));
+ rapop->rsc_id = strdup(lrmd_op->rsc_id);
+ rsc = lookup_rsc(lrmd_op->rsc_id);
+ if (rsc == NULL) {
+ lrmd_debug(LOG_WARNING
+ , "%s::%d: the rsc (id=%s) does not exist"
+ , __FUNCTION__, __LINE__, lrmd_op->rsc_id);
+ rapop->rsc_class = NULL;
+ } else {
+ rapop->rsc_class = strdup(rsc->class);
+ }
+
+ return rapop;
+}
+
+static void
+ra_pipe_op_destroy(ra_pipe_op_t * rapop)
+{
+ CHECK_ALLOCATED(rapop, "ra_pipe_op", );
+
+ if ( NULL != rapop->ra_stdout_gsource) {
+ G_main_del_fd(rapop->ra_stdout_gsource);
+ rapop->ra_stdout_gsource = NULL;
+ }
+
+ if ( NULL != rapop->ra_stderr_gsource) {
+ G_main_del_fd(rapop->ra_stderr_gsource);
+ rapop->ra_stderr_gsource = NULL;
+ }
+
+ if (rapop->ra_stdout_fd >= STDERR_FILENO) {
+ close(rapop->ra_stdout_fd);
+ rapop->ra_stdout_fd = -1;
+ }else if (rapop->ra_stdout_fd >= 0) {
+ lrmd_log(LOG_ERR, "%s: invalid stdout fd %d"
+ , __FUNCTION__, rapop->ra_stdout_fd);
+ }
+ if (rapop->ra_stderr_fd >= STDERR_FILENO) {
+ close(rapop->ra_stderr_fd);
+ rapop->ra_stderr_fd = -1;
+ }else if (rapop->ra_stderr_fd >= 0) {
+ lrmd_log(LOG_ERR, "%s: invalid stderr fd %d"
+ , __FUNCTION__, rapop->ra_stderr_fd);
+ }
+ rapop->first_line_read = FALSE;
+
+ free(rapop->rsc_id);
+ free(rapop->op_type);
+ rapop->op_type = NULL;
+ free(rapop->rsc_class);
+ rapop->rsc_class = NULL;
+
+ if (rapop->lrmd_op != NULL) {
+ rapop->lrmd_op->rapop = NULL;
+ rapop->lrmd_op = NULL;
+ }
+
+ free(rapop);
+}
+
+static void
+lrmd_op_destroy(lrmd_op_t* op)
+{
+ CHECK_ALLOCATED(op, "op", );
+ --lrm_objectstats.opcount;
+
+ if (op->exec_pid > 1) {
+ lrmd_log(LOG_CRIT
+ , "%s: lingering operation process %d, op %s"
+ , __FUNCTION__, op->exec_pid, small_op_info(op));
+ return;
+ }
+ lrmd_debug2(LOG_DEBUG, "%s: free the %s with address %p"
+ ,__FUNCTION__, op_info(op), op);
+ ha_msg_del(op->msg);
+ op->msg = NULL;
+ if( op->rsc_id ) {
+ free(op->rsc_id);
+ op->rsc_id = NULL;
+ }
+ op->exec_pid = 0;
+ if ( op->rapop != NULL ) {
+ op->rapop->lrmd_op = NULL;
+ op->rapop = NULL;
+ }
+ op->first_line_ra_stdout[0] = EOS;
+
+ if( op->repeat_timeout_tag ) {
+ Gmain_timeout_remove(op->repeat_timeout_tag);
+ }
+ free(op);
+}
+
+static lrmd_op_t*
+lrmd_op_new(void)
+{
+ lrmd_op_t* op = (lrmd_op_t*)calloc(sizeof(lrmd_op_t),1);
+
+ if (op == NULL) {
+ lrmd_log(LOG_ERR, "lrmd_op_new(): out of memory when "
+ "calloc a lrmd_op_t.");
+ return NULL;
+ }
+ op->rsc_id = NULL;
+ op->msg = NULL;
+ op->exec_pid = -1;
+ op->repeat_timeout_tag = 0;
+ op->rapop = NULL;
+ op->first_line_ra_stdout[0] = EOS;
+ op->t_recv = time_longclock();
+ op->t_perform = zero_longclock;
+ op->t_done = zero_longclock;
+ op->t_rcchange = zero_longclock;
+ op->t_lastlogmsg = zero_longclock;
+
+ memset(op->killseq, 0, sizeof(op->killseq));
+ ++lrm_objectstats.opcount;
+ return op;
+}
+
+static lrmd_op_t*
+lrmd_op_copy(const lrmd_op_t* op)
+{
+ lrmd_op_t* ret;
+
+ ret = lrmd_op_new();
+ if (NULL == ret) {
+ return NULL;
+ }
+ /* Do a "shallow" copy */
+ *ret = *op;
+ /*
+ * Some things, like timer ids and child pids are duplicated here
+ * but can be destroyed in one copy, but kept intact
+ * in the other, to later be destroyed.
+ * This isn't a complete disaster, since the timer ids aren't
+ * pointers, but it's still untidy at the least.
+ * Be sure and care of this situation when using this function.
+ */
+ /* Do a "deep" copy of the message structure */
+ ret->rapop = NULL;
+ ret->msg = ha_msg_copy(op->msg);
+ ret->rsc_id = strdup(op->rsc_id);
+ ret->rapop = NULL;
+ ret->first_line_ra_stdout[0] = EOS;
+ ret->repeat_timeout_tag = 0;
+ ret->exec_pid = -1;
+ ret->t_recv = op->t_recv;
+ ret->t_perform = op->t_perform;
+ ret->t_done = op->t_done;
+ ret->t_rcchange = op->t_rcchange;
+ ret->is_copy = TRUE;
+ ret->is_cancelled = FALSE;
+ ret->weight = op->weight;
+ return ret;
+}
+
+static
+const char *
+op_status_to_str(int op_status)
+{
+ static char whatwasthat[25];
+ switch (op_status) {
+ case LRM_OP_DONE:
+ return "LRM_OP_DONE";
+ case LRM_OP_CANCELLED:
+ return "LRM_OP_CANCELLED";
+ case LRM_OP_TIMEOUT:
+ return "LRM_OP_TIMEOUT";
+ case LRM_OP_NOTSUPPORTED:
+ return "LRM_OP_NOTSUPPORTED";
+ case -1:
+ return "N/A (-1)";
+ default:
+ break;
+ }
+ snprintf(whatwasthat, sizeof(whatwasthat), "UNDEFINED STATUS: %d?", op_status);
+ return whatwasthat;
+}
+static
+const char *
+op_target_rc_to_str(int target)
+{
+ static char whatwasthat[25];
+ switch (target) {
+ case EVERYTIME:
+ return "EVERYTIME";
+ case CHANGED:
+ return "CHANGED";
+ default:
+ break;
+ }
+ snprintf(whatwasthat, sizeof(whatwasthat)
+ ,"UNDEFINED TARGET_RC: %d", target);
+ return whatwasthat;
+}
+
+/*
+ * We need a separate function to dump out operations for
+ * debugging. Then we wouldn't have to have the code for this
+ * inline. In particular, we could then call this from on_op_done()
+ * which would shorten and simplify that code - which could use
+ * the help :-)
+ */
+
+
+/* Debug oriented funtions */
+static gboolean debug_level_adjust(int nsig, gpointer user_data);
+
+static void
+lrmd_op_dump(const lrmd_op_t* op, const char * text)
+{
+ int op_status = -1;
+ int target_rc = -1;
+ const char * pidstat;
+ longclock_t now = time_longclock();
+
+ CHECK_ALLOCATED(op, "op", );
+ if (op->exec_pid < 1
+ || ((kill(op->exec_pid, 0) < 0) && ESRCH == errno)) {
+ pidstat = "not running";
+ }else{
+ pidstat = "running";
+ }
+ ha_msg_value_int(op->msg, F_LRM_OPSTATUS, &op_status);
+ ha_msg_value_int(op->msg, F_LRM_TARGETRC, &target_rc);
+ lrmd_debug(LOG_DEBUG
+ , "%s: lrmd_op: %s status: %s, target_rc=%s, client pid %d call_id"
+ ": %d, child pid: %d (%s) %s %s"
+ , text, op_info(op), op_status_to_str(op_status)
+ , op_target_rc_to_str(target_rc)
+ , op->client_id, op->call_id, op->exec_pid, pidstat
+ , (op->is_copy ? "copy" : "original")
+ , (op->is_cancelled ? "cancelled" : ""));
+ lrmd_debug(LOG_DEBUG
+ , "%s: lrmd_op2: rt_tag: %d, interval: %d, delay: %d"
+ , text, op->repeat_timeout_tag
+ , op->interval, op->delay);
+ lrmd_debug(LOG_DEBUG
+ , "%s: lrmd_op3: t_recv: %ldms, t_add: %ldms"
+ ", t_perform: %ldms, t_done: %ldms, t_rcchange: %ldms"
+ , text, tm2age(op->t_recv), tm2age(op->t_addtolist)
+ , tm2age(op->t_perform), tm2age(op->t_done), tm2age(op->t_rcchange));
+ lrmd_rsc_dump(op->rsc_id, text);
+}
+
+
+static void
+lrmd_client_destroy(lrmd_client_t* client)
+{
+ CHECK_ALLOCATED(client, "client", );
+
+ --lrm_objectstats.clientcount;
+ /*
+ * Delete direct references to this client
+ * and repeating operations it might have scheduled
+ */
+ unregister_client(client);
+ if (client->app_name) {
+ free(client->app_name);
+ client->app_name = NULL;
+ }
+ free(client);
+}
+
+static lrmd_client_t*
+lrmd_client_new(void)
+{
+ lrmd_client_t* client;
+ client = calloc(sizeof(lrmd_client_t), 1);
+ if (client == NULL) {
+ lrmd_log(LOG_ERR, "lrmd_client_new(): out of memory when "
+ "calloc lrmd_client_t.");
+ return NULL;
+ }
+ client->g_src = NULL;
+ client->g_src_cbk = NULL;
+ ++lrm_objectstats.clientcount;
+ return client;
+}
+static void
+lrmd_client_dump(gpointer key, gpointer value, gpointer user_data)
+{
+ lrmd_client_t * client = (lrmd_client_t*)value;
+ CHECK_ALLOCATED(client, "client", );
+ if(!client) {
+ return;
+ }
+
+ lrmd_debug(LOG_DEBUG, "client name: %s, client pid: %d"
+ ", client uid: %d, gid: %d, last request: %s"
+ ", last op in: %s, lastop out: %s"
+ ", last op rc: %s"
+ , lrm_str(client->app_name)
+ , client->pid
+ , client->uid, client->gid
+ , client->lastrequest
+ , ctime(&client->lastreqstart)
+ , ctime(&client->lastreqend)
+ , ctime(&client->lastrcsent)
+ );
+ if (!client->ch_cmd) {
+ lrmd_debug(LOG_DEBUG, "NULL client ch_cmd in %s()", __FUNCTION__);
+ }else{
+ lrmd_debug(LOG_DEBUG
+ , "Command channel status: %d, read queue addr: %p, write queue addr: %p"
+ , client->ch_cmd->ch_status
+ , client->ch_cmd->recv_queue
+ , client->ch_cmd->send_queue );
+
+ if (client->ch_cmd->recv_queue && client->ch_cmd->send_queue) {
+ lrmd_debug(LOG_DEBUG, "read Qlen: %ld, write Qlen: %ld"
+ , (long)client->ch_cmd->recv_queue->current_qlen
+ , (long)client->ch_cmd->send_queue->current_qlen);
+ }
+ }
+ if (!client->ch_cbk) {
+ lrmd_debug(LOG_DEBUG, "NULL client ch_cbk in %s()", __FUNCTION__);
+ }else{
+ lrmd_debug(LOG_DEBUG
+ , "Callback channel status: %d, read Qlen: %ld, write Qlen: %ld"
+ , client->ch_cbk->ch_status
+ , (long)client->ch_cbk->recv_queue->current_qlen
+ , (long)client->ch_cbk->send_queue->current_qlen);
+ }
+}
+static void
+lrmd_dump_all_clients(void)
+{
+ static gboolean incall = FALSE;
+
+ if (incall) {
+ return;
+ }
+
+ incall = TRUE;
+
+ lrmd_debug(LOG_DEBUG, "%d clients connected to lrmd"
+ , g_hash_table_size(clients));
+
+ g_hash_table_foreach(clients, lrmd_client_dump, NULL);
+ incall = FALSE;
+}
+
+static void
+lrmd_rsc_destroy(lrmd_rsc_t* rsc)
+{
+ LRMAUDIT();
+ CHECK_ALLOCATED(rsc, "resource", );
+ --lrm_objectstats.rsccount;
+ if( rsc->op_list || rsc->repeat_op_list ) {
+ lrmd_log(LOG_ERR, "%s: refusing to remove resource %s"
+ " which is still holding operations"
+ , __FUNCTION__, lrm_str(rsc->id));
+ return;
+ } else {
+ lrmd_debug(LOG_DEBUG, "%s: removing resource %s"
+ , __FUNCTION__, lrm_str(rsc->id));
+ }
+ g_hash_table_remove(resources, rsc->id);
+ if (rsc->id) {
+ free(rsc->id);
+ rsc->id = NULL;
+ }
+ if (rsc->type) {
+ free(rsc->type);
+ rsc->type = NULL;
+ }
+ if (rsc->class) {
+ free(rsc->class);
+ rsc->class = NULL;
+ }
+ if (rsc->provider) {
+ free(rsc->provider);
+ rsc->provider = NULL;
+ }
+ if (NULL != rsc->params) {
+ free_str_table(rsc->params);
+ rsc->params = NULL;
+ }
+ if (rsc->last_op_table) {
+ g_hash_table_foreach_remove(rsc->last_op_table
+ , free_str_hash_pair, NULL);
+ g_hash_table_destroy(rsc->last_op_table);
+ rsc->last_op_table = NULL;
+ }
+ if (rsc->last_op_done) {
+ lrmd_op_destroy(rsc->last_op_done);
+ rsc->last_op_done = NULL;
+ }
+
+ if (rsc->delay_timeout > 0) {
+ Gmain_timeout_remove(rsc->delay_timeout);
+ rsc->delay_timeout = (guint)0;
+ }
+
+ free(rsc);
+ LRMAUDIT();
+}
+
+static lrmd_rsc_t*
+lrmd_rsc_new(const char * id, struct ha_msg* msg)
+{
+ lrmd_rsc_t* rsc;
+ rsc = (lrmd_rsc_t *)calloc(sizeof(lrmd_rsc_t),1);
+ if (rsc == NULL) {
+ lrmd_log(LOG_ERR, "%s: out of memory when calloc "
+ "a lrmd_rsc_t", __FUNCTION__);
+ return NULL;
+ }
+ rsc->delay_timeout = (guint)0;
+ if (id) {
+ rsc->id = strdup(id);
+ }
+ if (msg) {
+ rsc->type = strdup(ha_msg_value(msg, F_LRM_RTYPE));
+ rsc->class = strdup(ha_msg_value(msg, F_LRM_RCLASS));
+ if (NULL == ha_msg_value(msg, F_LRM_RPROVIDER)) {
+ lrmd_log(LOG_NOTICE, "%s(): No %s field in message"
+ , __FUNCTION__, F_LRM_RPROVIDER);
+ }else{
+ rsc->provider = strdup(ha_msg_value(msg, F_LRM_RPROVIDER));
+ if (rsc->provider == NULL) {
+ goto errout;
+ }
+ }
+ if (rsc->id == NULL
+ || rsc->type == NULL
+ || rsc->class == NULL) {
+ goto errout;
+ }
+ }
+ g_hash_table_insert(resources, strdup(id), rsc);
+ ++lrm_objectstats.rsccount;
+ return rsc;
+errout:
+ lrmd_rsc_destroy(rsc); /* violated property */ /* Or so BEAM thinks :-) */
+ rsc = NULL;
+ return rsc;
+}
+
+static void
+dump_op(gpointer key, gpointer val, gpointer data)
+{
+ lrmd_op_t* lrmd_op = (lrmd_op_t*) val;
+
+ lrmd_op_dump(lrmd_op, "rsc->last_op_table");
+}
+static void
+dump_op_table(gpointer key, gpointer val, gpointer data)
+{
+ GHashTable* table = (GHashTable*) val;
+
+ g_hash_table_foreach(table, dump_op, data);
+}
+static void
+lrmd_rsc_dump(char* rsc_id, const char * text)
+{
+ static gboolean incall = FALSE;
+ GList* oplist;
+ lrmd_rsc_t* rsc=NULL;
+
+ if( rsc_id ) {
+ rsc = lookup_rsc(rsc_id);
+ } else {
+ lrmd_debug(LOG_INFO
+ , "%s:%d: the rsc_id is NULL"
+ , __FUNCTION__, __LINE__);
+ return;
+ }
+ CHECK_ALLOCATED(rsc, "rsc", );
+ if(!rsc) {
+ return;
+ }
+
+ /* Avoid infinite recursion loops... */
+ if (incall) {
+ return;
+ }
+ incall = TRUE;
+ /* TODO: Dump params and last_op_table FIXME */
+
+ lrmd_debug(LOG_DEBUG, "%s: BEGIN resource dump", text);
+ lrmd_debug(LOG_DEBUG, "%s: resource %s/%s/%s/%s"
+ , text
+ , lrm_str(rsc->id)
+ , lrm_str(rsc->type)
+ , lrm_str(rsc->class)
+ , lrm_str(rsc->provider));
+
+ lrmd_debug(LOG_DEBUG, "%s: rsc->op_list...", text);
+ for(oplist = g_list_first(rsc->op_list); oplist;
+ oplist = g_list_next(oplist)) {
+ lrmd_op_dump(oplist->data, "rsc->op_list");
+ }
+
+ lrmd_debug(LOG_DEBUG, "%s: rsc->repeat_op_list...", text);
+ for(oplist = g_list_first(rsc->repeat_op_list); oplist;
+ oplist=g_list_next(oplist)) {
+ lrmd_op_dump(oplist->data, "rsc->repeat_op_list");
+ }
+
+ if (rsc->last_op_done != NULL) {
+ lrmd_debug(LOG_DEBUG, "%s: rsc->last_op_done...", text);
+ lrmd_op_dump(rsc->last_op_done, "rsc->last_op_done");
+ }
+ else {
+ lrmd_debug(LOG_DEBUG, "%s: rsc->last_op_done==NULL", text);
+ }
+ if (rsc->last_op_table) {
+ g_hash_table_foreach(rsc->last_op_table,dump_op_table,NULL);
+ }
+ else {
+ lrmd_debug(LOG_DEBUG, "%s: rsc->last_op_table==NULL", text);
+ }
+ lrmd_debug(LOG_DEBUG, "%s: END resource dump", text);
+ incall = FALSE;
+};
+static void
+dump_id_rsc_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ char* rid = (char*)key;
+ char* text = (char*)user_data;
+ lrmd_rsc_dump(rid,text);
+}
+static void
+lrmd_dump_all_resources(void)
+{
+ static gboolean incall = FALSE;
+ char text[]= "lrmd_dump_all_resources";
+ if (incall) {
+ return;
+ }
+ incall = TRUE;
+
+ lrmd_debug(LOG_DEBUG, "%d resources are managed by lrmd"
+ , g_hash_table_size(resources));
+ g_hash_table_foreach(resources, dump_id_rsc_pair, text);
+ incall = FALSE;
+}
+
+
+#if 0
+static void
+lrm_debug_running_op(lrmd_op_t* op, const char * text)
+{
+ char cmd[256];
+ lrmd_op_dump(op, text);
+ CHECK_ALLOCATED(op, "op", );
+ if (op->exec_pid >= 1) {
+ /* This really ought to use our logger
+ * So... it might not get forwarded to the central machine
+ * if you're testing with CTS -- FIXME!!!
+ */
+ snprintf(cmd, sizeof(cmd)
+ , "ps -l -f -s %d | logger -p daemon.info -t 'T/O PS:'"
+ , op->exec_pid);
+ lrmd_debug(LOG_DEBUG, "Running [%s]", cmd);
+ if (system(cmd) != 0) {
+ lrmd_log(LOG_ERR, "Running [%s] failed", cmd);
+ }
+ snprintf(cmd, sizeof(cmd)
+ , "ps axww | logger -p daemon.info -t 't/o ps:'");
+ lrmd_debug(LOG_DEBUG, "Running [%s]", cmd);
+ if (system(cmd) != 0) {
+ lrmd_log(LOG_ERR, "Running [%s] failed", cmd);
+ }
+ }
+}
+#endif
+int
+main(int argc, char ** argv)
+{
+ int req_restart = TRUE;
+ int req_status = FALSE;
+ int req_stop = FALSE;
+
+ int argerr = 0;
+ int flag;
+
+ while ((flag = getopt(argc, argv, OPTARGS)) != EOF) {
+ switch(flag) {
+ case 'h': /* Help message */
+ usage(lrm_system_name, LSB_EXIT_OK);
+ break;
+ case 'v': /* Debug mode, more logs*/
+ ++debug_level;
+ break;
+ case 's': /* Status */
+ req_status = TRUE;
+ break;
+ case 'k': /* Stop (kill) */
+ req_stop = TRUE;
+ break;
+ case 'r': /* Restart */
+ req_restart = TRUE;
+ break;
+ /* Register to apphbd then monitored by it */
+ case 'm':
+ reg_to_apphbd = TRUE;
+ break;
+ case 'i': /* Get apphb interval */
+ if (optarg) {
+ apphb_interval = atoi(optarg);
+ }
+ break;
+ default:
+ ++argerr;
+ break;
+ }
+ }
+
+ if (optind > argc) {
+ ++argerr;
+ }
+
+ if (argerr) {
+ usage(lrm_system_name, LSB_EXIT_GENERIC);
+ }
+
+ cl_log_set_entity(lrm_system_name);
+ cl_log_enable_stderr(debug_level?TRUE:FALSE);
+ cl_log_set_facility(HA_LOG_FACILITY);
+
+ /* Use logd if it's enabled by heartbeat */
+ cl_inherit_logging_environment(0);
+
+ if (req_status){
+ return init_status(PID_FILE, lrm_system_name);
+ }
+
+ if (req_stop){
+ return init_stop(PID_FILE);
+ }
+
+ if (req_restart) {
+ init_stop(PID_FILE);
+ }
+
+ return init_start();
+}
+
+int
+init_status(const char *pid_file, const char *client_name)
+{
+ long pid = cl_read_pidfile(pid_file);
+
+ if (pid > 0) {
+ fprintf(stderr, "%s is running [pid: %ld]\n"
+ , client_name, pid);
+ return LSB_STATUS_OK;
+ }
+ fprintf(stderr, "%s is stopped.\n", client_name);
+ return LSB_STATUS_STOPPED;
+}
+
+int
+init_stop(const char *pid_file)
+{
+ long pid;
+ int rc = LSB_EXIT_OK;
+
+
+
+ if (pid_file == NULL) {
+ lrmd_log(LOG_ERR, "No pid file specified to kill process");
+ return LSB_EXIT_GENERIC;
+ }
+ pid = cl_read_pidfile(pid_file);
+
+ if (pid > 0) {
+ if (CL_KILL((pid_t)pid, SIGTERM) < 0) {
+ rc = (errno == EPERM
+ ? LSB_EXIT_EPERM : LSB_EXIT_GENERIC);
+ fprintf(stderr, "Cannot kill pid %ld\n", pid);
+ }else{
+ lrmd_log(LOG_INFO,
+ "Signal sent to pid=%ld,"
+ " waiting for process to exit",
+ pid);
+
+ while (CL_PID_EXISTS(pid)) {
+ sleep(1);
+ }
+ }
+ }
+ return rc;
+}
+
+static const char usagemsg[] = "[-srkhv]\n\ts: status\n\tr: restart"
+ "\n\tk: kill\n\tm: register to apphbd\n\ti: the interval of apphb\n\t"
+ "h: help\n\tv: debug\n";
+
+void
+usage(const char* cmd, int exit_status)
+{
+ FILE* stream;
+
+ stream = exit_status ? stderr : stdout;
+
+ fprintf(stream, "usage: %s %s", cmd, usagemsg);
+ fflush(stream);
+
+ exit(exit_status);
+}
+/*
+ * In design, the lrmd should not know the meaning of operation type
+ * and the meaning of rc. This function is just for logging.
+ */
+static void
+warning_on_active_rsc(gpointer key, gpointer value, gpointer user_data)
+{
+ int op_status, rc;
+ const char* op_type;
+
+ lrmd_rsc_t* rsc = (lrmd_rsc_t*)value;
+ if (rsc->last_op_done != NULL) {
+ if (HA_OK != ha_msg_value_int(rsc->last_op_done->msg
+ , F_LRM_OPSTATUS, &op_status)) {
+ lrmd_debug(LOG_WARNING
+ ,"resource %s is left in UNKNOWN status." \
+ "(last op done is damaged..)"
+ ,rsc->id);
+ return;
+ }
+ op_type = ha_msg_value(rsc->last_op_done->msg, F_LRM_OP);
+ if (op_status != LRM_OP_DONE) {
+ lrmd_debug(LOG_WARNING
+ ,"resource %s is left in UNKNOWN status." \
+ "(last op %s finished without LRM_OP_DONE status.)"
+ ,rsc->id, op_type);
+ return;
+ }
+ if (HA_OK != ha_msg_value_int(rsc->last_op_done->msg
+ , F_LRM_RC, &rc)) {
+ lrmd_debug(LOG_WARNING
+ ,"resource %s is left in UNKNOWN status." \
+ "(last op done is damaged..)"
+ ,rsc->id);
+ return;
+ }
+ if((rc == 0) &&
+ (STRNCMP_CONST(op_type,"start") ==0
+ ||STRNCMP_CONST(op_type,"monitor") ==0
+ ||STRNCMP_CONST(op_type,"status") ==0)) {
+ lrmd_debug(LOG_WARNING
+ ,"resource %s is left in RUNNING status." \
+ "(last op %s finished with rc 0.)"
+ ,rsc->id, op_type);
+ return;
+ }
+ if ((rc !=0 ) &&
+ (STRNCMP_CONST(op_type,"start") ==0
+ ||STRNCMP_CONST(op_type,"stop") ==0)) {
+ lrmd_debug(LOG_WARNING
+ ,"resource %s is left in UNKNOWN status." \
+ "(last op %s finished with rc %d.)"
+ ,rsc->id, op_type, rc);
+ return;
+ }
+ }
+}
+
+static gboolean
+lrm_shutdown(void)
+{
+ lrmd_log(LOG_INFO,"lrmd is shutting down");
+ if (mainloop != NULL && g_main_is_running(mainloop)) {
+ g_hash_table_foreach(resources, warning_on_active_rsc, NULL);
+ g_main_quit(mainloop);
+ }else {
+ exit(LSB_EXIT_OK);
+ }
+ return FALSE;
+}
+static void
+has_pending_op(gpointer key, gpointer value, gpointer user_data)
+{
+ lrmd_rsc_t* rsc = (lrmd_rsc_t*)value;
+ int* result = (int*)user_data;
+ if (rsc->op_list != NULL) {
+ *result = TRUE;
+ }
+}
+static gboolean
+can_shutdown()
+{
+ int has_ops = FALSE;
+ g_hash_table_foreach(resources, has_pending_op, &has_ops);
+
+ return !has_ops;
+}
+gboolean
+sigterm_action(int nsig, gpointer user_data)
+{
+ shutdown_in_progress = TRUE;
+
+ if (can_shutdown()) {
+ lrm_shutdown();
+ } else {
+ lrmd_log(LOG_INFO, "sigterm_action: shutdown postponed, some operations are still running");
+ }
+ return TRUE;
+}
+
+static void
+register_pid(gboolean do_fork,
+ gboolean (*shutdown)(int nsig, gpointer userdata))
+{
+ int j;
+
+ umask(022);
+
+ for (j=0; j < 3; ++j) {
+ close(j);
+ (void)open("/dev/null", j == 0 ? O_RDONLY : O_WRONLY);
+ }
+ CL_IGNORE_SIG(SIGINT);
+ CL_IGNORE_SIG(SIGHUP);
+ CL_DEFAULT_SIG(SIGPIPE);
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM
+ , shutdown, NULL, NULL);
+ cl_signal_set_interrupt(SIGTERM, 1);
+ cl_signal_set_interrupt(SIGCHLD, 1);
+ /* At least they are harmless, I think. ;-) */
+ cl_signal_set_interrupt(SIGINT, 0);
+ cl_signal_set_interrupt(SIGHUP, 0);
+}
+
+static int
+init_using_apphb(void)
+{
+#ifdef ENABLE_APPHB
+ char lrmd_instance[40];
+
+ if (reg_to_apphbd == FALSE) {
+ return -1;
+ }
+
+ snprintf(lrmd_instance, sizeof(lrmd_instance), "%s_%ld"
+ , lrm_system_name, (long)getpid());
+ if (apphb_register(lrm_system_name, lrmd_instance) != 0) {
+ lrmd_log(LOG_ERR, "Failed when trying to register to apphbd.");
+ lrmd_log(LOG_ERR, "Maybe apphbd is not running. Quit.");
+ return -1;
+ }
+ lrmd_log(LOG_INFO, "Registered to apphbd.");
+
+ apphb_setinterval(apphb_interval);
+ apphb_setwarn(apphb_interval*APPHB_WARNTIME_FACTOR);
+
+ Gmain_timeout_add(apphb_interval - APPHB_INTVL_DETLA, emit_apphb, NULL);
+#endif
+ return 0;
+}
+
+static gboolean
+emit_apphb(gpointer data)
+{
+#ifdef ENABLE_APPHB
+ if (reg_to_apphbd == FALSE) {
+ return FALSE;
+ }
+
+ if (apphb_hb() != 0) {
+ lrmd_log(LOG_ERR, "emit_apphb: Failed to emit an apphb.");
+ reg_to_apphbd = FALSE;
+ return FALSE;
+ };
+#endif
+ return TRUE;
+}
+
+static void
+calc_max_children()
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ int nprocs;
+
+ nprocs = sysconf(_SC_NPROCESSORS_ONLN);
+ if( nprocs < 1 ) {
+ lrmd_log(LOG_WARNING, "%s: couldn't get the number of processors"
+ , __FUNCTION__);
+ } else {
+ if( nprocs/2 > max_child_count ) {
+ max_child_count = nprocs/2;
+ }
+ lrmd_log(LOG_INFO, "max-children set to %d "
+ "(%d processors online)", max_child_count, nprocs);
+ return;
+ }
+#else
+ lrmd_log(LOG_WARNING, "%s: cannot get the number of processors "
+ "on this platform", __FUNCTION__);
+#endif
+ lrmd_log(LOG_INFO, "max-children set to %d", max_child_count);
+}
+
+/* main loop of the daemon*/
+int
+init_start ()
+{
+ DIR* dir = NULL;
+ PILPluginUniv * PluginLoadingSystem = NULL;
+ struct dirent* subdir;
+ char* dot = NULL;
+ char* ra_name = NULL;
+ int len;
+ IPC_WaitConnection* conn_cmd = NULL;
+ IPC_WaitConnection* conn_cbk = NULL;
+
+ GHashTable* conn_cmd_attrs;
+ GHashTable* conn_cbk_attrs;
+
+ char path[] = IPC_PATH_ATTR;
+ char cmd_path[] = LRM_CMDPATH;
+ char cbk_path[] = LRM_CALLBACKPATH;
+
+ PILGenericIfMgmtRqst RegisterRqsts[]= {
+ {"RAExec", &RAExecFuncs, NULL, NULL, NULL},
+ { NULL, NULL, NULL, NULL, NULL} };
+
+ if( getenv("LRMD_MAX_CHILDREN") ) {
+ set_lrmd_param("max-children", getenv("LRMD_MAX_CHILDREN"));
+ } else {
+ calc_max_children();
+ }
+
+ qsort(msg_maps, MSG_NR, sizeof(struct msg_map), msg_type_cmp);
+
+ if (cl_lock_pidfile(PID_FILE) < 0) {
+ lrmd_log(LOG_ERR, "already running: [pid %d].", cl_read_pidfile(PID_FILE));
+ lrmd_log(LOG_ERR, "Startup aborted (already running). Shutting down.");
+ exit(100);
+ }
+
+ register_pid(FALSE, sigterm_action);
+
+ /* load RA plugins */
+ PluginLoadingSystem = NewPILPluginUniv (HA_PLUGIN_DIR);
+ PILLoadPlugin(PluginLoadingSystem, "InterfaceMgr", "generic",
+ &RegisterRqsts);
+
+ /*
+ * FIXME!!!
+ * Much of the code through the end of the next loop is
+ * unnecessary - The plugin system will do this for you quite
+ * nicely. And, it does it portably, too...
+ */
+
+ dir = opendir(LRM_PLUGIN_DIR);
+ if (NULL == dir) {
+ lrmd_log(LOG_ERR, "main: can not open RA plugin dir "LRM_PLUGIN_DIR);
+ lrmd_log(LOG_ERR, "Startup aborted (no RA plugin). Shutting down.");
+ exit(100);
+ }
+
+ while ( NULL != (subdir = readdir(dir))) {
+ /* skip . and .. */
+ if ( '.' == subdir->d_name[0]) {
+ continue;
+ }
+ /* skip the other type files */
+ if (NULL == strstr(subdir->d_name, ".so")) {
+ continue;
+ }
+ /* remove the ".so" */
+ dot = strchr(subdir->d_name,'.');
+ if (NULL != dot) {
+ len = (int)(dot - subdir->d_name);
+ ra_name = g_strndup(subdir->d_name,len);
+ }
+ else {
+ ra_name = g_strdup(subdir->d_name);
+ }
+ PILLoadPlugin(PluginLoadingSystem , "RAExec", ra_name, NULL);
+ ra_class_list = g_list_append(ra_class_list,ra_name);
+ }
+ closedir(dir); dir = NULL; /* Don't forget to close 'dir' */
+
+ /*
+ *create the waiting connections
+ *one for register the client,
+ *the other is for create the callback channel
+ */
+
+ /*Create a waiting connection to accept command connect from client*/
+ conn_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(conn_cmd_attrs, path, cmd_path);
+ conn_cmd = ipc_wait_conn_constructor(IPC_ANYTYPE, conn_cmd_attrs);
+ g_hash_table_destroy(conn_cmd_attrs);
+ if (NULL == conn_cmd) {
+ lrmd_log(LOG_ERR,
+ "main: can not create wait connection for command.");
+ lrmd_log(LOG_ERR, "Startup aborted (can't create comm channel). Shutting down.");
+
+ exit(100);
+ }
+
+ /*Create a source to handle new connect rquests for command*/
+ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cmd, NULL, FALSE,
+ on_connect_cmd, conn_cmd, NULL);
+
+ /* auth is static, but used when clients register */
+ auth = ipc_str_to_auth(ADMIN_UIDS, strlen(ADMIN_UIDS), "", 0);
+
+ /*
+ * Create a waiting connection to accept the callback connect from client
+ */
+ conn_cbk_attrs = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(conn_cbk_attrs, path, cbk_path);
+ conn_cbk = ipc_wait_conn_constructor( IPC_ANYTYPE, conn_cbk_attrs);
+ g_hash_table_destroy(conn_cbk_attrs);
+
+ if (NULL == conn_cbk) {
+ lrmd_log(LOG_ERR,
+ "main: can not create wait connection for callback.");
+ lrmd_log(LOG_ERR, "Startup aborted (can't create comm channel). Shutting down.");
+ exit(100);
+ }
+
+ /*Create a source to handle new connect rquests for callback*/
+ G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cbk, NULL, FALSE,
+ on_connect_cbk, conn_cbk, NULL);
+
+ /* our child signal handling involves calls with
+ * unpredictable timing; so we raise the limit to
+ * reduce the number of warnings
+ */
+ set_sigchld_proctrack(G_PRIORITY_HIGH,10*DEFAULT_MAXDISPATCHTIME);
+
+ lrmd_log(LOG_INFO, "enabling coredumps");
+ /* Although lrmd can count on the parent to enable coredump, still
+ * set it here for test, when start manually.
+ */
+ cl_cdtocoredir();
+ cl_enable_coredumps(TRUE);
+
+ /* Allow us to always take a "secure" core dump
+ * We might have STONITH logins and passwords, etc. in our address
+ * space - so we need to make sure it's only readable by root.
+ * Calling this function accomplishes that.
+ */
+ cl_set_all_coredump_signal_handlers();
+ if( drop_privs(0, 0) ) { /* become "nobody" */
+ lrmd_log(LOG_WARNING,"%s: failed to drop privileges: %s"
+ , __FUNCTION__, strerror(errno));
+ }
+
+ /*
+ * Add the signal handler for SIGUSR1, SIGUSR2.
+ * They are used to change the debug level.
+ */
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGUSR1,
+ debug_level_adjust, NULL, NULL);
+ G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGUSR2,
+ debug_level_adjust, NULL, NULL);
+
+ /*
+ * alloc memory for client table and resource table
+ */
+ clients = g_hash_table_new(g_int_hash, g_int_equal);
+ if (clients == NULL) {
+ cl_log(LOG_ERR, "can not new hash table clients");
+ exit(100);
+ }
+ resources = g_hash_table_new_full(g_str_hash
+ , g_str_equal, free, NULL);
+ if (resources == NULL) {
+ cl_log(LOG_ERR, "can not new hash table resources");
+ exit(100);
+ }
+
+ /*Create the mainloop and run it*/
+ mainloop = g_main_new(FALSE);
+ lrmd_debug(LOG_DEBUG, "main: run the loop...");
+ lrmd_log(LOG_INFO, "Started.");
+
+ /* apphb initializing */
+ init_using_apphb();
+ emit_apphb(NULL); /* Avoid warning */
+
+ g_main_run(mainloop);
+
+ emit_apphb(NULL);
+ if (reg_to_apphbd == TRUE) {
+#ifdef ENABLE_APPHB
+ apphb_unregister();
+#endif
+ reg_to_apphbd = FALSE;
+ }
+
+ if( return_to_orig_privs() ) {
+ cl_perror("%s: failed to raise privileges", __FUNCTION__);
+ }
+ conn_cmd->ops->destroy(conn_cmd);
+ conn_cmd = NULL;
+
+ conn_cbk->ops->destroy(conn_cbk);
+ conn_cbk = NULL;
+
+ ipc_destroy_auth(auth);
+ if (cl_unlock_pidfile(PID_FILE) == 0) {
+ lrmd_debug(LOG_DEBUG, "[%s] stopped", lrm_system_name);
+ }
+ return 0;
+}
+
+/*
+ *GLoop Message Handlers
+ */
+gboolean
+on_connect_cmd (IPC_Channel* ch, gpointer user_data)
+{
+ lrmd_client_t* client = NULL;
+
+ /* check paremeters */
+ if (NULL == ch) {
+ lrmd_log(LOG_ERR, "on_connect_cmd: channel is null");
+ return TRUE;
+ }
+ /* create new client */
+ /* the register will be finished in on_msg_register */
+ client = lrmd_client_new();
+ if (client == NULL) {
+ return TRUE;
+ }
+ client->app_name = NULL;
+ client->ch_cmd = ch;
+ client->g_src = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
+ ch, FALSE, on_receive_cmd, (gpointer)client,
+ on_remove_client);
+
+
+ return TRUE;
+}
+
+gboolean
+on_connect_cbk (IPC_Channel* ch, gpointer user_data)
+{
+ /*client connect for create the second channel for call back*/
+ pid_t pid;
+ const char* type = NULL;
+ struct ha_msg* msg = NULL;
+ lrmd_client_t* client = NULL;
+
+ if (NULL == ch) {
+ lrmd_log(LOG_ERR, "on_connect_cbk: channel is null");
+ return TRUE;
+ }
+
+ /* Isn't this kind of a tight timing assumption ??
+ * This operation is non-blocking -- IIRC
+ * Maybe this should be moved to the input dispatch function
+ * for this channel when we make a GSource from it.
+ * FIXME
+ */
+
+ /*get the message, ends up in socket_waitin */
+ msg = msgfromIPC_noauth(ch);
+ if (NULL == msg) {
+ lrmd_log(LOG_ERR, "on_connect_cbk: can not receive msg");
+ return TRUE;
+ }
+
+ /*check if it is a register message*/
+ type = ha_msg_value(msg, F_LRM_TYPE);
+ if (0 != STRNCMP_CONST(type, REGISTER)) {
+ lrmd_log(LOG_ERR, "on_connect_cbk: received a message which is "
+ "not known by lrmd.");
+ ha_msg_del(msg);
+ send_ret_msg(ch, HA_FAIL);
+ return TRUE;
+ }
+
+ /*get the pid of client */
+ if (HA_OK != ha_msg_value_int(msg, F_LRM_PID, &pid)) {
+ lrmd_log(LOG_ERR, "on_connect_cbk: can not get pid from the "
+ "message.");
+ ha_msg_del(msg);
+ send_ret_msg(ch, HA_FAIL);
+ return TRUE;
+ }
+ ha_msg_del(msg);
+
+ /*get the client in the client list*/
+ client = lookup_client(pid);
+ if (NULL == client) {
+ lrmd_log(LOG_ERR, "on_connect_cbk: donnot find the client "
+ "[pid:%d] in internal client list. ", pid);
+ send_ret_msg(ch, HA_FAIL);
+ return TRUE;
+ }
+ if (client->ch_cbk != NULL) {
+ client->ch_cbk->ops->destroy(client->ch_cbk);
+ client->ch_cbk = NULL;
+ }
+ client->g_src_cbk = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT
+ , ch, FALSE,NULL,NULL,NULL);
+
+ /*fill the channel of callback field*/
+ client->ch_cbk = ch;
+ send_ret_msg(ch, HA_OK);
+ return TRUE;
+}
+
+int
+msg_type_cmp(const void *p1, const void *p2)
+{
+
+ return strncmp(
+ ((const struct msg_map *)p1)->msg_type,
+ ((const struct msg_map *)p2)->msg_type,
+ MAX_MSGTYPELEN);
+}
+
+gboolean
+on_receive_cmd (IPC_Channel* ch, gpointer user_data)
+{
+ struct msg_map *msgmap_p, in_type;
+ lrmd_client_t* client = NULL;
+ struct ha_msg* msg = NULL;
+ char *msg_s;
+ int ret = FALSE;
+
+ client = (lrmd_client_t*)user_data;
+
+ if (IPC_DISCONNECT == ch->ch_status) {
+ lrmd_debug(LOG_DEBUG,
+ "on_receive_cmd: the IPC to client [pid:%d] disconnected."
+ , client->pid);
+ return FALSE;
+ }
+
+ if (!ch->ops->is_message_pending(ch)) {
+ lrmd_debug(LOG_DEBUG, "on_receive_cmd: no pending message in IPC "
+ "channel.");
+ return TRUE;
+ }
+
+
+ /*get the message */
+ msg = msgfromIPC(ch, 0);
+ if (NULL == msg) {
+ lrmd_log(LOG_ERR, "on_receive_cmd: can not receive messages.");
+ return TRUE;
+ }
+
+ if (TRUE == shutdown_in_progress ) {
+ send_ret_msg(ch,HA_FAIL);
+ ha_msg_del(msg);
+ lrmd_log(LOG_INFO, "%s: new requests denied," \
+ " we're about to shutdown", __FUNCTION__);
+ return TRUE;
+ }
+
+ /*dispatch the message*/
+ in_type.msg_type = ha_msg_value(msg, F_LRM_TYPE);
+ if( !in_type.msg_type ) {
+ LOG_FAILED_TO_GET_FIELD(F_LRM_TYPE);
+ ha_msg_del(msg);
+ return TRUE;
+ }
+ msg_s = msg2string(msg);
+ if( msg_s ) {
+ lrmd_debug2(LOG_DEBUG,"dumping request: %s",msg_s);
+ free(msg_s);
+ }
+
+ if (!(msgmap_p = bsearch(&in_type, msg_maps,
+ MSG_NR, sizeof(struct msg_map), msg_type_cmp)
+ )) {
+
+ lrmd_log(LOG_ERR, "on_receive_cmd: received an unknown msg");
+ } else {
+ if( !client->app_name && msgmap_p->handler != on_msg_register ) {
+ ha_msg_del(msg);
+ lrmd_log(LOG_ERR, "%s: the client needs to register first", __FUNCTION__);
+ return FALSE;
+ }
+
+ if( client->priv_lvl < msgmap_p->min_priv ) {
+ ha_msg_del(msg);
+ lrmd_log(LOG_ERR, "%s: insufficient privileges for %s (pid %d)"
+ , __FUNCTION__
+ , client->app_name, client->pid);
+ return FALSE;
+ }
+ strncpy(client->lastrequest, in_type.msg_type, sizeof(client->lastrequest));
+ client->lastrequest[sizeof(client->lastrequest)-1]='\0';
+ client->lastreqstart = time(NULL);
+ /*call the handler of the message*/
+ ret = msgmap_p->handler(client, msg);
+ client->lastreqend = time(NULL);
+
+ /*return rc to client if need*/
+ if (send_msg_now(msgmap_p)) {
+ send_ret_msg(ch, ret);
+ client->lastrcsent = time(NULL);
+ }
+ }
+
+ /*delete the msg*/
+ ha_msg_del(msg);
+
+ return ret;
+}
+static void
+remove_repeat_op_from_client(gpointer key, gpointer value, gpointer user_data)
+{
+ lrmd_rsc_t* rsc = (lrmd_rsc_t*)value;
+ pid_t pid = GPOINTER_TO_UINT(user_data); /* pointer cast as int */
+
+ (void)flush_all(&(rsc->repeat_op_list),pid);
+}
+
+/* Remove all direct pointer references to 'client' before destroying it */
+static int
+unregister_client(lrmd_client_t* client)
+{
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+
+ if (NULL == lookup_client(client->pid)) {
+ lrmd_log(LOG_ERR,"%s: can not find client %s [pid %d] when try "
+ "to unregister it."
+ , __FUNCTION__
+ , client->app_name, client->pid);
+ return HA_FAIL;
+ }
+
+ /* Search all resources for repeating ops this client owns */
+ g_hash_table_foreach(resources
+ , remove_repeat_op_from_client, GUINT_TO_POINTER(client->pid));
+
+ /* Remove from clients */
+ g_hash_table_remove(clients, (gpointer)&client->pid);
+
+ lrmd_debug(LOG_DEBUG, "%s: client %s [pid:%d] is unregistered"
+ , __FUNCTION__
+ , client->app_name
+ , client->pid);
+ return HA_OK;
+}
+
+void
+on_remove_client (gpointer user_data)
+{
+ lrmd_client_t* client = (lrmd_client_t*) user_data;
+
+ CHECK_ALLOCATED(client, "client", );
+ if (client->g_src != NULL) {
+ G_main_del_IPC_Channel(client->g_src);
+ }
+ if (client->g_src_cbk != NULL) {
+ G_main_del_IPC_Channel(client->g_src_cbk);
+ }
+ lrmd_client_destroy(client);
+
+}
+
+
+/* This function called when its time to run a repeating operation now */
+/* Move op from repeat queue to running queue */
+gboolean
+on_repeat_op_readytorun(gpointer data)
+{
+ lrmd_op_t* op = NULL;
+ lrmd_rsc_t* rsc = NULL;
+
+ LRMAUDIT();
+ op = (lrmd_op_t*)data;
+ CHECK_ALLOCATED(op, "op", FALSE );
+
+ if (op->exec_pid == 0) {
+ lrmd_log(LOG_ERR, "%s: exec_pid is 0 (internal error)"
+ , __FUNCTION__);
+ return FALSE;
+ }
+
+ lrmd_debug2(LOG_DEBUG
+ , "%s: remove operation %s from the repeat operation list and "
+ "add it to the operation list"
+ , __FUNCTION__, op_info(op));
+
+ if( op->rsc_id ) {
+ rsc = lookup_rsc(op->rsc_id);
+ } else {
+ lrmd_debug(LOG_INFO
+ , "%s: the rsc_id in op %s is NULL"
+ , __FUNCTION__, op_info(op));
+ return FALSE;
+ }
+
+ rsc->repeat_op_list = g_list_remove(rsc->repeat_op_list, op);
+ if (op->repeat_timeout_tag != 0) {
+ op->repeat_timeout_tag = (guint)0;
+ }
+
+ op->exec_pid = -1;
+
+ if (!shutdown_in_progress) {
+ add_op_to_runlist(rsc,op);
+ }
+ perform_op(rsc);
+
+ LRMAUDIT();
+ return FALSE;
+}
+
+/*LRM Message Handlers*/
+int
+on_msg_register(lrmd_client_t* client, struct ha_msg* msg)
+{
+ lrmd_client_t* exist = NULL;
+ const char* app_name = NULL;
+
+ CHECK_ALLOCATED(msg, "register message", HA_FAIL);
+
+ app_name = ha_msg_value(msg, F_LRM_APP);
+ if (NULL == app_name) {
+ lrmd_log(LOG_ERR, "on_msg_register: no app_name in "
+ "the ha message.");
+ return HA_FAIL;
+ }
+ client->app_name = strdup(app_name);
+
+ return_on_no_int_value(msg, F_LRM_PID, &client->pid);
+ return_on_no_int_value(msg, F_LRM_GID, (int *)&client->gid);
+ return_on_no_int_value(msg, F_LRM_UID, (int *)&client->uid);
+
+ exist = lookup_client(client->pid);
+ if (NULL != exist) {
+ g_hash_table_remove(clients, (gpointer)&client->pid);
+ on_remove_client(exist);
+ lrmd_log(LOG_NOTICE,
+ "on_msg_register: the client [pid:%d] already exists in "
+ "internal client list, let remove it at first."
+ , client->pid);
+ }
+
+ /* everybody can connect, but only certain UIDs can perform
+ * administrative actions
+ */
+ if( client->ch_cmd->ops->verify_auth(client->ch_cmd, auth) == IPC_OK )
+ client->priv_lvl = PRIV_ADMIN;
+ else
+ client->priv_lvl = 0;
+
+ g_hash_table_insert(clients, (gpointer)&client->pid, client);
+ lrmd_debug(LOG_DEBUG, "on_msg_register:client %s [%d] registered"
+ , client->app_name
+ , client->pid);
+
+ return HA_OK;
+}
+
+int
+on_msg_get_rsc_classes(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ lrmd_debug2(LOG_DEBUG
+ , "on_msg_get_rsc_classes:client [%d] wants to get rsc classes"
+ , client->pid);
+
+ ret = create_lrm_ret(HA_OK, 4);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ cl_msg_add_list(ret,F_LRM_RCLASS,ra_class_list);
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_rsc_classes: cannot send the ret mesage");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+
+int
+on_msg_get_rsc_types(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+ struct RAExecOps * RAExec = NULL;
+ GList* types = NULL;
+ GList* type;
+ const char* rclass = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ ret = create_lrm_ret(HA_OK,5);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ rclass = ha_msg_value(msg, F_LRM_RCLASS);
+ if (rclass == NULL) {
+ lrmd_log(LOG_ERR, "on_msg_get_rsc_types: cannot get the "
+ "resource class field from the message.");
+ send_ret_msg(client->ch_cmd, HA_FAIL);
+ return HA_FAIL;
+ }
+
+ lrmd_debug2(LOG_DEBUG, "on_msg_get_rsc_types: the client [pid:%d] "
+ "wants to get resource types of resource class %s"
+ , client->pid, rclass);
+
+ RAExec = g_hash_table_lookup(RAExecFuncs,rclass);
+
+ if (NULL == RAExec) {
+ lrmd_log(LOG_NOTICE, "on_msg_get_rsc_types: can not find this "
+ "RA class %s.", rclass);
+ } else {
+ if (0 <= RAExec->get_resource_list(&types) && types != NULL) {
+ cl_msg_add_list(ret, F_LRM_RTYPES, types);
+ while (NULL != (type = g_list_first(types))) {
+ types = g_list_remove_link(types, type);
+ g_free(type->data);
+ g_list_free_1(type);
+ }
+ g_list_free(types);
+ }
+ }
+
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_rsc_types: can not send the ret message.");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+
+int
+on_msg_get_rsc_providers(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+ struct RAExecOps * RAExec = NULL;
+ GList* providers = NULL;
+ GList* provider = NULL;
+ const char* rclass = NULL;
+ const char* rtype = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ ret = create_lrm_ret(HA_OK,5);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ rclass = ha_msg_value(msg, F_LRM_RCLASS);
+ rtype = ha_msg_value(msg, F_LRM_RTYPE);
+ if( !rclass || !rtype ) {
+ lrmd_log(LOG_NOTICE
+ , "%s: could not retrieve resource class or type"
+ , __FUNCTION__);
+ send_ret_msg(client->ch_cmd, HA_FAIL);
+ return HA_FAIL;
+ }
+
+ lrmd_debug2(LOG_DEBUG
+ , "%s: the client [%d] wants to get rsc privider of %s::%s"
+ , __FUNCTION__
+ , client->pid
+ , rclass
+ , rtype);
+
+ RAExec = g_hash_table_lookup(RAExecFuncs, rclass);
+
+ if (NULL == RAExec) {
+ lrmd_log(LOG_NOTICE
+ , "%s: can not find the class %s."
+ , __FUNCTION__
+ , rclass);
+ }
+ else {
+ if (0 <= RAExec->get_provider_list(rtype, &providers)) {
+ if (providers != NULL) {
+ cl_msg_add_list(ret, F_LRM_RPROVIDERS, providers);
+ }
+ while (NULL != (provider = g_list_first(providers))) {
+ providers = g_list_remove_link(providers, provider);
+ g_free(provider->data);
+ g_list_free_1(provider);
+ }
+ g_list_free(providers);
+ }
+ }
+
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_rsc_providers: can not send the ret msg");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+
+int
+on_msg_get_metadata(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+ struct RAExecOps * RAExec = NULL;
+ const char* rtype = NULL;
+ const char* rclass = NULL;
+ const char* provider = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ rtype = ha_msg_value(msg, F_LRM_RTYPE);
+ rclass = ha_msg_value(msg, F_LRM_RCLASS);
+ provider = ha_msg_value(msg, F_LRM_RPROVIDER);
+
+ lrmd_debug2(LOG_DEBUG
+ , "%s: the client [pid:%d] wants to get rsc metadata of %s::%s::%s."
+ , __FUNCTION__
+ , client->pid
+ , lrm_str(rclass)
+ , lrm_str(provider)
+ , lrm_str(rtype));
+
+ ret = create_lrm_ret(HA_OK, 5);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ RAExec = g_hash_table_lookup(RAExecFuncs,rclass);
+ if (NULL == RAExec) {
+ lrmd_log(LOG_NOTICE
+ , "%s: can not find the class %s."
+ , __FUNCTION__
+ , rclass);
+ }
+ else {
+ char* meta = RAExec->get_resource_meta(rtype,provider);
+ if (NULL != meta && strlen(meta) > 0) {
+ if (HA_OK != ha_msg_add(ret,F_LRM_METADATA, meta)) {
+ LOG_FAILED_TO_ADD_FIELD("metadata");
+ }
+ g_free(meta);
+ }
+ else {
+ lrmd_log(LOG_WARNING
+ , "%s: empty metadata for %s::%s::%s."
+ , __FUNCTION__
+ , lrm_str(rclass)
+ , lrm_str(provider)
+ , lrm_str(rtype));
+ ha_msg_mod_int(ret, F_LRM_RET, HA_FAIL);
+ }
+ }
+
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_metadata: can not send the ret msg");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+static void
+add_rid_to_msg(gpointer key, gpointer value, gpointer user_data)
+{
+ char* rid = (char*)key;
+ struct ha_msg* msg = (struct ha_msg*)user_data;
+ if (HA_OK != cl_msg_list_add_string(msg,F_LRM_RID,rid)) {
+ LOG_FAILED_TO_ADD_FIELD("resource id");
+ }
+}
+int
+on_msg_get_all(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ lrmd_debug2(LOG_DEBUG
+ , "on_msg_get_all:client [%d] want to get all rsc information."
+ , client->pid);
+
+ ret = create_lrm_ret(HA_OK, g_hash_table_size(resources) + 1);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ g_hash_table_foreach(resources, add_rid_to_msg, ret);
+
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR, "on_msg_get_all: can not send the ret msg");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+int
+on_msg_get_rsc(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+ lrmd_rsc_t* rsc = NULL;
+ const char* id = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ id = ha_msg_value(msg, F_LRM_RID);
+
+ lrmd_debug2(LOG_DEBUG
+ , "on_msg_get_rsc: the client [pid:%d] wants to get "
+ "the information of the resource [rsc_id: %s]"
+ , client->pid, lrmd_nullcheck(id));
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL == rsc) {
+ lrmd_debug2(LOG_DEBUG
+ , "on_msg_get_rsc: no rsc with id %s."
+ , lrmd_nullcheck(id));
+ ret = create_lrm_ret(HA_FAIL, 1);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+ }
+ else {
+ ret = create_lrm_ret(HA_OK, 5);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ if (HA_OK != ha_msg_add(ret, F_LRM_RID, rsc->id)
+ || HA_OK != ha_msg_add(ret, F_LRM_RTYPE, rsc->type)
+ || HA_OK != ha_msg_add(ret, F_LRM_RCLASS, rsc->class)) {
+ ha_msg_del(ret);
+ lrmd_log(LOG_ERR,
+ "on_msg_get_rsc: failed to add fields to msg.");
+ return HA_FAIL;
+ }
+ if( rsc->provider ) {
+ if (HA_OK != ha_msg_add(ret, F_LRM_RPROVIDER,
+ rsc->provider)) {
+ ha_msg_del(ret);
+ LOG_FAILED_TO_ADD_FIELD("provider");
+ return HA_FAIL;
+ }
+ }
+
+ if ( rsc->params &&
+ HA_OK!=ha_msg_add_str_table(ret,F_LRM_PARAM,rsc->params)) {
+ ha_msg_del(ret);
+ LOG_FAILED_TO_ADD_FIELD("parameter");
+ return HA_FAIL;
+ }
+
+ }
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR, "on_msg_get_rsc: can not send the ret msg");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+
+int
+on_msg_get_last_op(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+ const char* op_type = NULL;
+ lrmd_rsc_t* rsc = NULL;
+ const char* rid = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ rid = ha_msg_value(msg, F_LRM_RID);
+ op_type = ha_msg_value(msg, F_LRM_OP);
+
+ lrmd_debug2(LOG_DEBUG
+ , "on_msg_get_last_op:client %s[%d] want to get the information "
+ "regarding last %s op on %s"
+ , client->app_name, client->pid
+ , lrmd_nullcheck(op_type), lrmd_nullcheck(rid));
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL != rsc && NULL != op_type) {
+ GHashTable* table = g_hash_table_lookup(rsc->last_op_table
+ , client->app_name);
+ if (NULL != table ) {
+ lrmd_op_t* op = g_hash_table_lookup(table, op_type);
+ if (NULL != op) {
+ lrmd_debug(LOG_DEBUG
+ , "%s: will return op %s"
+ , __FUNCTION__
+ , op_type);
+
+ ret = op_to_msg(op);
+ if (NULL == ret) {
+ lrmd_log(LOG_ERR
+ , "%s: can't create a message with op_to_msg."
+ , __FUNCTION__);
+
+ } else
+ if (HA_OK != ha_msg_add_int(ret
+ , F_LRM_OPCNT, 1)) {
+ LOG_FAILED_TO_ADD_FIELD("operation count");
+ }
+ }
+ }
+ }
+
+ if (NULL == ret) {
+ lrmd_log(LOG_ERR
+ , "%s: return ha_msg ret is null, will re-create it again."
+ , __FUNCTION__);
+ ret = create_lrm_ret(HA_OK, 1);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ if (HA_OK != ha_msg_add_int(ret, F_LRM_OPCNT, 0)) {
+ LOG_FAILED_TO_ADD_FIELD("operation count");
+ }
+
+ }
+
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR, "on_msg_get_last_op: can not send the ret msg");
+ }
+ ha_msg_del(ret);
+
+ return HA_OK;
+}
+
+int
+on_msg_del_rsc(lrmd_client_t* client, struct ha_msg* msg)
+{
+ lrmd_rsc_t* rsc = NULL;
+ const char* id = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ id = ha_msg_value(msg, F_LRM_RID);
+ lrmd_debug2(LOG_DEBUG
+ , "%s: client [%d] wants to delete rsc %s"
+ , __FUNCTION__, client->pid, lrmd_nullcheck(id));
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL == rsc) {
+ lrmd_log(LOG_ERR, "%s: no rsc with id %s.",__FUNCTION__,id);
+ return -1;
+ }
+ LRMAUDIT();
+ (void)flush_all(&(rsc->repeat_op_list),0);
+ if( flush_all(&(rsc->op_list),0) ) {
+ set_rsc_removal_pending(rsc);
+ lrmd_log(LOG_INFO, "resource %s busy, removal pending", rsc->id);
+ LRMAUDIT();
+ return HA_RSCBUSY; /* resource is busy, removal delayed */
+ }
+ lrmd_rsc_destroy(rsc);
+ LRMAUDIT();
+ return HA_OK;
+}
+
+static int
+prepare_failmsg(struct ha_msg* msg, int fail_rc, const char *fail_reason)
+{
+ call_id++; /* use the next id */
+ if (HA_OK != ha_msg_mod(msg,F_LRM_OP,ASYNC_OP_NAME)
+ || HA_OK != ha_msg_add(msg,F_LRM_FAIL_REASON,fail_reason)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_ASYNCMON_RC,fail_rc)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_RC,fail_rc)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_OPSTATUS,(int)LRM_OP_DONE)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_CALLID,call_id)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_TIMEOUT,0)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_INTERVAL,0)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_TARGETRC,EVERYTIME)
+ || HA_OK != ha_msg_mod_int(msg,F_LRM_DELAY,0)
+ ) {
+ lrmd_log(LOG_ERR,"%s:%d: cannot add field to a message"
+ , __FUNCTION__, __LINE__);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+async_notify(gpointer key, gpointer val, gpointer data)
+{
+ struct ha_msg* msg = (struct ha_msg*)data;
+ lrmd_client_t* client;
+
+ client = lookup_client_by_name((char *)key);
+ if (!client) {
+ lrmd_log(LOG_INFO,
+ "%s: client %s not found, probably signed out", __FUNCTION__, (char *)key);
+ } else {
+ send_msg(msg, client);
+ }
+}
+
+int
+on_msg_fail_rsc(lrmd_client_t* client, struct ha_msg* msg)
+{
+ lrmd_rsc_t* rsc;
+ const char* id;
+ int fail_rc = -1;
+ const char *fail_reason;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ id = ha_msg_value(msg, F_LRM_RID);
+ lrmd_debug2(LOG_DEBUG
+ , "%s: client [%d] wants to fail rsc %s"
+ , __FUNCTION__, client->pid, lrmd_nullcheck(id));
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (!rsc) {
+ lrmd_log(LOG_ERR, "%s: no resource with id %s."
+ , __FUNCTION__, lrmd_nullcheck(id));
+ return HA_FAIL;
+ }
+ fail_reason = ha_msg_value(msg,F_LRM_FAIL_REASON);
+ if (!fail_reason || *fail_reason == '\0') {
+ fail_reason = DEFAULT_FAIL_REASON;
+ }
+ if (HA_OK != ha_msg_value_int(msg,F_LRM_ASYNCMON_RC,&fail_rc) || fail_rc <= 0) {
+ fail_rc = DEFAULT_FAIL_RC;
+ }
+ if (prepare_failmsg(msg,fail_rc,fail_reason))
+ return HA_FAIL;
+ lrmd_log(LOG_WARNING
+ , "received asynchronous failure for rsc %s (rc: %d, reason: %s)"
+ , lrmd_nullcheck(id), fail_rc, fail_reason);
+ /* notify all clients from last_op table about the failure */
+ if (rsc->last_op_table) {
+ g_hash_table_foreach(rsc->last_op_table,async_notify,msg);
+ } else {
+ lrmd_log(LOG_INFO
+ , "rsc to be failed %s had no operations so far", lrmd_nullcheck(id));
+ send_msg(msg, client);
+ }
+ return HA_OK;
+}
+
+static gboolean
+free_str_hash_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ GHashTable* table = (GHashTable*) value;
+ free(key);
+ g_hash_table_foreach_remove(table, free_str_op_pair, NULL);
+ g_hash_table_destroy(table);
+ return TRUE;
+}
+
+static gboolean
+free_str_op_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ lrmd_op_t* op = (lrmd_op_t*)value;
+
+ if (NULL == op) {
+ lrmd_log(LOG_ERR, "%s(): NULL op in op_pair(%s)" , __FUNCTION__
+ , (const char *)key);
+ }else{
+ lrmd_op_destroy(op);
+ }
+ return TRUE;
+}
+
+int
+on_msg_add_rsc(lrmd_client_t* client, struct ha_msg* msg)
+{
+ GList* node;
+ gboolean ra_type_exist = FALSE;
+ char* class = NULL;
+ lrmd_rsc_t* rsc = NULL;
+ const char* id = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ return_on_no_value(msg, F_LRM_RID,id);
+
+ lrmd_debug(LOG_DEBUG
+ , "on_msg_add_rsc:client [%d] adds resource %s"
+ , client->pid, lrmd_nullcheck(id));
+
+ if (RID_LEN <= strlen(id)) {
+ lrmd_log(LOG_ERR, "on_msg_add_rsc: rsc_id is too long.");
+ return HA_FAIL;
+ }
+
+ if (NULL != lookup_rsc(id)) {
+ lrmd_log(LOG_ERR, "on_msg_add_rsc: same id resource exists.");
+ return HA_FAIL;
+ }
+
+ LRMAUDIT();
+ rsc = lrmd_rsc_new(id, msg);
+ if (rsc == NULL) {
+ return HA_FAIL;
+ }
+
+ ra_type_exist = FALSE;
+ for(node=g_list_first(ra_class_list); NULL!=node; node=g_list_next(node)){
+ class = (char*)node->data;
+ if (0 == strncmp(class, rsc->class, MAX_CLASSNAMELEN)) {
+ ra_type_exist = TRUE;
+ break;
+ }
+ }
+ if (!ra_type_exist) {
+ lrmd_log(LOG_ERR
+ , "on_msg_add_rsc: RA class [%s] does not exist."
+ , rsc->class);
+ lrmd_rsc_destroy(rsc);
+ rsc = NULL;
+ LRMAUDIT();
+ return HA_FAIL;
+ }
+
+ rsc->last_op_done = NULL;
+ rsc->params = ha_msg_value_str_table(msg,F_LRM_PARAM);
+ rsc->last_op_table = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(resources, strdup(rsc->id), rsc);
+
+ LRMAUDIT();
+ return HA_OK;
+}
+
+static int
+cancel_op(GList** listp,int cancel_op_id)
+{
+ GList* node = NULL;
+ lrmd_op_t* op = NULL;
+ int rc = HA_FAIL;
+
+ for( node = g_list_first(*listp)
+ ; node; node = g_list_next(node) ) {
+ op = (lrmd_op_t*)node->data;
+ if( op->call_id == cancel_op_id ) {
+ lrmd_log(LOG_INFO
+ ,"%s: %s cancelled"
+ , __FUNCTION__, op_info(op));
+ rc = flush_op(op);
+ if( rc != HA_RSCBUSY && rc != HA_FAIL ) {
+ notify_client(op); /* send notification now */
+ *listp = g_list_remove(*listp, op);
+ remove_op_history(op);
+ lrmd_op_destroy(op);
+ }
+ return rc;
+ }
+ }
+ return rc;
+}
+
+int
+on_msg_cancel_op(lrmd_client_t* client, struct ha_msg* msg)
+{
+ lrmd_rsc_t* rsc = NULL;
+ int cancel_op_id = 0;
+ int op_cancelled = HA_OK;
+
+ LRMAUDIT();
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL == rsc) {
+ lrmd_log(LOG_ERR,
+ "%s: no resource with such id.", __FUNCTION__);
+ return HA_FAIL;
+ }
+
+ return_on_no_int_value(msg, F_LRM_CALLID, &cancel_op_id);
+
+ lrmd_debug2(LOG_DEBUG
+ , "%s:client [pid:%d] cancel the operation [callid:%d]"
+ , __FUNCTION__
+ , client->pid
+ , cancel_op_id);
+
+ if( cancel_op(&(rsc->repeat_op_list), cancel_op_id) != HA_OK ) {
+ op_cancelled = cancel_op(&(rsc->op_list), cancel_op_id);
+ }
+ if( op_cancelled == HA_FAIL ) {
+ lrmd_log(LOG_INFO, "%s: no operation with id %d",
+ __FUNCTION__, cancel_op_id);
+ } else if( op_cancelled == HA_RSCBUSY ) {
+ lrmd_log(LOG_INFO, "%s: operation %d running, cancel pending",
+ __FUNCTION__, cancel_op_id);
+ } else {
+ lrmd_debug(LOG_DEBUG, "%s: operation %d cancelled",
+ __FUNCTION__, cancel_op_id);
+ }
+ LRMAUDIT();
+ return op_cancelled;
+}
+
+static gboolean
+flush_all(GList** listp, int client_pid)
+{
+ GList* node = NULL;
+ lrmd_op_t* op = NULL;
+ gboolean rsc_busy = FALSE;
+
+ node = g_list_first(*listp);
+ while( node ) {
+ op = (lrmd_op_t*)node->data;
+ if (client_pid && op->client_id != client_pid) {
+ node = g_list_next(node);
+ continue; /* not the client's operation */
+ }
+ if( flush_op(op) == HA_RSCBUSY ) {
+ rsc_busy = TRUE;
+ node = g_list_next(node);
+ } else if (!client_pid || op->client_id == client_pid) {
+ node = *listp = g_list_remove(*listp, op);
+ remove_op_history(op);
+ lrmd_op_destroy(op);
+ } else {
+ node = g_list_next(node);
+ }
+ }
+ return rsc_busy;
+}
+
+int
+on_msg_flush_all(lrmd_client_t* client, struct ha_msg* msg)
+{
+ lrmd_rsc_t* rsc = NULL;
+ const char* id = NULL;
+
+ LRMAUDIT();
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ return_on_no_value(msg, F_LRM_RID,id);
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL == rsc) {
+ lrmd_log(LOG_ERR,
+ "%s: no resource with id %s.", __FUNCTION__,id);
+ LRMAUDIT();
+ return -1;
+ }
+
+ /* when a flush request arrived, flush all pending ops */
+ lrmd_debug2(LOG_DEBUG
+ , "%s:client [%d] flush operations"
+ , __FUNCTION__, client->pid);
+ (void)flush_all(&(rsc->repeat_op_list),0);
+ if( flush_all(&(rsc->op_list),0) ) {
+ set_rsc_flushing_ops(rsc); /* resource busy */
+ lrmd_log(LOG_INFO, "resource %s busy, all flush pending", rsc->id);
+ LRMAUDIT();
+ return HA_RSCBUSY;
+ }
+ LRMAUDIT();
+ return HA_OK;
+}
+
+int
+on_msg_perform_op(lrmd_client_t* client, struct ha_msg* msg)
+{
+ lrmd_rsc_t* rsc = NULL;
+ lrmd_op_t* op;
+ const char* id = NULL;
+ int timeout = 0;
+ int interval = 0;
+ int delay = 0;
+
+ LRMAUDIT();
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ return_on_no_value(msg, F_LRM_RID,id);
+ return_on_no_int_value(msg, F_LRM_INTERVAL, &interval);
+ return_on_no_int_value(msg, F_LRM_TIMEOUT, &timeout);
+ return_on_no_int_value(msg, F_LRM_DELAY, &delay);
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL == rsc) {
+ lrmd_log(LOG_ERR,
+ "%s: no resource with such id.", __FUNCTION__);
+ return -1;
+ }
+ if( rsc_frozen(rsc) ) {
+ lrmd_log(LOG_NOTICE, "%s: resource %s is frozen, "
+ "no ops can run.", __FUNCTION__, rsc->id);
+ return -1;
+ }
+
+ call_id++;
+ if( !(rsc->id) ) {
+ lrmd_debug(LOG_ERR
+ , "%s:%d: the resource id is NULL"
+ , __FUNCTION__, __LINE__);
+ return -1;
+ }
+ if (HA_OK != ha_msg_add_int(msg, F_LRM_CALLID, call_id)) {
+ LOG_FAILED_TO_ADD_FIELD("callid");
+ return -1;
+ }
+ if (HA_OK !=ha_msg_mod(msg, F_LRM_APP, client->app_name)) {
+ LOG_FAILED_TO_ADD_FIELD("app_name");
+ return -1;
+ }
+
+ op = lrmd_op_new();
+ if (op == NULL) {
+ return -1;
+ }
+ op->call_id = call_id;
+ op->client_id = client->pid;
+ op->rsc_id = strdup(rsc->id);
+ op->interval = interval;
+ op->delay = delay;
+ op->weight = no_child_count(rsc) ? 0 : 1;
+
+ op->msg = ha_msg_copy(msg);
+
+ if( ha_msg_value_int(msg,F_LRM_COPYPARAMS,&op->copyparams) == HA_OK
+ && op->copyparams ) {
+ lrmd_debug(LOG_DEBUG
+ , "%s:%d: copying parameters for rsc %s"
+ , __FUNCTION__, __LINE__,rsc->id);
+ if (rsc->params) {
+ free_str_table(rsc->params);
+ }
+ rsc->params = ha_msg_value_str_table(msg, F_LRM_PARAM);
+ }
+
+ lrmd_debug2(LOG_DEBUG
+ , "%s: client [%d] want to add an operation %s on resource %s."
+ , __FUNCTION__
+ , client->pid
+ , op_info(op)
+ , NULL!=op->rsc_id ? op->rsc_id : "#EMPTY#");
+
+ if ( 0 < op->delay ) {
+ op->repeat_timeout_tag = Gmain_timeout_add(op->delay
+ ,on_repeat_op_readytorun, op);
+ rsc->repeat_op_list =
+ g_list_append (rsc->repeat_op_list, op);
+ lrmd_debug(LOG_DEBUG
+ , "%s: an operation %s is added to the repeat "
+ "operation list for delay execution"
+ , __FUNCTION__
+ , op_info(op));
+ } else {
+ lrmd_debug(LOG_DEBUG
+ , "%s: add an operation %s to the operation list."
+ , __FUNCTION__
+ , op_info(op));
+ add_op_to_runlist(rsc,op);
+ }
+
+ perform_op(rsc);
+
+ LRMAUDIT();
+ return call_id;
+}
+
+static void
+send_last_op(gpointer key, gpointer value, gpointer user_data)
+{
+ IPC_Channel* ch = NULL;
+ lrmd_op_t* op = NULL;
+ struct ha_msg* msg = NULL;
+
+ ch = (IPC_Channel*)user_data;
+ op = (lrmd_op_t*)value;
+ msg = op_to_msg(op);
+ if (msg == NULL) {
+ lrmd_log(LOG_ERR, "send_last_op: failed to convert an operation "
+ "information to a ha_msg.");
+ return;
+ }
+ if (HA_OK != msg2ipcchan(msg, ch)) {
+ lrmd_log(LOG_ERR, "send_last_op: can not send a message.");
+ }
+ ha_msg_del(msg);
+}
+
+int
+on_msg_get_state(lrmd_client_t* client, struct ha_msg* msg)
+{
+ int op_count = 0;
+ lrmd_rsc_t* rsc = NULL;
+ GList* node;
+ struct ha_msg* ret = NULL;
+ lrmd_op_t* op = NULL;
+ struct ha_msg* op_msg = NULL;
+ const char* id = NULL;
+ GHashTable* last_ops = NULL;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ id = ha_msg_value(msg,F_LRM_RID);
+ lrmd_debug2(LOG_DEBUG
+ , "%s: client [%d] want to get the state of resource %s"
+ , __FUNCTION__, client->pid, lrmd_nullcheck(id));
+
+ rsc = lookup_rsc_by_msg(msg);
+ if (NULL == rsc) {
+ lrmd_log(LOG_ERR, "on_msg_get_state: no resource with id %s."
+ , lrmd_nullcheck(id));
+ send_ret_msg(client->ch_cmd, HA_FAIL);
+ return HA_FAIL;
+ }
+
+ ret = ha_msg_new(5);
+ if (NULL == ret) {
+ lrmd_log(LOG_ERR, "on_msg_get_state: can't create a ha_msg.");
+ return HA_FAIL;
+ }
+ /* add the F_LRM_STATE field */
+ if (HA_OK != ha_msg_add_int(ret, F_LRM_STATE
+ , rsc->op_list ? LRM_RSC_BUSY : LRM_RSC_IDLE)) {
+ LOG_FAILED_TO_ADD_FIELD("state");
+ ha_msg_del(ret);
+ return HA_FAIL;
+ }
+ lrmd_debug(LOG_DEBUG
+ , "on_msg_get_state:state of rsc %s is %s"
+ , lrmd_nullcheck(id)
+ , rsc->op_list ? "LRM_RSC_BUSY" : "LRM_RSC_IDLE" );
+ /* calculate the count of ops being returned */
+ last_ops = g_hash_table_lookup(rsc->last_op_table, client->app_name);
+ if (last_ops == NULL) {
+ op_count = g_list_length(rsc->op_list)
+ + g_list_length(rsc->repeat_op_list);
+ }
+ else {
+ op_count = g_hash_table_size(last_ops)
+ + g_list_length(rsc->op_list)
+ + g_list_length(rsc->repeat_op_list);
+ }
+ /* add the count of ops being returned */
+ if (HA_OK != ha_msg_add_int(ret, F_LRM_OPCNT, op_count)) {
+ LOG_FAILED_TO_ADD_FIELD("operation count");
+ ha_msg_del(ret);
+ return HA_FAIL;
+ }
+ /* send the first message to client */
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_state: can not send the ret message.");
+ ha_msg_del(ret);
+ return HA_FAIL;
+ }
+ ha_msg_del(ret);
+
+ /* send the ops in last ops table */
+ if(last_ops != NULL) {
+ g_hash_table_foreach(last_ops, send_last_op, client->ch_cmd);
+ }
+
+ /* send the ops in op list */
+ for(node = g_list_first(rsc->op_list)
+ ; NULL != node; node = g_list_next(node)){
+ op = (lrmd_op_t*)node->data;
+ op_msg = op_to_msg(op);
+ if (NULL == op_msg) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_state: failed to make a message "
+ "from a operation: %s", op_info(op));
+ continue;
+ }
+ if (HA_OK != msg2ipcchan(op_msg, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_state: failed to send a message.");
+ }
+ ha_msg_del(op_msg);
+ }
+
+ /* send the ops in repeat op list */
+ for(node = g_list_first(rsc->repeat_op_list)
+ ; NULL != node; node = g_list_next(node)){
+ op = (lrmd_op_t*)node->data;
+ op_msg = op_to_msg(op);
+ if (NULL == op_msg) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_state: failed to make a message "
+ "from a operation: %s", op_info(op));
+ continue;
+ }
+ if (HA_OK != msg2ipcchan(op_msg, client->ch_cmd)) {
+ lrmd_log(LOG_ERR,
+ "on_msg_get_state: failed to send a message.");
+ }
+ ha_msg_del(op_msg);
+ }
+ return HA_OK;
+}
+
+#define safe_len(s) (s ? strlen(s) : 0)
+
+static char *
+lrm_concat(const char *prefix, const char *suffix, char join)
+{
+ int len = 2;
+ char *new_str = NULL;
+ len += safe_len(prefix);
+ len += safe_len(suffix);
+
+ new_str = malloc(sizeof(char)*len);
+ if (NULL == new_str) {
+ lrmd_log(LOG_ERR,"%s:%d: malloc failed"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+
+ memset(new_str, 0, len);
+ sprintf(new_str, "%s%c%s", prefix?prefix:"", join, suffix?suffix:"");
+ new_str[len-1] = 0;
+ return new_str;
+}
+
+/* /////////////////////op functions////////////////////// */
+
+#define mk_op_id(op,id) do { \
+ const char *op_type = ha_msg_value(op->msg, F_LRM_OP); \
+ const char *op_interval = ha_msg_value(op->msg, F_LRM_INTERVAL); \
+ id = lrm_concat(op_type, op_interval, '_'); \
+} while(0)
+
+/* find the last operation for the client
+ * replace it with the new one (if requested)
+ */
+static void
+replace_last_op(lrmd_client_t* client, lrmd_rsc_t* rsc, lrmd_op_t* op)
+{
+ char *op_hash_key;
+ GHashTable *client_last_op;
+ lrmd_op_t *old_op, *new_op;
+
+ if (!client || !rsc || !op)
+ return;
+ client_last_op = g_hash_table_lookup(rsc->last_op_table, client->app_name);
+ if (!client_last_op) {
+ lrmd_debug2(LOG_DEBUG
+ , "%s: new last op table for client %s"
+ , __FUNCTION__, client->app_name);
+ client_last_op = g_hash_table_new_full( g_str_hash
+ , g_str_equal, free, NULL);
+ g_hash_table_insert(rsc->last_op_table
+ , (gpointer)strdup(client->app_name)
+ , (gpointer)client_last_op);
+ }
+ mk_op_id(op,op_hash_key);
+ old_op = (lrmd_op_t*)g_hash_table_lookup(client_last_op, op_hash_key);
+
+ /* make a copy of op and insert it into client_last_op */
+ if (!(new_op = lrmd_op_copy(op))) {
+ lrmd_log(LOG_ERR, "%s:%d out of memory"
+ , __FUNCTION__, __LINE__);
+ }
+ if (old_op) {
+ lrmd_debug2(LOG_DEBUG
+ , "%s: replace last op %s for client %s"
+ , __FUNCTION__, op_hash_key, client->app_name);
+ g_hash_table_replace(client_last_op,op_hash_key,(gpointer)new_op);
+ lrmd_op_destroy(old_op);
+ } else {
+ lrmd_debug2(LOG_DEBUG
+ , "%s: add last op %s for client %s"
+ , __FUNCTION__, op_hash_key, client->app_name);
+ g_hash_table_insert(client_last_op,op_hash_key,(gpointer)new_op);
+ }
+}
+
+static int
+record_op_completion(lrmd_rsc_t* rsc, lrmd_op_t* op)
+{
+ lrmd_client_t* client;
+
+ LRMAUDIT();
+ /*save the op in the last op finished*/
+ if (rsc->last_op_done != NULL) {
+ lrmd_op_destroy(rsc->last_op_done);
+ }
+ if (!(rsc->last_op_done = lrmd_op_copy(op))) {
+ lrmd_log(LOG_ERR, "%s:%d out of memory"
+ , __FUNCTION__, __LINE__);
+ return 1;
+ }
+ rsc->last_op_done->repeat_timeout_tag = (guint)0;
+
+ client = lookup_client(op->client_id);
+ if (!client) {
+ lrmd_log(LOG_INFO, "%s: cannot record %s: the client is gone"
+ , __FUNCTION__, small_op_info(op));
+ LRMAUDIT();
+ return 1;
+ }
+ /* insert (or replace) the new op in last_op_table for the client */
+ replace_last_op(client,rsc,op);
+ LRMAUDIT();
+ return 0;
+}
+
+static void
+to_repeatlist(lrmd_rsc_t* rsc, lrmd_op_t* op)
+{
+ lrmd_op_t *repeat_op;
+
+ if (!(repeat_op = lrmd_op_copy(op))) {
+ lrmd_log(LOG_ERR, "%s:%d out of memory"
+ , __FUNCTION__, __LINE__);
+ }
+ reset_timestamps(repeat_op);
+ repeat_op->is_copy = FALSE;
+ repeat_op->repeat_timeout_tag =
+ Gmain_timeout_add(op->interval,
+ on_repeat_op_readytorun, repeat_op);
+ rsc->repeat_op_list =
+ g_list_append (rsc->repeat_op_list, repeat_op);
+ lrmd_debug2(LOG_DEBUG
+ , "%s: repeat %s is added to repeat op list to wait"
+ , __FUNCTION__, op_info(op));
+}
+
+static void
+remove_op_history(lrmd_op_t* op)
+{
+ lrmd_client_t* client = lookup_client(op->client_id);
+ lrmd_rsc_t* rsc = NULL;
+ char *op_id, *last_op_id;
+ lrmd_op_t* old_op = NULL;
+ GHashTable* client_last_op = NULL;
+
+ LRMAUDIT();
+ if( !(rsc = lookup_rsc(op->rsc_id)) ) {
+ return;
+ }
+ lrmd_debug2(LOG_DEBUG, "%s: remove history of the op %s"
+ ,__FUNCTION__, op_info(op));
+ mk_op_id(op,op_id);
+ if (rsc->last_op_done != NULL ) {
+ mk_op_id(rsc->last_op_done,last_op_id);
+ if( !strcmp(op_id,last_op_id) ) {
+ lrmd_debug2(LOG_DEBUG, "%s: remove history of the last op done %s"
+ ,__FUNCTION__, op_info(rsc->last_op_done));
+ lrmd_op_destroy(rsc->last_op_done);
+ rsc->last_op_done = NULL;
+ }
+ free(last_op_id);
+ }
+ if( client &&
+ (client_last_op = g_hash_table_lookup(rsc->last_op_table
+ , client->app_name)) ) {
+ lrmd_debug2(LOG_DEBUG, "%s: found client %s in the last op table"
+ ,__FUNCTION__, client->app_name);
+ old_op = g_hash_table_lookup(client_last_op, op_id);
+ if (old_op) {
+ g_hash_table_remove(client_last_op, op_id);
+ lrmd_debug2(LOG_DEBUG, "%s: remove history of the client's last %s"
+ ,__FUNCTION__, op_info(old_op));
+ lrmd_op_destroy(old_op);
+ }
+ }
+ free(op_id);
+ LRMAUDIT();
+}
+
+static void
+add_op_to_runlist(lrmd_rsc_t* rsc, lrmd_op_t* op)
+{
+ op->t_addtolist = time_longclock();
+ rsc->op_list = g_list_append(rsc->op_list, op);
+ if (g_list_length(rsc->op_list) >= 4) {
+ lrmd_log(LOG_WARNING
+ , "operations list for %s is suspiciously"
+ " long [%d]"
+ , rsc->id
+ , g_list_length(rsc->op_list));
+ lrmd_rsc_dump(rsc->id, "rsc->op_list: too many ops");
+ }
+}
+
+/* 1. this function sends a message to the client:
+ * a) on operation instance exit using the callback channel
+ * b) in case a client requested that operation to be cancelled,
+ * using the command channel
+ * c) in case a client requested a resource removal or flushing
+ * all ops and this is the last operation that finished, again
+ * using the command channel
+ * 2. if the op was not cancelled:
+ * a) it is copied to the last_op_done field of rsc
+ * b) if it's a repeating op, it is put in the repeat_op_list
+ * c) the outcome is recorded for future reference
+ * 3. op is destroyed and removed from the op_list
+ */
+int
+on_op_done(lrmd_rsc_t* rsc, lrmd_op_t* op)
+{
+ int rc = HA_OK;
+ int target_rc, last_rc, op_rc;
+ int rc_changed;
+ op_status_t op_status;
+
+ LRMAUDIT();
+ CHECK_ALLOCATED(op, "op", HA_FAIL );
+ if (op->exec_pid == 0) {
+ lrmd_log(LOG_ERR, "%s: op->exec_pid == 0",__FUNCTION__);
+ return HA_FAIL;
+ }
+ op->t_done = time_longclock();
+
+ if (debug_level >= 2) {
+ lrmd_debug(LOG_DEBUG, "%s: %s",__FUNCTION__, op_info(op));
+ lrmd_op_dump(op, __FUNCTION__);
+ }
+
+ return_on_no_int_value(op->msg,F_LRM_TARGETRC,&target_rc);
+ return_on_no_int_value(op->msg,F_LRM_OPSTATUS,(int *)&op_status);
+
+ last_rc = op_rc = -1; /* set all rc to -1 */
+ ha_msg_value_int(op->msg,F_LRM_RC,&op_rc);
+ ha_msg_value_int(op->msg,F_LRM_LASTRC,&last_rc);
+ rc_changed = (
+ op_status == LRM_OP_DONE
+ && op_rc != -1
+ && ((last_rc == -1) || (last_rc != op_rc))
+ );
+ if (rc_changed) {
+ if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_LASTRC, op_rc)) {
+ lrmd_log(LOG_ERR,"%s: cannot save status to msg",__FUNCTION__);
+ return HA_FAIL;
+ }
+ op->t_rcchange = op->t_perform;
+ }
+ if (store_timestamps(op))
+ return HA_FAIL;
+
+ /* remove the op from op_list */
+ rsc->op_list = g_list_remove(rsc->op_list,op);
+ lrmd_debug2(LOG_DEBUG
+ , "%s:%s is removed from op list"
+ , __FUNCTION__, op_info(op));
+
+ if (!op->is_cancelled) {
+ if( !record_op_completion(rsc,op) ) { /*record the outcome of the op */
+ if (op->interval) /* copy op to the repeat list */
+ to_repeatlist(rsc,op);
+ }
+ } else {
+ if (HA_OK != ha_msg_mod_int(op->msg,F_LRM_OPSTATUS,(int)LRM_OP_CANCELLED)) {
+ LOG_FAILED_TO_ADD_FIELD(F_LRM_OPSTATUS);
+ return HA_FAIL;
+ }
+ op_status = LRM_OP_CANCELLED;
+ remove_op_history(op);
+ }
+
+ if (rsc_removal_pending(rsc)) {
+ if (HA_OK != ha_msg_add_int(op->msg,F_LRM_RSCDELETED,1)) {
+ LOG_FAILED_TO_ADD_FIELD(F_LRM_RSCDELETED);
+ }
+ }
+ if (op_status != LRM_OP_DONE
+ || (op_rc == -1)
+ || (op_rc == target_rc)
+ || (target_rc == EVERYTIME)
+ || ((target_rc == CHANGED) && rc_changed)
+ || rsc_removal_pending(rsc)
+ ) {
+ notify_client(op);
+ }
+ lrmd_op_destroy(op);
+ if( !rsc->op_list ) {
+ if( rsc_removal_pending(rsc) ) {
+ lrmd_log(LOG_INFO, "late removal of resource %s", rsc->id);
+ lrmd_rsc_destroy(rsc);
+ rc = -1; /* let the caller know that the rsc is gone */
+ } else {
+ rsc_reset_state(rsc);
+ }
+ }
+ LRMAUDIT();
+ if (shutdown_in_progress && can_shutdown()) {
+ lrm_shutdown();
+ }
+ return rc;
+}
+
+/*
+ * an operation is flushed only in case there is
+ * no process running initiated by this operation
+ * NB: the caller has to destroy the operation itself
+ */
+int
+flush_op(lrmd_op_t* op)
+{
+ CHECK_ALLOCATED(op, "op", HA_FAIL );
+ if (op->exec_pid == 0) {
+ lrmd_debug(LOG_ERR, "%s: op->exec_pid == 0",__FUNCTION__);
+ return HA_FAIL;
+ }
+
+ if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_RC, HA_FAIL)) {
+ LOG_FAILED_TO_ADD_FIELD("F_LRM_RC");
+ return HA_FAIL;
+ }
+
+ if( op->exec_pid == -1 ) {
+ if (HA_OK != ha_msg_mod_int(op->msg,F_LRM_OPSTATUS,(int)LRM_OP_CANCELLED)){
+ LOG_FAILED_TO_ADD_FIELD("opstatus");
+ return HA_FAIL;
+ }
+ return HA_OK;
+ } else {
+ op->is_cancelled = TRUE; /* mark the op as cancelled */
+ lrmd_log(LOG_INFO, "%s: process for %s still "
+ "running, flush delayed"
+ ,__FUNCTION__,small_op_info(op));
+ return HA_RSCBUSY;
+ }
+}
+
+/* Resume the execution of ops of the resource */
+static gboolean
+rsc_execution_freeze_timeout(gpointer data)
+{
+ lrmd_rsc_t* rsc = (lrmd_rsc_t*)data;
+
+ if (rsc == NULL) {
+ return FALSE;
+ }
+
+ if (rsc->delay_timeout > 0) {
+ rsc->delay_timeout = (guint)0;
+ }
+
+ perform_op(rsc);
+
+ return FALSE;
+}
+
+/* this function gets the first op in the rsc op list and execute it*/
+int
+perform_op(lrmd_rsc_t* rsc)
+{
+ GList* node = NULL;
+ lrmd_op_t* op = NULL;
+
+ LRMAUDIT();
+ CHECK_ALLOCATED(rsc, "resource", HA_FAIL);
+ if (shutdown_in_progress && can_shutdown()) {
+ lrm_shutdown();
+ }
+
+ if (rsc_frozen(rsc)) {
+ lrmd_log(LOG_INFO,"%s: resource %s is frozen, "
+ "no ops allowed to run"
+ , __FUNCTION__, rsc->id);
+ return HA_OK;
+ }
+
+ if (NULL == rsc->op_list) {
+ lrmd_debug2(LOG_DEBUG,"%s: no op to perform?", __FUNCTION__);
+ return HA_OK;
+ }
+
+ node = g_list_first(rsc->op_list);
+ while (NULL != node) {
+ op = node->data;
+ if (-1 != op->exec_pid) {
+ if (!g_list_next(node)) {
+ /* this is the only operation, no need to do
+ * anything further */
+ break;
+ }
+ lrmd_log(LOG_INFO, "%s:%d: %s for rsc is already running."
+ , __FUNCTION__, __LINE__, op_info(op));
+ if( rsc->delay_timeout > 0 ) {
+ lrmd_log(LOG_INFO
+ , "%s:%d: operations on resource %s already delayed"
+ , __FUNCTION__, __LINE__, lrm_str(rsc->id));
+ } else {
+ lrmd_log(LOG_INFO
+ , "%s:%d: postponing "
+ "all ops on resource %s by %d ms"
+ , __FUNCTION__, __LINE__
+ , lrm_str(rsc->id), retry_interval);
+ rsc->delay_timeout = Gmain_timeout_add(retry_interval
+ , rsc_execution_freeze_timeout, rsc);
+ }
+ break;
+ }
+ if (op->weight && child_count >= max_child_count) {
+ if ((int)rsc->delay_timeout > 0) {
+ lrmd_log(LOG_INFO
+ , "%s:%d: max_child_count (%d) reached and operations on resource %s already delayed"
+ , __FUNCTION__, __LINE__, max_child_count, lrm_str(rsc->id));
+ } else {
+ lrmd_debug(LOG_NOTICE
+ , "max_child_count (%d) reached, postponing "
+ "execution of %s by %d ms"
+ , max_child_count, op_info(op), retry_interval);
+ rsc->delay_timeout = Gmain_timeout_add(retry_interval
+ , rsc_execution_freeze_timeout, rsc);
+ }
+ break;
+ }
+
+ if (HA_OK != perform_ra_op(op)) {
+ lrmd_log(LOG_ERR
+ , "unable to perform_ra_op on %s"
+ , op_info(op));
+ if (HA_OK != ha_msg_add_int(op->msg, F_LRM_OPSTATUS,
+ LRM_OP_ERROR)) {
+ LOG_FAILED_TO_ADD_FIELD("opstatus");
+ }
+ on_op_done(rsc,op);
+ node = g_list_first(rsc->op_list);
+ }
+ else {
+ break;
+ }
+ }
+
+ LRMAUDIT();
+ return HA_OK;
+}
+
+static int
+store_timestamps(lrmd_op_t* op)
+{
+ struct ha_msg* msg = op->msg;
+ longclock_t now = time_longclock(), /* tm2unix() needs this */
+ exec_time = zero_longclock,
+ queue_time = zero_longclock;
+
+ if (op->t_perform) {
+ queue_time =
+ longclockto_ms(sub_longclock(op->t_perform,op->t_addtolist));
+ if (op->t_done) {
+ exec_time =
+ longclockto_ms(sub_longclock(op->t_done,op->t_perform));
+ }
+ }
+ if ((HA_OK!=ha_msg_mod_ul(msg,F_LRM_T_RUN,tm2unix(op->t_perform)))
+ || (HA_OK!=ha_msg_mod_ul(msg,F_LRM_T_RCCHANGE,tm2unix(op->t_rcchange)))
+ || (HA_OK!=ha_msg_mod_ul(msg,F_LRM_EXEC_TIME,exec_time))
+ || (HA_OK!=ha_msg_mod_ul(msg,F_LRM_QUEUE_TIME,queue_time))
+ ) {
+ lrmd_log(LOG_ERR,"%s: can not save timestamps to msg",__FUNCTION__);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+reset_timestamps(lrmd_op_t* op)
+{
+ op->t_perform = zero_longclock;
+ op->t_done = zero_longclock;
+ cl_msg_remove(op->msg, F_LRM_T_RUN);
+ cl_msg_remove(op->msg, F_LRM_T_RCCHANGE);
+ cl_msg_remove(op->msg, F_LRM_EXEC_TIME);
+ cl_msg_remove(op->msg, F_LRM_QUEUE_TIME);
+}
+
+struct ha_msg*
+op_to_msg(lrmd_op_t* op)
+{
+ struct ha_msg* msg = NULL;
+
+ CHECK_ALLOCATED(op, "op", NULL);
+ if (op->exec_pid == 0) {
+ lrmd_log(LOG_ERR, "%s: op->exec_pid is 0",__FUNCTION__);
+ return NULL;
+ }
+ msg = ha_msg_copy(op->msg);
+ if (NULL == msg) {
+ lrmd_log(LOG_ERR,"%s: can not copy the msg",__FUNCTION__);
+ return NULL;
+ }
+ if ((HA_OK!=ha_msg_mod_int(msg,F_LRM_CALLID,op->call_id))) {
+ lrmd_log(LOG_ERR,"%s: can not save F_LRM_CALLID to msg",__FUNCTION__);
+ ha_msg_del(msg);
+ msg = NULL;
+ }
+ return msg;
+}
+
+/* //////////////////////////////RA wrap funcs/////////////////////////////////// */
+int
+perform_ra_op(lrmd_op_t* op)
+{
+ int stdout_fd[2];
+ int stderr_fd[2];
+ pid_t pid;
+ int timeout;
+ struct RAExecOps * RAExec = NULL;
+ const char* op_type = NULL;
+ GHashTable* params = NULL;
+ GHashTable* op_params = NULL;
+ lrmd_rsc_t* rsc = NULL;
+ ra_pipe_op_t * rapop;
+
+ LRMAUDIT();
+ CHECK_ALLOCATED(op, "op", HA_FAIL);
+ rsc = (lrmd_rsc_t*)lookup_rsc(op->rsc_id);
+ CHECK_ALLOCATED(rsc, "rsc", HA_FAIL);
+
+ if ( pipe(stdout_fd) < 0 ) {
+ cl_perror("%s::%d: pipe", __FUNCTION__, __LINE__);
+ }
+
+ if ( pipe(stderr_fd) < 0 ) {
+ cl_perror("%s::%d: pipe", __FUNCTION__, __LINE__);
+ }
+
+ if (op->exec_pid == 0) {
+ lrmd_log(LOG_ERR, "%s::%d: op->exec_pid == 0.", __FUNCTION__, __LINE__);
+ return HA_FAIL;
+ }
+
+ op_type = ha_msg_value(op->msg, F_LRM_OP);
+ op->t_perform = time_longclock();
+ check_queue_duration(op);
+
+ if(HA_OK != ha_msg_value_int(op->msg, F_LRM_TIMEOUT, &timeout)){
+ timeout = 0;
+ lrmd_log(LOG_ERR,"%s::%d: failed to get timeout for %s"
+ , __FUNCTION__, __LINE__, small_op_info(op));
+ }
+
+ if( return_to_orig_privs() ) {
+ cl_perror("%s::%d: failed to raise privileges"
+ , __FUNCTION__, __LINE__);
+ }
+ switch(pid=fork()) {
+ case -1:
+ cl_perror("%s::%d: fork", __FUNCTION__, __LINE__);
+ close(stdout_fd[0]);
+ close(stdout_fd[1]);
+ close(stderr_fd[0]);
+ close(stderr_fd[1]);
+ if( return_to_dropped_privs() ) {
+ cl_perror("%s::%d: failed to drop privileges"
+ , __FUNCTION__, __LINE__);
+ }
+ return HA_FAIL;
+
+ default: /* Parent */
+ child_count += op->weight;
+ NewTrackedProc(pid, 1
+ , debug_level ?
+ ((op->interval && !is_logmsg_due(op)) ? PT_LOGNORMAL : PT_LOGVERBOSE) : PT_LOGNONE
+ , op, &ManagedChildTrackOps);
+
+ if (!op->interval || is_logmsg_due(op)) { /* log non-repeating ops */
+ lrmd_log(LOG_INFO,"rsc:%s %s[%d] (pid %d)",
+ rsc->id,probe_str(op,op_type),op->call_id,pid);
+ } else {
+ lrmd_debug(LOG_DEBUG,"rsc:%s %s[%d] (pid %d)",
+ rsc->id,op_type,op->call_id,pid);
+ }
+ close(stdout_fd[1]);
+ close(stderr_fd[1]);
+ rapop = ra_pipe_op_new(stdout_fd[0], stderr_fd[0], op);
+ op->rapop = rapop;
+ op->exec_pid = pid;
+ if (0 < timeout ) {
+
+ /* Wait 'timeout' ms then send SIGTERM */
+ /* allow for extra 15 seconds for stonith,
+ * because stonithd handles its children with the
+ * same timeout; in this case the lrmd child
+ * should never timeout, but return the timeout
+ * reported by stonithd
+ */
+ op->killseq[0].mstimeout = timeout
+ + (!strcmp(rsc->class,"stonith") ? 15000 : 0);
+ op->killseq[0].signalno = SIGTERM;
+
+ /* Wait 5 seconds then send SIGKILL */
+ op->killseq[1].mstimeout = 5000;
+ op->killseq[1].signalno = SIGKILL;
+
+ /* Wait 5 more seconds then moan and complain */
+ op->killseq[2].mstimeout = 5000;
+ op->killseq[2].signalno = 0;
+
+ SetTrackedProcTimeouts(pid, op->killseq);
+ }
+ if( return_to_dropped_privs() ) {
+ lrmd_log(LOG_WARNING,"%s::%d: failed to drop privileges: %s"
+ , __FUNCTION__, __LINE__, strerror(errno));
+ }
+
+ if ( rapop == NULL) {
+ return HA_FAIL;
+ }
+ LRMAUDIT();
+ return HA_OK;
+
+ case 0: /* Child */
+#ifdef DEFAULT_REALTIME_POLICY
+ if (sched_getscheduler(0) != SCHED_OTHER) {
+ struct sched_param sp;
+ lrmd_debug(LOG_DEBUG,
+ "perform_ra_op: resetting scheduler class to SCHED_OTHER");
+ sp.sched_priority = 0;
+ if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1)
+ cl_perror("%s::%d: sched_setscheduler",
+ __FUNCTION__, __LINE__);
+ }
+#endif
+ /* Man: The call setpgrp() is equivalent to setpgid(0,0)
+ * _and_ compiles on BSD variants too
+ * need to investigate if it works the same too.
+ */
+ setpgid(0,0);
+ close(stdout_fd[0]);
+ close(stderr_fd[0]);
+ if (STDOUT_FILENO != stdout_fd[1]) {
+ if (dup2(stdout_fd[1], STDOUT_FILENO)!=STDOUT_FILENO) {
+ cl_perror("%s::%d: dup2"
+ , __FUNCTION__, __LINE__);
+ }
+ close(stdout_fd[1]);
+ }
+ if (STDERR_FILENO != stderr_fd[1]) {
+ if (dup2(stderr_fd[1], STDERR_FILENO)!=STDERR_FILENO) {
+ cl_perror("%s::%d: dup2", __FUNCTION__, __LINE__);
+ }
+ close(stderr_fd[1]);
+ }
+ RAExec = g_hash_table_lookup(RAExecFuncs,rsc->class);
+ if (NULL == RAExec) {
+ close(stdout_fd[1]);
+ close(stderr_fd[1]);
+ lrmd_log(LOG_ERR,"%s::%d: can't find RAExec for class %s"
+ , __FUNCTION__, __LINE__, rsc->class);
+ exit(EXECRA_EXEC_UNKNOWN_ERROR);
+ }
+
+ /*should we use logging daemon or not in script*/
+ setenv(HALOGD, cl_log_get_uselogd()?"yes":"no",1);
+
+ /* Name of the resource and some others also
+ * need to be passed in. Maybe pass through the
+ * entire lrm_op_t too? */
+ lrmd_debug2(LOG_DEBUG
+ , "perform_ra_op:calling RA plugin to perform %s, pid: [%d]"
+ , op_info(op), getpid());
+
+ op_params = ha_msg_value_str_table(op->msg, F_LRM_PARAM);
+ params = merge_str_tables(rsc->params,op_params);
+ if (op_params) {
+ free_str_table(op_params);
+ op_params = NULL;
+ }
+
+ if (replace_secret_params(rsc->id, params) < 0) {
+ /* replacing secrets failed! */
+ if (!strcmp(op_type,"stop")) {
+ /* don't fail on stop! */
+ lrmd_log(LOG_INFO
+ , "%s:%d: proceeding with the stop operation for %s"
+ , __FUNCTION__, __LINE__, rsc->id);
+ } else {
+ lrmd_log(LOG_ERR
+ , "%s:%d: failed to get secrets for %s, "
+ "considering resource not configured"
+ , __FUNCTION__, __LINE__, rsc->id);
+ exit(EXECRA_NOT_CONFIGURED);
+ }
+ }
+ RAExec->execra (rsc->id,
+ rsc->type,
+ rsc->provider,
+ op_type,
+ timeout,
+ params);
+
+ /* execra should never return. */
+ exit(EXECRA_EXEC_UNKNOWN_ERROR);
+
+ }
+ lrmd_log(LOG_ERR, "perform_ra_op: end(impossible).");
+ return HA_OK;
+}
+
+static void
+on_ra_proc_registered(ProcTrack* p)
+{
+}
+
+/* Handle one of our ra child processes finished*/
+static void
+on_ra_proc_finished(ProcTrack* p, int status, int signo, int exitcode
+, int waslogged)
+{
+ lrmd_op_t* op = NULL;
+ lrmd_rsc_t* rsc = NULL;
+ struct RAExecOps * RAExec = NULL;
+ const char* op_type;
+ int rc = EXECRA_EXEC_UNKNOWN_ERROR;
+ int ret;
+ int op_status;
+
+ LRMAUDIT();
+
+ CHECK_ALLOCATED(p, "ProcTrack p", );
+ op = proctrack_data(p);
+
+ child_count -= op->weight;
+ if (child_count < 0) {
+ lrmd_log(LOG_ERR, "%s:%d: child count is less than zero: %d"
+ , __FUNCTION__, __LINE__, child_count);
+ child_count = 0;
+ }
+
+ lrmd_debug2(LOG_DEBUG, "on_ra_proc_finished: accessing the op whose "
+ "address is %p", op);
+ CHECK_ALLOCATED(op, "op", );
+ if (op->exec_pid == 0) {
+ lrmd_log(LOG_ERR, "on_ra_proc_finished: the op was freed.");
+ dump_data_for_debug();
+ return;
+ }
+ RemoveTrackedProcTimeouts(op->exec_pid);
+ op->exec_pid = -1;
+
+ rsc = lookup_rsc(op->rsc_id);
+ if (rsc == NULL) {
+ lrmd_log(LOG_ERR, "%s: the rsc (id=%s) does not exist"
+ , __FUNCTION__, lrm_str(op->rsc_id));
+ lrmd_op_dump(op, __FUNCTION__);
+ lrmd_dump_all_resources();
+ /* delete the op */
+ lrmd_op_destroy(op);
+ reset_proctrack_data(p);
+ LRMAUDIT();
+ return;
+ }
+
+ RAExec = g_hash_table_lookup(RAExecFuncs,rsc->class);
+ if (NULL == RAExec) {
+ lrmd_log(LOG_ERR,"on_ra_proc_finished: can not find RAExec for"
+ " resource class <%s>", rsc->class);
+ dump_data_for_debug();
+ return;
+ }
+
+ op_type = ha_msg_value(op->msg, F_LRM_OP);
+
+ if ( (NULL == strchr(op->first_line_ra_stdout, '\n'))
+ && (0==STRNCMP_CONST(rsc->class, "heartbeat"))
+ && ( (0==STRNCMP_CONST(op_type, "monitor"))
+ ||(0==STRNCMP_CONST(op_type, "status"))) ) {
+ if ( ( op->rapop != NULL )
+ && (op->rapop->ra_stdout_fd >= 0) ) {
+ handle_pipe_ra_stdout(op->rapop->ra_stdout_fd
+ , op->rapop);
+ } else {
+ lrmd_log(LOG_WARNING, "There is something wrong: the "
+ "first line isn't read in. Maybe the heartbeat "
+ "does not ouput string correctly for status "
+ "operation. Or the code (myself) is wrong.");
+ }
+ }
+
+ if( signo ) {
+ if( proctrack_timedout(p) ) {
+ lrmd_log(LOG_WARNING, "%s: pid %d timed out"
+ , small_op_info(op), proctrack_pid(p));
+ op_status = LRM_OP_TIMEOUT;
+ } else {
+ op_status = LRM_OP_ERROR;
+ }
+ } else {
+ rc = RAExec->map_ra_retvalue(exitcode, op_type
+ , op->first_line_ra_stdout);
+ if (!op->interval || is_logmsg_due(op) || debug_level > 0) { /* log non-repeating ops */
+ if (rc == exitcode) {
+ lrmd_log(LOG_INFO
+ , "%s: pid %d exited with"
+ " return code %d", small_op_info(op), proctrack_pid(p), rc);
+ }else{
+ lrmd_log(LOG_INFO
+ , "%s: pid %d exited with"
+ " return code %d (mapped from %d)"
+ , small_op_info(op), proctrack_pid(p), rc, exitcode);
+ }
+ }
+ if (EXECRA_EXEC_UNKNOWN_ERROR == rc || EXECRA_NO_RA == rc) {
+ op_status = LRM_OP_ERROR;
+ lrmd_log(LOG_CRIT
+ , "on_ra_proc_finished: the exit code indicates a problem.");
+ } else {
+ op_status = LRM_OP_DONE;
+ }
+ }
+ if (op->interval && is_logmsg_due(op)) {
+ op->t_lastlogmsg = time_longclock();
+ }
+ if (HA_OK !=
+ ha_msg_mod_int(op->msg, F_LRM_OPSTATUS, op_status)) {
+ LOG_FAILED_TO_ADD_FIELD("opstatus");
+ return ;
+ }
+ if (HA_OK != ha_msg_mod_int(op->msg, F_LRM_RC, rc)) {
+ LOG_FAILED_TO_ADD_FIELD("F_LRM_RC");
+ return ;
+ }
+
+ if ( 0 < strlen(op->first_line_ra_stdout) ) {
+ if (NULL != cl_get_string(op->msg, F_LRM_DATA)) {
+ cl_msg_remove(op->msg, F_LRM_DATA);
+ }
+ ret = ha_msg_add(op->msg, F_LRM_DATA, op->first_line_ra_stdout);
+ if (HA_OK != ret) {
+ LOG_FAILED_TO_ADD_FIELD("data");
+ }
+ }
+
+ if (on_op_done(rsc,op) >= 0) {
+ perform_op(rsc);
+ }
+ reset_proctrack_data(p);
+ LRMAUDIT();
+}
+
+/* Handle the death of one of our managed child processes */
+static const char *
+on_ra_proc_query_name(ProcTrack* p)
+{
+ static char proc_name[MAX_PROC_NAME];
+ lrmd_op_t* op = NULL;
+ lrmd_rsc_t* rsc = NULL;
+ const char* op_type = NULL;
+
+ LRMAUDIT();
+ op = (lrmd_op_t*)(proctrack_data(p));
+ if (NULL == op || op->exec_pid == 0) {
+ return "*unknown*";
+ }
+
+ op_type = ha_msg_value(op->msg, F_LRM_OP);
+ rsc = lookup_rsc(op->rsc_id);
+ if (rsc == NULL) {
+ snprintf(proc_name
+ , MAX_PROC_NAME
+ , "unknown rsc(%s):%s maybe deleted"
+ , op->rsc_id, op_type);
+ }else {
+ snprintf(proc_name, MAX_PROC_NAME, "%s:%s", rsc->id, op_type);
+ }
+ LRMAUDIT();
+ return proc_name;
+}
+
+static int
+get_lrmd_param(const char *name, char *value, int maxstring)
+{
+ if (!name) {
+ lrmd_log(LOG_ERR, "%s: empty name", __FUNCTION__);
+ return HA_FAIL;
+ }
+ if (!strcmp(name,"max-children")) {
+ snprintf(value, maxstring, "%d", max_child_count);
+ return HA_OK;
+ } else {
+ lrmd_log(LOG_ERR, "%s: unknown lrmd parameter %s", __FUNCTION__, name);
+ return HA_FAIL;
+ }
+}
+
+static int
+set_lrmd_param(const char *name, const char *value)
+{
+ int ival;
+
+ if (!name) {
+ lrmd_log(LOG_ERR, "%s: empty name", __FUNCTION__);
+ return HA_FAIL;
+ }
+ if (!value) {
+ lrmd_log(LOG_ERR, "%s: empty value", __FUNCTION__);
+ return HA_FAIL;
+ }
+ if (!strcmp(name,"max-children")) {
+ ival = atoi(value);
+ if (ival <= 0) {
+ lrmd_log(LOG_ERR, "%s: invalid value for lrmd parameter %s"
+ , __FUNCTION__, name);
+ return HA_FAIL;
+ } else if (ival == max_child_count) {
+ lrmd_log(LOG_INFO, "max-children already set to %d", ival);
+ return HA_OK;
+ }
+ lrmd_log(LOG_INFO, "setting max-children to %d", ival);
+ max_child_count = ival;
+ return HA_OK;
+ } else {
+ lrmd_log(LOG_ERR, "%s: unknown lrmd parameter %s"
+ , __FUNCTION__, name);
+ return HA_FAIL;
+ }
+}
+
+int
+on_msg_set_lrmd_param(lrmd_client_t* client, struct ha_msg* msg)
+{
+ const char *name, *value;
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ name = ha_msg_value(msg,F_LRM_LRMD_PARAM_NAME);
+ value = ha_msg_value(msg,F_LRM_LRMD_PARAM_VAL);
+ if (!name || !value) {
+ lrmd_log(LOG_ERR, "%s: no parameter defined"
+ , __FUNCTION__);
+ return HA_FAIL;
+ }
+ return set_lrmd_param(name,value);
+}
+
+int
+on_msg_get_lrmd_param(lrmd_client_t* client, struct ha_msg* msg)
+{
+ struct ha_msg* ret = NULL;
+ const char *name;
+ char value[MAX_NAME_LEN];
+
+ CHECK_ALLOCATED(client, "client", HA_FAIL);
+ CHECK_ALLOCATED(msg, "message", HA_FAIL);
+
+ ret = create_lrm_ret(HA_OK, 1);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ name = ha_msg_value(msg,F_LRM_LRMD_PARAM_NAME);
+ if (get_lrmd_param(name, value, MAX_NAME_LEN) != HA_OK) {
+ return HA_FAIL;
+ }
+ if (HA_OK != ha_msg_add(ret, F_LRM_LRMD_PARAM_VAL, value)) {
+ ha_msg_del(ret);
+ LOG_FAILED_TO_ADD_FIELD(F_LRM_LRMD_PARAM_VAL);
+ return HA_FAIL;
+ }
+ if (HA_OK != msg2ipcchan(ret, client->ch_cmd)) {
+ lrmd_log(LOG_ERR, "%s: can not send the ret msg",__FUNCTION__);
+ }
+ ha_msg_del(ret);
+ return HA_OK;
+}
+
+
+/* /////////////////Util Functions////////////////////////////////////////////// */
+int
+send_ret_msg (IPC_Channel* ch, int ret)
+{
+ struct ha_msg* msg = NULL;
+
+ msg = create_lrm_ret(ret, 1);
+ CHECK_RETURN_OF_CREATE_LRM_RET;
+
+ if (HA_OK != msg2ipcchan(msg, ch)) {
+ lrmd_log(LOG_ERR, "send_ret_msg: can not send the ret msg");
+ }
+ ha_msg_del(msg);
+ return HA_OK;
+}
+
+static void
+send_cbk_msg(struct ha_msg* msg, lrmd_client_t* client)
+{
+ if (!client) {
+ lrmd_log(LOG_WARNING,
+ "%s: zero client", __FUNCTION__);
+ return;
+ }
+ if (!client->ch_cbk) {
+ lrmd_log(LOG_WARNING,
+ "%s: callback channel is null", __FUNCTION__);
+ } else if (HA_OK != msg2ipcchan(msg, client->ch_cbk)) {
+ lrmd_log(LOG_WARNING,
+ "%s: can not send the ret msg", __FUNCTION__);
+ }
+}
+
+static void
+send_msg(struct ha_msg* msg, lrmd_client_t* client)
+{
+ if (!client) {
+ lrmd_log(LOG_WARNING,
+ "%s: zero client", __FUNCTION__);
+ return;
+ }
+ if (HA_OK != ha_msg_mod(msg,F_LRM_APP,client->app_name)) {
+ lrmd_log(LOG_ERR,"%s:%d: cannot add field to a message"
+ , __FUNCTION__, __LINE__);
+ return;
+ }
+ send_cbk_msg(msg, client);
+}
+
+void
+notify_client(lrmd_op_t* op)
+{
+ lrmd_client_t* client = lookup_client(op->client_id);
+
+ if (client) {
+ /* send the result to client */
+ send_cbk_msg(op->msg, client);
+ } else {
+ lrmd_log(LOG_WARNING
+ , "%s: client for the operation %s does not exist"
+ " and client requested notification."
+ , __FUNCTION__, op_info(op));
+ }
+}
+
+lrmd_client_t*
+lookup_client (pid_t pid)
+{
+ return (lrmd_client_t*) g_hash_table_lookup(clients, &pid);
+}
+
+static gboolean
+client_cmp_name(gpointer key, gpointer val, gpointer app_name)
+{
+ return strcmp(((lrmd_client_t*)val)->app_name,(char *)app_name) ?
+ FALSE : TRUE;
+}
+
+static lrmd_client_t*
+lookup_client_by_name(char *app_name)
+{
+ return (lrmd_client_t*)g_hash_table_find(clients,client_cmp_name,app_name);
+}
+
+lrmd_rsc_t*
+lookup_rsc (const char* rid)
+{
+ return rid ?
+ (lrmd_rsc_t*)g_hash_table_lookup(resources, rid) :
+ NULL;
+}
+
+lrmd_rsc_t*
+lookup_rsc_by_msg (struct ha_msg* msg)
+{
+ const char* id = NULL;
+ lrmd_rsc_t* rsc = NULL;
+
+ CHECK_ALLOCATED(msg, "msg", NULL);
+ id = ha_msg_value(msg, F_LRM_RID);
+ if (id == NULL) {
+ lrmd_log(LOG_ERR, "lookup_rsc_by_msg: got a NULL resource id.");
+ return NULL;
+ }
+ if (RID_LEN <= strnlen(id, RID_LEN+2)) {
+ lrmd_log(LOG_ERR, "lookup_rsc_by_msg: resource id is too long.");
+ return NULL;
+ }
+ rsc = lookup_rsc(id);
+ return rsc;
+}
+
+static void
+destroy_pipe_ra_stdout(gpointer user_data)
+{
+ ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data;
+
+ CHECK_ALLOCATED(rapop, "ra_pipe_op",);
+ if (rapop->ra_stderr_fd < 0) {
+ ra_pipe_op_destroy(rapop);
+ }
+}
+
+static void
+destroy_pipe_ra_stderr(gpointer user_data)
+{
+ ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data;
+
+ CHECK_ALLOCATED(rapop, "ra_pipe_op",);
+ if (rapop->ra_stdout_fd < 0) {
+ ra_pipe_op_destroy(rapop);
+ }
+}
+
+static gboolean
+handle_pipe_ra_stdout(int fd, gpointer user_data)
+{
+ gboolean rc = TRUE;
+ ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data;
+ char * data = NULL;
+ lrmd_op_t* lrmd_op = NULL;
+
+ CHECK_ALLOCATED(rapop, "ra_pipe_op", FALSE);
+
+ if (rapop->lrmd_op == NULL) {
+ lrmd_debug2(LOG_DEBUG, "%s:%d: Unallocated lrmd_op 0x%lx!!"
+ , __FUNCTION__, __LINE__
+ , (unsigned long)rapop->lrmd_op);
+ } else {
+ lrmd_op = rapop->lrmd_op;
+ }
+
+ if (fd <= STDERR_FILENO) {
+ lrmd_log(LOG_CRIT, "%s:%d: Attempt to read from "
+ "closed/invalid file descriptor %d."
+ , __FUNCTION__, __LINE__, fd);
+ return FALSE;
+ }
+
+ if (0 != read_pipe(fd, &data, rapop)) {
+ /* error or reach the EOF */
+ if (fd > STDERR_FILENO) {
+ close(fd);
+ if (fd == rapop->ra_stdout_fd) {
+ rapop->ra_stdout_fd = -1;
+ }
+ }
+ if ( NULL != rapop->ra_stdout_gsource) {
+ /*
+ * Returning FALSE will trigger ipc code to release
+ * the GFDSource, so donn't release it here.
+ */
+ rapop->ra_stdout_gsource = NULL;
+ }
+ rc = FALSE;
+ }
+
+ if ( data!=NULL ) {
+ if ( (0==STRNCMP_CONST(rapop->op_type, "meta-data"))
+ ||(0==STRNCMP_CONST(rapop->op_type, "monitor"))
+ ||(0==STRNCMP_CONST(rapop->op_type, "status")) ) {
+ lrmd_debug(LOG_DEBUG, "RA output: (%s:%s:stdout) %s"
+ , lrm_str(rapop->rsc_id), rapop->op_type, data);
+ } else {
+ lrmd_log(LOG_INFO, "RA output: (%s:%s:stdout) %s"
+ , lrm_str(rapop->rsc_id), rapop->op_type, data);
+ }
+
+ /*
+ * This code isn't good enough, it produces erratic and hard-to
+ * read messages in the logs. But this does not affect the
+ * function correctness, since the first line output is ensured
+ * to be collected into the buffer completely.
+ * Anyway, the meta-data (which is _many_ lines long) can be
+ * handled by another function, see raexec.h
+ */
+ if ( (rapop->first_line_read == FALSE)
+ && (0==STRNCMP_CONST(rapop->rsc_class, "heartbeat"))
+ && ( lrmd_op != NULL )
+ && ( (0==STRNCMP_CONST(rapop->op_type, "monitor"))
+ ||(0==STRNCMP_CONST(rapop->op_type, "status")) )) {
+ if (lrmd_op != NULL) {
+ strncat(lrmd_op->first_line_ra_stdout, data
+ , sizeof(lrmd_op->first_line_ra_stdout) -
+ strlen(lrmd_op->first_line_ra_stdout)-1);
+ if (strchr(lrmd_op->first_line_ra_stdout, '\n')
+ != NULL) {
+ rapop->first_line_read = TRUE;
+ }
+ } else {
+ lrmd_log(LOG_CRIT
+ , "Before read the first line, the RA "
+ "execution child quitted and waited.");
+ }
+ }
+
+ g_free(data);
+ }
+
+ return rc;
+}
+
+static gboolean
+handle_pipe_ra_stderr(int fd, gpointer user_data)
+{
+ gboolean rc = TRUE;
+ char * data = NULL;
+ ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data;
+
+ CHECK_ALLOCATED(rapop, "ra_pipe_op", FALSE);
+
+ if (fd <= STDERR_FILENO) {
+ lrmd_log(LOG_CRIT, "%s:%d: Attempt to read from "
+ " closed/invalid file descriptor %d."
+ , __FUNCTION__, __LINE__, fd);
+ return FALSE;
+ }
+
+ if (0 != read_pipe(fd, &data, rapop)) {
+ /* error or reach the EOF */
+ if (fd > STDERR_FILENO) {
+ close(fd);
+ if (fd == rapop->ra_stderr_fd) {
+ rapop->ra_stderr_fd = -1;
+ }
+ }
+ if ( NULL != rapop->ra_stderr_gsource) {
+ /*
+ * G_main_del_fd will trigger
+ * destroy_pipe_ra_stderr
+ * ra_pipe_op_destroy
+ *
+ * Returning FALSE will trigger ipc code to release
+ * the GFDSource, so donn't release it here.
+ */
+ rapop->ra_stderr_gsource = NULL;
+ }
+ rc = FALSE;
+ }
+
+ if (data!=NULL) {
+ lrmd_log(LOG_INFO, "RA output: (%s:%s:stderr) %s"
+ , lrm_str(rapop->rsc_id), probe_str(rapop->lrmd_op,rapop->op_type), data);
+ g_free(data);
+ }
+
+ return rc;
+}
+
+int
+read_pipe(int fd, char ** data, void * user_data)
+{
+ const int BUFFLEN = 81;
+ char buffer[BUFFLEN];
+ int readlen;
+ GString * gstr_tmp;
+ int rc = 0;
+ lrmd_op_t * op = NULL;
+ ra_pipe_op_t * rapop = (ra_pipe_op_t *)user_data;
+
+ lrmd_debug3(LOG_DEBUG, "%s begin.", __FUNCTION__);
+
+ CHECK_ALLOCATED(rapop, "ra_pipe_op", FALSE);
+
+ op = (lrmd_op_t *)rapop->lrmd_op;
+ if (NULL == op) {
+ lrmd_debug2(LOG_DEBUG, "%s:%d: Unallocated lrmd_op 0x%lx!!"
+ , __FUNCTION__, __LINE__
+ , (unsigned long)op);
+ }
+
+ *data = NULL;
+ gstr_tmp = g_string_new("");
+
+ do {
+ errno = 0;
+ readlen = read(fd, buffer, BUFFLEN - 1);
+ if (NULL == op) {
+ lrmd_debug2(LOG_NOTICE
+ , "read's ret: %d when lrmd_op finished"
+ , readlen);
+ }
+ if ( readlen > 0 ) {
+ buffer[readlen] = EOS;
+ g_string_append(gstr_tmp, buffer);
+ }
+ } while (readlen == BUFFLEN - 1 || errno == EINTR);
+
+ if (errno == EINTR || errno == EAGAIN) {
+ errno = 0;
+ }
+
+ /* Reach the EOF */
+ if (readlen == 0) {
+ rc = -1;
+ }
+
+ if ((readlen < 0) && (errno !=0)) {
+ rc = -1;
+ switch (errno) {
+ default:
+ cl_perror("%s:%d read error: fd %d errno=%d"
+ , __FUNCTION__, __LINE__
+ , fd, errno);
+ if (NULL != op) {
+ lrmd_op_dump(op, "op w/bad errno");
+ } else {
+ lrmd_log(LOG_NOTICE
+ , "%s::%d: lrmd_op has been freed"
+ , __FUNCTION__, __LINE__);
+ }
+ break;
+
+ case EBADF:
+ lrmd_log(LOG_CRIT
+ , "%s:%d"
+ " Attempt to read from closed file descriptor %d."
+ , __FUNCTION__, __LINE__, fd);
+ if (NULL != op) {
+ lrmd_op_dump(op, "op w/bad errno");
+ } else {
+ lrmd_log(LOG_NOTICE
+ , "%s::%d: lrmd_op has been freed"
+ , __FUNCTION__, __LINE__);
+ }
+ break;
+ }
+ }
+
+ if ( gstr_tmp->len == 0 ) {
+ g_string_free(gstr_tmp, TRUE);
+ } else {
+ *data = gstr_tmp->str;
+ g_string_free(gstr_tmp, FALSE);
+ }
+
+ lrmd_debug3(LOG_DEBUG, "%s end.", __FUNCTION__);
+ return rc;
+}
+
+
+static gboolean
+debug_level_adjust(int nsig, gpointer user_data)
+{
+ char s[16];
+
+ switch (nsig) {
+ case SIGUSR1:
+ debug_level++;
+ dump_data_for_debug();
+ break;
+
+ case SIGUSR2:
+ dump_data_for_debug();
+ debug_level--;
+ if (debug_level < 0) {
+ debug_level = 0;
+ }
+ break;
+
+ default:
+ lrmd_log(LOG_WARNING, "debug_level_adjust: Received an "
+ "unexpected signal(%d). Something wrong?.",nsig);
+ }
+
+ snprintf(s, sizeof(s), "%d", debug_level);
+ setenv(HADEBUGVAL, s, 1);
+ return TRUE;
+}
+
+static void
+dump_data_for_debug(void)
+{
+ lrmd_debug(LOG_DEBUG, "begin to dump internal data for debugging.");
+ lrmd_dump_all_clients();
+ lrmd_dump_all_resources();
+ lrmd_debug(LOG_DEBUG, "end to dump internal data for debugging.");
+}
+
+const char*
+gen_op_info(const lrmd_op_t* op, gboolean add_params)
+{
+ static char info[512];
+ lrmd_rsc_t* rsc = NULL;
+ const char * op_type;
+ GString * param_gstr;
+ GHashTable* op_params = NULL;
+
+ if (NULL == op) {
+ lrmd_log(LOG_ERR, "%s:%d: op==NULL"
+ , __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ rsc = lookup_rsc(op->rsc_id);
+ op_type = ha_msg_value(op->msg, F_LRM_OP);
+
+ if (rsc == NULL) {
+ snprintf(info,sizeof(info)
+ ,"operation %s[%d] on unknown rsc(maybe deleted) for client %d"
+ ,lrm_str(op_type)
+ ,op->call_id ,op->client_id);
+
+ }else{
+ if (op->exec_pid > 1) {
+ snprintf(info, sizeof(info)
+ ,"operation %s[%d] with pid %d on %s for client %d"
+ ,lrm_str(op_type), op->call_id, op->exec_pid, lrm_str(rsc->id)
+ ,op->client_id);
+ } else {
+ snprintf(info, sizeof(info)
+ ,"operation %s[%d] on %s for client %d"
+ ,lrm_str(op_type), op->call_id, lrm_str(rsc->id)
+ ,op->client_id);
+ }
+
+ if( add_params ) {
+ param_gstr = g_string_new("");
+ op_params = ha_msg_value_str_table(op->msg, F_LRM_PARAM);
+ hash_to_str(op_params, param_gstr);
+ if (op_params) {
+ free_str_table(op_params);
+ op_params = NULL;
+ }
+
+ snprintf(info+strlen(info), sizeof(info)-strlen(info)
+ ,", its parameters: %s",param_gstr->str);
+
+ g_string_free(param_gstr, TRUE);
+ }
+ }
+ return info;
+}
+
+static void
+hash_to_str(GHashTable * params , GString * str)
+{
+ if (params) {
+ g_hash_table_foreach(params, hash_to_str_foreach, str);
+ }
+}
+
+static void
+hash_to_str_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ char buffer_tmp[80];
+ GString * str = (GString *)user_data;
+
+ g_snprintf(buffer_tmp, sizeof(buffer_tmp), "%s=[%s] "
+ , (char *)key, (char *)value);
+ str = g_string_append(str, buffer_tmp);
+}
+
+static void
+check_queue_duration(lrmd_op_t* op)
+{
+ unsigned long t_stay_in_list = 0;
+ static struct msg_ctrl *ml;
+
+ CHECK_ALLOCATED(op, "op", );
+ t_stay_in_list = longclockto_ms(op->t_perform - op->t_addtolist);
+ if ( t_stay_in_list > WARNINGTIME_IN_LIST)
+ {
+ if (!ml)
+ ml = cl_limit_log_new(logmsg_ctrl_defs + OP_STAYED_TOO_LONG);
+ cl_limit_log(ml, LOG_WARNING
+ , "perform_ra_op: the %s stayed in operation "
+ "list for %lu ms (longer than %d ms)"
+ , small_op_info(op), t_stay_in_list
+ , WARNINGTIME_IN_LIST
+ );
+ if (debug_level >= 2) {
+ dump_data_for_debug();
+ }
+ }
+}
+
diff --git a/lrm/lrmd/lrmd.h b/lrm/lrmd/lrmd.h
new file mode 100644
index 0000000..eadea88
--- /dev/null
+++ b/lrm/lrmd/lrmd.h
@@ -0,0 +1,282 @@
+#define MAX_PID_LEN 256
+#define MAX_PROC_NAME 256
+#define MAX_MSGTYPELEN 32
+#define MAX_CLASSNAMELEN 32
+#define WARNINGTIME_IN_LIST 10000
+#define OPTARGS "skrhvmi:"
+#define PID_FILE HA_VARRUNDIR"/lrmd.pid"
+#define LRMD_COREDUMP_ROOT_DIR HA_COREDIR
+#define APPHB_WARNTIME_FACTOR 3
+#define APPHB_INTVL_DETLA 30 /* Millisecond */
+
+#define lrmd_log(priority, fmt...); \
+ cl_log(priority, fmt);
+
+#define lrmd_debug(priority, fmt...); \
+ if ( debug_level >= 1 ) { \
+ cl_log(priority, fmt); \
+ }
+
+#define lrmd_debug2(priority, fmt...); \
+ if ( debug_level >= 2 ) { \
+ cl_log(priority, fmt); \
+ }
+
+#define lrmd_debug3(priority, fmt...); \
+ if ( debug_level >= 3 ) { \
+ cl_log(priority, fmt); \
+ }
+
+#define lrmd_nullcheck(p) ((p) ? (p) : "<null>")
+#define lrm_str(p) (lrmd_nullcheck(p))
+
+#define CHECK_ALLOCATED(thing, name, result) \
+ if (!thing) { \
+ lrmd_log(LOG_ERR \
+ , "%s: %s pointer 0x%lx is not allocated." \
+ , __FUNCTION__, name, (unsigned long)thing); \
+ if (!in_alloc_dump) { \
+ in_alloc_dump = TRUE; \
+ dump_data_for_debug(); \
+ in_alloc_dump = FALSE; \
+ return result; \
+ } \
+ }
+
+#define CHECK_RETURN_OF_CREATE_LRM_RET do { \
+ if (NULL == msg) { \
+ lrmd_log(LOG_ERR \
+ , "%s: cannot create a ret message with create_lrm_ret." \
+ , __FUNCTION__); \
+ return HA_FAIL; \
+ } \
+} while(0)
+
+#define LOG_FAILED_TO_GET_FIELD(field) \
+ lrmd_log(LOG_ERR \
+ , "%s:%d: cannot get field %s from message." \
+ ,__FUNCTION__,__LINE__,field)
+
+#define LOG_FAILED_TO_ADD_FIELD(field) \
+ lrmd_log(LOG_ERR \
+ , "%s:%d: cannot add the field %s to a message." \
+ , __FUNCTION__ \
+ , __LINE__ \
+ , field)
+
+/* NB: There's a return in these macros, hence the names */
+#define return_on_no_int_value(msg,fld,i) do { \
+ if (HA_OK != ha_msg_value_int(msg,fld,i)) { \
+ LOG_FAILED_TO_GET_FIELD(fld); \
+ return HA_FAIL; \
+ } \
+} while(0)
+#define return_on_no_value(msg,fld,v) do { \
+ v = ha_msg_value(msg,fld); \
+ if (!v) { \
+ LOG_FAILED_TO_GET_FIELD(fld); \
+ return HA_FAIL; \
+ } \
+} while(0)
+
+#define LRMD_APPHB_HB \
+ if (reg_to_apphb == TRUE) { \
+ if (apphb_hb() != 0) { \
+ reg_to_apphb = FALSE; \
+ } \
+ }
+
+#define tm2age(tm) \
+ (cmp_longclock(tm, zero_longclock) <= 0) ? \
+ 0 : longclockto_ms(sub_longclock(now, tm))
+#define tm2unix(tm) \
+ (time(NULL)-(tm2age(tm)+999)/1000)
+
+/*
+ * The basic objects in our world:
+ *
+ * lrmd_client_t:
+ * Client - a process which has connected to us for service.
+ *
+ * lrmd_rsc_t:
+ * Resource - an abstract HA cluster resource implemented by a
+ * resource agent through our RA plugins
+ * It has two list of operations (lrmd_op_t) associated with it
+ * op_list - operations to be run as soon as they're ready
+ * repeat_op_list - operations to be run later
+ * It maintains the following tracking structures:
+ * last_op_done Last operation performed on this resource
+ * last_op_table Last operations of each type done per client
+ *
+ * lrmd_op_t:
+ * Resource operation - an operation on a resource -- requested
+ * by a client.
+ *
+ * ProcTrack - tracks a currently running resource operation.
+ * It points back to the lrmd_op_t that started it.
+ *
+ * Global structures containing these things:
+ *
+ * clients - a hash table of all (currently connected) clients
+ *
+ * resources - a hash table of all (currently configured) resources
+ *
+ * Proctrack keeps its own private data structures to keep track of
+ * child processes that it created. They in turn point to the
+ * lrmd_op_t objects that caused us to fork the child process.
+ *
+ *
+ */
+
+/*
+ * Recognized privilege levels
+ */
+
+#define PRIV_ADMIN 8 /* ADMIN_UIDS are administrators */
+#define ADMIN_UIDS "0,"HA_CCMUSER
+#define ADMIN_GIDS "0,"HA_APIGROUP /* unused */
+
+typedef struct
+{
+ char* app_name;
+ pid_t pid;
+ gid_t gid;
+ uid_t uid;
+
+ IPC_Channel* ch_cmd;
+ IPC_Channel* ch_cbk;
+
+ GCHSource* g_src;
+ GCHSource* g_src_cbk;
+ char lastrequest[MAX_MSGTYPELEN];
+ time_t lastreqstart;
+ time_t lastreqend;
+ time_t lastrcsent;
+ int priv_lvl; /* client privilege level (depends on uid/gid) */
+}lrmd_client_t;
+
+typedef struct lrmd_rsc lrmd_rsc_t;
+typedef struct lrmd_op lrmd_op_t;
+typedef struct ra_pipe_op ra_pipe_op_t;
+
+#define RSC_REMOVAL_PENDING 1
+#define RSC_FLUSHING_OPS 2
+#define rsc_frozen(r) \
+ ((r)->state==RSC_REMOVAL_PENDING || (r)->state==RSC_FLUSHING_OPS)
+#define rsc_removal_pending(r) \
+ ((r)->state==RSC_REMOVAL_PENDING)
+#define set_rsc_removal_pending(r) \
+ (r)->state = RSC_REMOVAL_PENDING
+#define set_rsc_flushing_ops(r) \
+ (r)->state = RSC_FLUSHING_OPS
+#define rsc_reset_state(r) (r)->state = 0
+/* log messages for repeating ops (monitor) once an hour */
+#define LOGMSG_INTERVAL (60*60)
+#define is_logmsg_due(op) \
+ (longclockto_ms(sub_longclock(time_longclock(), op->t_lastlogmsg))/1000 >= \
+ (unsigned long)LOGMSG_INTERVAL)
+#define probe_str(op,op_type) \
+ ((op && !op->interval && !strcmp(op_type,"monitor")) ? "probe" : op_type)
+/* exclude stonith class from child count */
+#define no_child_count(rsc) \
+ (strcmp((rsc)->class,"stonith") == 0)
+
+struct lrmd_rsc
+{
+ char* id; /* Unique resource identifier */
+ char* type; /* */
+ char* class; /* */
+ char* provider; /* Resource provider (optional) */
+ GHashTable* params; /* Parameters to this resource */
+ /* as name/value pairs */
+ GList* op_list; /* Queue of operations to run */
+ GList* repeat_op_list; /* Unordered list of repeating */
+ /* ops They will run later */
+ GHashTable* last_op_table; /* Last operation of each type */
+ lrmd_op_t* last_op_done; /* The last finished op of the resource */
+ guint delay_timeout; /* The delay value of op_list execution */
+ int state; /* status of the resource */
+};
+
+struct lrmd_op
+{
+ char* rsc_id;
+ gboolean is_copy;
+ pid_t client_id;
+ int call_id;
+ int exec_pid;
+ guint repeat_timeout_tag;
+ int interval;
+ int delay;
+ gboolean is_cancelled;
+ int weight;
+ int copyparams;
+ struct ha_msg* msg;
+ ra_pipe_op_t * rapop;
+ char first_line_ra_stdout[80]; /* only for heartbeat RAs*/
+ /*time stamps*/
+ longclock_t t_recv; /* set in lrmd_op_new(), i.e. on op create */
+ longclock_t t_addtolist; /* set in add_op_to_runlist() */
+ longclock_t t_perform; /* set in perform_ra_op() */
+ longclock_t t_done; /* set in on_op_done() */
+ longclock_t t_rcchange; /* set in on_op_done(), could equal t_perform */
+ longclock_t t_lastlogmsg; /* the last time the monitor op was logged */
+ ProcTrackKillInfo killseq[3];
+};
+
+
+/* For reading the output from executing the RA */
+struct ra_pipe_op
+{
+ /* The same value of the one in corresponding lrmd_op */
+ lrmd_op_t * lrmd_op;
+ int ra_stdout_fd;
+ int ra_stderr_fd;
+ GFDSource * ra_stdout_gsource;
+ GFDSource * ra_stderr_gsource;
+ gboolean first_line_read;
+
+ /* For providing more detailed information in log */
+ char * rsc_id;
+ char * op_type;
+ char * rsc_class;
+};
+
+
+const char *gen_op_info(const lrmd_op_t* op, gboolean add_params);
+#define op_info(op) gen_op_info(op,TRUE)
+#define small_op_info(op) gen_op_info(op,FALSE)
+
+#define DOLRMAUDITS
+#undef DOLRMAUDITS
+
+#define DOMEGALRMAUDITS
+#define LRMAUDIT_CLIENTS
+#define LRMAUDIT_RESOURCES
+
+#ifdef DOLRMAUDITS
+
+ void lrmd_audit(const char *function, int line);
+ void audit_clients(void);
+ void audit_resources(void);
+ void audit_ops(GList* rsc_ops, lrmd_rsc_t *rsc, const char *desc);
+ void on_client(gpointer key, gpointer value, gpointer user_data);
+ void on_resource(gpointer key, gpointer value, gpointer user_data);
+ void on_op(lrmd_op_t *op, lrmd_rsc_t *rsc, const char *desc);
+ void on_ra_pipe_op(ra_pipe_op_t *rapop, lrmd_op_t *op, const char *desc);
+
+# define LRMAUDIT() lrmd_audit(__FUNCTION__,__LINE__)
+# ifdef DOMEGALRMAUDITS
+# define MEGALRMAUDIT lrmd_audit(__FUNCTION__,__LINE__)
+# else
+# define MEGALRMAUDIT /*nothing*/
+# endif
+#else
+# define LRMAUDIT() /*nothing*/
+# define MEGALRMAUDIT() /*nothing*/
+#endif
+
+/*
+ * load parameters from an ini file (cib_secrets.c)
+ */
+int replace_secret_params(char* rsc_id, GHashTable* params);
diff --git a/lrm/lrmd/lrmd_fdecl.h b/lrm/lrmd/lrmd_fdecl.h
new file mode 100644
index 0000000..9c97385
--- /dev/null
+++ b/lrm/lrmd/lrmd_fdecl.h
@@ -0,0 +1,111 @@
+/* TODO: This ought to be broken up into several source files for easier
+ * reading and debugging. */
+
+/* Debug oriented funtions */
+static gboolean debug_level_adjust(int nsig, gpointer user_data);
+static void dump_data_for_debug(void);
+
+/* glib loop call back functions */
+static gboolean on_connect_cmd(IPC_Channel* ch_cmd, gpointer user_data);
+static gboolean on_connect_cbk(IPC_Channel* ch_cbk, gpointer user_data);
+static int msg_type_cmp(const void *p1, const void *p2);
+static gboolean on_receive_cmd(IPC_Channel* ch_cmd, gpointer user_data);
+static gboolean on_repeat_op_readytorun(gpointer data);
+static void on_remove_client(gpointer user_data);
+static void destroy_pipe_ra_stderr(gpointer user_data);
+static void destroy_pipe_ra_stdout(gpointer user_data);
+
+/* message handlers */
+static int on_msg_register(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_rsc_classes(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_rsc_types(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_rsc_providers(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_metadata(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_add_rsc(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_rsc(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_last_op(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_all(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_del_rsc(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_fail_rsc(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_cancel_op(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_flush_all(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_perform_op(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_state(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_set_lrmd_param(lrmd_client_t* client, struct ha_msg* msg);
+static int on_msg_get_lrmd_param(lrmd_client_t* client, struct ha_msg* msg);
+static int set_lrmd_param(const char *name, const char *value);
+static int get_lrmd_param(const char *name, char *value, int maxstring);
+static gboolean sigterm_action(int nsig, gpointer unused);
+
+/* functions wrap the call to ra plugins */
+static int perform_ra_op(lrmd_op_t* op);
+
+/* Apphb related functions */
+static int init_using_apphb(void);
+static gboolean emit_apphb(gpointer data);
+
+/* Utility functions */
+static int flush_op(lrmd_op_t* op);
+static gboolean rsc_execution_freeze_timeout(gpointer data);
+static void add_op_to_runlist(lrmd_rsc_t* rsc, lrmd_op_t* op);
+static int perform_op(lrmd_rsc_t* rsc);
+static int unregister_client(lrmd_client_t* client);
+static int on_op_done(lrmd_rsc_t* rsc, lrmd_op_t* op);
+static int send_ret_msg ( IPC_Channel* ch, int rc);
+static void send_cbk_msg(struct ha_msg* msg, lrmd_client_t* client);
+static void send_msg(struct ha_msg* msg, lrmd_client_t* client);
+static void notify_client(lrmd_op_t* op);
+static lrmd_client_t* lookup_client (pid_t pid);
+static lrmd_rsc_t* lookup_rsc (const char* rid);
+static lrmd_rsc_t* lookup_rsc_by_msg (struct ha_msg* msg);
+static int read_pipe(int fd, char ** data, gpointer user_data);
+static gboolean handle_pipe_ra_stdout(int fd, gpointer user_data);
+static gboolean handle_pipe_ra_stderr(int fd, gpointer user_data);
+static struct ha_msg* op_to_msg(lrmd_op_t* op);
+static int store_timestamps(lrmd_op_t* op);
+static void reset_timestamps(lrmd_op_t* op);
+static gboolean lrm_shutdown(void);
+static gboolean can_shutdown(void);
+static gboolean free_str_hash_pair(gpointer key
+, gpointer value, gpointer user_data);
+static gboolean free_str_op_pair(gpointer key
+, gpointer value, gpointer user_data);
+static lrmd_op_t* lrmd_op_copy(const lrmd_op_t* op);
+static void send_last_op(gpointer key, gpointer value, gpointer user_data);
+static void replace_last_op(lrmd_client_t* client, lrmd_rsc_t* rsc, lrmd_op_t* op);
+static int record_op_completion(lrmd_rsc_t* rsc, lrmd_op_t* op);
+static void to_repeatlist(lrmd_rsc_t* rsc, lrmd_op_t* op);
+static void remove_op_history(lrmd_op_t* op);
+static void hash_to_str(GHashTable * , GString *);
+static void hash_to_str_foreach(gpointer key, gpointer value, gpointer userdata);
+static void warning_on_active_rsc(gpointer key, gpointer value, gpointer user_data);
+static void check_queue_duration(lrmd_op_t* op);
+static gboolean flush_all(GList** listp, int client_pid);
+static gboolean cancel_op(GList** listp,int cancel_op_id);
+static int prepare_failmsg(struct ha_msg* msg,
+ int fail_rc, const char *fail_reason);
+static void async_notify(gpointer key, gpointer val, gpointer data);
+static gboolean client_cmp_name(gpointer key, gpointer val, gpointer app_name);
+static lrmd_client_t* lookup_client_by_name(char *app_name);
+static void calc_max_children(void);
+
+/*
+ * following functions are used to monitor the exit of ra proc
+ */
+static void on_ra_proc_registered(ProcTrack* p);
+static void on_ra_proc_finished(ProcTrack* p, int status
+, int signo, int exitcode, int waslogged);
+static const char* on_ra_proc_query_name(ProcTrack* p);
+
+
+
+/*
+ * Daemon functions
+ *
+ * copy from the code of Andrew Beekhof <andrew@beekhof.net>
+ */
+static void usage(const char* cmd, int exit_status);
+static int init_start(void);
+static int init_stop(const char *pid_file);
+static int init_status(const char *pid_file, const char *client_name);
+static void lrmd_rsc_dump(char* rsc_id, const char * text);
diff --git a/lrm/test/LRMBasicSanityCheck.in b/lrm/test/LRMBasicSanityCheck.in
new file mode 100755
index 0000000..dbe8548
--- /dev/null
+++ b/lrm/test/LRMBasicSanityCheck.in
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+ # Copyright (c) 2004 International Business Machines
+ # Author: Huang Zhen <zhenhltc@cn.ibm.com>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+HBLIB=@libdir@/heartbeat
+LRMD=$HBLIB/lrmd
+LRMADMIN=@sbindir@/lrmadmin
+export LRMD LRMADMIN
+
+if [ $# -gt 0 ]; then
+ LRMD=$1/lrmd
+fi
+
+if [ ! -f $LRMD ]; then
+ echo $LRMD does not exist
+ exit 1
+fi
+
+if [ ! -f $LRMADMIN ]; then
+ echo $LRMADMIN does not exist
+ exit 1
+fi
+
+OUTDIR=/tmp/LRM_BSC_$$
+export OUTDIR
+[ -d $OUTDIR ] && {
+ echo $OUTDIR exists, please cleanup
+ exit 1
+}
+
+`dirname $0`/regression.sh -q set:BSC
+rc=$?
+if [ $rc -eq 0 ]; then
+ echo "LRM tests PASSED"
+ rm -rf $OUTDIR
+else
+ echo "LRM tests FAILED"
+ echo "Please check $OUTDIR for results"
+fi
+exit $rc
diff --git a/lrm/test/Makefile.am b/lrm/test/Makefile.am
new file mode 100644
index 0000000..84f6657
--- /dev/null
+++ b/lrm/test/Makefile.am
@@ -0,0 +1,48 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS = testcases
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \
+ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl
+
+COMMONLIBS = $(top_builddir)/lib/clplumbing/libplumb.la $(GLIBLIB)
+
+noinst_PROGRAMS = apitest plugintest callbacktest
+
+apitest_SOURCES = apitest.c
+apitest_LDFLAGS = $(COMMONLIBS)
+apitest_LDADD = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la
+apitest_DEPENDENCIES = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la
+
+plugintest_SOURCES = plugintest.c
+plugintest_LDADD = $(COMMONLIBS)
+plugintest_LDFLAGS = -L$(top_builddir)/lib/pils -lpils @LIBLTDL@
+
+callbacktest_SOURCES = callbacktest.c
+callbacktest_LDFLAGS = $(COMMONLIBS)
+callbacktest_LDADD = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la
+callbacktest_DEPENDENCIES = $(top_builddir)/lib/$(LRM_DIR)/liblrm.la
+
+testdir = $(datadir)/$(PACKAGE_NAME)/lrmtest
+test_SCRIPTS = LRMBasicSanityCheck regression.sh evaltest.sh lrmregtest lrmregtest-lsb
+test_DATA = README.regression defaults descriptions lrmadmin-interface language
+# shouldn't need this, but we do for some versions of autofoo tools
+EXTRA_DIST = $(test_SCRIPTS) $(test_DATA)
diff --git a/lrm/test/README.regression b/lrm/test/README.regression
new file mode 100644
index 0000000..3588172
--- /dev/null
+++ b/lrm/test/README.regression
@@ -0,0 +1,164 @@
+LRM regression tests
+
+* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *
+*
+* evaltest.sh uses eval to an extent you don't really want to
+* know about. Beware. Beware twice. Any input from the testcases
+* directory is considered to be trusted. So, think twice before
+* devising your tests lest you kill your precious data. Got it?
+* Good.
+*
+* Furthermore, we are deliberately small on testing the user
+* input and no one should try to predict what is to happen on
+* random input from the testcases.
+*
+* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *
+
+Manifest
+
+ regression.sh: the top level program
+ evaltest.sh: the engine test engine
+
+ lrmadmin-interface: interface to lrmd (lrmadmin)
+ descriptions: describe what we are about to do
+ defaults: the default settings for test commands
+
+ testcases/: here are the testcases and filters
+ output/: here goes the output
+
+All volatile data lives in the testcases/ directory.
+
+NB: You should never ever need to edit regression.sh and
+evaltest.sh. If you really have to, please talk to me and I will
+try to fix it so that you do not have to.
+
+Please write new test cases. The more the merrier :)
+
+Usage
+
+The usage is:
+
+ ./regression.sh ["prepare"] ["set:"<setname>|<testcase>]
+
+Test cases are collected in test sets. The default test set is
+basicset and running regression.sh without arguments will do all
+tests from that set.
+
+To show progress, for each test a '.' is printed. For sleeps,
+a '+' for each second. Once all tests have been evaluated, the
+output is checked against the expect file. If successful, "PASS"
+is printed, otherwise "FAIL".
+
+Specifying "prepare" will make regression.sh create expect
+output files for the given set of tests or testcase.
+
+The script will start and stop lrmd itself. stonithd is also
+started to test the XML descriptions printed by stonith agents.
+No other parts of stonithd functionality is tested.
+
+The following files may be generated:
+
+ output/<testcase>.out: the output of the testcase
+ output/regression.out: the output of regression.sh
+ output/lrmd.out: the output of lrmd
+
+On success output from testcases is removed and regression.out is
+empty.
+
+Driving the test cases yourself
+
+evaltest.sh accepts input from stdin, evaluates it immediately,
+and prints results to stdout/stderr. One can perhaps get a better
+feeling of what's actually going on by running it interactively.
+Please note that you have to start the lrmd yourself beforehand.
+
+Test cases
+
+Tests are written in a simple metalanguage. The list of commands
+with rough translation to lrmadmin's options is in the language
+file. The best description of the language is in the
+lrmadmin-interface and descriptions scripts:
+
+$ egrep '^lrm|echo' lrmadmin-interface descriptions
+
+A test case is a list of tests, one per line. A few examples:
+
+ add # add a resource with default name
+ list # list all resources
+ del rsc=wiwi # remove a wiwi resource
+
+A set of defaults for LRM options is in the defaults file. That's
+why we can write short forms instead of
+
+ add rsc=r1 class=ocf type=lrmregtest provider=heartbeat ...
+
+Special operations
+
+There are special operations with which it is possible to change
+environment and do other useful things. All special ops start
+with the '%' sign and may be followed by additional parameters.
+
+%setenv
+ change the environment variable; see defaults for the
+ set of global variables and resetvars() in evaltest.sh
+
+%sleep
+ sleep
+
+%stop
+ skip the rest of the tests
+
+%extcheck
+ feed the output of the next test case to the specified
+ external program/filter; the program should either reside in
+ testcases/ or be in the PATH, i.e.
+
+ %extcheck cat
+
+ simulates a null op :)
+
+ see testcases/metadata for some examples
+
+%repeat num
+ repeat the next test num times
+ there are several variables which are substituted in the test
+ lines, so that we can simulate a for loop:
+
+ s/%t/$test_cnt/g
+ s/%l/$line/g
+ s/%j/$job_cnt/g
+ s/%i/$repeat_cnt/g
+
+ for example, to add 10 resources:
+
+ %repeat 10
+ add rsc=r-%i
+
+%bg [num]
+ run next num (or just the next one) tests in background
+
+%bgrepeat [num]
+ a combination of the previous two (used often)
+
+%wait
+ wait for the last background test to finish
+
+%shell
+ feed whatever is in the rest of the line to 'sh -s'
+
+Filters and except files
+
+Some output is necessarily very volatile, such as time stamps.
+It is possible to specify a filter for each testcase to get rid
+of superfluous information. A filter is a filter in UNIX
+sense, it takes input from stdin and prints results to stdout.
+
+There is a common filter called very inventively
+testcases/common.filter which is applied to all test cases.
+
+Except files are a list of extended regular expressions fed to
+egrep(1). That way one can filter out lines which are not
+interesting. Again, the one applied to all is
+testcases/common.excl.
+
+
diff --git a/lrm/test/apitest.c b/lrm/test/apitest.c
new file mode 100644
index 0000000..0d4c572
--- /dev/null
+++ b/lrm/test/apitest.c
@@ -0,0 +1,317 @@
+
+/*
+ * Test program for Local Resource Manager API.
+ *
+ * Copyright (C) 2004 Huang Zhen <zhenh@cn.ibm.com>
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/poll.h>
+#include <string.h>
+#include <glib.h>
+#include <lrm/lrm_api.h>
+#include <clplumbing/cl_log.h>
+#include <syslog.h>
+
+void lrm_op_done_callback (lrm_op_t* op);
+void printf_rsc(lrm_rsc_t* rsc);
+void printf_op(lrm_op_t* op);
+void printf_hash_table(GHashTable* hash_table);
+void get_all_rsc(ll_lrm_t* lrm);
+void get_cur_state(lrm_rsc_t* rsc);
+
+int main (int argc, char* argv[])
+{
+ ll_lrm_t* lrm;
+ lrm_rsc_t* rsc = NULL;
+ lrm_op_t* op = NULL;
+ const char* rid = "ip248";
+ GHashTable* param = NULL;
+ GList* classes;
+ int i;
+
+ cl_log_set_entity("apitest");
+ cl_log_set_facility(LOG_USER);
+
+ lrm = ll_lrm_new("lrm");
+
+ if(NULL == lrm)
+ {
+ printf("lrm==NULL\n");
+ return 1;
+ }
+ puts("sigon...");
+ lrm->lrm_ops->signon(lrm,"apitest");
+
+ classes = lrm->lrm_ops->get_rsc_class_supported(lrm);
+ lrm_free_str_list(classes);
+
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ puts("add_rsc...");
+ lrm->lrm_ops->add_rsc(lrm, rid, "heartbeat", "IPaddr", "heartbeat", param);
+ puts("get_rsc...");
+ rsc = lrm->lrm_ops->get_rsc(lrm, rid);
+ printf_rsc(rsc);
+
+ puts("perform_op(start)...");
+ op = lrm_op_new();
+ op->op_type = g_strdup("start");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a start op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 0;
+ op->target_rc = EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ puts("perform_op(status)...");
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ op = lrm_op_new();
+ op->op_type = g_strdup("status");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a status op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 1000;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ puts("perform_op(stop)...");
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ op = lrm_op_new();
+ op->op_type = g_strdup("stop");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a stop op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 0;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ puts("perform_op(status)...");
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ op = lrm_op_new();
+ op->op_type = g_strdup("status");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a status op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 2000;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ puts("perform_op(start)...");
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ op = lrm_op_new();
+ op->op_type = g_strdup("start");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a start op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 0;
+ op->target_rc = EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ puts("perform_op(status)...");
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ op = lrm_op_new();
+ op->op_type = g_strdup("status");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a status op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 3000;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ puts("perform_op(stop)...");
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("192.168.192.100"));
+ op = lrm_op_new();
+ op->op_type = g_strdup("stop");
+ op->params = param;
+ op->timeout = 0;
+ op->user_data = strdup("It is a stop op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 0;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+ lrm_free_op(op);
+
+ for(i = 0; i < 5; i++) {
+ puts("get_cur_state...");
+ get_cur_state(rsc);
+ puts("sleep a while...");
+ sleep(1);
+ }
+
+ puts("delete_rsc...");
+ lrm->lrm_ops->delete_rsc(lrm, rid);
+ lrm_free_rsc(rsc);
+
+ puts("signoff...");
+ lrm->lrm_ops->signoff(lrm);
+
+ return 0;
+}
+void lrm_op_done_callback(lrm_op_t* op)
+{
+ puts("lrm_op_done_callback...");
+ printf_op(op);
+}
+void printf_rsc(lrm_rsc_t* rsc)
+{
+ printf("print resource>>>>>>>>>\n");
+ if (NULL == rsc) {
+ printf("resource is null\n");
+ printf("print end\n");
+ return;
+ }
+ printf("\tresource of id:%s\n", rsc->id);
+ printf("\ttype:%s\n", rsc->type);
+ printf("\tclass:%s\n", rsc->class);
+ printf("\tparams:\n");
+ printf_hash_table(rsc->params);
+ printf("print end<<<<<<<<<<<<<<<\n");
+}
+
+void printf_op(lrm_op_t* op)
+{
+ printf("print op>>>>>>>>>>>>>>>>\n");
+
+ if (NULL == op) {
+ printf("op is null\n");
+ printf("print end\n");
+ return;
+ }
+
+ printf("\top_type:%s\n",op->op_type?op->op_type:"null");
+ printf("\tparams:\n");
+ printf_hash_table(op->params);
+ printf("\ttimeout:%d\n",op->timeout);
+ printf("\tuser_data:%s\n",op->user_data?(char*)op->user_data:"null");
+ printf("\top_status:%d\n",op->op_status);
+ printf("\tapp_name:%s\n",op->app_name?op->app_name:"null");
+ printf("\toutput:%s\n",op->output?op->output:"null");
+ printf("\trc:%d\n",op->rc);
+ printf("\tcall_id:%d\n",op->call_id);
+ printf("print end<<<<<<<<<<<<<<<<<<\n");
+}
+
+static void
+printf_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ printf("\t\t%s=%s\n",(char*)key,(char*)value);
+}
+void
+printf_hash_table(GHashTable* hash_table)
+{
+ if (NULL == hash_table) {
+ printf("\t\tnull\n");
+ return;
+ }
+ g_hash_table_foreach(hash_table, printf_pair, NULL);
+}
+void
+get_all_rsc(ll_lrm_t* lrm)
+{
+ GList* element = NULL, * rid_list = NULL;
+
+ puts("get_all_rscs...");
+ rid_list = lrm->lrm_ops->get_all_rscs(lrm);
+ if (NULL != rid_list) {
+ element = g_list_first(rid_list);
+ while (NULL != element) {
+ printf("\tid:%s\n",(char*)element->data);
+ element = g_list_next(element);
+ }
+ } else {
+ puts("\tnone.");
+ }
+ lrm_free_str_list(rid_list);
+}
+void
+get_cur_state(lrm_rsc_t* rsc)
+{
+ state_flag_t state;
+ GList* node = NULL, * op_list = NULL;
+ lrm_op_t* op = NULL;
+ printf("current state>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+
+ op_list = rsc->ops->get_cur_state(rsc, &state);
+
+ printf("\tcurrent state:%s\n",state==LRM_RSC_IDLE?"Idle":"Busy");
+
+
+ for(node = g_list_first(op_list); NULL != node;
+ node = g_list_next(node)) {
+ op = (lrm_op_t*)node->data;
+ printf_op(op);
+ }
+ lrm_free_op_list(op_list);
+ printf("current end<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
diff --git a/lrm/test/apitest.exp b/lrm/test/apitest.exp
new file mode 100644
index 0000000..b153ee3
--- /dev/null
+++ b/lrm/test/apitest.exp
@@ -0,0 +1,122 @@
+sigon...
+add_rsc...
+get_rsc...
+print resource
+ resource of id:ip248
+ type:IPv6addr
+ class:heartbeat
+ params:
+ 1=3ffe:ffff:0:f101::3
+print end
+perform_op(start)...
+print op
+ op_type:start
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:It is a start op!
+ op_status:0
+ app_name:null
+ output:null
+ rc:0
+print end
+perform_op(status)...
+print op
+ op_type:status
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:It is a status op!
+ op_status:0
+ app_name:null
+ output:null
+ rc:0
+print end
+perform_op(stop)...
+print op
+ op_type:stop
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:It is a stop op!
+ op_status:0
+ app_name:null
+ output:null
+ rc:0
+print end
+get_cur_state...
+ current state:Busy
+print op
+ op_type:start
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:It is a start op!
+ op_status:-1
+ app_name:apitest
+ output:null
+ rc:0
+print end
+print op
+ op_type:status
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:It is a status op!
+ op_status:-1
+ app_name:apitest
+ output:null
+ rc:0
+print end
+print op
+ op_type:stop
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:It is a stop op!
+ op_status:-1
+ app_name:apitest
+ output:null
+ rc:0
+print end
+stop_op...
+get_cur_state...
+ current state:Busy
+print op
+ op_type:start
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:null
+ op_status:-1
+ app_name:apitest
+ output:null
+ rc:0
+print end
+print op
+ op_type:stop
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:null
+ op_status:-1
+ app_name:apitest
+ output:null
+ rc:0
+print end
+sleep a while...
+get_cur_state...
+ current state:Idel
+print op
+ op_type:stop
+ params:
+ 1=3ffe:ffff:0:f101::3
+ timeout:0
+ user_data:null
+ op_status:0
+ app_name:apitest
+ output:null
+ rc:0
+print end
+delete_rsc...
+signoff...
diff --git a/lrm/test/callbacktest.c b/lrm/test/callbacktest.c
new file mode 100644
index 0000000..48f4d49
--- /dev/null
+++ b/lrm/test/callbacktest.c
@@ -0,0 +1,204 @@
+
+/*
+ * Test program for the callback function of Local Resource Manager API.
+ *
+ * Copyright (C) 2004 Huang Zhen <zhenh@cn.ibm.com>
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/poll.h>
+#include <string.h>
+#include <glib.h>
+#include <lrm/lrm_api.h>
+#include <syslog.h>
+#include <clplumbing/GSource.h>
+
+static void lrm_op_done_callback(lrm_op_t *op);
+static void printf_rsc(lrm_rsc_t *rsc);
+static void printf_op(lrm_op_t *op);
+static void printf_hash_table(GHashTable *hash_table);
+static gboolean lrm_dispatch(IPC_Channel *notused, gpointer user_data);
+static GMainLoop *mainloop;
+
+int
+main(int argc, char *argv[])
+{
+ ll_lrm_t* lrm;
+ lrm_rsc_t* rsc = NULL;
+ lrm_op_t* op = NULL;
+ const char* rid = "ip248";
+ GHashTable* param = NULL;
+
+ lrm = ll_lrm_new("lrm");
+
+ if(NULL == lrm)
+ {
+ printf("lrm==NULL\n");
+ return 1;
+ }
+ puts("sigon...");
+ lrm->lrm_ops->signon(lrm,"apitest");
+ lrm->lrm_ops->set_lrm_callback(lrm, lrm_op_done_callback);
+
+ param = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(param, strdup("1"), strdup("3ffe:ffff:0:f101::3"));
+ puts("add_rsc...");
+ lrm->lrm_ops->add_rsc(lrm, rid, "heartbeat", "IPv6addr", NULL, param);
+ puts("get_rsc...");
+ rsc = lrm->lrm_ops->get_rsc(lrm, rid);
+ printf_rsc(rsc);
+
+ puts("perform_op(start)...");
+ op = lrm_op_new();
+ op->op_type = g_strdup("start");
+ op->params = NULL;
+ op->timeout = 0;
+ op->user_data = strdup("It is a start op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 0;
+ op->target_rc = EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+
+ puts("perform_op(status)...");
+ op = lrm_op_new();
+ op->op_type = g_strdup("status");
+ op->params = NULL;
+ op->timeout = 0;
+ op->user_data = strdup("It is a status op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 1000;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+
+ puts("perform_op(stop)...");
+ op = lrm_op_new();
+ op->op_type = g_strdup("stop");
+ op->params = NULL;
+ op->timeout = 0;
+ op->user_data = strdup("It is a stop op!");
+ if ( op->user_data == NULL ) {
+ fprintf(stderr, "No enough memory.\n");
+ return -1;
+ }
+ op->user_data_len = strlen(op->user_data)+1;
+ op->interval = 0;
+ op->target_rc=EVERYTIME;
+ rsc->ops->perform_op(rsc,op);
+ printf_op(op);
+
+ G_main_add_IPC_Channel(G_PRIORITY_LOW,
+ lrm->lrm_ops->ipcchan(lrm),
+ FALSE,
+ lrm_dispatch, lrm,
+ NULL);
+
+ mainloop = g_main_new(FALSE);
+ g_main_run(mainloop);
+
+ puts("delete_rsc...");
+ lrm->lrm_ops->delete_rsc(lrm, rid);
+
+ puts("signoff...");
+ lrm->lrm_ops->signoff(lrm);
+
+ return 0;
+}
+
+static void
+lrm_op_done_callback(lrm_op_t *op)
+{
+ puts("lrm_op_done_callback...");
+ printf_op(op);
+}
+
+static gboolean
+lrm_dispatch(IPC_Channel *notused, gpointer user_data)
+{
+ ll_lrm_t *lrm = (ll_lrm_t*)user_data;
+ lrm->lrm_ops->rcvmsg(lrm, FALSE);
+ return TRUE;
+}
+
+static void
+printf_rsc(lrm_rsc_t *rsc)
+{
+ printf("print resource\n");
+ if (NULL == rsc) {
+ printf("resource is null\n");
+ printf("print end\n");
+ return;
+ }
+ printf("\tresource of id:%s\n", rsc->id);
+ printf("\ttype:%s\n", rsc->type);
+ printf("\tclass:%s\n", rsc->class);
+ printf("\tparams:\n");
+ printf_hash_table(rsc->params);
+ printf("print end\n");
+}
+
+static void
+printf_op(lrm_op_t *op)
+{
+ printf("print op\n");
+
+ if (NULL == op) {
+ printf("op is null\n");
+ printf("print end\n");
+ return;
+ }
+
+ printf("\top_type:%s\n",op->op_type?op->op_type:"null");
+ printf("\tparams:\n");
+ printf_hash_table(op->params);
+ printf("\ttimeout:%d\n",op->timeout);
+ printf("\tuser_data:%s\n",op->user_data?(char*)op->user_data:"null");
+ printf("\tuser_data pointer:%p\n",op->user_data);
+ printf("\top_status:%d\n",op->op_status);
+ printf("\tapp_name:%s\n",op->app_name?op->app_name:"null");
+ printf("\toutput:%s\n",op->output?op->output:"null");
+ printf("\trc:%d\n",op->rc);
+/* printf("\tcall_id:%d\n",op->call_id); */
+ printf("print end\n");
+}
+
+static void
+printf_pair(gpointer key, gpointer value, gpointer user_data)
+{
+ printf("\t\t%s=%s\n",(char*)key,(char*)value);
+}
+
+static void
+printf_hash_table(GHashTable *hash_table)
+{
+ if (NULL == hash_table) {
+ printf("\t\tnull\n");
+ return;
+ }
+ g_hash_table_foreach(hash_table, printf_pair, NULL);
+}
diff --git a/lrm/test/defaults b/lrm/test/defaults
new file mode 100644
index 0000000..039915b
--- /dev/null
+++ b/lrm/test/defaults
@@ -0,0 +1,9 @@
+# defaults
+: ${dflt_rsc:=r1}
+: ${dflt_type:=lrmregtest}
+: ${dflt_class:=ocf}
+: ${dflt_provider:=heartbeat}
+: ${dflt_timeout:=1000}
+: ${dflt_interval:=0}
+: ${dflt_targetrc:=EVERYTIME}
+dflt_args=""
diff --git a/lrm/test/descriptions b/lrm/test/descriptions
new file mode 100644
index 0000000..f2aab6b
--- /dev/null
+++ b/lrm/test/descriptions
@@ -0,0 +1,55 @@
+lead=".TRY"
+describe_list() {
+ echo $lead List resources
+}
+describe_add() {
+ echo $lead Add resource \
+ ${rsc:-$dflt_rsc} \
+ class=${class:-$dflt_class} type=${type:-$dflt_type} \
+ provider=${provider:-$dflt_provider} \
+ args=$args
+}
+describe_del() {
+ echo $lead Delete resource \
+ ${rsc:-$dflt_rsc}
+}
+describe_flush() {
+ echo $lead Flush resource \
+ ${rsc:-$dflt_rsc}
+}
+describe_state() {
+ echo $lead Show state \
+ ${rsc:-$dflt_rsc}
+}
+describe_info() {
+ echo $lead Show info \
+ ${rsc:-$dflt_rsc}
+}
+describe_exec() {
+ echo $lead Exec \
+ ${rsc:-$dflt_rsc} \
+ op=${operation:-$dflt_operation} \
+ timeout=${timeout:-$dflt_timeout} interval=${interval:-$dflt_interval} \
+ target=${targetrc:-$dflt_targetrc} args=$args
+}
+
+describe_classes() {
+ echo $lead List classes
+}
+describe_types() {
+ echo $lead List types \
+ class=${class:-$dflt_class}
+}
+describe_classmeta() {
+ echo $lead Meta-data \
+ class=${class:-$dflt_class}
+}
+describe_meta() {
+ echo $lead Show meta-data \
+ class=${class:-$dflt_class} \
+ type=${type:-$dflt_type} provider=${provider:-$dflt_provider}
+}
+describe_provider() {
+ echo $lead Show provider \
+ class=${class:-$dflt_class} type=${type:-$dflt_type}
+}
diff --git a/lrm/test/evaltest.sh b/lrm/test/evaltest.sh
new file mode 100755
index 0000000..f369102
--- /dev/null
+++ b/lrm/test/evaltest.sh
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+ # Copyright (C) 2007 Dejan Muhamedagic <dejan@suse.de>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+
+: ${TESTDIR:=testcases}
+: ${LRMADMIN:=../admin/lrmadmin}
+test -x $LRMADMIN || LRMADMIN=lrmadmin
+: ${OCF_ROOT:=/usr/lib/ocf}
+
+. ./defaults
+. ./lrmadmin-interface
+. ./descriptions
+
+resetvars() {
+ unset rsc type class provider timeout interval targetrc args
+ unset extcheck
+}
+
+#
+# special operations squad
+#
+specopt_setenv() {
+ eval $rest
+}
+specopt_sleep() {
+ #sleep $rest
+ # the while loop below is the same
+ # but we give user some feedback on what's happening
+ while [ "$rest" -gt 0 ]; do
+ sleep 1
+ echo -n "+" >&3
+ rest=$(($rest-1))
+ done
+}
+specopt_extcheck() {
+ extcheck="$rest"
+ set $extcheck
+ which "$1" >/dev/null 2>&1 || # a program in the PATH
+ extcheck="$TESTDIR/$extcheck" # or our script
+}
+specopt_repeat() {
+ repeat_limit=$rest
+}
+specopt_bg() {
+ if [ "$job_cnt" -gt "$bgprocs_num" ]; then
+ bgprocs_num=${rest:-1}
+ job_cnt=1
+ else
+ echo ".BG bad usage: more tests yet to be backgrounded"
+ fi
+}
+specopt_bgrepeat() { # common
+ specopt_bg
+ specopt_repeat
+}
+specopt_wait() { # common
+ waitforbgprocs
+}
+specopt_shell() { # run command with shell
+ echo "$rest" | sh -s | # and execute the command
+ { [ "$extcheck" ] && $extcheck || cat;}
+}
+specopt() {
+ cmd=`echo $cmd | sed 's/%//'` # strip leading '%'
+ echo ".`echo $cmd | tr '[a-z]' '[A-Z]'` $rest" # show what we got
+ specopt_$cmd # do what they asked for
+}
+
+#
+# wait for background processes to finish
+# and print their output
+# NB: We wait for processes in a FIFO order
+# The order in which they finish does not matter
+#
+waitforbgprocs() {
+ while [ "$bgprocs" ]; do
+ set $bgprocs
+ proc=$1 # get the first one
+ shift 1 # remove it from the list
+ bgprocs="$@"
+ IFS=":"
+ set $proc # split into lineno,pid
+ testline=$1 jobnum=$2 pid=$3
+ unset IFS
+
+ while kill -0 $pid 2>/dev/null; do
+ sleep 1
+ done
+ wait $pid # capture the exit code
+
+ echo ".BG test line $testline/job $jobnum finished (exit code: $?):"
+ echo "==========test:$testline:$jobnum start output=========="
+ cat $OUTDIR/bg$$-$testline-$jobnum
+ echo "==========test:$testline:$jobnum end output=========="
+ rm -f $OUTDIR/bg$$-$testline-$jobnum
+ done
+}
+
+#
+# substitute variables in the test line
+#
+substvars() {
+ sed "
+ s/%t/$test_cnt/g
+ s/%l/$line/g
+ s/%j/$job_cnt/g
+ s/%i/$repeat_cnt/g
+ "
+}
+
+dotest() {
+ echo -n "." >&3
+ test_cnt=$(($test_cnt+1))
+ describe_$cmd # show what we are about to do
+ lrm_$cmd | # and execute the command
+ { [ "$extcheck" ] && $extcheck || cat;}
+}
+runonetest() {
+ eval `echo $rest | substvars` # set parameters
+ if [ "$job_cnt" -le "$bgprocs_num" ]; then
+ echo .BG test line $line/job $job_cnt runs in background
+ dotest > $OUTDIR/bg$$-$line-$job_cnt 2>&1 &
+ bgprocs="$bgprocs $line:$job_cnt:$!"
+ job_cnt=$(($job_cnt+1))
+ else
+ dotest
+ fi
+}
+runtest() {
+ while [ $repeat_cnt -le $repeat_limit ]; do
+ runonetest
+ resetvars # unset all variables
+ repeat_cnt=$(($repeat_cnt+1))
+ done
+ repeat_limit=1 repeat_cnt=1
+}
+
+#
+# run the tests
+#
+bgprocs_num=0 job_cnt=1
+repeat_limit=1 repeat_cnt=1
+line=1
+test_cnt=1
+
+while read cmd rest; do
+ case "$cmd" in
+ "") : empty ;;
+ "#"*) : a comment ;;
+ "%stop") break ;;
+ "%"*) specopt ;;
+ *) runtest ;;
+ esac
+ line=$(($line+1))
+done
+waitforbgprocs
diff --git a/lrm/test/language b/lrm/test/language
new file mode 100644
index 0000000..d2785e8
--- /dev/null
+++ b/lrm/test/language
@@ -0,0 +1,16 @@
+The meta language and how it translates to the lrmadmin options:
+
+list:-L
+add:-A %r %C %T %P
+del:-D %r
+flush:-F %r
+state:-S %r
+info:-I %r
+exec:-E %r %o %t %i %e
+
+classes:-C
+types:-T %C
+classmeta:-O %C
+meta:-M %C %T %P
+provider:-P %C %T
+
diff --git a/lrm/test/lrmadmin-interface b/lrm/test/lrmadmin-interface
new file mode 100644
index 0000000..4eb1656
--- /dev/null
+++ b/lrm/test/lrmadmin-interface
@@ -0,0 +1,43 @@
+lrm_list() {
+ $LRMADMIN -L
+}
+lrm_add() {
+ $LRMADMIN -A ${rsc:-$dflt_rsc} \
+ ${class:-$dflt_class} ${type:-$dflt_type} \
+ ${provider:-$dflt_provider} \
+ $args
+}
+lrm_del() {
+ $LRMADMIN -D ${rsc:-$dflt_rsc}
+}
+lrm_flush() {
+ $LRMADMIN -F ${rsc:-$dflt_rsc}
+}
+lrm_state() {
+ $LRMADMIN -S ${rsc:-$dflt_rsc}
+}
+lrm_info() {
+ $LRMADMIN -I ${rsc:-$dflt_rsc}
+}
+lrm_exec() {
+ $LRMADMIN -E ${rsc:-$dflt_rsc} \
+ ${operation:-$dflt_operation} \
+ ${timeout:-$dflt_timeout} ${interval:-$dflt_interval} \
+ ${targetrc:-$dflt_targetrc} $args
+}
+
+lrm_classes() {
+ $LRMADMIN -C
+}
+lrm_types() {
+ $LRMADMIN -T ${class:-$dflt_class}
+}
+lrm_classmeta() {
+ $LRMADMIN -O ${class:-$dflt_class}
+}
+lrm_meta() {
+ $LRMADMIN -M ${class:-$dflt_class} ${type:-$dflt_type} ${provider:-$dflt_provider}
+}
+lrm_provider() {
+ $LRMADMIN -P ${class:-$dflt_class} ${type:-$dflt_type}
+}
diff --git a/lrm/test/lrmregtest-lsb b/lrm/test/lrmregtest-lsb
new file mode 100644
index 0000000..4692b17
--- /dev/null
+++ b/lrm/test/lrmregtest-lsb
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# WARNING: This script is for LRM regressions tests only
+#
+### BEGIN INIT INFO
+# Provides: lrmregtest
+# Required-Start:
+# Should-Start:
+# Required-Stop:
+# Should-Stop:
+# Default-Start:
+# Default-Stop:
+# Short-Description: LRM regression tests LSB RA
+# Description: LRM regression tests LSB RA
+### END INIT INFO
+
+TYPE=lrmregtest
+# depends on resource-agents and the OCF
+: ${OCF_ROOT:=/usr/lib/ocf}
+. ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs
+
+case "$1" in
+ start)
+ echo -n "Starting $TYPE"
+ ha_pseudo_resource lrmregtest_lsb start
+ ;;
+ stop)
+ echo -n "Shutting down $TYPE"
+ ha_pseudo_resource lrmregtest_lsb stop
+ ;;
+ status)
+ echo -n "Checking for $TYPE"
+ ha_pseudo_resource lrmregtest_lsb monitor
+ if [ $? -eq 0 ]; then
+ echo " running"
+ exit 0
+ else
+ echo " stopped"
+ exit 3
+ fi
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|status}"
+ exit 1
+ ;;
+esac
+
+if [ $? -eq 0 ]; then
+ echo " OK"
+ exit 0
+else
+ echo " failed"
+ exit 1
+fi
diff --git a/lrm/test/lrmregtest.in b/lrm/test/lrmregtest.in
new file mode 100644
index 0000000..001a662
--- /dev/null
+++ b/lrm/test/lrmregtest.in
@@ -0,0 +1,220 @@
+#!/bin/sh
+#
+#
+# lrmregtest OCF RA. Does nothing but wait a few seconds, can be
+# configured to fail occassionally.
+#
+# updated to support the LRM regression testing.
+#
+# Copyright (c) 2007 SUSE LINUX AG, Dejan Muhamedagic
+# All Rights Reserved.
+#
+# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+
+#######################################################################
+# Initialization:
+
+. ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs
+
+#######################################################################
+
+meta_data() {
+ cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="lrmregtest" version="0.9">
+<version>1.0</version>
+
+<longdesc lang="en">
+This is a lrmregtest Resource Agent. Use for LRM regression
+testing.
+</longdesc>
+<shortdesc lang="en">lrmregtest resource agent</shortdesc>
+
+<parameters>
+<parameter name="delay" unique="0">
+<longdesc lang="en">
+How long to delay before each action.
+</longdesc>
+<shortdesc lang="en">Action delay</shortdesc>
+<content type="integer" default="0" />
+</parameter>
+
+<parameter name="check_parallel" unique="0">
+<longdesc lang="en">
+Complain loudly if they try to run us in parallel on the same resource.
+</longdesc>
+<shortdesc lang="en">Report error if run twice at the same time</shortdesc>
+<content type="boolean" default="true" />
+</parameter>
+
+<parameter name="ignore_TERM" unique="0">
+<longdesc lang="en">
+Process the TERM signal and don't exit.
+</longdesc>
+<shortdesc lang="en">No TERM ain't gonna kill us.</shortdesc>
+<content type="boolean" default="false" />
+</parameter>
+
+<parameter name="verbose" unique="0">
+<longdesc lang="en">
+Print more information.
+</longdesc>
+<shortdesc lang="en">Be verbose.</shortdesc>
+<content type="boolean" default="false" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="90" />
+<action name="stop" timeout="100" />
+<action name="monitor" timeout="20" interval="10" depth="0" start-delay="0" />
+<action name="reload" timeout="90" />
+<action name="migrate_to" timeout="100" />
+<action name="migrate_from" timeout="90" />
+<action name="meta-data" timeout="5" />
+<action name="validate-all" timeout="30" />
+</actions>
+</resource-agent>
+END
+}
+
+#######################################################################
+
+# don't exit on TERM, to test that lrmd makes sure that we do exit
+sigterm_handler() {
+ ocf_log info "They use TERM to bring us down. No such luck."
+ return
+}
+
+dummy_usage() {
+ cat <<END
+usage: $0 {start|stop|monitor|migrate_to|migrate_from|validate-all|meta-data}
+
+Expects to have a fully populated OCF RA-compliant environment set.
+END
+}
+
+# signals interrupt slow calls (sleep)
+# this is an approximation (after all it's just a dummy)
+sleepsleep() {
+ delay=$1
+ now=`perl -e 'print time()'`
+ by=$(($now+$delay))
+ while [ $now -lt $by ]; do
+ ocf_log debug "Gonna sleep for $(($by-$now)) seconds..."
+ sleep $(($by-$now))
+ now=`perl -e 'print time()'`
+ done
+}
+dummy_start() {
+ sleepsleep $OCF_RESKEY_delay
+ ha_pseudo_resource lrmregtest_${OCF_RESOURCE_INSTANCE} start
+}
+
+dummy_stop() {
+ sleepsleep $OCF_RESKEY_delay
+ ha_pseudo_resource lrmregtest_${OCF_RESOURCE_INSTANCE} stop
+}
+
+dummy_monitor() {
+ sleepsleep $OCF_RESKEY_delay
+ ha_pseudo_resource lrmregtest_${OCF_RESOURCE_INSTANCE} monitor
+}
+
+dummy_validate() {
+ exit $OC_ERR_UNIMPLEMENTED
+}
+
+verbose() {
+ [ "$OCF_RESKEY_verbose" != 0 ]
+}
+environment() {
+ echo "OCF environment variables:"
+ set | egrep 'OCF_RESKEY|OCF_RESOURCE_INSTANCE'
+}
+invocation() {
+ echo "invoked with args: $@"
+}
+
+: ${OCF_RESKEY_delay=0}
+: ${OCF_RESKEY_check_parallel=1}
+: ${OCF_RESKEY_verbose=0}
+: ${OCF_RESKEY_ignore_TERM=0}
+
+verbose && environment
+
+lockf=`
+ ha_pseudo_resource lrmregtest_${OCF_RESOURCE_INSTANCE} print |
+ sed 's/$/.lock/'
+`
+
+check4parallel() {
+ if [ -f "$lockf" ] && kill -0 `cat $lockf` 2>/dev/null
+ then
+ ocf_log err "There is another instance of ${OCF_RESOURCE_INSTANCE} running: pid `cat $lockf`."
+ exit $OCF_ERR_GENERIC
+ fi
+}
+
+[ "$OCF_RESKEY_check_parallel" = 1 ] &&
+ check4parallel
+
+[ "$OCF_RESKEY_ignore_TERM" = 1 ] &&
+ trap sigterm_handler TERM
+
+echo $$ > $lockf
+trap "rm -f $lockf" EXIT
+
+verbose && invocation $@
+
+case $__OCF_ACTION in
+meta-data) meta_data
+ exit $OCF_SUCCESS
+ ;;
+start) dummy_start;;
+stop) dummy_stop;;
+monitor) dummy_monitor;;
+migrate_to) ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrate_to}."
+ dummy_stop
+ ;;
+migrate_from) ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrated_from}."
+ dummy_start
+ ;;
+reload) ocf_log err "Reloading..."
+ dummy_start
+ ;;
+validate-all) dummy_validate;;
+usage|help) dummy_usage
+ exit $OCF_SUCCESS
+ ;;
+*) dummy_usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+esac
+rc=$?
+ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
+exit $rc
+
diff --git a/lrm/test/plugintest.c b/lrm/test/plugintest.c
new file mode 100644
index 0000000..d25c46d
--- /dev/null
+++ b/lrm/test/plugintest.c
@@ -0,0 +1,84 @@
+/* File: plugintest.c
+ * Description: A small,simple tool to test RA execution plugin
+ *
+ * Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+ * Copyright (c) 2004 International Business Machines
+ *
+ * Todo: security verification
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 software 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pils/plugin.h>
+#include <pils/generic.h>
+#include <lrm/raexec.h>
+
+static void
+g_print_item(gpointer data, gpointer user_data)
+{
+ printf("%s\n", (char*)data);
+}
+
+
+int main(void)
+{
+ PILPluginUniv * PluginLoadingSystem = NULL;
+ GHashTable * RAExecFuncs = NULL;
+ GList * ratype_list;
+ struct RAExecOps * RAExec;
+ /*
+ GHashTable * cmd_params;
+ */
+ int ret;
+
+ PILGenericIfMgmtRqst RegisterRqsts[]= {
+ {"RAExec", &RAExecFuncs, NULL, NULL, NULL},
+ { NULL, NULL, NULL, NULL, NULL} };
+
+ PluginLoadingSystem = NewPILPluginUniv ("/usr/lib/heartbeat/plugins");
+
+ PILLoadPlugin(PluginLoadingSystem , "InterfaceMgr", "generic" , &RegisterRqsts);
+
+ PILLoadPlugin(PluginLoadingSystem , "RAExec", "ocf", NULL);
+ RAExec = g_hash_table_lookup(RAExecFuncs,"ocf");
+ ret = RAExec->get_resource_list(&ratype_list);
+ printf("length=%d\n", g_list_length(ratype_list));
+ if (ret >= 0) {
+ g_list_foreach(ratype_list, g_print_item, NULL);
+ }
+
+ /*
+ PILLoadPlugin(PluginLoadingSystem , "RAExec", "lsb", NULL);
+ RAExec = g_hash_table_lookup(RAExecFuncs,"lsb");
+ cmd_params = g_hash_table_new(g_str_hash, g_str_equal);
+ g_hash_table_insert(cmd_params, g_strdup("1"), g_strdup("par1"));
+ g_hash_table_insert(cmd_params, g_strdup("2"), g_strdup("par2"));
+ ret = RAExec->execra("/tmp/test.sh", "start", cmd_params,NULL);
+ */
+
+ /* For test the dealing with directory appended to RA */
+ /*
+ PILLoadPlugin(PluginLoadingSystem , "RAExec", "ocf", NULL);
+ RAExec = g_hash_table_lookup(RAExecFuncs,"ocf");
+ if (0>RAExec->execra("/root/linux-ha-checkout/linux-ha/lrm/test.sh",
+ "stop",NULL,NULL, TRUE, &key))
+ */
+ printf("execra result: ret = %d\n", ret);
+ return -1;
+}
diff --git a/lrm/test/regression.sh.in b/lrm/test/regression.sh.in
new file mode 100755
index 0000000..550321e
--- /dev/null
+++ b/lrm/test/regression.sh.in
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic@suse.de>
+ #
+ # This program is free software; you can redistribute it and/or
+ # modify it under the terms of the GNU 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 software 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
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU 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
+ #
+
+OCF_ROOT=@OCF_ROOT_DIR@
+export OCF_ROOT
+if [ -z "$OCF_ROOT" ]; then
+ [ -d /usr/lib/ocf ] && OCF_ROOT=/usr/lib/ocf
+fi
+if [ ! -d "$OCF_ROOT" ]; then
+ echo "OCF_ROOT environment variable not set"
+ exit 2
+fi
+
+TESTDIR=${TESTDIR:-testcases}
+DFLT_TESTSET=basicset
+OUTDIR=${OUTDIR:-output}
+LRMD_OUTF="$OUTDIR/lrmd.out"
+LRMD_LOGF="$OUTDIR/lrmd.log"
+LRMD_DEBUGF="$OUTDIR/lrmd.debug"
+OUTF="$OUTDIR/regression.out"
+LRMADMIN="@sbindir@/lrmadmin"
+LRMD_OPTS="-vvv"
+STONITHD_OPTS="-at"
+DIFF_OPTS="--ignore-all-space -U 1"
+common_filter=$TESTDIR/common.filter
+common_exclf=$TESTDIR/common.excl
+OCF_RA=$OCF_ROOT/resource.d/heartbeat/lrmregtest
+LSB_RA=@LSB_RA_DIR@/lrmregtest
+export OUTDIR TESTDIR LRMADMIN
+
+logmsg() {
+ echo "`date`: $*" | tee -a $LRMD_DEBUGF | tee -a $LRMD_LOGF
+}
+abspath() {
+ echo $1 | grep -qs "^/" &&
+ echo $1 ||
+ echo `pwd`/$1
+}
+
+usage() {
+ cat<<EOF
+
+usage: $0 [-q] [testcase...|set:testset]
+
+Test lrmd using supplied testcases. If none are given,
+set:basicset is used. All testcases and sets are in testcases/.
+See also README.regression for description.
+
+-q: quiet operation (no progress shown)
+
+EOF
+exit 2
+}
+
+if [ `id -u` != 0 ]; then
+ echo "sorry, but i talk to root only"
+ exit 2
+fi
+cd `dirname $0`
+if [ ! -d "$TESTDIR" ]; then
+ echo "$0: $TESTDIR does not exit"
+ usage
+fi
+
+which xmllint >/dev/null 2>&1 || {
+ echo "WARNING: xmllint not available, some of the tests may fail"
+}
+
+rm -f $LRMD_LOGF $LRMD_DEBUGF
+
+# make lrmd log to our files only
+HA_logfile=`abspath $LRMD_LOGF`
+HA_debugfile=`abspath $LRMD_DEBUGF`
+HA_use_logd=no
+HA_logfacility=""
+export HA_logfile HA_debugfile HA_use_logd HA_logfacility
+
+mkdir -p $OUTDIR
+. ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs
+
+args=`getopt hq $*`
+[ $? -ne 0 ] && usage
+eval set -- "$args"
+
+SILENT=""
+while [ x"$1" != x ]; do
+ case "$1" in
+ -h) usage;;
+ -q) SILENT=1;;
+ --) shift 1; break;;
+ *) usage;;
+ esac
+ shift 1
+done
+
+exec >$OUTF 2>&1
+if [ "$SILENT" = 1 ]; then
+ exec 3>/dev/null
+else
+ exec 3>/dev/tty
+fi
+
+start_stonithd() {
+ echo "starting stonithd" >&3
+ $HA_BIN/stonithd -s 2>/dev/null
+ if [ $? -ne 0 ]; then
+ STOP_STONITHD=1
+ $HA_BIN/stonithd $STONITHD_OPTS
+ sleep 1
+ $HA_BIN/stonithd -s 2>/dev/null
+ else
+ STOP_STONITHD=
+ fi
+}
+stop_stonithd() {
+ if [ "$STOP_STONITHD" ]; then
+ echo "stopping stonithd" >&3
+ $HA_BIN/stonithd -k >/dev/null 2>&1
+ fi
+}
+start_lrmd() {
+ echo "starting lrmd" >&3
+ $HA_BIN/lrmd -s 2>/dev/null
+ if [ $? -eq 3 ]; then
+ #strace -o /tmp/lrmd.trc $HA_BIN/lrmd $LRMD_OPTS >$LRMD_OUTF 2>&1 &
+ $HA_BIN/lrmd $LRMD_OPTS >$LRMD_OUTF 2>&1 &
+ sleep 1
+ $HA_BIN/lrmd -s 2>/dev/null
+ else
+ echo "lrmd already running; can't proceed" >&3
+ return 2
+ fi
+}
+stop_lrmd() {
+ echo "stopping lrmd" >&3
+ $HA_BIN/lrmd -k
+}
+cp_ra() {
+ cp -p lrmregtest $OCF_RA
+ chmod +x $OCF_RA
+ cp -p lrmregtest-lsb $LSB_RA
+ chmod +x $LSB_RA
+}
+rm_ra() {
+ rm -f $OCF_RA $LSB_RA
+}
+
+cp_ra
+start_lrmd || exit $?
+# start_stonithd || exit $?
+trap "stop_lrmd; stop_stonithd; rm_ra" EXIT
+
+setenvironment() {
+ filterf=$TESTDIR/$testcase.filter
+ exclf=$TESTDIR/$testcase.excl
+ log_filter=$TESTDIR/$testcase.log_filter
+ expf=$TESTDIR/$testcase.exp
+ outf=$OUTDIR/$testcase.out
+ difff=$OUTDIR/$testcase.diff
+}
+
+filter_output() {
+ { [ -x $common_filter ] && $common_filter || cat;} |
+ { [ -f $common_exclf ] && egrep -vf $common_exclf || cat;} |
+ { [ -x $filterf ] && $filterf || cat;} |
+ { [ -f $exclf ] && egrep -vf $exclf || cat;}
+}
+
+dumpcase() {
+ cat<<EOF
+----------
+testcase $testcase failed
+output is in $outf
+diff (from $difff):
+`cat $difff`
+----------
+EOF
+}
+
+runtestcase() {
+ setenvironment
+ echo -n "$testcase" >&3
+ logmsg "BEGIN testcase $testcase"
+ ./evaltest.sh < $TESTDIR/$testcase > $outf 2>&1
+
+ filter_output < $outf |
+ if [ "$prepare" ]; then
+ echo " saving to expect file" >&3
+ cat > $expf
+ else
+ echo -n " checking..." >&3
+ diff $DIFF_OPTS $expf - > $difff
+ if [ $? -ne 0 ]; then
+ echo " FAIL" >&3
+ dumpcase
+ return 1
+ else
+ echo " PASS" >&3
+ rm -f $outf $difff
+ fi
+ fi
+ sed -n "/BEGIN testcase $testcase/,\$p" $LRMD_LOGF |
+ { [ -x $log_filter ] && $log_filter || cat;} |
+ egrep '(CRIT|ERROR):'
+ logmsg "END testcase $testcase"
+}
+
+[ "$1" = prepare ] && { prepare=1; shift 1;}
+[ $# -eq 0 ] && set "set:$DFLT_TESTSET"
+
+for a; do
+ if [ "$a" -a -f "$TESTDIR/$a" ]; then
+ testcase=$a
+ runtestcase
+ else
+ echo "$a" | grep -q "^set:" &&
+ TESTSET=$TESTDIR/`echo $a | sed 's/set://'`
+ while read testcase; do
+ runtestcase
+ done < $TESTSET
+ fi
+done
+
+if egrep -wv '(BEGIN|END) testcase' $OUTF >/dev/null
+then
+ echo "seems like some tests failed or else something not expected"
+ echo "check $OUTF and diff files in $OUTDIR"
+ echo "in case you wonder what lrmd was doing, read $LRMD_LOGF and $LRMD_DEBUGF"
+ exit 1
+else
+ rm -f $OUTF $LRMD_OUTF
+fi >&3
diff --git a/lrm/test/testcases/BSC b/lrm/test/testcases/BSC
new file mode 100644
index 0000000..157fb6c
--- /dev/null
+++ b/lrm/test/testcases/BSC
@@ -0,0 +1,4 @@
+rscmgmt
+metadata
+rscexec
+stonith
diff --git a/lrm/test/testcases/Makefile.am b/lrm/test/testcases/Makefile.am
new file mode 100644
index 0000000..49728d9
--- /dev/null
+++ b/lrm/test/testcases/Makefile.am
@@ -0,0 +1,27 @@
+#
+# Author: Sun Jiang Dong <sunjd@cn.ibm.com>
+# Copyright (c) 2004 International Business Machines
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+MAINTAINERCLEANFILES = Makefile.in
+
+testcasesdir = $(datadir)/$(PACKAGE_NAME)/lrmtest/testcases
+testcases_SCRIPTS = common.filter ra-list.sh rscmgmt.log_filter xmllint.sh
+testcases_DATA = BSC basicset metadata metadata.exp rscexec \
+ rscexec.exp rscmgmt rscmgmt.exp \
+ stonith stonith.exp
+# shouldn't need this next line...
+EXTRA_DIST = $(testcases_SCRIPTS) $(testcases_DATA)
diff --git a/lrm/test/testcases/basicset b/lrm/test/testcases/basicset
new file mode 100644
index 0000000..62b9c04
--- /dev/null
+++ b/lrm/test/testcases/basicset
@@ -0,0 +1,6 @@
+rscmgmt
+metadata
+rscexec
+stonith
+serialize
+flood
diff --git a/lrm/test/testcases/common.filter b/lrm/test/testcases/common.filter
new file mode 100755
index 0000000..f95e9d8
--- /dev/null
+++ b/lrm/test/testcases/common.filter
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+sed '
+/^lrmadmin/s/([0-9][0-9]*)/()/
+/^lrmadmin/s/^lrmadmin[^:]*: [^ ]* //
+s/call_id=[0-9][0-9]*/call_id=(removed)/
+/run at:/d
+/last rc change at:/d
+/queue time:/d
+' |
+awk '
+/Waiting for lrmd to callback.../ { n=1; next; }
+n==1 && /----------------operation--------------/ { n++; next; }
+n==2 && /type:/ { op=$0; sub("type:","",op); next }
+n==2 && /operation status:/ { desc=$0; sub("operation status:","",desc); next }
+n==2 && /op_status:/ { stat=$0; sub("op_status: *","",stat); next }
+n==2 && /return code:/ { rc=$0; sub("return code: *","",rc); next }
+n==2 && /output data:/ { n++; next; }
+n==3 && /---------------------------------------/ {
+ printf("> %s %s (status=%s,rc=%s): %s\n",op,desc,stat,rc,substr(output,2));
+ n=0;
+ output="";
+ next;
+}
+n==3 && $1!="" { output=output"/"$0; next; }
+{ print }
+'
diff --git a/lrm/test/testcases/flood b/lrm/test/testcases/flood
new file mode 100644
index 0000000..de6d742
--- /dev/null
+++ b/lrm/test/testcases/flood
@@ -0,0 +1,19 @@
+# 30 secs should be enough even on slow machines
+list
+%setenv dflt_timeout=30000
+# add 64 resources
+%repeat 64
+add rsc=r%i args="delay=0"
+# start all in background
+%bgrepeat 64
+exec rsc=r%i operation=start
+%sleep 1
+# and run a monitor on all in background
+%bgrepeat 64
+exec rsc=r%i operation=monitor
+%sleep 1
+# finally, stop all
+%repeat 64
+exec rsc=r%i operation=stop
+%repeat 64
+del rsc=r%i
diff --git a/lrm/test/testcases/flood.exp b/lrm/test/testcases/flood.exp
new file mode 100644
index 0000000..cf8a2bb
--- /dev/null
+++ b/lrm/test/testcases/flood.exp
@@ -0,0 +1,1354 @@
+.TRY List resources
+Currently no resources are managed by LRM.
+.SETENV dflt_timeout=30000
+.REPEAT 64
+.TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r2 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r3 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r4 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r5 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r6 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r7 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r8 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r9 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r10 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r11 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r12 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r13 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r14 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r15 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r16 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r17 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r18 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r19 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r20 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r21 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r22 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r23 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r24 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r25 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r26 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r27 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r28 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r29 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r30 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r31 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r32 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r33 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r34 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r35 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r36 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r37 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r38 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r39 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r40 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r41 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r42 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r43 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r44 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r45 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r46 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r47 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r48 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r49 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r50 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r51 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r52 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r53 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r54 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r55 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r56 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r57 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r58 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r59 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r60 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r61 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r62 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r63 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY Add resource r64 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.BGREPEAT 64
+.BG test line 9/job 1 runs in background
+.BG test line 9/job 2 runs in background
+.BG test line 9/job 3 runs in background
+.BG test line 9/job 4 runs in background
+.BG test line 9/job 5 runs in background
+.BG test line 9/job 6 runs in background
+.BG test line 9/job 7 runs in background
+.BG test line 9/job 8 runs in background
+.BG test line 9/job 9 runs in background
+.BG test line 9/job 10 runs in background
+.BG test line 9/job 11 runs in background
+.BG test line 9/job 12 runs in background
+.BG test line 9/job 13 runs in background
+.BG test line 9/job 14 runs in background
+.BG test line 9/job 15 runs in background
+.BG test line 9/job 16 runs in background
+.BG test line 9/job 17 runs in background
+.BG test line 9/job 18 runs in background
+.BG test line 9/job 19 runs in background
+.BG test line 9/job 20 runs in background
+.BG test line 9/job 21 runs in background
+.BG test line 9/job 22 runs in background
+.BG test line 9/job 23 runs in background
+.BG test line 9/job 24 runs in background
+.BG test line 9/job 25 runs in background
+.BG test line 9/job 26 runs in background
+.BG test line 9/job 27 runs in background
+.BG test line 9/job 28 runs in background
+.BG test line 9/job 29 runs in background
+.BG test line 9/job 30 runs in background
+.BG test line 9/job 31 runs in background
+.BG test line 9/job 32 runs in background
+.BG test line 9/job 33 runs in background
+.BG test line 9/job 34 runs in background
+.BG test line 9/job 35 runs in background
+.BG test line 9/job 36 runs in background
+.BG test line 9/job 37 runs in background
+.BG test line 9/job 38 runs in background
+.BG test line 9/job 39 runs in background
+.BG test line 9/job 40 runs in background
+.BG test line 9/job 41 runs in background
+.BG test line 9/job 42 runs in background
+.BG test line 9/job 43 runs in background
+.BG test line 9/job 44 runs in background
+.BG test line 9/job 45 runs in background
+.BG test line 9/job 46 runs in background
+.BG test line 9/job 47 runs in background
+.BG test line 9/job 48 runs in background
+.BG test line 9/job 49 runs in background
+.BG test line 9/job 50 runs in background
+.BG test line 9/job 51 runs in background
+.BG test line 9/job 52 runs in background
+.BG test line 9/job 53 runs in background
+.BG test line 9/job 54 runs in background
+.BG test line 9/job 55 runs in background
+.BG test line 9/job 56 runs in background
+.BG test line 9/job 57 runs in background
+.BG test line 9/job 58 runs in background
+.BG test line 9/job 59 runs in background
+.BG test line 9/job 60 runs in background
+.BG test line 9/job 61 runs in background
+.BG test line 9/job 62 runs in background
+.BG test line 9/job 63 runs in background
+.BG test line 9/job 64 runs in background
+.SLEEP 1
+.BGREPEAT 64
+.BG test line 13/job 1 runs in background
+.BG test line 13/job 2 runs in background
+.BG test line 13/job 3 runs in background
+.BG test line 13/job 4 runs in background
+.BG test line 13/job 5 runs in background
+.BG test line 13/job 6 runs in background
+.BG test line 13/job 7 runs in background
+.BG test line 13/job 8 runs in background
+.BG test line 13/job 9 runs in background
+.BG test line 13/job 10 runs in background
+.BG test line 13/job 11 runs in background
+.BG test line 13/job 12 runs in background
+.BG test line 13/job 13 runs in background
+.BG test line 13/job 14 runs in background
+.BG test line 13/job 15 runs in background
+.BG test line 13/job 16 runs in background
+.BG test line 13/job 17 runs in background
+.BG test line 13/job 18 runs in background
+.BG test line 13/job 19 runs in background
+.BG test line 13/job 20 runs in background
+.BG test line 13/job 21 runs in background
+.BG test line 13/job 22 runs in background
+.BG test line 13/job 23 runs in background
+.BG test line 13/job 24 runs in background
+.BG test line 13/job 25 runs in background
+.BG test line 13/job 26 runs in background
+.BG test line 13/job 27 runs in background
+.BG test line 13/job 28 runs in background
+.BG test line 13/job 29 runs in background
+.BG test line 13/job 30 runs in background
+.BG test line 13/job 31 runs in background
+.BG test line 13/job 32 runs in background
+.BG test line 13/job 33 runs in background
+.BG test line 13/job 34 runs in background
+.BG test line 13/job 35 runs in background
+.BG test line 13/job 36 runs in background
+.BG test line 13/job 37 runs in background
+.BG test line 13/job 38 runs in background
+.BG test line 13/job 39 runs in background
+.BG test line 13/job 40 runs in background
+.BG test line 13/job 41 runs in background
+.BG test line 13/job 42 runs in background
+.BG test line 13/job 43 runs in background
+.BG test line 13/job 44 runs in background
+.BG test line 13/job 45 runs in background
+.BG test line 13/job 46 runs in background
+.BG test line 13/job 47 runs in background
+.BG test line 13/job 48 runs in background
+.BG test line 13/job 49 runs in background
+.BG test line 13/job 50 runs in background
+.BG test line 13/job 51 runs in background
+.BG test line 13/job 52 runs in background
+.BG test line 13/job 53 runs in background
+.BG test line 13/job 54 runs in background
+.BG test line 13/job 55 runs in background
+.BG test line 13/job 56 runs in background
+.BG test line 13/job 57 runs in background
+.BG test line 13/job 58 runs in background
+.BG test line 13/job 59 runs in background
+.BG test line 13/job 60 runs in background
+.BG test line 13/job 61 runs in background
+.BG test line 13/job 62 runs in background
+.BG test line 13/job 63 runs in background
+.BG test line 13/job 64 runs in background
+.SLEEP 1
+.REPEAT 64
+.TRY Exec r1 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r2 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r3 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r4 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r5 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r6 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r7 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r8 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r9 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r10 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r11 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r12 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r13 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r14 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r15 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r16 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r17 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r18 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r19 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r20 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r21 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r22 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r23 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r24 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r25 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r26 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r27 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r28 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r29 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r30 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r31 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r32 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r33 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r34 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r35 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r36 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r37 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r38 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r39 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r40 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r41 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r42 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r43 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r44 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r45 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r46 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r47 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r48 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r49 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r50 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r51 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r52 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r53 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r54 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r55 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r56 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r57 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r58 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r59 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r60 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r61 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r62 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r63 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec r64 op=stop timeout=30000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.REPEAT 64
+.TRY Delete resource r1
+Succeeded in deleting this resource.
+.TRY Delete resource r2
+Succeeded in deleting this resource.
+.TRY Delete resource r3
+Succeeded in deleting this resource.
+.TRY Delete resource r4
+Succeeded in deleting this resource.
+.TRY Delete resource r5
+Succeeded in deleting this resource.
+.TRY Delete resource r6
+Succeeded in deleting this resource.
+.TRY Delete resource r7
+Succeeded in deleting this resource.
+.TRY Delete resource r8
+Succeeded in deleting this resource.
+.TRY Delete resource r9
+Succeeded in deleting this resource.
+.TRY Delete resource r10
+Succeeded in deleting this resource.
+.TRY Delete resource r11
+Succeeded in deleting this resource.
+.TRY Delete resource r12
+Succeeded in deleting this resource.
+.TRY Delete resource r13
+Succeeded in deleting this resource.
+.TRY Delete resource r14
+Succeeded in deleting this resource.
+.TRY Delete resource r15
+Succeeded in deleting this resource.
+.TRY Delete resource r16
+Succeeded in deleting this resource.
+.TRY Delete resource r17
+Succeeded in deleting this resource.
+.TRY Delete resource r18
+Succeeded in deleting this resource.
+.TRY Delete resource r19
+Succeeded in deleting this resource.
+.TRY Delete resource r20
+Succeeded in deleting this resource.
+.TRY Delete resource r21
+Succeeded in deleting this resource.
+.TRY Delete resource r22
+Succeeded in deleting this resource.
+.TRY Delete resource r23
+Succeeded in deleting this resource.
+.TRY Delete resource r24
+Succeeded in deleting this resource.
+.TRY Delete resource r25
+Succeeded in deleting this resource.
+.TRY Delete resource r26
+Succeeded in deleting this resource.
+.TRY Delete resource r27
+Succeeded in deleting this resource.
+.TRY Delete resource r28
+Succeeded in deleting this resource.
+.TRY Delete resource r29
+Succeeded in deleting this resource.
+.TRY Delete resource r30
+Succeeded in deleting this resource.
+.TRY Delete resource r31
+Succeeded in deleting this resource.
+.TRY Delete resource r32
+Succeeded in deleting this resource.
+.TRY Delete resource r33
+Succeeded in deleting this resource.
+.TRY Delete resource r34
+Succeeded in deleting this resource.
+.TRY Delete resource r35
+Succeeded in deleting this resource.
+.TRY Delete resource r36
+Succeeded in deleting this resource.
+.TRY Delete resource r37
+Succeeded in deleting this resource.
+.TRY Delete resource r38
+Succeeded in deleting this resource.
+.TRY Delete resource r39
+Succeeded in deleting this resource.
+.TRY Delete resource r40
+Succeeded in deleting this resource.
+.TRY Delete resource r41
+Succeeded in deleting this resource.
+.TRY Delete resource r42
+Succeeded in deleting this resource.
+.TRY Delete resource r43
+Succeeded in deleting this resource.
+.TRY Delete resource r44
+Succeeded in deleting this resource.
+.TRY Delete resource r45
+Succeeded in deleting this resource.
+.TRY Delete resource r46
+Succeeded in deleting this resource.
+.TRY Delete resource r47
+Succeeded in deleting this resource.
+.TRY Delete resource r48
+Succeeded in deleting this resource.
+.TRY Delete resource r49
+Succeeded in deleting this resource.
+.TRY Delete resource r50
+Succeeded in deleting this resource.
+.TRY Delete resource r51
+Succeeded in deleting this resource.
+.TRY Delete resource r52
+Succeeded in deleting this resource.
+.TRY Delete resource r53
+Succeeded in deleting this resource.
+.TRY Delete resource r54
+Succeeded in deleting this resource.
+.TRY Delete resource r55
+Succeeded in deleting this resource.
+.TRY Delete resource r56
+Succeeded in deleting this resource.
+.TRY Delete resource r57
+Succeeded in deleting this resource.
+.TRY Delete resource r58
+Succeeded in deleting this resource.
+.TRY Delete resource r59
+Succeeded in deleting this resource.
+.TRY Delete resource r60
+Succeeded in deleting this resource.
+.TRY Delete resource r61
+Succeeded in deleting this resource.
+.TRY Delete resource r62
+Succeeded in deleting this resource.
+.TRY Delete resource r63
+Succeeded in deleting this resource.
+.TRY Delete resource r64
+Succeeded in deleting this resource.
+.BG test line 9/job 1 finished (exit code: 0):
+==========test:9:1 start output==========
+.TRY Exec r1 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:1 end output==========
+.BG test line 9/job 2 finished (exit code: 0):
+==========test:9:2 start output==========
+.TRY Exec r2 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:2 end output==========
+.BG test line 9/job 3 finished (exit code: 0):
+==========test:9:3 start output==========
+.TRY Exec r3 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:3 end output==========
+.BG test line 9/job 4 finished (exit code: 0):
+==========test:9:4 start output==========
+.TRY Exec r4 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:4 end output==========
+.BG test line 9/job 5 finished (exit code: 0):
+==========test:9:5 start output==========
+.TRY Exec r5 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:5 end output==========
+.BG test line 9/job 6 finished (exit code: 0):
+==========test:9:6 start output==========
+.TRY Exec r6 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:6 end output==========
+.BG test line 9/job 7 finished (exit code: 0):
+==========test:9:7 start output==========
+.TRY Exec r7 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:7 end output==========
+.BG test line 9/job 8 finished (exit code: 0):
+==========test:9:8 start output==========
+.TRY Exec r8 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:8 end output==========
+.BG test line 9/job 9 finished (exit code: 0):
+==========test:9:9 start output==========
+.TRY Exec r9 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:9 end output==========
+.BG test line 9/job 10 finished (exit code: 0):
+==========test:9:10 start output==========
+.TRY Exec r10 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:10 end output==========
+.BG test line 9/job 11 finished (exit code: 0):
+==========test:9:11 start output==========
+.TRY Exec r11 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:11 end output==========
+.BG test line 9/job 12 finished (exit code: 0):
+==========test:9:12 start output==========
+.TRY Exec r12 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:12 end output==========
+.BG test line 9/job 13 finished (exit code: 0):
+==========test:9:13 start output==========
+.TRY Exec r13 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:13 end output==========
+.BG test line 9/job 14 finished (exit code: 0):
+==========test:9:14 start output==========
+.TRY Exec r14 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:14 end output==========
+.BG test line 9/job 15 finished (exit code: 0):
+==========test:9:15 start output==========
+.TRY Exec r15 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:15 end output==========
+.BG test line 9/job 16 finished (exit code: 0):
+==========test:9:16 start output==========
+.TRY Exec r16 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:16 end output==========
+.BG test line 9/job 17 finished (exit code: 0):
+==========test:9:17 start output==========
+.TRY Exec r17 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:17 end output==========
+.BG test line 9/job 18 finished (exit code: 0):
+==========test:9:18 start output==========
+.TRY Exec r18 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:18 end output==========
+.BG test line 9/job 19 finished (exit code: 0):
+==========test:9:19 start output==========
+.TRY Exec r19 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:19 end output==========
+.BG test line 9/job 20 finished (exit code: 0):
+==========test:9:20 start output==========
+.TRY Exec r20 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:20 end output==========
+.BG test line 9/job 21 finished (exit code: 0):
+==========test:9:21 start output==========
+.TRY Exec r21 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:21 end output==========
+.BG test line 9/job 22 finished (exit code: 0):
+==========test:9:22 start output==========
+.TRY Exec r22 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:22 end output==========
+.BG test line 9/job 23 finished (exit code: 0):
+==========test:9:23 start output==========
+.TRY Exec r23 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:23 end output==========
+.BG test line 9/job 24 finished (exit code: 0):
+==========test:9:24 start output==========
+.TRY Exec r24 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:24 end output==========
+.BG test line 9/job 25 finished (exit code: 0):
+==========test:9:25 start output==========
+.TRY Exec r25 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:25 end output==========
+.BG test line 9/job 26 finished (exit code: 0):
+==========test:9:26 start output==========
+.TRY Exec r26 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:26 end output==========
+.BG test line 9/job 27 finished (exit code: 0):
+==========test:9:27 start output==========
+.TRY Exec r27 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:27 end output==========
+.BG test line 9/job 28 finished (exit code: 0):
+==========test:9:28 start output==========
+.TRY Exec r28 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:28 end output==========
+.BG test line 9/job 29 finished (exit code: 0):
+==========test:9:29 start output==========
+.TRY Exec r29 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:29 end output==========
+.BG test line 9/job 30 finished (exit code: 0):
+==========test:9:30 start output==========
+.TRY Exec r30 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:30 end output==========
+.BG test line 9/job 31 finished (exit code: 0):
+==========test:9:31 start output==========
+.TRY Exec r31 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:31 end output==========
+.BG test line 9/job 32 finished (exit code: 0):
+==========test:9:32 start output==========
+.TRY Exec r32 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:32 end output==========
+.BG test line 9/job 33 finished (exit code: 0):
+==========test:9:33 start output==========
+.TRY Exec r33 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:33 end output==========
+.BG test line 9/job 34 finished (exit code: 0):
+==========test:9:34 start output==========
+.TRY Exec r34 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:34 end output==========
+.BG test line 9/job 35 finished (exit code: 0):
+==========test:9:35 start output==========
+.TRY Exec r35 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:35 end output==========
+.BG test line 9/job 36 finished (exit code: 0):
+==========test:9:36 start output==========
+.TRY Exec r36 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:36 end output==========
+.BG test line 9/job 37 finished (exit code: 0):
+==========test:9:37 start output==========
+.TRY Exec r37 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:37 end output==========
+.BG test line 9/job 38 finished (exit code: 0):
+==========test:9:38 start output==========
+.TRY Exec r38 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:38 end output==========
+.BG test line 9/job 39 finished (exit code: 0):
+==========test:9:39 start output==========
+.TRY Exec r39 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:39 end output==========
+.BG test line 9/job 40 finished (exit code: 0):
+==========test:9:40 start output==========
+.TRY Exec r40 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:40 end output==========
+.BG test line 9/job 41 finished (exit code: 0):
+==========test:9:41 start output==========
+.TRY Exec r41 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:41 end output==========
+.BG test line 9/job 42 finished (exit code: 0):
+==========test:9:42 start output==========
+.TRY Exec r42 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:42 end output==========
+.BG test line 9/job 43 finished (exit code: 0):
+==========test:9:43 start output==========
+.TRY Exec r43 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:43 end output==========
+.BG test line 9/job 44 finished (exit code: 0):
+==========test:9:44 start output==========
+.TRY Exec r44 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:44 end output==========
+.BG test line 9/job 45 finished (exit code: 0):
+==========test:9:45 start output==========
+.TRY Exec r45 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:45 end output==========
+.BG test line 9/job 46 finished (exit code: 0):
+==========test:9:46 start output==========
+.TRY Exec r46 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:46 end output==========
+.BG test line 9/job 47 finished (exit code: 0):
+==========test:9:47 start output==========
+.TRY Exec r47 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:47 end output==========
+.BG test line 9/job 48 finished (exit code: 0):
+==========test:9:48 start output==========
+.TRY Exec r48 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:48 end output==========
+.BG test line 9/job 49 finished (exit code: 0):
+==========test:9:49 start output==========
+.TRY Exec r49 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:49 end output==========
+.BG test line 9/job 50 finished (exit code: 0):
+==========test:9:50 start output==========
+.TRY Exec r50 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:50 end output==========
+.BG test line 9/job 51 finished (exit code: 0):
+==========test:9:51 start output==========
+.TRY Exec r51 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:51 end output==========
+.BG test line 9/job 52 finished (exit code: 0):
+==========test:9:52 start output==========
+.TRY Exec r52 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:52 end output==========
+.BG test line 9/job 53 finished (exit code: 0):
+==========test:9:53 start output==========
+.TRY Exec r53 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:53 end output==========
+.BG test line 9/job 54 finished (exit code: 0):
+==========test:9:54 start output==========
+.TRY Exec r54 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:54 end output==========
+.BG test line 9/job 55 finished (exit code: 0):
+==========test:9:55 start output==========
+.TRY Exec r55 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:55 end output==========
+.BG test line 9/job 56 finished (exit code: 0):
+==========test:9:56 start output==========
+.TRY Exec r56 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:56 end output==========
+.BG test line 9/job 57 finished (exit code: 0):
+==========test:9:57 start output==========
+.TRY Exec r57 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:57 end output==========
+.BG test line 9/job 58 finished (exit code: 0):
+==========test:9:58 start output==========
+.TRY Exec r58 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:58 end output==========
+.BG test line 9/job 59 finished (exit code: 0):
+==========test:9:59 start output==========
+.TRY Exec r59 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:59 end output==========
+.BG test line 9/job 60 finished (exit code: 0):
+==========test:9:60 start output==========
+.TRY Exec r60 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:60 end output==========
+.BG test line 9/job 61 finished (exit code: 0):
+==========test:9:61 start output==========
+.TRY Exec r61 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:61 end output==========
+.BG test line 9/job 62 finished (exit code: 0):
+==========test:9:62 start output==========
+.TRY Exec r62 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:62 end output==========
+.BG test line 9/job 63 finished (exit code: 0):
+==========test:9:63 start output==========
+.TRY Exec r63 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:63 end output==========
+.BG test line 9/job 64 finished (exit code: 0):
+==========test:9:64 start output==========
+.TRY Exec r64 op=start timeout=30000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:9:64 end output==========
+.BG test line 13/job 1 finished (exit code: 0):
+==========test:13:1 start output==========
+.TRY Exec r1 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:1 end output==========
+.BG test line 13/job 2 finished (exit code: 0):
+==========test:13:2 start output==========
+.TRY Exec r2 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:2 end output==========
+.BG test line 13/job 3 finished (exit code: 0):
+==========test:13:3 start output==========
+.TRY Exec r3 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:3 end output==========
+.BG test line 13/job 4 finished (exit code: 0):
+==========test:13:4 start output==========
+.TRY Exec r4 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:4 end output==========
+.BG test line 13/job 5 finished (exit code: 0):
+==========test:13:5 start output==========
+.TRY Exec r5 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:5 end output==========
+.BG test line 13/job 6 finished (exit code: 0):
+==========test:13:6 start output==========
+.TRY Exec r6 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:6 end output==========
+.BG test line 13/job 7 finished (exit code: 0):
+==========test:13:7 start output==========
+.TRY Exec r7 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:7 end output==========
+.BG test line 13/job 8 finished (exit code: 0):
+==========test:13:8 start output==========
+.TRY Exec r8 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:8 end output==========
+.BG test line 13/job 9 finished (exit code: 0):
+==========test:13:9 start output==========
+.TRY Exec r9 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:9 end output==========
+.BG test line 13/job 10 finished (exit code: 0):
+==========test:13:10 start output==========
+.TRY Exec r10 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:10 end output==========
+.BG test line 13/job 11 finished (exit code: 0):
+==========test:13:11 start output==========
+.TRY Exec r11 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:11 end output==========
+.BG test line 13/job 12 finished (exit code: 0):
+==========test:13:12 start output==========
+.TRY Exec r12 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:12 end output==========
+.BG test line 13/job 13 finished (exit code: 0):
+==========test:13:13 start output==========
+.TRY Exec r13 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:13 end output==========
+.BG test line 13/job 14 finished (exit code: 0):
+==========test:13:14 start output==========
+.TRY Exec r14 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:14 end output==========
+.BG test line 13/job 15 finished (exit code: 0):
+==========test:13:15 start output==========
+.TRY Exec r15 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:15 end output==========
+.BG test line 13/job 16 finished (exit code: 0):
+==========test:13:16 start output==========
+.TRY Exec r16 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:16 end output==========
+.BG test line 13/job 17 finished (exit code: 0):
+==========test:13:17 start output==========
+.TRY Exec r17 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:17 end output==========
+.BG test line 13/job 18 finished (exit code: 0):
+==========test:13:18 start output==========
+.TRY Exec r18 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:18 end output==========
+.BG test line 13/job 19 finished (exit code: 0):
+==========test:13:19 start output==========
+.TRY Exec r19 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:19 end output==========
+.BG test line 13/job 20 finished (exit code: 0):
+==========test:13:20 start output==========
+.TRY Exec r20 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:20 end output==========
+.BG test line 13/job 21 finished (exit code: 0):
+==========test:13:21 start output==========
+.TRY Exec r21 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:21 end output==========
+.BG test line 13/job 22 finished (exit code: 0):
+==========test:13:22 start output==========
+.TRY Exec r22 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:22 end output==========
+.BG test line 13/job 23 finished (exit code: 0):
+==========test:13:23 start output==========
+.TRY Exec r23 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:23 end output==========
+.BG test line 13/job 24 finished (exit code: 0):
+==========test:13:24 start output==========
+.TRY Exec r24 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:24 end output==========
+.BG test line 13/job 25 finished (exit code: 0):
+==========test:13:25 start output==========
+.TRY Exec r25 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:25 end output==========
+.BG test line 13/job 26 finished (exit code: 0):
+==========test:13:26 start output==========
+.TRY Exec r26 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:26 end output==========
+.BG test line 13/job 27 finished (exit code: 0):
+==========test:13:27 start output==========
+.TRY Exec r27 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:27 end output==========
+.BG test line 13/job 28 finished (exit code: 0):
+==========test:13:28 start output==========
+.TRY Exec r28 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:28 end output==========
+.BG test line 13/job 29 finished (exit code: 0):
+==========test:13:29 start output==========
+.TRY Exec r29 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:29 end output==========
+.BG test line 13/job 30 finished (exit code: 0):
+==========test:13:30 start output==========
+.TRY Exec r30 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:30 end output==========
+.BG test line 13/job 31 finished (exit code: 0):
+==========test:13:31 start output==========
+.TRY Exec r31 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:31 end output==========
+.BG test line 13/job 32 finished (exit code: 0):
+==========test:13:32 start output==========
+.TRY Exec r32 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:32 end output==========
+.BG test line 13/job 33 finished (exit code: 0):
+==========test:13:33 start output==========
+.TRY Exec r33 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:33 end output==========
+.BG test line 13/job 34 finished (exit code: 0):
+==========test:13:34 start output==========
+.TRY Exec r34 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:34 end output==========
+.BG test line 13/job 35 finished (exit code: 0):
+==========test:13:35 start output==========
+.TRY Exec r35 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:35 end output==========
+.BG test line 13/job 36 finished (exit code: 0):
+==========test:13:36 start output==========
+.TRY Exec r36 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:36 end output==========
+.BG test line 13/job 37 finished (exit code: 0):
+==========test:13:37 start output==========
+.TRY Exec r37 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:37 end output==========
+.BG test line 13/job 38 finished (exit code: 0):
+==========test:13:38 start output==========
+.TRY Exec r38 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:38 end output==========
+.BG test line 13/job 39 finished (exit code: 0):
+==========test:13:39 start output==========
+.TRY Exec r39 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:39 end output==========
+.BG test line 13/job 40 finished (exit code: 0):
+==========test:13:40 start output==========
+.TRY Exec r40 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:40 end output==========
+.BG test line 13/job 41 finished (exit code: 0):
+==========test:13:41 start output==========
+.TRY Exec r41 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:41 end output==========
+.BG test line 13/job 42 finished (exit code: 0):
+==========test:13:42 start output==========
+.TRY Exec r42 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:42 end output==========
+.BG test line 13/job 43 finished (exit code: 0):
+==========test:13:43 start output==========
+.TRY Exec r43 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:43 end output==========
+.BG test line 13/job 44 finished (exit code: 0):
+==========test:13:44 start output==========
+.TRY Exec r44 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:44 end output==========
+.BG test line 13/job 45 finished (exit code: 0):
+==========test:13:45 start output==========
+.TRY Exec r45 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:45 end output==========
+.BG test line 13/job 46 finished (exit code: 0):
+==========test:13:46 start output==========
+.TRY Exec r46 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:46 end output==========
+.BG test line 13/job 47 finished (exit code: 0):
+==========test:13:47 start output==========
+.TRY Exec r47 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:47 end output==========
+.BG test line 13/job 48 finished (exit code: 0):
+==========test:13:48 start output==========
+.TRY Exec r48 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:48 end output==========
+.BG test line 13/job 49 finished (exit code: 0):
+==========test:13:49 start output==========
+.TRY Exec r49 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:49 end output==========
+.BG test line 13/job 50 finished (exit code: 0):
+==========test:13:50 start output==========
+.TRY Exec r50 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:50 end output==========
+.BG test line 13/job 51 finished (exit code: 0):
+==========test:13:51 start output==========
+.TRY Exec r51 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:51 end output==========
+.BG test line 13/job 52 finished (exit code: 0):
+==========test:13:52 start output==========
+.TRY Exec r52 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:52 end output==========
+.BG test line 13/job 53 finished (exit code: 0):
+==========test:13:53 start output==========
+.TRY Exec r53 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:53 end output==========
+.BG test line 13/job 54 finished (exit code: 0):
+==========test:13:54 start output==========
+.TRY Exec r54 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:54 end output==========
+.BG test line 13/job 55 finished (exit code: 0):
+==========test:13:55 start output==========
+.TRY Exec r55 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:55 end output==========
+.BG test line 13/job 56 finished (exit code: 0):
+==========test:13:56 start output==========
+.TRY Exec r56 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:56 end output==========
+.BG test line 13/job 57 finished (exit code: 0):
+==========test:13:57 start output==========
+.TRY Exec r57 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:57 end output==========
+.BG test line 13/job 58 finished (exit code: 0):
+==========test:13:58 start output==========
+.TRY Exec r58 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:58 end output==========
+.BG test line 13/job 59 finished (exit code: 0):
+==========test:13:59 start output==========
+.TRY Exec r59 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:59 end output==========
+.BG test line 13/job 60 finished (exit code: 0):
+==========test:13:60 start output==========
+.TRY Exec r60 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:60 end output==========
+.BG test line 13/job 61 finished (exit code: 0):
+==========test:13:61 start output==========
+.TRY Exec r61 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:61 end output==========
+.BG test line 13/job 62 finished (exit code: 0):
+==========test:13:62 start output==========
+.TRY Exec r62 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:62 end output==========
+.BG test line 13/job 63 finished (exit code: 0):
+==========test:13:63 start output==========
+.TRY Exec r63 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:63 end output==========
+.BG test line 13/job 64 finished (exit code: 0):
+==========test:13:64 start output==========
+.TRY Exec r64 op=monitor timeout=30000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:13:64 end output==========
diff --git a/lrm/test/testcases/metadata b/lrm/test/testcases/metadata
new file mode 100644
index 0000000..d155757
--- /dev/null
+++ b/lrm/test/testcases/metadata
@@ -0,0 +1,29 @@
+# list various meta-data
+%setenv LANG=POSIX
+%extcheck sort
+classes
+%extcheck ra-list.sh
+types class=ocf
+%extcheck ra-list.sh
+types class=lsb
+%extcheck ra-list.sh
+types class=heartbeat
+#%extcheck ra-list.sh
+#types class=stonith
+%extcheck xmllint.sh many
+classmeta class=ocf
+%extcheck xmllint.sh many
+classmeta class=lsb
+%extcheck xmllint.sh many
+classmeta class=heartbeat
+#%extcheck xmllint.sh many
+#classmeta class=stonith
+%extcheck xmllint.sh
+meta class=ocf type=Dummy
+%extcheck xmllint.sh
+meta class=lsb type=lrmregtest
+%extcheck xmllint.sh
+meta class=heartbeat type=Dummy
+#%extcheck xmllint.sh
+#meta class=stonith type=ssh
+provider class=ocf type=IPaddr
diff --git a/lrm/test/testcases/metadata.exp b/lrm/test/testcases/metadata.exp
new file mode 100644
index 0000000..158bad2
--- /dev/null
+++ b/lrm/test/testcases/metadata.exp
@@ -0,0 +1,31 @@
+.SETENV LANG=POSIX
+.EXTCHECK sort
+.TRY List classes
+There are 4 RA classes supported:
+heartbeat
+lsb
+ocf
+stonith
+.EXTCHECK ra-list.sh
+.TRY List types class=ocf
+Cool. RA list passed.
+.EXTCHECK ra-list.sh
+.TRY List types class=lsb
+Cool. RA list passed.
+.EXTCHECK ra-list.sh
+.TRY List types class=heartbeat
+Cool. RA list passed.
+.EXTCHECK xmllint.sh many
+.TRY Meta-data class=ocf
+.EXTCHECK xmllint.sh many
+.TRY Meta-data class=lsb
+.EXTCHECK xmllint.sh many
+.TRY Meta-data class=heartbeat
+.EXTCHECK xmllint.sh
+.TRY Show meta-data class=ocf type=Dummy provider=heartbeat
+.EXTCHECK xmllint.sh
+.TRY Show meta-data class=lsb type=lrmregtest provider=heartbeat
+.EXTCHECK xmllint.sh
+.TRY Show meta-data class=heartbeat type=Dummy provider=heartbeat
+.TRY Show provider class=ocf type=IPaddr
+heartbeat
diff --git a/lrm/test/testcases/ra-list.sh b/lrm/test/testcases/ra-list.sh
new file mode 100755
index 0000000..38fb67b
--- /dev/null
+++ b/lrm/test/testcases/ra-list.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+awk '
+NR==1 {num=$3;next}
+{in_num++}
+END{
+ if( num!=in_num )
+ print "ERROR: A mismatch in number of reported RAs!";
+ else
+ print "Cool. RA list passed.";
+}
+'
diff --git a/lrm/test/testcases/rscexec b/lrm/test/testcases/rscexec
new file mode 100644
index 0000000..e118ae1
--- /dev/null
+++ b/lrm/test/testcases/rscexec
@@ -0,0 +1,48 @@
+list
+# ocf
+%setenv dflt_rsc=rscexec_rsc_r1
+add rsc=rscexec_rsc_r1 args="delay=0"
+list
+exec operation=start
+state
+exec operation=monitor
+exec operation=start
+exec operation=monitor
+exec operation=stop
+state
+exec operation=monitor
+exec operation=stop
+exec operation=monitor
+exec operation=meta-data
+del
+# lsb
+%setenv dflt_class=lsb dftl_rsc=rscexec_rsc_r1-lsb
+add
+exec operation=start
+state
+exec operation=monitor
+exec operation=start
+exec operation=monitor
+exec operation=stop
+state
+exec operation=monitor
+exec operation=stop
+exec operation=monitor
+exec operation=meta-data
+del
+%stop
+# stonith
+%setenv dflt_class=stonith dftl_rsc=rscexec_rsc_r1-stonith
+add type=null args="hostlist=node1"
+exec operation=start
+state
+exec operation=monitor
+exec operation=start
+exec operation=monitor
+exec operation=stop
+state
+exec operation=monitor
+exec operation=stop
+exec operation=monitor
+exec operation=meta-data
+del
diff --git a/lrm/test/testcases/rscexec.exp b/lrm/test/testcases/rscexec.exp
new file mode 100644
index 0000000..71bdc2e
--- /dev/null
+++ b/lrm/test/testcases/rscexec.exp
@@ -0,0 +1,117 @@
+.TRY List resources
+Currently no resources are managed by LRM.
+.SETENV dflt_rsc=rscexec_rsc_r1
+.TRY Add resource rscexec_rsc_r1 class=ocf type=lrmregtest provider=heartbeat args=delay=0
+Succeeded in adding this resource.
+.TRY List resources
+
+Resource ID:rscexec_rsc_r1
+Resource agent class:ocf
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+Resource agent parameters:delay=0
+.TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+.TRY Show state rscexec_rsc_r1
+resource state:LRM_RSC_IDLE
+The resource 1 operations' information:
+ operation 'start' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=0
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Show state rscexec_rsc_r1
+resource state:LRM_RSC_IDLE
+The resource 3 operations' information:
+ operation 'start' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=0
+ operation 'monitor' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=0
+ operation 'stop' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=0
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=7): [null]
+
+.TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=7): [null]
+
+.TRY Exec rscexec_rsc_r1 op=meta-data timeout=1000 interval=0 target=EVERYTIME args=
+> meta-data succeed (status=0,rc=0): [null]
+
+.TRY Delete resource rscexec_rsc_r1
+Succeeded in deleting this resource.
+.SETENV dflt_class=lsb dftl_rsc=rscexec_rsc_r1-lsb
+.TRY Add resource rscexec_rsc_r1 class=lsb type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+.TRY Show state rscexec_rsc_r1
+resource state:LRM_RSC_IDLE
+The resource 1 operations' information:
+ operation 'start' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters:
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=start timeout=1000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Show state rscexec_rsc_r1
+resource state:LRM_RSC_IDLE
+The resource 3 operations' information:
+ operation 'start' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters:
+ operation 'monitor' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters:
+ operation 'stop' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters:
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=7): [null]
+
+.TRY Exec rscexec_rsc_r1 op=stop timeout=1000 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=7): [null]
+
+.TRY Exec rscexec_rsc_r1 op=meta-data timeout=1000 interval=0 target=EVERYTIME args=
+> meta-data succeed (status=0,rc=0): [null]
+
+.TRY Delete resource rscexec_rsc_r1
+Succeeded in deleting this resource.
diff --git a/lrm/test/testcases/rscmgmt b/lrm/test/testcases/rscmgmt
new file mode 100644
index 0000000..8d745d3
--- /dev/null
+++ b/lrm/test/testcases/rscmgmt
@@ -0,0 +1,29 @@
+list
+# add/remove resources
+#
+add rsc=r1
+info rsc=r1
+list
+del rsc=r1
+%setenv dflt_class=lsb dflt_type=lrmregtest
+list
+add rsc=r1
+list
+del rsc=r1
+list
+#
+# a bit of mix
+#
+%setenv dflt_class=ocf
+add rsc=r1
+add rsc=r1 class=lsb type=lrmregtest
+add rsc=r1
+del rsc=r1
+add rsc=r1 class=lsb type=lrmregtest
+list
+add rsc=r2
+list
+del rsc=r1
+del rsc=r2
+list
+del rsc=r1
diff --git a/lrm/test/testcases/rscmgmt.exp b/lrm/test/testcases/rscmgmt.exp
new file mode 100644
index 0000000..3a5c4bf
--- /dev/null
+++ b/lrm/test/testcases/rscmgmt.exp
@@ -0,0 +1,74 @@
+.TRY List resources
+Currently no resources are managed by LRM.
+.TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY Show info r1
+
+Resource ID:r1
+Resource agent class:ocf
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+.TRY List resources
+
+Resource ID:r1
+Resource agent class:ocf
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+.TRY Delete resource r1
+Succeeded in deleting this resource.
+.SETENV dflt_class=lsb dflt_type=lrmregtest
+.TRY List resources
+Currently no resources are managed by LRM.
+.TRY Add resource r1 class=lsb type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY List resources
+
+Resource ID:r1
+Resource agent class:lsb
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+.TRY Delete resource r1
+Succeeded in deleting this resource.
+.TRY List resources
+Currently no resources are managed by LRM.
+.SETENV dflt_class=ocf
+.TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY Add resource r1 class=lsb type=lrmregtest provider=heartbeat args=
+ERROR: lrm_add_rsc(): got a return code HA_FAIL from a reply message of addrsc with function get_ret_from_msg.
+Failed to add this resource.
+.TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=
+ERROR: lrm_add_rsc(): got a return code HA_FAIL from a reply message of addrsc with function get_ret_from_msg.
+Failed to add this resource.
+.TRY Delete resource r1
+Succeeded in deleting this resource.
+.TRY Add resource r1 class=lsb type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY List resources
+
+Resource ID:r1
+Resource agent class:lsb
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+.TRY Add resource r2 class=ocf type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY List resources
+
+Resource ID:r2
+Resource agent class:ocf
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+
+Resource ID:r1
+Resource agent class:lsb
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+.TRY Delete resource r1
+Succeeded in deleting this resource.
+.TRY Delete resource r2
+Succeeded in deleting this resource.
+.TRY List resources
+Currently no resources are managed by LRM.
+.TRY Delete resource r1
+ERROR: lrm_delete_rsc(): got a return code HA_FAIL from a reply message of delrsc with function get_ret_from_msg.
+Failed to delete this resource.
diff --git a/lrm/test/testcases/rscmgmt.log_filter b/lrm/test/testcases/rscmgmt.log_filter
new file mode 100755
index 0000000..34debc5
--- /dev/null
+++ b/lrm/test/testcases/rscmgmt.log_filter
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+awk '
+n<2 && /ERROR: on_msg_add_rsc: same id resource exists./ {n++; next}
+m<1 && /ERROR: on_msg_del_rsc: no rsc with id/ {m++; next}
+{print}
+END{
+ if( n!=2 )
+ print "ERROR: missed on_msg_add_rsc errors";
+ if( m!=1 )
+ print "ERROR: missed on_msg_del_rsc errors";
+}
+'
diff --git a/lrm/test/testcases/serialize b/lrm/test/testcases/serialize
new file mode 100644
index 0000000..cad96b3
--- /dev/null
+++ b/lrm/test/testcases/serialize
@@ -0,0 +1,33 @@
+list
+# allow for a delay of 2 seconds
+%setenv dflt_timeout=2500
+add rsc=r1 args="delay=2"
+#
+# we run the next three ops in the background
+# in case ops are not serialized, the lrmregtest RA should complain
+#
+%bg 2
+exec operation=start
+# insert sleeps to make sure that the operations are started in
+# the order given here
+%sleep 1
+# set timeouts high enough so that no op fails
+exec operation=start timeout=3000
+%sleep 1
+%bgrepeat 4
+exec operation=monitor timeout=11000
+%sleep 11
+state
+exec operation=stop
+state
+del rsc=r1
+#
+#
+#
+%setenv dflt_rsc=r2 dflt_timeout=10500
+add rsc=r2 args="ignore_TERM=1 delay=9"
+exec operation=start
+%bg
+exec operation=monitor timeout=500
+exec operation=monitor
+del rsc=r2
diff --git a/lrm/test/testcases/serialize.exp b/lrm/test/testcases/serialize.exp
new file mode 100644
index 0000000..b290c95
--- /dev/null
+++ b/lrm/test/testcases/serialize.exp
@@ -0,0 +1,100 @@
+.TRY List resources
+Currently no resources are managed by LRM.
+.SETENV dflt_timeout=2500
+.TRY Add resource r1 class=ocf type=lrmregtest provider=heartbeat args=delay=2
+Succeeded in adding this resource.
+.BG 2
+.BG test line 10/job 1 runs in background
+.SLEEP 1
+.BG test line 15/job 2 runs in background
+.SLEEP 1
+.BGREPEAT 4
+.BG test line 18/job 1 runs in background
+.BG test line 18/job 2 runs in background
+.BG test line 18/job 3 runs in background
+.BG test line 18/job 4 runs in background
+.SLEEP 11
+.TRY Show state r1
+resource state:LRM_RSC_IDLE
+The resource 2 operations' information:
+ operation 'start' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=3000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=2
+ operation 'monitor' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=11000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=2
+.TRY Exec r1 op=stop timeout=2500 interval=0 target=EVERYTIME args=
+> stop succeed (status=0,rc=0): [null]
+
+.TRY Show state r1
+resource state:LRM_RSC_IDLE
+The resource 3 operations' information:
+ operation 'start' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=3000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=2
+ operation 'monitor' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=11000, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=2
+ operation 'stop' [call_id=(removed)]:
+ start_delay=0, interval=0, timeout=2500, app_name=lrmadmin
+ rc=0 (ok), op_status=0 (succeed)
+ parameters: delay=2
+.TRY Delete resource r1
+Succeeded in deleting this resource.
+.SETENV dflt_rsc=r2 dflt_timeout=10500
+.TRY Add resource r2 class=ocf type=lrmregtest provider=heartbeat args=ignore_TERM=1 delay=9
+Succeeded in adding this resource.
+.TRY Exec r2 op=start timeout=10500 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+.BG
+.BG test line 31/job 1 runs in background
+.TRY Exec r2 op=monitor timeout=10500 interval=0 target=EVERYTIME args=
+ERROR: This operation has timed out - no result from lrmd.
+.TRY Delete resource r2
+Succeeded in deleting this resource.
+.BG test line 10/job 1 finished (exit code: 0):
+==========test:10:1 start output==========
+.TRY Exec r1 op=start timeout=2500 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:10:1 end output==========
+.BG test line 15/job 2 finished (exit code: 0):
+==========test:15:2 start output==========
+.TRY Exec r1 op=start timeout=3000 interval=0 target=EVERYTIME args=
+> start succeed (status=0,rc=0): [null]
+
+==========test:15:2 end output==========
+.BG test line 18/job 1 finished (exit code: 0):
+==========test:18:1 start output==========
+.TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:18:1 end output==========
+.BG test line 18/job 2 finished (exit code: 0):
+==========test:18:2 start output==========
+.TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:18:2 end output==========
+.BG test line 18/job 3 finished (exit code: 0):
+==========test:18:3 start output==========
+.TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:18:3 end output==========
+.BG test line 18/job 4 finished (exit code: 0):
+==========test:18:4 start output==========
+.TRY Exec r1 op=monitor timeout=11000 interval=0 target=EVERYTIME args=
+> monitor succeed (status=0,rc=0): [null]
+
+==========test:18:4 end output==========
+.BG test line 31/job 1 finished (exit code: 0):
+==========test:31:1 start output==========
+.TRY Exec r2 op=monitor timeout=500 interval=0 target=EVERYTIME args=
+ERROR: This operation has timed out - no result from lrmd.
+==========test:31:1 end output==========
diff --git a/lrm/test/testcases/stonith b/lrm/test/testcases/stonith
new file mode 100644
index 0000000..f21cf18
--- /dev/null
+++ b/lrm/test/testcases/stonith
@@ -0,0 +1,2 @@
+%extcheck xmllint.sh many
+%shell stonith -L | while read p; do echo $p:heartbeat; stonith -m -t $p; done
diff --git a/lrm/test/testcases/stonith.exp b/lrm/test/testcases/stonith.exp
new file mode 100644
index 0000000..f9f1042
--- /dev/null
+++ b/lrm/test/testcases/stonith.exp
@@ -0,0 +1,2 @@
+.EXTCHECK xmllint.sh many
+.SHELL stonith -L | while read p; do echo $p:heartbeat; stonith -m -t $p; done
diff --git a/lrm/test/testcases/xmllint.sh b/lrm/test/testcases/xmllint.sh
new file mode 100755
index 0000000..f61288c
--- /dev/null
+++ b/lrm/test/testcases/xmllint.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+gawk -v many="$1" '
+BEGIN{XMLLINT="xmllint --noout -";}
+function chkoutput(ra) {
+ if( ra=="" ) return;
+ if( close(XMLLINT) ) # we need gawk for this
+ print "xmllint reported error in RA:",ra;
+}
+many=="many" && /^[a-zA-Z][^:]*:[a-zA-Z0-9]+$/ {
+ chkoutput(ra);
+ ra=$0;
+ next;
+}
+{ print | XMLLINT }
+END{
+ if( many!="many" )
+ chkoutput("noname");
+}
+'
diff --git a/replace/Makefile.am b/replace/Makefile.am
new file mode 100644
index 0000000..52892ba
--- /dev/null
+++ b/replace/Makefile.am
@@ -0,0 +1,29 @@
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \
+ -I$(top_srcdir)/linux-ha -I$(top_builddir)/linux-ha
+
+QUIET_LIBTOOL_OPTS = @QUIET_LIBTOOL_OPTS@
+LIBTOOL = @LIBTOOL@ @QUIET_LIBTOOL_OPTS@
+
+
+noinst_LTLIBRARIES = libreplace.la
+libreplace_la_SOURCES =
+libreplace_la_LIBADD = @LTLIBOBJS@
diff --git a/replace/NoSuchFunctionName.c b/replace/NoSuchFunctionName.c
new file mode 100644
index 0000000..373eabd
--- /dev/null
+++ b/replace/NoSuchFunctionName.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2002 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+void nosuchfunctionname(void);
+
+/*
+ * This is a completely useless function put here only to make OpenBSD make
+ * procedures happy. I hope no one ever makes such a function ;-)
+ */
+void
+nosuchfunctionname(void)
+{
+ return;
+}
diff --git a/replace/alphasort.c b/replace/alphasort.c
new file mode 100644
index 0000000..94cd811
--- /dev/null
+++ b/replace/alphasort.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * alphasort - replacement for alphasort functions.
+ *
+ * Matt Soffen
+
+ * Copyright (C) 2001 Matt Soffen <matt@soffen.com>
+ *
+ * Taken from the FreeBSD file (with copyright notice)
+ * /usr/src/gnu/lib/libdialog/dir.c
+ ***************************************************************************
+ * Program: dir.c
+ * Author: Marc van Kempen
+ * desc: Directory routines, sorting and reading
+ *
+ * Copyright (c) 1995, Marc van Kempen
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and
+ * sold, in both source and binary form provided that the above
+ * copyright and these terms are retained, verbatim, as the first
+ * lines of this file. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with
+ * its use.
+ *
+ ***************************************************************************
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h> /* XXX for _POSIX_VERSION ifdefs */
+
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if !defined sgi && !defined _POSIX_VERSION
+#include <sys/dir.h>
+#endif
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+int alphasort(const void *dirent1, const void *dirent2) {
+ return(strcmp((*(const struct dirent **)dirent1)->d_name,
+ (*(const struct dirent **)dirent2)->d_name));
+}
diff --git a/replace/daemon.c b/replace/daemon.c
new file mode 100644
index 0000000..7697113
--- /dev/null
+++ b/replace/daemon.c
@@ -0,0 +1,83 @@
+/*-
+ *
+ * daemon - replacement for daemon function.
+ *
+ * Matt Soffen
+ * Copyright (C) 2004 Matt Soffen <matt@soffen.com>
+ *
+ * Taken from the FreeBSD file (with copyright notice)
+ * ------------------------------------------------------------
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/gen/daemon.c,v 1.3 2000/01/27 23:06:14 jasone Exp $
+ *
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <lha_internal.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+int
+daemon(nochdir, noclose)
+ int nochdir, noclose;
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close(fd);
+ }
+ return (0);
+}
diff --git a/replace/inet_pton.c b/replace/inet_pton.c
new file mode 100644
index 0000000..f5aa93b
--- /dev/null
+++ b/replace/inet_pton.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/* Chris Wright <chris@wirex.com> June 22, 2001
+ * Merged contents of inet_pton.c from Apache2.0.16 and BIND8
+ * The Apache base is more portable within heartbeat's envrionment,
+ * however, the BIND8 version has two small logic changes that are
+ * newer.
+ */
+
+#include <lha_internal.h>
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ 16
+#endif
+
+#ifndef INT16SZ
+#define INT16SZ sizeof(short)
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+#ifndef __P
+#define __P(x) x
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4 __P((const char *src, unsigned char *dst));
+#if HAVE_IPV6
+static int inet_pton6 __P((const char *src, unsigned char *dst));
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+inet_pton(int af, const char *src, void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+#if HAVE_IPV6
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (pch - digits);
+
+ if (new > 255)
+ return (0);
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+
+ memcpy(dst, tmp, INADDRSZ);
+ return (1);
+}
+
+#if HAVE_IPV6
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', IN6ADDRSZ);
+ endp = tp + IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ } else if (*src == '\0') {
+ return (0);
+ }
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ if (tp == endp)
+ return (0);
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, IN6ADDRSZ);
+ return (1);
+}
+#endif /* HAVE_IPV6 */
diff --git a/replace/scandir.c b/replace/scandir.c
new file mode 100644
index 0000000..528f544
--- /dev/null
+++ b/replace/scandir.c
@@ -0,0 +1,236 @@
+/* scandir: Scan a directory, collecting all (selected) items into a an array.
+ *
+ * This code borrowed from 'libit', which can be found here:
+ *
+ * http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/
+ *
+ * The original author put this code in the public domain.
+ * It has been modified slightly to get rid of warnings, etc.
+ *
+ * Below is the email I received from pinard@iro.umontreal.ca (François Pinard)
+ * when I sent him an email asking him about the license, etc. of this
+ * code which I obtained from his site.
+ *
+ * I think the correct spelling of his name is Rich Salz. I think he's now
+ * rsalz@datapower.com...
+ * --
+ * Rich Salz, Chief Security Architect
+ * DataPower Technology http://www.datapower.com
+ * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html
+ *
+ * Copyright(C): none (public domain)
+ * License: none (public domain)
+ * Author: Rich Salz <rsalz@datapower.com>
+ *
+ *
+ *
+ * -- Alan Robertson
+ * alanr@unix.sh
+ *
+ **************************************************************************
+ *
+ * Subject: Re: Scandir replacement function
+ * Date: 18 May 2001 12:00:48 -0400
+ * From: pinard@iro.umontreal.ca (François Pinard)
+ * To: Alan Robertson <alanr@unix.sh>
+ * References: 1
+ *
+ *
+ * [Alan Robertson]
+ *
+ * > Hi, I'd like to use your scandir replacement function found here:
+ * > http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ But, it does
+ * > not indicate authorship or licensing terms in it. Could you tell me
+ * > who wrote this code, under what license you distribute it, and whether
+ * > and under what terms I may further distribute it?
+ *
+ * Hello, Alan. These are (somewhat) explained in UNSHAR.HDR found in the
+ * same directory. The routines have been written by Rick Saltz (I'm not
+ * completely sure of the spelling) a long while ago. I think that nowadays,
+ * Rick is better known as the main author of the nice INN package.
+ *
+ **************************************************************************
+ *
+ * I spent a little time verifying this with Rick Salz.
+ * The results are below:
+ *
+ **************************************************************************
+ *
+ * Date: Tue, 20 Sep 2005 21:52:09 -0400 (EDT)
+ * From: Rich Salz <rsalz@datapower.com>
+ * To: Alan Robertson <alanr@unix.sh>
+ * Subject: Re: Verifying permissions/licenses/etc on some old code of yours -
+ * scandir.c
+ * In-Reply-To: <433071CA.8000107@unix.sh>
+ * Message-ID: <Pine.LNX.4.44L0.0509202151270.9198-100000@smtp.datapower.com>
+ * Content-Type: TEXT/PLAIN; charset=US-ASCII
+ *
+ * yes, it's most definitely in the public domain.
+ *
+ * I'm glad you find it useful. I'm surprised it hasn't been replaced by,
+ * e.g,. something in GLibC. Ii'm impressed you tracked me down.
+ *
+ * /r$
+ *
+ * --
+ * Rich Salz Chief Security Architect
+ * DataPower Technology http://www.datapower.com
+ * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html
+ * ---------------------------------------------------------------------->
+ * Subject: scandir, ftw REDUX
+ * Date: 1 Jan 88 00:47:01 GMT
+ * From: rsalz@pebbles.bbn.com
+ * Newsgroups: comp.sources.misc
+ *
+ *
+ * Forget my previous message -- I just decided for completeness's sake to
+ * implement the SysV ftw(3) routine, too.
+ *
+ * To repeat, these are public-domain implementations of the SystemV ftw()
+ * routine, the BSD scandir() and alphasort() routines, and documentation for
+ * same. The FTW manpage could be more readable, but so it goes.
+ *
+ * Anyhow, feel free to post these, and incorporate them into your existing
+ * packages. I have readdir() routiens for MSDOS and the Amiga if anyone
+ * wants them, and should have them for VMS by the end of January; let me
+ * know if you want copies.
+ *
+ * Yours in filesystems,
+ * /r$
+ *
+ * Anyhow, feel free to post
+ * ----------------------------------------------------------------------<
+ *
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#ifndef NULL
+# define NULL ((void *) 0)
+#endif
+
+/* Initial guess at directory allocated. */
+#define INITIAL_ALLOCATION 20
+
+int
+scandir (const char *directory_name,
+ struct dirent ***array_pointer,
+ int (*select_function) (const struct dirent *),
+
+#ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
+ /* This is what the Linux man page says */
+ int (*compare_function) (const struct dirent**, const struct dirent**)
+#else
+ /* This is what the Linux header file says ... */
+ int (*compare_function) (const void *, const void *)
+#endif
+ );
+
+int
+scandir (const char *directory_name,
+ struct dirent ***array_pointer,
+ int (*select_function) (const struct dirent *),
+#ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT
+ /* This is what the linux man page says */
+ int (*compare_function) (const struct dirent**, const struct dirent**)
+#else
+ /* This is what the linux header file says ... */
+ int (*compare_function) (const void *, const void *)
+#endif
+ )
+{
+ DIR *directory;
+ struct dirent **array;
+ struct dirent *entry;
+ struct dirent *copy;
+ int allocated = INITIAL_ALLOCATION;
+ int counter = 0;
+
+ /* Get initial list space and open directory. */
+
+ if (directory = opendir (directory_name), directory == NULL)
+ return -1;
+
+ if (array = (struct dirent **) malloc (allocated * sizeof (struct dirent *)),
+ array == NULL)
+ return -1;
+
+ /* Read entries in the directory. */
+
+ while (entry = readdir (directory), entry)
+ if (select_function == NULL || (*select_function) (entry))
+ {
+ /* User wants them all, or he wants this one. Copy the entry. */
+
+ /*
+ * On some OSes the declaration of "entry->d_name" is a minimal-length
+ * placeholder. Example: Solaris:
+ * /usr/include/sys/dirent.h:
+ * "char d_name[1];"
+ * man page "dirent(3)":
+ * The field d_name is the beginning of the character array
+ * giving the name of the directory entry. This name is
+ * null terminated and may have at most MAXNAMLEN chars.
+ * So our malloc length may need to be increased accordingly.
+ * sizeof(entry->d_name): space (possibly minimal) in struct.
+ * strlen(entry->d_name): actual length of the entry.
+ *
+ * John Kavadias <john_kavadias@hotmail.com>
+ * David Lee <t.d.lee@durham.ac.uk>
+ */
+ int namelength = strlen(entry->d_name) + 1; /* length with NULL */
+ int extra = 0;
+
+ if (sizeof(entry->d_name) <= namelength) {
+ /* allocated space <= required space */
+ extra += namelength - sizeof(entry->d_name);
+ }
+
+ if (copy = (struct dirent *) malloc (sizeof (struct dirent) + extra),
+ copy == NULL)
+ {
+ closedir (directory);
+ free (array);
+ return -1;
+ }
+ copy->d_ino = entry->d_ino;
+ copy->d_reclen = entry->d_reclen;
+ strcpy (copy->d_name, entry->d_name);
+
+ /* Save the copy. */
+
+ if (counter + 1 == allocated)
+ {
+ allocated <<= 1;
+ array = (struct dirent **)
+ realloc ((char *) array, allocated * sizeof (struct dirent *));
+ if (array == NULL)
+ {
+ closedir (directory);
+ free (array);
+ free (copy);
+ return -1;
+ }
+ }
+ array[counter++] = copy;
+ }
+
+ /* Close things off. */
+
+ array[counter] = NULL;
+ *array_pointer = array;
+ closedir (directory);
+
+ /* Sort? */
+
+ if (counter > 1 && compare_function)
+ qsort ((char *) array, counter, sizeof (struct dirent *)
+ , (int (*)(const void *, const void *))(compare_function));
+
+ return counter;
+}
diff --git a/replace/setenv.c b/replace/setenv.c
new file mode 100644
index 0000000..e8cafb1
--- /dev/null
+++ b/replace/setenv.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Small replacement function for setenv()
+ */
+int
+setenv(const char *name, const char * value, int why)
+{
+ int rc = -1;
+
+ if ( name && value ) {
+ char * envp = NULL;
+ envp = malloc(strlen(name)+strlen(value)+2);
+ if (envp) {
+ /*
+ * Unfortunately, the putenv API guarantees memory leaks when
+ * changing environment variables repeatedly... :-(
+ */
+
+ sprintf(envp, "%s=%s", name, value);
+
+ /* Cannot free envp (!) */
+ rc = putenv(envp);
+ }
+
+ }
+ return(rc);
+}
diff --git a/replace/strerror.c b/replace/strerror.c
new file mode 100644
index 0000000..477239f
--- /dev/null
+++ b/replace/strerror.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2002 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+#include <lha_internal.h>
+#include <errno.h>
+#include <stdio.h>
+extern const char * sys_err[];
+extern int sys_nerr;
+char *
+strerror(int errnum)
+{
+ static char whaterr[32];
+
+ if (errnum < 0) {
+ return "negative errno";
+ }
+ if (errnum >= sys_nerr) {
+ snprintf(whaterr, sizeof(whaterr),"error %d", errnum);
+ return whaterr;
+ }
+ return sys_err[sys_nerr];
+}
diff --git a/replace/strlcat.c b/replace/strlcat.c
new file mode 100644
index 0000000..8b909f9
--- /dev/null
+++ b/replace/strlcat.c
@@ -0,0 +1,33 @@
+#include <lha_internal.h>
+#include <string.h>
+/*
+ * Copyright (C) 2007 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+size_t
+strlcat(char *dest, const char * src, size_t maxlen)
+{
+ size_t curlen = strlen(dest);
+ size_t addlen = strlen(src);
+ size_t appendlen = (maxlen-1) - curlen;
+ if (appendlen > 0) {
+ strlcpy(dest+curlen, src, maxlen-curlen);
+ }
+ return curlen + addlen;
+}
diff --git a/replace/strlcpy.c b/replace/strlcpy.c
new file mode 100644
index 0000000..661d02e
--- /dev/null
+++ b/replace/strlcpy.c
@@ -0,0 +1,32 @@
+#include <lha_internal.h>
+#include <string.h>
+/*
+ * Copyright (C) 2007 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+size_t
+strlcpy(char *dest, const char * src, size_t maxlen)
+{
+ size_t srclen = strlen(src);
+ if (maxlen > 0) {
+ strncpy(dest, src, maxlen);
+ dest[maxlen-1]=EOS;
+ }
+ return srclen;
+}
diff --git a/replace/strndup.c b/replace/strndup.c
new file mode 100644
index 0000000..4312743
--- /dev/null
+++ b/replace/strndup.c
@@ -0,0 +1,38 @@
+#include <lha_internal.h>
+#include <stdlib.h>
+#include <string.h>
+/*
+ * Copyright (C) 2004 Matt Soffen <sirgeek-ha@mrsucko.org>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+/* Taken from the GlibC implementation of strndup */
+
+char *strndup(const char *str, size_t len)
+{
+ size_t n = strnlen(str,len);
+ char *new = (char *) malloc (len+1);
+
+ if (NULL == new) {
+ return NULL;
+ }
+
+ new[n] = '\0';
+ return (char *)memcpy (new, str, len);
+}
+
diff --git a/replace/strnlen.c b/replace/strnlen.c
new file mode 100644
index 0000000..8b3bcd2
--- /dev/null
+++ b/replace/strnlen.c
@@ -0,0 +1,31 @@
+#include <lha_internal.h>
+#include <string.h>
+/*
+ * Copyright (C) 2003 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ * 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
+ *
+ */
+
+size_t
+strnlen(const char *s, size_t maxlen)
+{
+ const char * eospos;
+
+ eospos = memchr(s, (int)'\0', maxlen);
+
+ return (eospos == NULL ? maxlen : (size_t)(eospos-s));
+}
diff --git a/replace/unsetenv.c b/replace/unsetenv.c
new file mode 100644
index 0000000..aeb84a3
--- /dev/null
+++ b/replace/unsetenv.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr@unix.sh>
+ * This software licensed under the GNU LGPL.
+ *
+ *
+ * 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
+ *
+ */
+
+#include <lha_internal.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define __environ environ
+#ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+#endif
+
+int
+unsetenv (const char *name)
+{
+ const size_t len = strlen (name);
+ char **ep;
+
+ for (ep = __environ; *ep; ++ep) {
+ if (!strncmp (*ep, name, len) && (*ep)[len] == '=') {
+ /* Found it. */
+ /* Remove this pointer by moving later ones back. */
+ char **dp = ep;
+ do
+ dp[0] = dp[1];
+ while (*dp++);
+ /* Continue the loop in case NAME appears again. */
+ }
+ }
+ return 0;
+}
diff --git a/replace/uuid_parse.c b/replace/uuid_parse.c
new file mode 100644
index 0000000..beecac6
--- /dev/null
+++ b/replace/uuid_parse.c
@@ -0,0 +1,519 @@
+/*
+ * uuid: emulation of e2fsprogs interface if implementation lacking.
+ *
+ * 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
+ *
+ * Original uuid implementation: copyright (C) Theodore Ts'o
+ *
+ * This importation into heartbeat:
+ * Copyright (C) 2004 David Lee <t.d.lee@durham.ac.uk>
+ *
+ */
+
+#include <lha_internal.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+
+#include <replace_uuid.h>
+
+/*
+ * Local "replace" implementation of uuid functions.
+ */
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+/* UUID Variant definitions */
+#define UUID_VARIANT_NCS 0
+#define UUID_VARIANT_DCE 1
+#define UUID_VARIANT_MICROSOFT 2
+#define UUID_VARIANT_OTHER 3
+
+/* UUID Type definitions */
+#define UUID_TYPE_DCE_TIME 1
+#define UUID_TYPE_DCE_RANDOM 4
+
+
+
+/* For uuid_compare() */
+#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1);
+
+/************************************
+ * Private types
+ ************************************/
+
+#define longlong long long
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW 0x13814000
+
+#if (SIZEOF_INT == 4)
+typedef unsigned int __u32;
+#elif (SIZEOF_LONG == 4)
+typedef unsigned long __u32;
+#endif
+
+#if (SIZEOF_INT == 2)
+typedef int __s16;
+typedef unsigned int __u16;
+#elif (SIZEOF_SHORT == 2)
+typedef short __s16;
+typedef unsigned short __u16;
+#endif
+
+typedef unsigned char __u8;
+
+struct uuid {
+ __u32 time_low;
+ __u16 time_mid;
+ __u16 time_hi_and_version;
+ __u16 clock_seq;
+ __u8 node[6];
+};
+
+/************************************
+ * internal routines
+ ************************************/
+static void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+ __u32 tmp;
+ unsigned char *out = ptr;
+
+ tmp = uu->time_low;
+ out[3] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[2] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[1] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[0] = (unsigned char) tmp;
+
+ tmp = uu->time_mid;
+ out[5] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[4] = (unsigned char) tmp;
+
+ tmp = uu->time_hi_and_version;
+ out[7] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[6] = (unsigned char) tmp;
+
+ tmp = uu->clock_seq;
+ out[9] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[8] = (unsigned char) tmp;
+
+ memcpy(out+10, uu->node, 6);
+}
+
+static void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+ const __u8 *ptr = in;
+ __u32 tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_low = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_mid = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_hi_and_version = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->clock_seq = tmp;
+
+ memcpy(uu->node, ptr, 6);
+}
+
+/************************************
+ * Main routines, except uuid_generate*()
+ ************************************/
+void
+uuid_clear(uuid_t uu)
+{
+ memset(uu, 0, 16);
+}
+
+int
+uuid_compare(const uuid_t uu1, const uuid_t uu2)
+{
+ struct uuid uuid1, uuid2;
+
+ uuid_unpack(uu1, &uuid1);
+ uuid_unpack(uu2, &uuid2);
+
+ UUCMP(uuid1.time_low, uuid2.time_low);
+ UUCMP(uuid1.time_mid, uuid2.time_mid);
+ UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
+ UUCMP(uuid1.clock_seq, uuid2.clock_seq);
+ return memcmp(uuid1.node, uuid2.node, 6);
+}
+
+void
+uuid_copy(uuid_t dst, const uuid_t src)
+{
+ unsigned char *cp1;
+ const unsigned char *cp2;
+ int i;
+
+ for (i=0, cp1 = dst, cp2 = src; i < 16; i++)
+ *cp1++ = *cp2++;
+}
+
+/* if uu is the null uuid, return 1 else 0 */
+int
+uuid_is_null(const uuid_t uu)
+{
+ const unsigned char *cp;
+ int i;
+
+ for (i=0, cp = uu; i < 16; i++)
+ if (*cp++)
+ return 0;
+ return 1;
+}
+
+/* 36byte-string=>uuid */
+int
+uuid_parse(const char *in, uuid_t uu)
+{
+ struct uuid uuid;
+ int i;
+ const char *cp;
+ char buf[3];
+
+ if (strlen(in) != 36)
+ return -1;
+ for (i=0, cp = in; i <= 36; i++,cp++) {
+ if ((i == 8) || (i == 13) || (i == 18) ||
+ (i == 23)) {
+ if (*cp == '-')
+ continue;
+ else
+ return -1;
+ }
+ if (i== 36)
+ if (*cp == 0)
+ continue;
+ if (!isxdigit((int) *cp))
+ return -1;
+ }
+ uuid.time_low = strtoul(in, NULL, 16);
+ uuid.time_mid = strtoul(in+9, NULL, 16);
+ uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
+ uuid.clock_seq = strtoul(in+19, NULL, 16);
+ cp = in+24;
+ buf[2] = 0;
+ for (i=0; i < 6; i++) {
+ buf[0] = *cp++;
+ buf[1] = *cp++;
+ uuid.node[i] = strtoul(buf, NULL, 16);
+ }
+
+ uuid_pack(&uuid, uu);
+ return 0;
+}
+
+/* uuid=>36byte-string-with-null */
+void
+uuid_unparse(const uuid_t uu, char *out)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ sprintf(out,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+ uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+ uuid.node[0], uuid.node[1], uuid.node[2],
+ uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+
+/************************************
+ * Main routines: uuid_generate*()
+ ************************************/
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+static int get_random_fd(void)
+{
+ struct timeval tv;
+ static int fd = -2;
+ int i;
+
+ if (fd == -2) {
+ gettimeofday(&tv, 0);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+ }
+ /* Crank the random number generator a few times */
+ gettimeofday(&tv, 0);
+ for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+ rand();
+ return fd;
+}
+
+/*
+ * Generate a series of random bytes. Use /dev/urandom if possible,
+ * and if not, use srandom/random.
+ */
+static void get_random_bytes(void *buf, int nbytes)
+{
+ int i, n = nbytes, fd = get_random_fd();
+ int lose_counter = 0;
+ unsigned char *cp = (unsigned char *) buf;
+
+ if (fd >= 0) {
+ while (n > 0) {
+ i = read(fd, cp, n);
+ if (i <= 0) {
+ if (lose_counter++ > 16)
+ break;
+ continue;
+ }
+ n -= i;
+ cp += i;
+ lose_counter = 0;
+ }
+ }
+
+ /*
+ * We do this all the time, but this is the only source of
+ * randomness if /dev/random/urandom is out to lunch.
+ */
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (rand() >> 7) & 0xFF;
+ return;
+}
+
+/*
+ * Get the ethernet hardware address, if we can find it...
+ */
+static int get_node_id(unsigned char *node_id)
+{
+#ifdef HAVE_NET_IF_H
+ int sd;
+ struct ifreq ifr, *ifrp;
+ struct ifconf ifc;
+ char buf[1024];
+ int n, i;
+ unsigned char *a;
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+ sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
+ close(sd);
+ return -1;
+ }
+ n = ifc.ifc_len;
+ for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
+ ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
+ strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
+#ifdef SIOCGIFHWADDR
+ if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+ if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) ifr.ifr_enaddr;
+#else
+ /*
+ * XXX we don't have a way of getting the hardware
+ * address
+ */
+ close(sd);
+ return 0;
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+ continue;
+ if (node_id) {
+ memcpy(node_id, a, 6);
+ close(sd);
+ return 1;
+ }
+ }
+ close(sd);
+#endif
+ return 0;
+}
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+static int
+get_clock(__u32 *clock_high, __u32 *clock_low, __u16 *ret_clock_seq)
+{
+ static int adjustment = 0;
+ static struct timeval last = {0, 0};
+ static __u16 clock_seq;
+ struct timeval tv;
+ unsigned longlong clock_reg;
+
+try_again:
+ gettimeofday(&tv, 0);
+ if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+ get_random_bytes(&clock_seq, sizeof(clock_seq));
+ clock_seq &= 0x1FFF;
+ last = tv;
+ last.tv_sec--;
+ }
+ if ((tv.tv_sec < last.tv_sec) ||
+ ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec < last.tv_usec))) {
+ clock_seq = (clock_seq+1) & 0x1FFF;
+ adjustment = 0;
+ last = tv;
+ } else if ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec == last.tv_usec)) {
+ if (adjustment >= MAX_ADJUSTMENT)
+ goto try_again;
+ adjustment++;
+ } else {
+ adjustment = 0;
+ last = tv;
+ }
+
+ clock_reg = tv.tv_usec*10 + adjustment;
+ clock_reg += ((unsigned longlong) tv.tv_sec)*10000000;
+ clock_reg += (((unsigned longlong) 0x01B21DD2) << 32) + 0x13814000;
+
+ *clock_high = clock_reg >> 32;
+ *clock_low = clock_reg;
+ *ret_clock_seq = clock_seq;
+ return 0;
+}
+
+/* create a new uuid, based on randomness */
+void
+uuid_generate_random(uuid_t out)
+{
+ uuid_t buf;
+ struct uuid uu;
+
+ get_random_bytes(buf, sizeof(buf));
+ uuid_unpack(buf, &uu);
+
+ uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+ uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
+ uuid_pack(&uu, out);
+}
+
+/* create a new uuid, based on time */
+static void
+uuid_generate_time(uuid_t out)
+{
+ static unsigned char node_id[6];
+ static int has_init = 0;
+ struct uuid uu;
+ __u32 clock_mid;
+
+ if (!has_init) {
+ if (get_node_id(node_id) <= 0) {
+ get_random_bytes(node_id, 6);
+ /*
+ * Set multicast bit, to prevent conflicts
+ * with IEEE 802 addresses obtained from
+ * network cards
+ */
+ node_id[0] |= 0x80;
+ }
+ has_init = 1;
+ }
+ get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
+ uu.clock_seq |= 0x8000;
+ uu.time_mid = (__u16) clock_mid;
+ uu.time_hi_and_version = (clock_mid >> 16) | 0x1000;
+ memcpy(uu.node, node_id, 6);
+ uuid_pack(&uu, out);
+}
+
+void
+uuid_generate(uuid_t out)
+{
+ if (get_random_fd() >= 0) {
+ uuid_generate_random(out);
+ }else{
+ uuid_generate_time(out);
+ }
+}