[Debian-ha-commits] [cluster-glue] 02/13: Imported Upstream version 1.0.3+hg2366

Richard Winters devrik-guest at moszumanska.debian.org
Sat Apr 18 20:18:08 UTC 2015


This is an automated email from the git hooks/post-receive script.

devrik-guest pushed a commit to branch upstream
in repository cluster-glue.

commit 271f26f2b812e4b3fbf2be63cab5c68dda112c96
Author: Richard B Winters <rik at mmogp.com>
Date:   Sat Apr 18 15:53:55 2015 -0400

    Imported Upstream version 1.0.3+hg2366
---
 .hg_archival.txt                                   |    2 +
 .hgignore                                          |   92 +
 .hgsigs                                            |    3 +
 .hgtags                                            |   55 +
 AUTHORS                                            |   19 +
 COPYING                                            |  339 ++
 COPYING.LIB                                        |  504 +++
 ChangeLog                                          |   74 +
 GNUmakefile                                        |   47 +
 Makefile.am                                        |   35 +
 NEWS                                               |    1 +
 README                                             |    1 +
 autogen.sh                                         |  193 +
 cluster-glue-fedora.spec                           |  224 ++
 cluster-glue-suse.spec                             |  272 ++
 configure.ac                                       | 1356 +++++++
 doc/Makefile.am                                    |   49 +
 doc/ha_logd.xml.in                                 |  134 +
 doc/ha_logger.xml.in                               |  110 +
 doc/hb_report.8.txt                                |  452 +++
 doc/hb_report.xml                                  |  665 ++++
 doc/meatclient.xml.in                              |   77 +
 doc/stonith.xml.in                                 |  315 ++
 doc/stonith/Makefile.am                            |   32 +
 doc/stonith/README.bladehpi                        |  101 +
 doc/stonith/README.cyclades                        |   61 +
 doc/stonith/README.drac3                           |   18 +
 doc/stonith/README.dracmc                          |   87 +
 doc/stonith/README.external                        |   90 +
 doc/stonith/README.ibmrsa                          |    9 +
 doc/stonith/README.ibmrsa-telnet                   |   55 +
 doc/stonith/README.ipmilan                         |  131 +
 doc/stonith/README.meatware                        |   26 +
 doc/stonith/README.rcd_serial                      |  186 +
 doc/stonith/README.riloe                           |   22 +
 doc/stonith/README.vacm                            |   40 +
 doc/stonith/README.wti_mpc                         |   85 +
 doc/stonith/README_kdumpcheck.txt                  |  151 +
 hb_report/Makefile.am                              |   27 +
 hb_report/combine-logs.pl                          |  136 +
 hb_report/ha_cf_support.sh                         |   82 +
 hb_report/hb_report.in                             | 1236 ++++++
 hb_report/openais_conf_support.sh                  |   97 +
 hb_report/utillib.sh                               |  594 +++
 include/Makefile.am                                |   25 +
 include/clplumbing/.cvsignore                      |    6 +
 include/clplumbing/GSource.h                       |  236 ++
 include/clplumbing/GSource_internal.h              |  111 +
 include/clplumbing/Gmain_timeout.h                 |   44 +
 include/clplumbing/Makefile.am                     |   59 +
 include/clplumbing/apphb_cs.h                      |   75 +
 include/clplumbing/base64.h                        |   50 +
 include/clplumbing/cl_log.h                        |   94 +
 include/clplumbing/cl_misc.h                       |   31 +
 include/clplumbing/cl_pidfile.h                    |   25 +
 include/clplumbing/cl_plugin.h                     |   29 +
 include/clplumbing/cl_poll.h                       |   46 +
 include/clplumbing/cl_quorum.h                     |   44 +
 include/clplumbing/cl_quorumd.h                    |   48 +
 include/clplumbing/cl_random.h                     |   23 +
 include/clplumbing/cl_reboot.h                     |    6 +
 include/clplumbing/cl_signal.h                     |   91 +
 include/clplumbing/cl_syslog.h                     |   32 +
 include/clplumbing/cl_tiebreaker.h                 |   35 +
 include/clplumbing/cl_uuid.h                       |   40 +
 include/clplumbing/coredumps.h                     |   36 +
 include/clplumbing/cpulimits.h                     |   66 +
 include/clplumbing/ipc.h                           |  764 ++++
 include/clplumbing/loggingdaemon.h                 |   32 +
 include/clplumbing/longclock.h                     |  143 +
 include/clplumbing/lsb_exitcodes.h                 |   92 +
 include/clplumbing/md5.h                           |   49 +
 include/clplumbing/mkstemp_mode.h                  |   34 +
 include/clplumbing/netstring.h                     |   48 +
 include/clplumbing/proctrack.h                     |  120 +
 include/clplumbing/realtime.h                      |   65 +
 include/clplumbing/replytrack.h                    |  208 ++
 include/clplumbing/setproctitle.h                  |   64 +
 include/clplumbing/timers.h                        |   23 +
 include/clplumbing/uids.h                          |   32 +
 include/compress.h                                 |   50 +
 include/glue_config.h.in                           |   96 +
 include/ha_msg.h                                   |  464 +++
 include/lha_internal.h                             |  163 +
 include/lrm/.cvsignore                             |    6 +
 include/lrm/Makefile.am                            |   22 +
 include/lrm/lrm_api.h                              |  451 +++
 include/lrm/lrm_msg.h                              |  160 +
 include/lrm/racommon.h                             |   29 +
 include/lrm/raexec.h                               |  157 +
 include/pils/.cvsignore                            |    6 +
 include/pils/Makefile.am                           |   25 +
 include/pils/generic.h                             |  118 +
 include/pils/interface.h                           |  159 +
 include/pils/plugin.h.in                           |  733 ++++
 include/replace_uuid.h                             |   50 +
 include/stonith/.cvsignore                         |   10 +
 include/stonith/Makefile.am                        |   25 +
 include/stonith/expect.h                           |   61 +
 include/stonith/st_ttylock.h                       |   21 +
 include/stonith/stonith.h                          |  184 +
 include/stonith/stonith_plugin.h                   |  125 +
 lib/Makefile.am                                    |   20 +
 lib/clplumbing/.cvsignore                          |   14 +
 lib/clplumbing/GSource.c                           | 1861 +++++++++
 lib/clplumbing/Gmain_timeout.c                     |  198 +
 lib/clplumbing/Makefile.am                         |  100 +
 lib/clplumbing/base64.c                            |  422 +++
 lib/clplumbing/base64_md5_test.c                   |  112 +
 lib/clplumbing/cl_compress.c                       |  491 +++
 lib/clplumbing/cl_log.c                            | 1163 ++++++
 lib/clplumbing/cl_malloc.c                         | 1044 ++++++
 lib/clplumbing/cl_misc.c                           |  179 +
 lib/clplumbing/cl_msg.c                            | 2581 +++++++++++++
 lib/clplumbing/cl_msg_types.c                      | 1755 +++++++++
 lib/clplumbing/cl_netstring.c                      |  570 +++
 lib/clplumbing/cl_pidfile.c                        |  294 ++
 lib/clplumbing/cl_plugin.c                         |  140 +
 lib/clplumbing/cl_poll.c                           |  811 ++++
 lib/clplumbing/cl_random.c                         |  234 ++
 lib/clplumbing/cl_reboot.c                         |   59 +
 lib/clplumbing/cl_signal.c                         |  209 ++
 lib/clplumbing/cl_syslog.c                         |  153 +
 lib/clplumbing/cl_uuid.c                           |  180 +
 lib/clplumbing/coredumps.c                         |  309 ++
 lib/clplumbing/cpulimits.c                         |  219 ++
 lib/clplumbing/ipcsocket.c                         | 2897 ++++++++++++++
 lib/clplumbing/ipctest.c                           | 1368 +++++++
 lib/clplumbing/ipctransient.h                      |   50 +
 lib/clplumbing/ipctransientclient.c                |  222 ++
 lib/clplumbing/ipctransientlib.c                   |   97 +
 lib/clplumbing/ipctransientserver.c                |  204 +
 lib/clplumbing/longclock.c                         |  270 ++
 lib/clplumbing/md5.c                               |  335 ++
 lib/clplumbing/mkstemp_mode.c                      |   56 +
 lib/clplumbing/netstring_test.c                    |  255 ++
 lib/clplumbing/ocf_ipc.c                           |  647 ++++
 lib/clplumbing/proctrack.c                         |  515 +++
 lib/clplumbing/realtime.c                          |  354 ++
 lib/clplumbing/replytrack.c                        |  643 ++++
 lib/clplumbing/setproctitle.c                      |  235 ++
 lib/clplumbing/timers.c                            |  119 +
 lib/clplumbing/transient-test.sh                   |  120 +
 lib/clplumbing/uids.c                              |  140 +
 lib/lrm/.cvsignore                                 |   10 +
 lib/lrm/Makefile.am                                |   36 +
 lib/lrm/clientlib.c                                | 1608 ++++++++
 lib/lrm/lrm_msg.c                                  |  212 ++
 lib/lrm/racommon.c                                 |  172 +
 lib/pils/.cvsignore                                |   11 +
 lib/pils/Makefile.am                               |   57 +
 lib/pils/main.c                                    |  122 +
 lib/pils/pils.c                                    | 2151 +++++++++++
 lib/pils/test.c                                    |  107 +
 lib/plugins/InterfaceMgr/HBauth.c                  |  171 +
 lib/plugins/InterfaceMgr/Makefile.am               |   33 +
 lib/plugins/InterfaceMgr/generic.c                 |  452 +++
 lib/plugins/Makefile.am                            |   20 +
 lib/plugins/lrm/.cvsignore                         |   11 +
 lib/plugins/lrm/Makefile.am                        |   41 +
 lib/plugins/lrm/raexechb.c                         |  416 +++
 lib/plugins/lrm/raexeclsb.c                        |  605 +++
 lib/plugins/lrm/raexecocf.c                        |  492 +++
 lib/plugins/stonith/.cvsignore                     |   11 +
 lib/plugins/stonith/Makefile.am                    |  214 ++
 lib/plugins/stonith/apcmaster.c                    |  822 ++++
 lib/plugins/stonith/apcmastersnmp.c                |  890 +++++
 lib/plugins/stonith/apcmastersnmp.cfg.example      |   39 +
 lib/plugins/stonith/apcsmart.c                     | 1028 +++++
 lib/plugins/stonith/apcsmart.cfg.example           |    1 +
 lib/plugins/stonith/baytech.c                      |  924 +++++
 lib/plugins/stonith/bladehpi.c                     | 1101 ++++++
 lib/plugins/stonith/cyclades.c                     |  633 ++++
 lib/plugins/stonith/drac3.c                        |  359 ++
 lib/plugins/stonith/drac3_command.c                |  342 ++
 lib/plugins/stonith/drac3_command.h                |   29 +
 lib/plugins/stonith/drac3_hash.c                   |  106 +
 lib/plugins/stonith/drac3_hash.h                   |   28 +
 lib/plugins/stonith/external.c                     |  845 +++++
 lib/plugins/stonith/external/.cvsignore            |    5 +
 lib/plugins/stonith/external/Makefile.am           |   32 +
 lib/plugins/stonith/external/drac5.in              |  113 +
 lib/plugins/stonith/external/dracmc-telnet         |  377 ++
 lib/plugins/stonith/external/hmchttp               |  218 ++
 lib/plugins/stonith/external/ibmrsa                |  157 +
 lib/plugins/stonith/external/ibmrsa-telnet         |  319 ++
 lib/plugins/stonith/external/ipmi                  |  215 ++
 lib/plugins/stonith/external/kdumpcheck.in         |  274 ++
 lib/plugins/stonith/external/rackpdu               |  270 ++
 lib/plugins/stonith/external/riloe                 |  486 +++
 lib/plugins/stonith/external/sbd                   |   91 +
 lib/plugins/stonith/external/ssh.in                |  176 +
 lib/plugins/stonith/external/vmware                |  216 ++
 lib/plugins/stonith/external/xen0                  |  253 ++
 .../stonith/external/xen0-ha-dom0-stonith-helper   |   72 +
 lib/plugins/stonith/external/xen0-ha.in            |   96 +
 lib/plugins/stonith/ibmhmc.c                       | 1261 +++++++
 lib/plugins/stonith/ipmi_os_handler.c              |  257 ++
 lib/plugins/stonith/ipmilan.c                      |  586 +++
 lib/plugins/stonith/ipmilan.h                      |   41 +
 lib/plugins/stonith/ipmilan_command.c              |  398 ++
 lib/plugins/stonith/ipmilan_test.c                 |   63 +
 lib/plugins/stonith/meatware.c                     |  349 ++
 lib/plugins/stonith/null.c                         |  260 ++
 lib/plugins/stonith/nw_rpc100s.c                   |  779 ++++
 lib/plugins/stonith/rcd_serial.c                   |  602 +++
 lib/plugins/stonith/rhcs.c                         | 1026 +++++
 lib/plugins/stonith/ribcl.py.in                    |  101 +
 lib/plugins/stonith/riloe.c                        |  338 ++
 lib/plugins/stonith/rps10.c                        | 1070 ++++++
 lib/plugins/stonith/ssh.c                          |  351 ++
 lib/plugins/stonith/stonith_config_xml.h           |  157 +
 lib/plugins/stonith/stonith_expect_helpers.h       |  120 +
 lib/plugins/stonith/stonith_plugin_common.h        |  127 +
 lib/plugins/stonith/stonith_signal.h               |   68 +
 lib/plugins/stonith/suicide.c                      |  274 ++
 lib/plugins/stonith/vacm.c                         |  485 +++
 lib/plugins/stonith/wti_mpc.c                      |  856 +++++
 lib/plugins/stonith/wti_nps.c                      |  813 ++++
 lib/stonith/.cvsignore                             |   13 +
 lib/stonith/Makefile.am                            |   61 +
 lib/stonith/README                                 |   31 +
 lib/stonith/expect.c                               |  539 +++
 lib/stonith/ha_log.sh                              |  104 +
 lib/stonith/main.c                                 |  615 +++
 lib/stonith/meatclient.c                           |  152 +
 lib/stonith/sbd.c                                  |  961 +++++
 lib/stonith/sbd.h                                  |   91 +
 lib/stonith/st_ttylock.c                           |  225 ++
 lib/stonith/stonith.c                              |  581 +++
 logd/.cvsignore                                    |    7 +
 logd/Makefile.am                                   |   52 +
 logd/ha_logd.c                                     | 1029 +++++
 logd/ha_logger.1                                   |   38 +
 logd/ha_logger.c                                   |  166 +
 logd/logd.cf                                       |   44 +
 logd/logd.in                                       |   99 +
 logd/logtest.c                                     |  126 +
 lrm/.cvsignore                                     |   10 +
 lrm/Makefile.am                                    |   20 +
 lrm/admin/.cvsignore                               |   11 +
 lrm/admin/Makefile.am                              |   31 +
 lrm/admin/lrmadmin.c                               | 1124 ++++++
 lrm/admin/lrmadmin.txt                             |   60 +
 lrm/lrmd/.cvsignore                                |   11 +
 lrm/lrmd/Makefile.am                               |   42 +
 lrm/lrmd/audit.c                                   |  191 +
 lrm/lrmd/lrmd.c                                    | 3937 ++++++++++++++++++++
 lrm/lrmd/lrmd.h                                    |  265 ++
 lrm/lrmd/lrmd_fdecl.h                              |  110 +
 lrm/test/.cvsignore                                |   19 +
 lrm/test/LRMBasicSanityCheck.in                    |   55 +
 lrm/test/Makefile.am                               |   48 +
 lrm/test/README.regression                         |  161 +
 lrm/test/apitest.c                                 |  318 ++
 lrm/test/apitest.exp                               |  122 +
 lrm/test/callbacktest.c                            |  205 +
 lrm/test/defaults                                  |    9 +
 lrm/test/descriptions                              |   55 +
 lrm/test/evaltest.sh                               |  167 +
 lrm/test/language                                  |   16 +
 lrm/test/lrmadmin-interface                        |   43 +
 lrm/test/lrmregtest-heartbeat.in                   |   17 +
 lrm/test/lrmregtest-lsb.in                         |   12 +
 lrm/test/lrmregtest.in                             |  220 ++
 lrm/test/plugintest.c                              |   84 +
 lrm/test/regression.sh.in                          |  262 ++
 lrm/test/testcases/.cvsignore                      |   10 +
 lrm/test/testcases/BSC                             |    3 +
 lrm/test/testcases/Makefile.am                     |   26 +
 lrm/test/testcases/basicset                        |    5 +
 lrm/test/testcases/common.filter                   |   27 +
 lrm/test/testcases/flood                           |   19 +
 lrm/test/testcases/flood.exp                       | 1354 +++++++
 lrm/test/testcases/metadata                        |   29 +
 lrm/test/testcases/metadata.exp                    |   38 +
 lrm/test/testcases/ra-list.sh                      |   12 +
 lrm/test/testcases/rscexec                         |   62 +
 lrm/test/testcases/rscexec.exp                     |  229 ++
 lrm/test/testcases/rscmgmt                         |   34 +
 lrm/test/testcases/rscmgmt.exp                     |   87 +
 lrm/test/testcases/rscmgmt.log_filter              |   13 +
 lrm/test/testcases/serialize                       |   33 +
 lrm/test/testcases/serialize.exp                   |  100 +
 lrm/test/testcases/xmllint.sh                      |   20 +
 replace/Makefile.am                                |   29 +
 replace/NoSuchFunctionName.c                       |   31 +
 replace/alphasort.c                                |   53 +
 replace/daemon.c                                   |   83 +
 replace/inet_pton.c                                |  245 ++
 replace/scandir.c                                  |  236 ++
 replace/setenv.c                                   |   50 +
 replace/strerror.c                                 |   37 +
 replace/strlcat.c                                  |   33 +
 replace/strlcpy.c                                  |   32 +
 replace/strndup.c                                  |   38 +
 replace/strnlen.c                                  |   31 +
 replace/unsetenv.c                                 |   51 +
 replace/uuid_parse.c                               |  519 +++
 299 files changed, 79950 insertions(+)

diff --git a/.hg_archival.txt b/.hg_archival.txt
new file mode 100644
index 0000000..f3eb368
--- /dev/null
+++ b/.hg_archival.txt
@@ -0,0 +1,2 @@
+repo: e3ffdd7ae81c596b2be7e1e110d2c1255161340e
+node: 6c8645d6a4c2d6452aa7cd29eca05c7d5dfd8ca8
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..1c5b645
--- /dev/null
+++ b/.hgsigs
@@ -0,0 +1,3 @@
+b6dca003bb176978af803eeb33019b6aef3c58b0 0 iEYEABECAAYFAktnGJAACgkQWnQN9wr0w1ywBACghXYwYkv/70Xg5AQMzVjRWKZecIoAnjRUytRoYl+dhhqbhfdXSD+/Bfvw
+6007185b487e3f2dc3b24674a9105761b2cde6ea 0 iEYEABECAAYFAktoWfsACgkQWnQN9wr0w1ySZwCfQILyC2VJrCnVEU2zvTIyI7ustDAAn37hhb9JM8JQVKLfPEbqIloz1m3m
+979c4ffae287976631a30d10258903aea6fb28fa 0 iEYEABECAAYFAktoY38ACgkQWnQN9wr0w1wHxgCeMZyOt8ccxmIsvIHg4/y6KmqtTVAAn2jn7dOmFMjA8m4ju59YaQ1Bznhb
diff --git a/.hgtags b/.hgtags
new file mode 100644
index 0000000..2d7e703
--- /dev/null
+++ b/.hgtags
@@ -0,0 +1,55 @@
+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
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..ec67fd0
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,19 @@
+Alan Robertson <alanr at unix.sh>
+Andreas Mock <andreas.mock at web.de>
+Andrew Beekhof <andrew at beekhof.net>
+Dave Blaschke <debltc at us.ibm.com>
+David Lee <t.d.lee at durham.ac.uk>
+Dejan Muhamedagic <dejan at hello-penguin.com>
+Hannes Eder <heder at google.com>
+Huang Zhen <zhenhltc at cn.ibm.com>
+Junko Ikeda <ikedaj at intellilink.co.jp>
+Lars Marowsky-Bree <lmb at suse.de>
+Martin Bene <martin.bene at icomedias.com>
+Phil Carns <carns at mcs.anl.gov>
+Satomi Taniguchi <taniguchis at intellilink.co.jp>
+Sean Reifschneider <jafo at tummy.com>
+Sebastian Reitenbach <itlistuser at rapideye.de>
+Serge Dubrouski <sergeyfd at gmail.com>
+Simon Horman <horms at verge.net.au>
+Xinwei Hu <hxinwei at 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..a27d2ae
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,74 @@
+* Tue Feb 02 2010 Dejan Muhamedagic <dejan at 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 at 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 at 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 at 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 at 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..e92c925
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,35 @@
+#
+# 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 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
+
+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 at 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..06f3143
--- /dev/null
+++ b/cluster-glue-fedora.spec
@@ -0,0 +1,224 @@
+# 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.3
+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
+
+# 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
+BuildRequires: OpenIPMI-devel openssl-devel
+BuildRequires: libxslt docbook-dtds docbook-style-xsl
+
+%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} < 12
+BuildRequires: e2fsprogs-devel
+%else
+BuildRequires: libuuid-devel
+%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} \
+    --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}
+%{_sysconfdir}/init.d/logd
+%{_datadir}/%{name}/ha_cf_support.sh
+%{_datadir}/%{name}/openais_conf_support.sh
+%{_datadir}/%{name}/utillib.sh
+%{_datadir}/%{name}/combine-logs.pl
+%{_datadir}/%{name}/ha_log.sh
+
+%{_sbindir}/ha_logger
+%{_sbindir}/hb_report
+%{_sbindir}/lrmadmin
+%{_sbindir}/meatclient
+%{_sbindir}/stonith
+%{_sbindir}/sbd
+%dir %{_libdir}/heartbeat
+%dir %{_libdir}/heartbeat/plugins
+%dir %{_libdir}/heartbeat/plugins/RAExec
+%dir %{_libdir}/heartbeat/plugins/InterfaceMgr
+%{_libdir}/heartbeat/lrmd
+%{_libdir}/heartbeat/ha_logd
+%{_libdir}/heartbeat/plugins/RAExec/*.so
+%{_libdir}/heartbeat/plugins/InterfaceMgr/*.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
+Requires:	%{name} = %{version}-%{release}
+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
+
+%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:	%{name} = %{version}-%{release}
+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..2193908
--- /dev/null
+++ b/cluster-glue-suse.spec
@@ -0,0 +1,272 @@
+#
+# 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.3
+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
+
+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
+
+%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} \
+    --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
+
+test -d $RPM_BUILD_ROOT/sbin || mkdir $RPM_BUILD_ROOT/sbin
+(
+  cd $RPM_BUILD_ROOT/sbin
+  ln -s /etc/init.d/logd rclogd
+)
+
+###########################################################
+
+%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
+
+%preun
+%stop_on_removal logd
+
+%post
+%{insserv_force_if_yast logd}
+
+%postun
+%insserv_cleanup
+
+%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}/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}/combine-logs.pl
+%{_datadir}/%{name}/ha_log.sh
+
+%{_sbindir}/ha_logger
+%{_sbindir}/hb_report
+%{_sbindir}/lrmadmin
+%{_sbindir}/meatclient
+%{_sbindir}/stonith
+%{_sbindir}/sbd
+
+%{_sysconfdir}/init.d/logd
+
+%doc %{_mandir}/man1/*
+%doc %{_mandir}/man8/*
+%doc AUTHORS
+%doc COPYING
+%doc ChangeLog
+%doc logd/logd.cf
+%doc doc/stonith/README*
+
+/sbin/rclogd
+
+%{_libdir}/heartbeat/lrmd
+%{_libdir}/heartbeat/ha_logd
+
+%{_libdir}/heartbeat/plugins/RAExec/*.so
+%{_libdir}/heartbeat/plugins/InterfaceMgr/*.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/configure.ac b/configure.ac
new file mode 100644
index 0000000..3701971
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,1356 @@
+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.3, linux-ha-dev at lists.linux-ha.org)
+
+PKG_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" ])
+
+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_STRUCT_TIMEZONE
+
+dnl ===============================================
+dnl Helpers 
+dnl ===============================================
+cc_supports_flag() {
+         local CFLAGS="$@"
+         AC_MSG_CHECKING(whether $CC supports "$@")
+         AC_COMPILE_IFELSE([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=""
+
+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" ],
+  )
+
+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)
+
+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)
+BUILD_VERSION=unknown
+if test -f $srcdir/.hg_archival.txt; then
+   BUILD_VERSION=`cat $srcdir/.hg_archival.txt | awk '/node:/ { print $2 }'`
+elif test -x $HG -a -d .hg; then
+   BUILD_VERSION=`$HG id -itb`
+   if test $? != 0; then
+       BUILD_VERSION=unknown
+   fi
+fi
+
+AC_DEFINE_UNQUOTED(BUILD_VERSION, "$BUILD_VERSION", Build version)
+AC_MSG_RESULT($BUILD_VERSION)
+AC_SUBST(BUILD_VERSION)
+
+
+dnl check if there are getpid() inconsistency
+dnl	Note: reduce LIBS; in particular, ltdl can cause confusion.
+dnl	Possibly better:  move 'LIBS="$LIBS -lltdl"' from above to beyond here.
+dnl
+AC_MSG_CHECKING(for getpid() consistency in multi-process/threads program)
+ac_save_LIBS=$LIBS
+LIBS="-lpthread"
+AC_TRY_RUN(`cat $srcdir/config/pidtest.c`, 
+AC_MSG_RESULT(ok), 
+[AC_MSG_RESULT(fail); AC_DEFINE(GETPID_INCONSISTENT, 1 , [pid inconsistent])],)
+LIBS=$ac_save_LIBS
+
+dnl check byte order
+AC_MSG_CHECKING(for byteorder)
+AC_TRY_RUN(`cat $srcdir/config/byteorder_test.c`,
+[AC_MSG_RESULT(little-endian); AC_DEFINE(CONFIG_LITTLE_ENDIAN, 1, [little-endian])],
+[AC_MSG_RESULT(big-endian); AC_DEFINE(CONFIG_BIG_ENDIAN, 1, [big-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_PATH_PROGS(XSLTPROC, xsltproc)
+AM_CONDITIONAL(BUILD_DOC, test "x$XSLTPROC" != "x" )
+if test "x$XSLTPROC" = "x"; then
+   AC_MSG_WARN([xsltproc not installed, unable to (re-)build manual pages])
+fi
+
+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
+   PKG_FEATURES="$PKG_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)
+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 '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_long -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=$withval], [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=`net-snmp-config --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    BZ2
+dnl ========================================================================
+
+AC_CHECK_HEADERS(bzlib.h)
+AC_CHECK_LIB(bz2, BZ2_bzBuffToBuffCompress)
+
+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 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
+	export -n CFLAGS || true # We don't want to bomb out if this fails
+fi
+
+if test "$GCC" != yes; then
+        CFLAGS="$CFLAGS -g"
+	enable_fatal_warnings=no
+else
+        CFLAGS="$CFLAGS -ggdb3 -O0"
+
+	# 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				        \
+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/lrm/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/xen0-ha		\
+lrm/Makefile					        	\
+   lrm/lrmd/Makefile				        	\
+   lrm/admin/Makefile				        	\
+   lrm/test/Makefile				        	\
+   lrm/test/regression.sh					\
+   lrm/test/lrmregtest						\
+   lrm/test/lrmregtest-lsb					\
+   lrm/test/lrmregtest-heartbeat				\
+   lrm/test/LRMBasicSanityCheck			        	\
+   lrm/test/testcases/Makefile			        	\
+logd/Makefile					        	\
+logd/logd					        	\
+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: $BUILD_VERSION)])
+AC_MSG_RESULT([  Features                 =${PKG_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..9181dc0
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,49 @@
+#
+# 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
+endif
+
+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) $<
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 at unix.sh</email>
+      </author>
+      <author>
+	<surname>Shi</surname>
+	<firstname>Guochun</firstname>
+	<contrib>ha_logd</contrib>
+	<email>gshi at ncsa.uiuc.edu</email>
+      </author>
+      <author>
+	<surname>Lars</surname>
+	<firstname>Marowsky-Bree</firstname>
+	<contrib>ha_logd</contrib>
+	<email>lmb at suse.de</email>
+      </author>
+      <author>
+	<firstname>Florian</firstname>
+	<surname>Haas</surname>
+	<contrib>man page</contrib>
+	<email>florian.haas at 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> – PID file</para>
+      </listitem>
+      <listitem>
+	<para><filename>ha_logd.cf</filename> – 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 at unix.sh</email>
+      </author>
+      <author>
+	<surname>Shi</surname>
+	<firstname>Guochun</firstname>
+	<contrib>ha_logd</contrib>
+	<email>gshi at ncsa.uiuc.edu</email>
+      </author>
+      <author>
+	<surname>Lars</surname>
+	<firstname>Marowsky-Bree</firstname>
+	<contrib>ha_logd</contrib>
+	<email>lmb at suse.de</email>
+      </author>
+      <author>
+	<firstname>Florian</firstname>
+	<surname>Haas</surname>
+	<contrib>man page</contrib>
+	<email>florian.haas at 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..232851a
--- /dev/null
+++ b/doc/hb_report.8.txt
@@ -0,0 +1,452 @@
+: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] [-MSDCZAVsvhd] [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 destination directory. Must be an absolute path. The
+	resulting tarball is placed in the parent directory and
+	contains the last directory element of this path. Typically
+	something like /tmp/standby-failed. If left out, the tarball
+	is created in your home 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.*").
+
+*-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.
+
+*-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 destination directories exist, remove them 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" /tmp/report
+
+collects everything from all nodes from 3am to 4am last night.
+The files are compressed to a tarball /tmp/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 /tmp/hb1
+
+	# if hb_report can't figure out that this is openais
+	node1# hb_report -f 18:51 -A /tmp/hb1
+
+	# if hb_report can't figure out the cluster members
+	node1# hb_report -f 18:51 -n "node1 node2" /tmp/hb1
+
+The files are compressed to a tarball /tmp/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. 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
+	please use the `-u` option.
+
+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
+
+If you reconsider and want the ssh setup, take a look at the CTS
+README file for instructions.
+
+
+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 (openais, 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 at 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/hb_report.xml b/doc/hb_report.xml
new file mode 100644
index 0000000..87baa9e
--- /dev/null
+++ b/doc/hb_report.xml
@@ -0,0 +1,665 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry lang="en">
+<refmeta>
+<refentrytitle>hb_report</refentrytitle>
+<manvolnum>8</manvolnum>
+<refmiscinfo class="source">hb_report</refmiscinfo>
+<refmiscinfo class="version">1.2</refmiscinfo>
+<refmiscinfo class="manual">Pacemaker documentation</refmiscinfo>
+</refmeta>
+<refnamediv>
+    <refname>hb_report</refname>
+    <refpurpose>create report for CRM based clusters (Pacemaker)</refpurpose>
+</refnamediv>
+<refsynopsisdiv>
+<simpara><emphasis role="strong">hb_report</emphasis> -f {time|"cts:"testnum} [-t time] [-u user] [-l file]
+       [-n nodes] [-E files] [-p patt] [-L patt] [-e prog] [-MSDCZAVsvhd] [dest]</simpara>
+</refsynopsisdiv>
+<refsect1 id="_description">
+<title>DESCRIPTION</title>
+<simpara>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.</simpara>
+</refsect1>
+<refsect1 id="_options">
+<title>OPTIONS</title>
+<variablelist>
+<varlistentry>
+<term>
+dest
+</term>
+<listitem>
+<simpara>
+        The destination directory. Must be an absolute path. The
+        resulting tarball is placed in the parent directory and
+        contains the last directory element of this path. Typically
+        something like /tmp/standby-failed. If left out, the tarball
+        is created in your home directory named "hb_report-current_date",
+        for instance hb_report-Wed-03-Mar-2010.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-d</emphasis>
+</term>
+<listitem>
+<simpara>
+        Don’t create the compressed tar, but leave the result in a
+        directory.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-f</emphasis> { time | "cts:"testnum }
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-t</emphasis> time
+</term>
+<listitem>
+<simpara>
+        The end time to which to collect logs. Defaults to now.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-n</emphasis> nodes
+</term>
+<listitem>
+<simpara>
+        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).
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-l</emphasis> file
+</term>
+<listitem>
+<simpara>
+        Log file location. If, for whatever reason, hb_report cannot
+        find the log files, you can specify its absolute path.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-E</emphasis> files
+</term>
+<listitem>
+<simpara>
+        Extra log files to collect. This option is cumulative. By
+        default, /var/log/messages are collected along with the
+        cluster logs.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-M</emphasis>
+</term>
+<listitem>
+<simpara>
+        Don’t collect extra log files, but only the file containing
+        messages from the cluster subsystems.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-L</emphasis> patt
+</term>
+<listitem>
+<simpara>
+        A list of regular expressions to match in log files for
+        analysis. This option is additive (default: "CRIT: ERROR:").
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-p</emphasis> patt
+</term>
+<listitem>
+<simpara>
+        Additional patterns to match parameter name which contain
+        sensitive information. This option is additive (default: "passw.*").
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-A</emphasis>
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-u</emphasis> user
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-S</emphasis>
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-Z</emphasis>
+</term>
+<listitem>
+<simpara>
+        If destination directories exist, remove them instead of exiting
+        (this is default for CTS).
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-V</emphasis>
+</term>
+<listitem>
+<simpara>
+        Print the version including the last repository changeset.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-v</emphasis>
+</term>
+<listitem>
+<simpara>
+        Increase verbosity. Normally used to debug unexpected
+        behaviour.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-h</emphasis>
+</term>
+<listitem>
+<simpara>
+        Show usage and some examples.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-D</emphasis> (obsolete)
+</term>
+<listitem>
+<simpara>
+        Don’t invoke editor to fill the description text file.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-e</emphasis> prog (obsolete)
+</term>
+<listitem>
+<simpara>
+        Your favourite text editor. Defaults to $EDITOR, vim, vi,
+        emacs, or nano, whichever is found first.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">-C</emphasis> (obsolete)
+</term>
+<listitem>
+<simpara>
+        Remove the destination directory once the report has been put
+        in a tarball.
+</simpara>
+</listitem>
+</varlistentry>
+</variablelist>
+</refsect1>
+<refsect1 id="_examples">
+<title>EXAMPLES</title>
+<simpara>Last night during the backup there were several warnings
+encountered (logserver is the log host):</simpara>
+<literallayout class="monospaced">logserver# hb_report -f 3:00 -t 4:00 -n "node1 node2" /tmp/report</literallayout>
+<simpara>collects everything from all nodes from 3am to 4am last night.
+The files are compressed to a tarball /tmp/report.tar.bz2.</simpara>
+<simpara>Just found a problem during testing:</simpara>
+<literallayout class="monospaced"># 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 /tmp/hb1</literallayout>
+<literallayout class="monospaced"># if hb_report can't figure out that this is openais
+node1# hb_report -f 18:51 -A /tmp/hb1</literallayout>
+<literallayout class="monospaced"># if hb_report can't figure out the cluster members
+node1# hb_report -f 18:51 -n "node1 node2" /tmp/hb1</literallayout>
+<simpara>The files are compressed to a tarball /tmp/hb1.tar.bz2.</simpara>
+</refsect1>
+<refsect1 id="_interpreting_results">
+<title>INTERPRETING RESULTS</title>
+<simpara>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:</simpara>
+<literallayout class="monospaced">$ ls -RF 001-Restart</literallayout>
+<literallayout class="monospaced">001-Restart:
+analysis.txt     events.txt  logd.cf       s390vm13/  s390vm16/
+description.txt  ha-log.txt  openais.conf  s390vm14/</literallayout>
+<literallayout class="monospaced">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</literallayout>
+<literallayout class="monospaced">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</literallayout>
+<literallayout class="monospaced">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</literallayout>
+<literallayout class="monospaced">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</literallayout>
+<simpara>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).</simpara>
+<simpara>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. Files named
+messages are excerpts of /var/log/messages from nodes.</simpara>
+<simpara>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.</simpara>
+<simpara>Some files are result of a more involved processing:</simpara>
+<variablelist>
+<varlistentry>
+<term>
+<emphasis role="strong">analysis.txt</emphasis>
+</term>
+<listitem>
+<simpara>
+        A set of log messages matching user defined patterns (may be
+        provided with the -L option).
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">events.txt</emphasis>
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">permissions.txt</emphasis>
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">backtraces.txt</emphasis>
+</term>
+<listitem>
+<simpara>
+        gdb generated backtrace information for cores dumped
+        within the specified period.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">sysinfo.txt</emphasis>
+</term>
+<listitem>
+<simpara>
+        Various release information about the platform, kernel,
+        operating system, packages, and anything else deemed to be
+        relevant. The static part of the system.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<emphasis role="strong">sysstats.txt</emphasis>
+</term>
+<listitem>
+<simpara>
+        Output of various system commands such as ps(1), uptime(1),
+        netstat(8), and ifconfig(8). The dynamic part of the system.
+</simpara>
+</listitem>
+</varlistentry>
+</variablelist>
+<simpara>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.</simpara>
+</refsect1>
+<refsect1 id="_prerequisites">
+<title>PREREQUISITES</title>
+<variablelist>
+<varlistentry>
+<term>
+ssh
+</term>
+<listitem>
+<simpara>
+        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.
+       <?asciidoc-br?>
+        If you need to supply a password for your passphrase/login, then
+        please use the <literal>-u</literal> option.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+Times
+</term>
+<listitem>
+<simpara>
+        In order to find files and messages in the given period and to
+        parse the <literal>-f</literal> and <literal>-t</literal> options, <literal>hb_report</literal> uses perl and one of the
+        <literal>Date::Parse</literal> or <literal>Date::Manip</literal> perl modules. Note that you need
+        only one of these. Furthermore, on nodes which have no logs and
+        where you don’t run <literal>hb_report</literal> 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.
+       <?asciidoc-br?>
+        On rpm based distributions, you can find <literal>Date::Parse</literal> in
+        <literal>perl-TimeDate</literal> and on Debian and its derivatives in
+        <literal>libtimedate-perl</literal>.
+</simpara>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+Core dumps
+</term>
+<listitem>
+<simpara>
+        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.
+</simpara>
+</listitem>
+</varlistentry>
+</variablelist>
+</refsect1>
+<refsect1 id="_times">
+<title>TIMES</title>
+<simpara>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
+<ulink url="http://search.cpan.org/dist/TimeDate/lib/Date/Parse.pm#EXAMPLE_DATES">Date::Parse
+examples page</ulink>.
+or just rely on common sense and try stuff like:</simpara>
+<literallayout class="monospaced">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)</literallayout>
+<simpara><literal>hb_report</literal> will (probably) complain if it can’t figure out what do
+you mean.</simpara>
+<simpara>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.</simpara>
+<simpara><literal>-f</literal> is not optional. And don’t forget to quote dates when they
+contain spaces.</simpara>
+</refsect1>
+<refsect1 id="_should_i_send_all_this_to_the_rest_of_internet">
+<title>Should I send all this to the rest of Internet?</title>
+<simpara>By default, the sensitive data in CIB and PE files is not mangled
+by <literal>hb_report</literal> 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 <literal>-s</literal> option. Without this option, <literal>hb_report</literal>
+will issue a warning if it finds information which should not be
+exposed. By default, parameters matching <emphasis>passw.*</emphasis> are considered
+sensitive.  Use the <literal>-p</literal> option to specify additional regular
+expressions to match variable names which may contain information
+you don’t want to leak. For example:</simpara>
+<literallayout class="monospaced"># hb_report -f 18:00 -p "user.*" -p "secret.*" /var/tmp/report</literallayout>
+<simpara>Heartbeat’s ha.cf is always sanitized. Logs and other files are
+not filtered.</simpara>
+</refsect1>
+<refsect1 id="_logs">
+<title>LOGS</title>
+<simpara>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:</simpara>
+<literallayout class="monospaced"># hb_report -f 5pm -l /var/log/cluster1/ha-log -S /tmp/report_node1</literallayout>
+<simpara>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.</simpara>
+<simpara>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.</simpara>
+<simpara>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).</simpara>
+<simpara>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.</simpara>
+</refsect1>
+<refsect1 id="_manual_report_collection">
+<title>MANUAL REPORT COLLECTION</title>
+<simpara>So, your ssh doesn’t work. In that case, you will have to run
+this procedure on all nodes. Use <literal>-S</literal> so that <literal>hb_report</literal> doesn’t
+bother with ssh:</simpara>
+<literallayout class="monospaced"># hb_report -f 5:20pm -t 5:30pm -S /tmp/report_node1</literallayout>
+<simpara>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:</simpara>
+<literallayout class="monospaced"># hb_report -f 5:20pm -t 5:30pm -l /var/tmp/ha-log -S /tmp/report_node1</literallayout>
+<simpara>If you reconsider and want the ssh setup, take a look at the CTS
+README file for instructions.</simpara>
+</refsect1>
+<refsect1 id="_operation">
+<title>OPERATION</title>
+<simpara>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.</simpara>
+<simpara>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.</simpara>
+<simpara>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.</simpara>
+<simpara>The second phase is the most involved. During this phase all
+local information is collected, which includes:</simpara>
+<itemizedlist>
+<listitem>
+<simpara>
+logs (both current and archived if the start time is far in the past)
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+various configuration files (openais, heartbeat, logd)
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+the CIB (both as xml and as represented by the crm shell)
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+pengine inputs (if this node was the DC at any point in
+  time over the given period)
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+system information and status
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+package information and status
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+dlm lock information
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+backtraces (if there were core dumps)
+</simpara>
+</listitem>
+</itemizedlist>
+<simpara>The third phase is collecting information from all nodes and
+analyzing it. The analyzis consists of the following tasks:</simpara>
+<itemizedlist>
+<listitem>
+<simpara>
+identify files equal on all nodes which may then be moved to
+  the top directory
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+save log messages matching user defined patterns
+  (defaults to ERRORs and CRITical conditions)
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+report if there were coredumps and by whom
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+report crm_verify(8) results
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+save log messages matching major events to events.txt
+</simpara>
+</listitem>
+<listitem>
+<simpara>
+in case logging is configured without loghost, node logs and
+  events files are combined using a perl utility
+</simpara>
+</listitem>
+</itemizedlist>
+</refsect1>
+<refsect1 id="_bugs">
+<title>BUGS</title>
+<simpara>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.</simpara>
+<simpara>If you think you found a bug, please rerun with the -v option and
+attach the output to bugzilla.</simpara>
+<simpara>hb_report can function in a satisfactory way only if ssh works to
+all nodes using authorized_keys (without password).</simpara>
+<simpara>There are way too many options.</simpara>
+</refsect1>
+<refsect1 id="_author">
+<title>AUTHOR</title>
+<simpara>Written by Dejan Muhamedagic, <<ulink url="mailto:dejan at suse.de">dejan at suse.de</ulink>></simpara>
+</refsect1>
+<refsect1 id="_resources">
+<title>RESOURCES</title>
+<simpara>Pacemaker: <ulink url="http://clusterlabs.org/">http://clusterlabs.org/</ulink></simpara>
+<simpara>Heartbeat and other Linux HA resources: <ulink url="http://linux-ha.org/wiki">http://linux-ha.org/wiki</ulink></simpara>
+<simpara>OpenAIS: <ulink url="http://www.openais.org/">http://www.openais.org/</ulink></simpara>
+<simpara>Corosync: <ulink url="http://www.corosync.org/">http://www.corosync.org/</ulink></simpara>
+</refsect1>
+<refsect1 id="_see_also">
+<title>SEE ALSO</title>
+<simpara>Date::Parse(3)</simpara>
+</refsect1>
+<refsect1 id="_copying">
+<title>COPYING</title>
+<simpara>Copyright (C) 2007-2009 Dejan Muhamedagic. Free use of this
+software is granted under the terms of the GNU General Public License (GPL).</simpara>
+</refsect1>
+</refentry>
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 at sysfive.com</email>
+      </author>
+      <author>
+	<firstname>Michael</firstname>
+	<surname>Mörz</surname>
+	<contrib>man page</contrib>
+	<email>mimem at debian.org</email>
+      </author>
+      <author>
+	<firstname>Simon</firstname>
+	<surname>Horman</surname>
+	<contrib>man page</contrib>
+	<email>horms at vergenet.net</email>
+      </author>
+      <author>
+	<firstname>Florian</firstname>
+	<surname>Haas</surname>
+	<contrib>man page</contrib>
+	<email>florian.haas at 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 at unix.sh</email>
+      </author>
+      <author>
+	<firstname>Simon</firstname>
+	<surname>Horman</surname>
+	<contrib>man page</contrib>
+	<email>horms at vergenet.net</email>
+      </author>
+      <author>
+	<firstname>Florian</firstname>
+	<surname>Haas</surname>
+	<contrib>man page</contrib>
+	<email>florian.haas at 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..3cb8248
--- /dev/null
+++ b/doc/stonith/Makefile.am
@@ -0,0 +1,32 @@
+# 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.ipmilan \
+			  README.meatware \
+			  README.rcd_serial \
+			  README.riloe \
+			  README.vacm \
+			  README.wti_mpc \
+			  README_kdumpcheck.txt
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 at 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 at clusterhost]# scp /root/.ssh/id_rsa.pub root at alterpath:/tmp 
+
+login to the TS/ACS/KVM box normally and append the public key. 
+
+# ssh root at alterpath 
+Password: ....
+
+[root at 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 at clusterhost root]# ssh root at alterpath
+[root at 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 at cyclades.com> 
+or <marcelo.tosatti at 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 at 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 at 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.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 at sysfive.com>
+
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 at scl.co.uk
+October 2002
diff --git a/doc/stonith/README.riloe b/doc/stonith/README.riloe
new file mode 100644
index 0000000..ccd365a
--- /dev/null
+++ b/doc/stonith/README.riloe
@@ -0,0 +1,22 @@
+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.
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 at compgen.com
+
+------------------------------------------------------------------------------
+
+From: Mike Tilstra <conrad at sistina.com>
+Sender: linux-ha-dev-admin at lists.tummy.com
+To: linux-ha-dev at 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 at sistina.com
\ No newline at end of file
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 at 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 at target_node:"~/.ssh/."
+           $ ssh kdumpchecker at 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 at 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..574f9e1
--- /dev/null
+++ b/hb_report/Makefile.am
@@ -0,0 +1,27 @@
+#
+# 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_SCRIPTS	= combine-logs.pl
+hanoarch_DATA	= utillib.sh ha_cf_support.sh openais_conf_support.sh
+sbin_SCRIPTS		= hb_report
+
diff --git a/hb_report/combine-logs.pl b/hb_report/combine-logs.pl
new file mode 100755
index 0000000..2cfacd7
--- /dev/null
+++ b/hb_report/combine-logs.pl
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+#
+# combine-logs v1.0
+#
+# Copyright (c) 1999 Steven J. Madsen.  All rights reserved.
+#
+# Combines multiple syslog-format logs into a single chronological log.  Very
+# handy for syslog report generators such as cksyslog.
+#
+# usage: combine-logs <log file> [...]
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Note by Dejan Muhamedagic <dejan at suse.de>
+#
+# This program was downloaded from
+# http://www.moonglade.com/syslog/combine-logs-1.0.tar.gz
+#
+
+$debugging = 0;
+
+# Open all of the logs.
+$handle = "fh00";
+foreach $file (@ARGV)
+{
+    $handle++;
+    open $handle, $file || die "Could not open $file: $!\n";
+    push @fh, $handle;
+}
+
+# Get the first line from each of the files.
+$i = 0;
+foreach $handle (@fh)
+{
+    $current_line[$i++] = get_next_line($handle);
+}
+
+# Process the logs.
+while (1)
+{
+    $first = 0;
+    for ($i = 1; $i < @fh; $i++)
+    {
+	if (first_entry($current_line[$first], $current_line[$i]))
+	{
+	    $first = $i;
+	}
+    }
+    # Fall out if the entry isn't defined (no more entries to print).
+    last if !defined($current_line[$first]);
+
+    # Print the entry and get the next line from that log.
+    print $current_line[$first];
+    $current_line[$first] = get_next_line($fh[$first]);
+}
+
+# Gets the next line from the provided file handle.
+sub get_next_line()
+{
+    my($handle) = @_;
+    my($line);
+    while ($line = <$handle>)
+    {
+	print " read $line" if $debugging;
+
+	# Weed out useless "last message repeated" messages.
+	next if $line =~ m/last message repeated \d+ times$/;
+	
+	# Fall out if the line passes the above tests.
+	last;
+    }
+    return $line;
+}
+
+# Determines which syslog-style log entry comes first.  If $a comes first,
+# the function returns 0.  If $b comes first, the function returns 1.
+sub first_entry()
+{
+    my($a, $b) = @_;
+    print "  \$a=$a  \$b=$b" if $debugging;
+    return 0 if !defined($b);
+    return 1 if !defined($a);
+
+    my(%month) = (Jan => 0, Feb => 1, Mar => 2, Apr => 3, May => 4, Jun => 5,
+		  Jul => 6, Aug => 7, Sep => 8, Oct => 9, Nov => 10, Dec => 11);
+    my($a_month, $a_day, $a_hour, $a_minute, $a_second) =
+      $a =~ /^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/;
+    my($b_month, $b_day, $b_hour, $b_minute, $b_second) =
+      $b =~ /^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s/;
+
+    print "  a: $a_month $a_day $a_hour:$a_minute:$a_second\n" if $debugging;
+    print "  b: $b_month $b_day $b_hour:$b_minute:$b_second\n" if $debugging;
+    
+    # Strictly speaking, Jan comes before Dec, but in the case that we are
+    # comparing exactly those two, we consider Jan to come later.  In the
+    # context of a log, this probably means a new year.
+    return 0 if $a_month eq "Dec" && $b_month eq "Jan";
+    return 1 if $a_month eq "Jan" && $b_month eq "Dec";
+    
+    # All other comparisons are as you'd expect.
+    if ($a_month ne $b_month)
+    {
+	return $month{$a_month} > $month{$b_month};
+    }
+    if ($a_day ne $b_day)
+    {
+	return $a_day > $b_day;
+    }
+    if ($a_hour ne $b_hour)
+    {
+	return $a_hour > $b_hour;
+    }
+    if ($a_minute ne $b_minute)
+    {
+	return $a_minute > $b_minute;
+    }
+    if ($a_second ne $b_second)
+    {
+	return $a_second > $b_second;
+    }
+    
+    # They have identical times, so just pick the first one.
+    return 0;
+}
diff --git a/hb_report/ha_cf_support.sh b/hb_report/ha_cf_support.sh
new file mode 100644
index 0000000..9776dd3
--- /dev/null
+++ b/hb_report/ha_cf_support.sh
@@ -0,0 +1,82 @@
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic at 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" ] || {
+			info "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 `dirname $HA_VARLIB`/pengine 0750 hacluster haclient
+d $HA_VARLIB/crm 0750 hacluster haclient
+EOF
+}
diff --git a/hb_report/hb_report.in b/hb_report/hb_report.in
new file mode 100755
index 0000000..f1d851a
--- /dev/null
+++ b/hb_report/hb_report.in
@@ -0,0 +1,1236 @@
+#!/bin/sh
+
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic at 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@/resource.d/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_OPTS="-T"}
+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	pcmk_peer_update.*(lost|memb):
+quorum		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	corosync.*Initializing transport..UDP|Executive.Service.RELEASE|crm_shutdown:.Requesting.shutdown|pcmk_shutdown:.Shutdown.complete
+"
+
+#
+# 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] [-l file]
+       [-n nodes] [-E files] [-p patt] [-L patt] [-e prog] [-MSDCZAVsvhd] [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)
+	-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
+	-M     : don't collect extra logs (/var/log/messages)
+	-D     : don't invoke editor to write description
+	-C     : remove the destination directory
+	-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   : destination directory
+EOF
+
+[ "$1" != short ] &&
+	cat<<EOF
+
+	. the multifile output is first stored in a directory {dest}
+	  of which a tarball {dest}.tar.bz2 (or .gz) is created
+	. 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
+	. 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 /tmp/report_1
+	  hb_report -f "2007/9/5 12:30" -t "2007/9/5 14:00" /tmp/report_2
+	  hb_report -f 1:00 -t 3:00 -l /var/log/cluster/ha-debug /tmp/report_3
+	  hb_report -f "09sep07 2:00" -u hbadmin /tmp/report_4
+	  hb_report -f 18:00 -p "usern.*" -p "admin.*" /tmp/report_5
+	  hb_report -f cts:133 /tmp/ctstest_133
+
+	. WARNING . WARNING . WARNING . WARNING . WARNING . WARNING .
+	  We try to sanitize the CIB and the peinputs files. If you
+	  have more sensitive information, please supply additional
+	  patterns yourself. 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@ (@BUILD_VERSION@)"
+	exit
+}
+#
+# these are "global" variables
+#
+setvarsanddefaults() {
+	local now=`perl -e 'print time()'`
+	# used by all
+	DESTDIR=""
+	FROM_TIME=""
+	CTS=""
+	TO_TIME=0
+	HA_LOG=""
+	UNIQUE_MSG="Mark:HB_REPORT:$now"
+	SANITIZE="passw.*"
+	DO_SANITIZE=""
+	REMOVE_DEST="1"
+	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"
+	VERBOSITY=0
+}
+chkdirname() {
+	[ "$1" ] || usage short
+	[ $# -ne 1 ] && fatal "bad directory name: $1"
+	echo $1 | grep -qs '^/' ||
+		fatal "destination directory must be an absolute path"
+	echo $1 | egrep -qs '/[.][.]*(/|$)' &&
+		fatal "sorry, no . or .. references in the destination"
+	[ "$1" = / ] &&
+		fatal "no root directory here, thank you"
+}
+destdir_inode_test() {
+	local root_inode=`ls -id / 2>/dev/null | awk '{print $1}'`
+	local dest_inode=`ls -id $1 2>/dev/null | awk '{print $1}'`
+	[ "$root_inode" = "$dest_inode" ] &&
+		fatal "no root directory here, thank you"
+}
+chktime() {
+	[ "$1" ] || fatal "bad time specification: $2 (try 'YYYY-M-D H:M:S') "
+}
+msgcleanup() {
+	fatal "destination directory $DESTDIR exists on $1, please cleanup"
+}
+nodistdirectory() {
+	fatal "could not create the destination directory $DESTDIR"
+}
+time2str() {
+	perl -e "use POSIX; print strftime('%x %X',localtime($1));"
+}
+
+#
+# find log files
+#
+logmark() {
+	logger -p $*
+	debug "run: logger -p $*"
+}
+#
+# a special stamp is logged through syslog on _all_ nodes
+#
+logmarks() {
+	local msg=$1
+	local c="hb_report __slave $DESTDIR logmark $msg"
+
+	for n in $NODES; do
+		if [ "$n" = "`uname -n`" ]; then
+			[ "$THIS_IS_NODE" ] && logmark $HA_LOGFACILITY.$HA_LOGLEVEL $msg
+		else
+			[ "$ssh_good" ] &&
+				echo $c | ssh $ssh_opts $n
+		fi
+	done
+}
+#
+# 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 '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=$(str2time "`$cat $logf | head -1 | $getstampproc`")
+	local last_time=$(str2time "`$cat $logf | tail -1 | $getstampproc`")
+	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 [ x = "x$to_time" -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 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
+			info "found log $next_log"
+			;;
+		2) break;; # don't go through older logs!
+		3) echo $next_log  # include log and continue
+			info "found log $next_log"
+			break
+			;; # don't go through older logs!
+		esac
+	done
+}
+#
+# print part of the log
+#
+drop_tmp_file() {
+	[ -z "$tmp" ] || rm -f "$tmp"
+}
+print_log() {
+	local cat=`find_decompressor $1`
+	$cat $1
+}
+print_logseg() {
+	local logf=$1
+	local from_time=$2
+	local to_time=$3
+
+	# uncompress to a temp file (if necessary)
+	local cat=`find_decompressor $logf`
+	if [ "$cat" != "cat" ]; then
+		tmp=`mktemp`
+		$cat $logf > $tmp
+		trap drop_tmp_file 0
+		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"
+	drop_tmp_file
+	trap "" 0
+}
+#
+# 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
+FROM_TIME=$FROM_TIME
+TO_TIME=$TO_TIME
+USER_NODES="$USER_NODES"
+NODES="$NODES"
+HA_LOG=$HA_LOG
+MASTER_IS_HOSTLOG=$MASTER_IS_HOSTLOG
+DESTDIR=$DESTDIR
+UNIQUE_MSG=$UNIQUE_MSG
+SANITIZE="$SANITIZE"
+DO_SANITIZE="$DO_SANITIZE"
+EXTRA_LOGS="$EXTRA_LOGS"
+REMOVE_DEST="$REMOVE_DEST"
+USER_CLUSTER_TYPE="$USER_CLUSTER_TYPE"
+CONF="$CONF"
+B_CONF="$B_CONF"
+PACKAGES="$PACKAGES"
+CORES_DIRS="$CORES_DIRS"
+VERBOSITY="$VERBOSITY"
+EOF
+}
+start_remote_collectors() {
+	for node in $NODES; do
+		[ "$node" = "$WE" ] && continue
+		dumpenv | ssh $ssh_opts $node \
+			"cat > $DESTDIR/.env; hb_report __slave $DESTDIR" |
+			(cd $DESTDIR && tar xf -) &
+		SLAVEPIDS="$SLAVEPIDS $!"
+	done
+}
+
+#
+# does ssh work?
+#
+testsshuser() {
+	if [ "$2" ]; then
+		ssh -T -o Batchmode=yes $2@$1 true 2>/dev/null
+	else
+		ssh -T -o Batchmode=yes $1 true 2>/dev/null
+	fi
+}
+findsshuser() {
+	for u in "" $TRY_SSH; do
+		rc=0
+		failed_nodes=""
+		for n in $NODES; do
+			[ "$n" = "$WE" ] && continue
+			u_print=$u
+			[ "$u_print" ] || u_print=`id -un`
+			debug "test ssh $u_print@$n"
+			if testsshuser $n $u; then
+				debug "ssh $u_print@$n OK"
+			else
+				rc=1
+				failed_nodes="$failed_nodes $n"
+				debug "ssh $u_print@$n failed"
+			fi
+		done
+		if [ $rc -eq 0 ]; then
+			echo $u
+			return 0
+		else
+			if [ "$u" ]; then
+				info "ssh with user $u does not work to $failed_nodes"
+			else
+				info "ssh without user does not work to $failed_nodes"
+			fi
+			info "will try next user"
+		fi
+	done
+	return 1
+}
+
+#
+# the usual stuff
+#
+getbacktraces() {
+	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"
+	}
+}
+pe2png() {
+	local pef=`basename $1`
+	local dotf=`basename $pef .bz2`.dot
+	local pngf=`basename $pef .bz2`.png
+	(
+	cd `dirname $1`
+	ptest -D $dotf -x $pef
+	dot -Tpng -o $pngf $dotf >/dev/null 2>&1
+	)
+}
+getpeinputs() {
+	local f
+	debug "looking for PE files in $HA_VARLIB/pengine, `dirname $HA_VARLIB`/pengine"
+	for pe_dir in $HA_VARLIB/pengine `dirname $HA_VARLIB`/pengine
+	do
+		test -d $pe_dir ||
+			continue
+		flist=$(
+			find_files $pe_dir $1 $2 | sed "s,`dirname $pe_dir`/,,g"
+		)
+		[ "$flist" ] && {
+			(cd `dirname $pe_dir` && tar cf - $flist) | (cd $3 && tar xf -)
+			debug "found `echo $flist | wc -w` pengine input files in $pe_dir"
+			which dot >/dev/null 2>&1 ||
+				info "if you had graphviz, we'd also produce png graphics for all PE files"
+		}
+		if [ `echo $flist | wc -w` -le 20 ]; then
+			for f in $flist; do
+				pe2png $3/$f
+			done
+		else
+			info "too many PE inputs to create dot files"
+		fi
+	done
+}
+touch_DC_if_dc() {
+	#dc=`crmadmin -D 2>/dev/null | awk '{print $NF}'`
+	dc=`crm_mon -1 2>/dev/null | awk '/Current DC/ {print $3}'`
+	if [ "$WE" = "$dc" ]; then
+		touch $1/DC
+	fi
+}
+
+#
+# some basic system info and stats
+#
+sys_info() {
+	cluster_info
+	hb_report -V # our info
+	echo "resource-agents: `grep 'Build version:' /usr/lib/ocf/resource.d/heartbeat/.ocf-shellfuncs`"
+	crm_info
+	pkg_ver $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
+	set +x
+}
+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() {
+	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() {
+	logs=$(find $1 -name $HALOG_F;
+		for l in $EXTRA_LOGS; do find $1/* -name `basename $l`; done)
+	[ "$logs" ] || return
+	trap '[ -z "$pattfile" ] || rm -f "$pattfile"' 0
+	pattfile=`mktemp` ||
+		fatal "cannot create temporary files"
+	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
+	rm -f $pattfile
+	trap "" 0
+}
+
+#
+# check if files have same content in the cluster
+#
+cibdiff() {
+	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 -u $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() {
+	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() {
+	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() {
+	Epatt=`echo "$EVENT_PATTERNS" |
+		while read title p; do [ -n "$p" ] && echo -n "|$p"; done |
+		sed 's/.//'
+	`
+	grep -E "$Epatt" $1
+}
+events() {
+	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
+}
+
+#
+# if there is no central log, let's combine logs from individual
+# nodes; the ordering may not be perfect, but then centralized
+# logging doesn't guarantee the order of messages either
+# (network delay, machine load)
+#
+combine_logs() {
+	destdir=$1
+	test $NODECNT -gt 1 ||
+		return
+	test -x $HA_NOARCHBIN/combine-logs.pl ||
+		warning "cannot combine logs: no $HA_NOARCHBIN/combine-logs.pl"
+	$HA_NOARCHBIN/combine-logs.pl $destdir/*/$HALOG_F > $destdir/$HALOG_F
+	$HA_NOARCHBIN/combine-logs.pl $destdir/*/events.txt > $destdir/events.txt
+}
+
+#
+# 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 at 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 $DESTDIR/$SYSINFO_F ]; then
+		echo "Common system info found:"
+		cat $DESTDIR/$SYSINFO_F
+	else
+		for n in $NODES; do
+			if [ -f $DESTDIR/$n/$SYSINFO_F ]; then
+				echo "System info $n:"
+				sed 's/^/	/' $DESTDIR/$n/$SYSINFO_F
+			fi
+		done
+	fi
+}
+edittemplate() {
+	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`; then
+		if [ "$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
+}
+finalword() {
+	if [ "$COMPRESS" = "1" ]; then
+		echo "The report is saved in $DESTDIR.tar$COMPRESS_EXT"
+	else
+		echo "The report is saved in $DESTDIR"
+	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
+	[ "$SLAVE" ] ||
+		fatal "please install the perl Date::Parse module"
+fi
+
+#
+# part 1: get and check options; and the destination
+#
+if [ "$SLAVE" = "" ]; then
+	setvarsanddefaults
+	userargs="$@"
+	DESTDIR="$HOME/hb_report-"`date +"%a-%d-%b-%Y"`
+	while getopts f:t:l:u:p:L:e:E:n:MSDCZAVsvhd 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://'`
+					DESTDIR="$HOME/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";;
+			l) HA_LOG="$OPTARG";;
+			e) EDITOR="$OPTARG";;
+			s) DO_SANITIZE="1";;
+			p) SANITIZE="$SANITIZE $OPTARG";;
+			s) DO_SANITIZE="1";;
+			L) LOG_PATTERNS="$LOG_PATTERNS $OPTARG";;
+			S) NO_SSH=1;;
+			D) NO_DESCRIPTION=1;;
+			C) REMOVE_DEST=1;;
+			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="0"; REMOVE_DEST="0";;
+			[?]) usage short;;
+		esac
+	done
+	shift $(($OPTIND-1))
+	[ $# -gt 1 ] && usage short
+	if [ $# -eq 1 ]; then
+		DESTDIR=`echo $1 | sed 's,/*$,,'`
+	fi
+	chkdirname $DESTDIR
+	destdir_inode_test $DESTDIR
+	[ "$FROM_TIME" ] || usage short
+else
+	# slave gets the environment from .env
+	DESTDIR=$2
+	[ -d $DESTDIR ] || nodistdirectory
+	. $DESTDIR/.env
+fi
+
+[ $VERBOSITY -gt 1 ] && set -x
+
+# 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=$HA_VARLIB/cores
+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" -o "x$SLAVE" != "x" ]; then
+	getlogvars
+	debug "log settings: facility=$HA_LOGFACILITY logfile=$HA_LOGFILE debugfile=$HA_DEBUGFILE"
+elif [ "x$SLAVE" = "x" ]; 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
+
+if [ "$SLAVE" -a "$3" = logmark ]; then
+	msg="$4"
+	logmark $HA_LOGFACILITY.$HA_LOGLEVEL $msg
+	exit
+fi
+
+WE=`uname -n`  # who am i?
+THIS_IS_NODE=""
+if [ "$SLAVE" = "" ]; then
+	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 || [ "$SLAVE" ]; then # are we a node?
+	THIS_IS_NODE=1
+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
+export ANALYSIS_F DESCRIPTION_F HALOG_F BT_F SYSINFO_F SYSSTATS_F DLM_DUMP_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
+export CRM_MON_F MEMBERSHIP_F CRM_VERIFY_F CIB_F CIB_TXT_F HB_UUID_F PERMISSIONS_F
+
+# this only on master
+if [ "$SLAVE" = "" ]; then
+
+	# if this is not a node, then some things afterwards might
+	# make no sense (not work)
+	if [ -z "$THIS_IS_NODE" -a "$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
+	ssh_good=""
+	if [ -z "$NO_SSH" ]; then
+		[ "$SSH_USER" ] ||
+			SSH_USER=`findsshuser`
+		if [ $? -eq 0 ]; then
+			ssh_good=1
+			if [ "$SSH_USER" ]; then
+				ssh_opts="-l $SSH_USER $SSH_OPTS"
+			else
+				ssh_opts="$SSH_OPTS"
+			fi
+		fi
+	fi
+# final check: don't run if the destination directory exists
+	[ -d $DESTDIR ] && {
+		if [ "$FORCE_REMOVE_DEST" ]; then
+			rm -r $DESTDIR
+		else
+			msgcleanup "local node"
+		fi
+	}
+	mkdir -p $DESTDIR
+	[ -d $DESTDIR ] || nodistdirectory
+	[ "$ssh_good" ] &&
+		for node in $NODES; do
+			[ "$node" = "$WE" ] && continue
+			ssh $ssh_opts $node "test -d $DESTDIR" && {
+				if [ "$CTS" ]; then # relax a bit for CTS
+					ssh $ssh_opts $node "rm -r $DESTDIR"
+				else
+					test -d $DESTDIR && rmdir $DESTDIR
+					msgcleanup $node
+				fi
+			}
+			dumpenv |
+			ssh $ssh_opts $node "mkdir -p $DESTDIR && cat > $DESTDIR/.env"
+		done
+fi
+
+if [ "$SLAVE" = "" ]; then
+#
+# part 3: log marks to be searched for later
+#         important to do this now on _all_ nodes
+# 
+	if [ "$HA_LOGFACILITY" ]; then
+		logmarks $UNIQUE_MSG
+	fi
+fi
+
+# only cluster nodes need their own directories
+[ "$THIS_IS_NODE" ] && mkdir -p $DESTDIR/$WE
+
+#
+# part 4: find the logs and cut out the segment for the period
+#
+if [ "$HA_LOG" ]; then  # log provided by the user?
+	[ -f "$HA_LOG" ] || {  # not present
+		[ "$SLAVE" ] ||  # 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 [ "$cnt" ] && [ $cnt -eq $NODECNT ]; then
+	MASTER_IS_HOSTLOG=1
+	info "found the central log!"
+fi
+
+if [ "$THIS_IS_NODE" ]; then
+	outf=$DESTDIR/$WE/$HALOG_F
+else
+	outf=$DESTDIR/$HALOG_F # we are log server, probably
+fi
+if [ -f "$HA_LOG" ]; then
+	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
+		else
+			warning "could not figure out the log format of $HA_LOG"
+		fi
+	fi
+elif [ "$CTS" ]; then
+	cts_findlogseg $CTS > $outf
+else
+	[ "$MASTER_IS_HOSTLOG" ] ||
+		warning "could not find $HA_LOG on $WE"
+fi
+
+#
+# part 5: start this program on other nodes
+#
+if [ ! "$SLAVE" ]; then
+	if [ "$ssh_good" ]; then
+		start_remote_collectors
+	else
+		if [ -z "$NO_SSH" -a $NODECNT -gt 1 ]; then
+			warning "ssh does not work to all nodes"
+			warning "please use the -u option if you want to supply a password"
+		fi
+	fi
+fi
+
+#
+# part 6: get all other info (config, stats, etc)
+#
+if [ "$THIS_IS_NODE" ]; then
+	getconfig $DESTDIR/$WE
+	getpeinputs $FROM_TIME $TO_TIME $DESTDIR/$WE
+	getbacktraces $FROM_TIME $TO_TIME $DESTDIR/$WE/$BT_F
+	touch_DC_if_dc $DESTDIR/$WE
+	sanitize $DESTDIR/$WE
+	check_perms > $DESTDIR/$WE/$PERMISSIONS_F 2>&1
+	sys_info > $DESTDIR/$WE/$SYSINFO_F 2>&1
+	dlm_dump > $DESTDIR/$WE/$DLM_DUMP_F 2>&1
+	sys_stats > $DESTDIR/$WE/$SYSSTATS_F 2>&1
+
+	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 $DESTDIR/$WE/`basename $l`
+			continue
+		fi
+		getstampproc=`find_getstampproc < $l`
+		if [ "$getstampproc" ]; then
+			export getstampproc # used by linetime
+			dumplogset $l $FROM_TIME $TO_TIME > $DESTDIR/$WE/`basename $l`
+		else
+			warning "could not figure out the log format of $l"
+		fi
+	done
+fi
+
+#
+# part 7: 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 [ "$SLAVE" ]; then
+	(cd $DESTDIR && tar cf - $WE)
+else
+	wait $SLAVEPIDS
+	analyze $DESTDIR > $DESTDIR/$ANALYSIS_F
+	events $DESTDIR
+	mktemplate > $DESTDIR/$DESCRIPTION_F
+	[ "$NO_DESCRIPTION" ] || {
+		echo press enter to edit the problem description...
+		read junk
+		edittemplate $DESTDIR/$DESCRIPTION_F
+	}
+	test -f $DESTDIR/$HALOG_F ||
+		combine_logs $DESTDIR
+	cd $DESTDIR/..
+	if [ "$COMPRESS" = "1" ]; then
+		pickcompress
+		tar cf - `basename $DESTDIR` | $COMPRESS_PROG > $DESTDIR.tar$COMPRESS_EXT
+	fi
+	finalword
+fi
+
+[ "$REMOVE_DEST" = "1" ] &&
+	rm -r $DESTDIR
diff --git a/hb_report/openais_conf_support.sh b/hb_report/openais_conf_support.sh
new file mode 100644
index 0000000..7f6ccb8
--- /dev/null
+++ b/hb_report/openais_conf_support.sh
@@ -0,0 +1,97 @@
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic at 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" ] || {
+			info "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 $HA_VARLIB 0755 root root
+d `dirname $HA_VARLIB`/pengine 0750 hacluster haclient
+d $HA_VARLIB/crm 0750 hacluster haclient
+EOF
+}
diff --git a/hb_report/utillib.sh b/hb_report/utillib.sh
new file mode 100644
index 0000000..96c22ed
--- /dev/null
+++ b/hb_report/utillib.sh
@@ -0,0 +1,594 @@
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic at 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() {
+	membership_tools="ccm_tool crm_node"
+	for f in $membership_tools; 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
+	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() {
+	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() {
+	severity=$1
+	shift 1
+	logtag=""
+	[ "$HA_LOGTAG" ] && logtag="-t $HA_LOGTAG"
+	logger -p ${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY}.$severity $logtag $*
+}
+
+#
+# find log destination
+#
+findmsg() {
+	# this is tricky, we try a few directories
+	syslogdirs="/var/log /var/logs /var/syslog /var/adm /var/log/ha /var/log/cluster"
+	favourites="ha-*"
+	mark=$1
+	log=""
+	for d in $syslogdirs; do
+		[ -d $d ] || continue
+		log=`grep -l -e "$mark" $d/$favourites` && break
+		log=`grep -l -e "$mark" $d/*` && 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 '
+	eval "use Date::Parse";
+	if (!$@) {
+		print str2time($time);
+	} else {
+		eval "use Date::Manip";
+		if (!$@) {
+			print UnixDate(ParseDateString($time), "%s");
+		}
+	}
+	'
+}
+getstamp_syslog() {
+	awk '{print $1,$2,$3}'
+}
+getstamp_legacy() {
+	awk '{print $2}' | sed 's/_/ /'
+}
+linetime() {
+	l=`tail -n +$2 $1 | head -1 | $getstampproc`
+	str2time "$l"
+}
+find_getstampproc() {
+	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_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
+}
+findln_by_time() {
+	local logf=$1
+	local tm=$2
+	local first=1
+	local last=`wc -l < $logf`
+	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() {
+	t=`mktemp` &&
+	perl -e "\$file=\"$t\"; \$tm=$1;" -e 'utime $tm, $tm, $file;' &&
+	echo $t
+}
+find_files_clean() {
+	[ -z "$to_stamp" ] || rm -f "$to_stamp"
+	to_stamp=""
+	[ -z "$from_stamp" ] || rm -f "$from_stamp"
+	from_stamp=""
+}
+find_files() {
+	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
+	}
+	trap find_files_clean 0
+	if ! from_stamp=`touchfile $from_time`; then
+		warning "sorry, can't create temporary file for find_files"
+		return
+	fi
+	findexp="-newer $from_stamp"
+	if isnumber "$to_time" && [ "$to_time" -gt 0 ]; then
+		if ! to_stamp=`touchfile $to_time`; then
+			warning "sorry, can't create temporary file for" \
+				"find_files"
+			find_files_clean
+			return
+		fi
+		findexp="$findexp ! -newer $to_stamp"
+	fi
+	find $dirs -type f $findexp
+	find_files_clean
+	trap "" 0
+}
+
+#
+# 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() {
+	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
+#
+findbinary() {
+	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
+		if [ -x $HA_BIN/$binary ]; then
+			echo $HA_BIN/$binary
+			debug "found the program at $HA_BIN/$binary for core $1"
+		else
+			warning "could not find the program path for core $1"
+		fi
+	else
+		echo $fullpath
+		debug "found the program at $fullpath for core $1"
+	fi
+}
+getbt() {
+	which gdb > /dev/null 2>&1 || {
+		warning "please install gdb to get backtraces"
+		return
+	}
+	for corefile; do
+		absbinpath=`findbinary $corefile`
+		[ x = x"$absbinpath" ] && continue
+		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() {
+	ps -ef | grep -qs [c]rmd || return 1
+	#crmadmin -D >/dev/null 2>&1 &
+	crm_mon -1 >/dev/null 2>&1 &
+	pid=$!
+	maxwait=10
+	while kill -0 $pid 2>/dev/null && [ $maxwait -gt 0 ]; do
+		sleep 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 $HA_VARLIB/crm/$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
+	[ -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 '
+	/type="normal"/ {
+		for( i=1; i<=NF; i++ )
+			if( $i~/^uname=/ ) {
+				sub("uname=.","",$i);
+				sub("\".*","",$i);
+				print $i;
+				next;
+			}
+	}
+	'
+}
+
+#
+# remove values of sensitive attributes
+#
+# this is not proper xml parsing, but it will work under the
+# circumstances
+is_sensitive_xml() {
+	epatt=""
+	for patt in $SANITIZE; do
+		epatt="$epatt|$patt"
+	done
+	epatt="`echo $epatt|sed 's/.//'`"
+	egrep -qs "name=\"$epatt\""
+}
+test_sensitive_one() {
+	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() {
+	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_clean() {
+	[ -z "$tmp" ] || rm -f "$tmp"
+	tmp=""
+	[ -z "$ref" ] || rm -f "$ref"
+	ref=""
+}
+sanitize_one() {
+	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
+	trap sanitize_one_clean 0
+	tmp=`mktemp`
+	ref=`mktemp`
+	if [ -z "$tmp" -o -z "$ref" ]; then
+		sanitize_one_clean
+		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
+	# note: cleaning $tmp up is still needed even after it's renamed
+	# because its temp directory is still there.
+
+	touch -r $ref $file
+	sanitize_one_clean
+	trap "" 0
+}
+
+#
+# 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
+}
+pickfirst() {
+	for x; do
+		which $x >/dev/null 2>&1 && {
+			echo $x
+			return 0
+		}
+	done
+	return 1
+}
+
+#
+# get some system info
+#
+distro() {
+	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() {
+	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
+			echo "Unknown package manager!"
+			return
+	fi
+	debug "the package manager is $pkg_mgr"
+
+	# for Linux .deb based systems
+	for pkg ; do
+		case $pkg_mgr in
+		deb)
+			if dpkg-query -f '${Name} ${Version}' -W $pkg 2>/dev/null ; then
+				debsums -s $pkg 2>/dev/null
+			fi
+			;;
+		rpm)
+			if rpm -q --qf '%{name} %{version}-%{release} - %{distribution} %{arch}\n' $pkg ; then
+				rpm --verify $pkg
+			fi
+			;;
+		pkg_info)
+			pkg_info | grep $pkg
+			;;
+		pkginfo)
+			pkginfo | awk '{print $3}'  # format?
+			;;
+		esac
+	done
+}
+
+crm_info() {
+	$HA_BIN/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/.cvsignore b/include/clplumbing/.cvsignore
new file mode 100644
index 0000000..3f213c3
--- /dev/null
+++ b/include/clplumbing/.cvsignore
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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 at 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 at 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 at 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..39c1292
--- /dev/null
+++ b/include/clplumbing/cl_log.h
@@ -0,0 +1,94 @@
+/*
+ * 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)
+
+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_perror(const char * fmt, ...) G_GNUC_PRINTF(1,2);
+void		cl_log_enable_stderr(int truefalse);
+void		cl_log_enable_stdout(int truefalse);
+int		cl_set_logging_wqueue_maxlen(int);
+gboolean	cl_log_test_logd(void);
+void		cl_log_set_uselogd(int truefalse);
+void		cl_log_enable_syslog_filefmt(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_logfile(const char *	path);
+void		cl_log_set_debugfile(const char * path);
+void		inherit_compress(void);
+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);
+
+
+typedef struct CircularBuffer_s 
+{
+	const char*	name;
+	size_t		size;
+	gboolean	empty_after_dump;
+	GQueue*		queue;
+	
+} CircularBuffer_t;
+
+typedef struct CircularBufferEntry_s 
+{
+	int level;
+	char *buf;
+	
+} CircularBufferEntry_t;
+
+CircularBuffer_t *NewCircularBuffer(
+	const char *name, unsigned int size, gboolean empty_after_dump);
+void LogToCircularBuffer(
+	CircularBuffer_t *buffer, int level, const char *fmt, ...) G_GNUC_PRINTF(3,4);
+
+void EmptyCircularBuffer(CircularBuffer_t *buffer);
+
+/* the prototype is designed to be easy to give to G_main_add_SignalHandler() */
+gboolean DumpCircularBuffer(int nsig, gpointer buffer);
+
+#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 at 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 at 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 at 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 at 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..a6721e9
--- /dev/null
+++ b/include/clplumbing/cl_random.h
@@ -0,0 +1,23 @@
+
+/*							
+ * Copyright (C) 2005 Guochun Shi <gshi at 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
+ */
+
+
+unsigned int	cl_randseed(void);
+int		get_next_random(void);	 /* Assumes mainloop setup */
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 at 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 at 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..7f53f5c
--- /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/gtypes.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..fca1d7c
--- /dev/null
+++ b/include/clplumbing/ipc.h
@@ -0,0 +1,764 @@
+/*
+ * ipc.h IPC abstraction data structures.
+ *
+ * author  Xiaoxiang Liu <xiliu at ncsa.uiuc.edu>,
+ *	Alan Robertson <alanr at unix.sh>
+ *
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Copyright (c) 2002  Xiaoxiang Liu <xiliu at 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_MESSAGE_SIZE 4096
+#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 */
+	uid_t		farside_uid;	/* far side uid */
+	gid_t		farside_gid;	/* far side gid */
+	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];
+};
+
+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 only supports unix domain socket 
+ *    whose type is IPC_DOMAIN_SOCKET 
+ *
+ */
+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:
+ *   current implementation only support unix domain socket 
+ *   whose type is IPC_DOMAIN_SOCKET 
+ *
+ */
+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));
+
+
+#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);
+
+#define	IPC_PATH_ATTR		"path"		/* pathname attribute */
+#define	IPC_DOMAIN_SOCKET	"uds"		/* Unix domain socket */
+#define IPC_MODE_ATTR           "sockmode"      /* socket mode attribute */
+
+#ifdef IPC_DOMAIN_SOCKET
+#	define	IPC_ANYTYPE		IPC_DOMAIN_SOCKET
+#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..69698ff
--- /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	32
+
+/* 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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..e1e4977
--- /dev/null
+++ b/include/compress.h
@@ -0,0 +1,50 @@
+/*
+ * compress.h: Compression functions for Linux-HA
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi at 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	HBcompress
+#define HB_COMPRESS_TYPE_S	"HBcompress"
+
+/*
+ *	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..a139da0
--- /dev/null
+++ b/include/glue_config.h.in
@@ -0,0 +1,96 @@
+/* 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
+
+/* 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 pacemaker version */
+#undef GLUE_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 at 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..5df81fb
--- /dev/null
+++ b/include/lha_internal.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr at 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*/
+
+#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
+
+#endif /* LHA_INTERNAL_H */
diff --git a/include/lrm/.cvsignore b/include/lrm/.cvsignore
new file mode 100644
index 0000000..3f213c3
--- /dev/null
+++ b/include/lrm/.cvsignore
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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..a273aaa
--- /dev/null
+++ b/include/lrm/lrm_api.h
@@ -0,0 +1,451 @@
+/*
+ * Client-side Local Resource Manager API.
+ *
+ * Author: Huang Zhen <zhenh at 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 at 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 */
+}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"
+
+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..5620565
--- /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 at 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_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 at 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 at 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/.cvsignore b/include/pils/.cvsignore
new file mode 100644
index 0000000..bbf3142
--- /dev/null
+++ b/include/pils/.cvsignore
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+plugin.h
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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 at 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..0348aae
--- /dev/null
+++ b/include/pils/plugin.h.in
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2000 Alan Robertson <alanr at 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, ...);
+
+/*
+ * 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, ...);
+        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, ...));
+
+
+/* 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 at 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/.cvsignore b/include/stonith/.cvsignore
new file mode 100644
index 0000000..cab0d2e
--- /dev/null
+++ b/include/stonith/.cvsignore
@@ -0,0 +1,10 @@
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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..3c2f792
--- /dev/null
+++ b/include/stonith/stonith.h
@@ -0,0 +1,184 @@
+/*
+ *	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 at 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 <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**	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);
+
+
+/* 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 */
+
+/*
+ * 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..1dd66b4
--- /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 at 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** (*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 ** 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/.cvsignore b/lib/clplumbing/.cvsignore
new file mode 100644
index 0000000..4d36492
--- /dev/null
+++ b/lib/clplumbing/.cvsignore
@@ -0,0 +1,14 @@
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+ipctest
+ipctransientclient
+ipctransientserver
+*.beam
+parser-messages
+MISC_ERRORS
+base64_md5_test
diff --git a/lib/clplumbing/GSource.c b/lib/clplumbing/GSource.c
new file mode 100644
index 0000000..d185259
--- /dev/null
+++ b/lib/clplumbing/GSource.c
@@ -0,0 +1,1861 @@
+/*
+ * Copyright (c) 2002 Alan Robertson <alanr at 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 {
+		cl_log(LOG_INFO
+		, "%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 {
+		cl_log(LOG_INFO, "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);
+	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);
+	
+	g_source_remove(tag);
+	
+	if (source == NULL){
+		cl_log(LOG_ERR, "Attempt to remove timeout (%u)"
+		" with NULL source",	tag);
+	}else{
+		g_assert(IS_TIMEOUTSRC(append));
+		g_source_unref(source);
+	}
+	
+	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/Gmain_timeout.c b/lib/clplumbing/Gmain_timeout.c
new file mode 100644
index 0000000..611c118
--- /dev/null
+++ b/lib/clplumbing/Gmain_timeout.c
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ *
+ * Copyright (c) 2002 Alan Robertson <alanr at 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
+ *
+ */
+#if 0
+#include <glib.h>
+#include <clplumbing/longclock.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/Gmain_timeout.h>
+#include <string.h>
+
+
+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 = {
+	prepare: Gmain_timeout_prepare,
+	check: Gmain_timeout_check,
+	dispatch: Gmain_timeout_dispatch,
+};
+
+
+struct GTimeoutAppend {
+	GSource		Source;
+	longclock_t	nexttime;
+	guint		interval;
+	unsigned long	maxdispatchdelayms;
+	unsigned long	maxdispatchms;
+};
+
+#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->nexttime = add_longclock(time_longclock()
+					 ,msto_longclock(interval));
+  	append->interval = interval; 
+	append->maxdispatchms = 0;
+	append->maxdispatchdelayms = 10000;
+	
+	g_source_set_priority(source, priority);
+	
+	g_source_set_can_recurse(source, FALSE);
+	
+	g_source_set_callback(source, function, data, notify); 
+	
+	return g_source_attach(source, NULL);
+
+}
+
+void
+Gmain_timeout_remove(guint tag)
+{
+	GSource* source = g_main_context_find_source_by_id(NULL,tag);
+	
+	g_source_remove(tag);
+	
+	if (source != NULL){
+		g_source_unref(source);
+	}
+	
+	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;
+	
+	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();
+	
+	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	lstart = time_longclock();
+	long		ms = longclockto_ms(sub_longclock(lstart, append->nexttime));
+	gboolean	ret;
+
+	if (append->maxdispatchdelayms > 0 && ms > append->maxdispatchdelayms) {
+		cl_log(LOG_WARNING, "Timeout dispatch function [%lx] called %ld ms late."
+		,	(unsigned long)func, ms);
+	}
+	
+
+	/* Schedule our next dispatch */
+	append->nexttime = add_longclock(time_longclock()
+					  , msto_longclock(append->interval));
+
+	/* Then call the user function */
+	ret = func(user_data);
+
+	/* Time it if requested */
+	if (append->maxdispatchms > 0) {
+		longclock_t	lend = time_longclock();
+		ms = longclockto_ms(sub_longclock(lend, lstart));
+		if (ms > append->maxdispatchms) {
+			cl_log(LOG_WARNING, "Timeout dispatch function [%lx] took %ld ms."
+			,	(unsigned long)func, ms);
+		}
+	}
+	return ret;
+}
+
+void
+Gmain_timeout_setmaxdispatchtime(GSource* src, long dispatchms)
+{
+	struct GTimeoutAppend* append = GTIMEOUT(src);
+	append->maxdispatchms = dispatchms;
+}
+
+void
+Gmain_timeout_setmaxdispatchdelay(GSource* src, long delayms)
+{
+	struct GTimeoutAppend* append = GTIMEOUT(src);
+	append->maxdispatchdelayms = delayms;
+}
+#endif
diff --git a/lib/clplumbing/Makefile.am b/lib/clplumbing/Makefile.am
new file mode 100644
index 0000000..5fb0a66
--- /dev/null
+++ b/lib/clplumbing/Makefile.am
@@ -0,0 +1,100 @@
+#
+# 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	\
+			Gmain_timeout.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 2:0:0
+
+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..f0fb0f3
--- /dev/null
+++ b/lib/clplumbing/base64_md5_test.c
@@ -0,0 +1,112 @@
+/* File: base64_md5_test.c
+ * Description: base64 and md5 algorithm tests
+ *
+ * Author: Sun Jiang Dong <sunjd at 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++;
+	}
+
+	return error_count;
+}
diff --git a/lib/clplumbing/cl_compress.c b/lib/clplumbing/cl_compress.c
new file mode 100644
index 0000000..bcb6d19
--- /dev/null
+++ b/lib/clplumbing/cl_compress.c
@@ -0,0 +1,491 @@
+
+/*
+ * compress.c: Compression functions for Linux-HA
+ *
+ * Copyright (C) 2005 Guochun Shi <gshi at 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/HBcompress
+ *
+ */
+
+#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"
+static struct hb_compress_fns* msg_compress_fns = NULL;
+static char*  compress_name = NULL;
+GHashTable*		CompressFuncs = NULL;
+
+static PILGenericIfMgmtRqst	Reqs[] =
+	{
+		{"HBcompress", &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)
+{
+	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..72149c0
--- /dev/null
+++ b/lib/clplumbing/cl_log.c
@@ -0,0 +1,1163 @@
+/*
+ * 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 at 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 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);
+IPC_Message* ChildLogIPCMessage(int priority, const char *buf, int bstrlen, 
+				gboolean use_priority_str, IPC_Channel* ch);
+void	FreeChildLogIPCMessage(IPC_Message* msg);
+gboolean send_dropped_message(gboolean use_pri_str, IPC_Channel *chan);
+
+const char * prio2str(int priority);
+static int		use_logging_daemon =  FALSE;
+static int		conn_logd_time = 0;
+static char		cl_log_entity[MAXENTITY]= DFLT_ENTITY;
+static char		common_log_entity[MAXENTITY]= DFLT_ENTITY;
+static int		cl_log_facility = LOG_USER;
+
+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;
+}
+
+
+#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"
+
+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_logfile(const char *	path)
+{
+    if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+	path = NULL;
+    }
+	logfile_name = path;
+}
+void
+cl_log_set_debugfile(const char * path)
+{
+    if(path != NULL && strcasecmp("/dev/null", path) == 0) {
+	path = NULL;
+    }
+    debugfile_name = path;
+}
+
+
+/* 
+ * 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
+append_log( const char * fname, const char * entity, int entity_pid
+,	TIME_T timestamp, const char * pristr, const char * msg)
+{
+	FILE *			fp = fopen(fname,"a");
+	static int		got_uname = FALSE;
+	static struct utsname	un;
+
+	if (!fp) {
+		syslog(LOG_ERR, "Cannot append to %s: %s", fname
+		,	strerror(errno)); 
+		return;
+	}
+	if (!syslogformatfile) {
+		print_logline(fp, entity, entity_pid, timestamp, pristr, msg);
+		fclose(fp);
+		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);
+	fclose(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.
+ */
+
+/* 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();
+
+	if (entity == NULL){
+		entity =cl_log_entity;
+	}
+	
+	pristr = use_priority_str ? prio2str(priority) : NULL;
+
+	if (needprivs) {
+		return_to_orig_privs();
+	}
+	
+	if (syslog_enabled) {
+		if (entity) {
+			strncpy(common_log_entity, entity, MAXENTITY);
+		} else {
+			strncpy(common_log_entity, DFLT_ENTITY,MAXENTITY);
+		}
+
+		common_log_entity[MAXENTITY-1] = '\0';
+
+		if (pristr) {
+			syslog(priority, "[%d]: %s: %s%c",
+			       entity_pid, pristr,  buf, 0);
+		}else {
+			syslog(priority, "[%d]: %s%c", entity_pid, buf, 0);
+		}
+	}
+
+	if (debugfile_name != NULL) {
+		append_log(debugfile_name,entity,entity_pid,ts,pristr,buf);
+	}
+
+	if (priority != LOG_DEBUG && logfile_name != NULL) {
+		append_log(logfile_name,entity,entity_pid,ts,pristr,buf);
+	}
+
+	if (needprivs) {
+		return_to_dropped_privs();
+	}
+	
+	return;
+}
+
+
+
+/*
+ * 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) {
+		print_logline(stderr, cl_log_entity,cl_process_pid,
+			NULLTIME, prio2str(priority), buf);
+	}
+
+	if (stdout_enabled) {
+		print_logline(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;
+}
+
+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);
+}
+
+
+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;
+	
+	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;
+}
+
+
+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 int childlog_ipcmsg_allocated = 0;
+static int childlog_ipcmsg_freed = 0;
+void	childlog_dump_ipcmsg_stats(void);
+void
+childlog_dump_ipcmsg_stats(void)
+{
+	
+	cl_log(LOG_INFO, "childlog ipcmsg allocated:%d, freed=%d, diff =%d",
+	       childlog_ipcmsg_allocated,
+	       childlog_ipcmsg_freed,
+	       childlog_ipcmsg_allocated - childlog_ipcmsg_freed);
+	
+	return;
+	
+	
+}
+
+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;
+
+	childlog_ipcmsg_allocated++;
+
+	return ret;
+}
+
+
+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);
+	
+	childlog_ipcmsg_freed ++;
+	
+	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);
+}
+
+/* What a horrible substitute for a low-overhead event log!! - FIXME!! */
+
+CircularBuffer_t *
+NewCircularBuffer(const char *name, uint size, gboolean empty_after_dump)
+{
+	CircularBuffer_t *buffer = malloc(sizeof(CircularBuffer_t));
+	if (!buffer) {
+		return buffer;
+	}
+	buffer->name = name;
+	buffer->size = size;
+	buffer->empty_after_dump = empty_after_dump;
+	buffer->queue = g_queue_new();
+
+#if 1
+	if(empty_after_dump == FALSE) {
+		cl_log(LOG_ERR, "This requires glib 2.4");
+		empty_after_dump = TRUE;
+	}
+#endif
+
+	return buffer;
+}
+
+void
+LogToCircularBuffer(CircularBuffer_t *buffer, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[MAXLINE];
+	int	nbytes;
+	CircularBufferEntry_t *entry = malloc(sizeof(CircularBufferEntry_t));
+	
+	if (!entry) {
+		return;
+	}
+	va_start(ap, fmt);
+	nbytes=vsnprintf(buf, MAXLINE, fmt, ap);
+	/*	nbytes=vasprintf(&buf, fmt, ap); */
+	va_end(ap);
+
+	entry->buf = buf;
+	entry->level = level;
+
+	g_queue_push_tail(buffer->queue, entry);
+
+	while(buffer->queue->length > buffer->size) {
+		entry = g_queue_pop_head(buffer->queue);
+		free(entry->buf);
+		free(entry);
+	}
+}
+
+void
+EmptyCircularBuffer(CircularBuffer_t *buffer) 
+{
+	CircularBufferEntry_t *entry = NULL;
+	while(buffer->queue->length > 0) {
+		entry = g_queue_pop_head(buffer->queue);
+		free(entry->buf);
+		free(entry);
+	}
+}
+
+gboolean
+DumpCircularBuffer(int nsig, gpointer user_data) 
+{
+	CircularBuffer_t *buffer = user_data;
+	CircularBufferEntry_t *entry = NULL;
+	
+	if(buffer == NULL) {
+		/* error */
+		cl_log(LOG_ERR, "No buffer supplied to dump.");
+		return FALSE;
+	}
+
+	if(logging_daemon_chan != NULL
+	   && logging_daemon_chan->send_queue->max_qlen < buffer->size) {
+		/* We have no hope of getting the whole buffer out via the
+		 *  logging daemon.  Use direct log instead so the messages
+		 *  come out in the right order.
+		 */ 
+		cl_log_depth++;
+	}
+	
+	cl_log(LOG_INFO, "Mark: Begin dump of buffer %s", buffer->name);
+	if(buffer->empty_after_dump) {
+		while(buffer->queue->length > 0) {
+			entry = g_queue_pop_head(buffer->queue);
+			cl_log(entry->level, "%s", entry->buf);
+			free(entry->buf);
+			free(entry);
+		}
+
+	} else {
+#if 1
+		cl_log(LOG_ERR, "This requires g_queue_peek_nth() from glib 2.4");
+#else
+		uint lpc = 0;
+		uint queue_len = buffer->queue->length;
+		for(lpc = 0; lpc < queue_len; lpc++) {
+			entry = g_queue_peek_nth(buffer->queue, lpc);
+			cl_log(entry->level, "%s", entry->buf);
+		}
+#endif
+	}
+	if(logging_daemon_chan != NULL
+	   && logging_daemon_chan->send_queue->max_qlen < buffer->size) {
+		/* Return is back to normal */
+		cl_log_depth--;
+	}
+	cl_log(LOG_INFO, "Mark: End dump of buffer %s", buffer->name);
+	return TRUE;
+}
+
+
+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 at 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 at 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..f5119de
--- /dev/null
+++ b/lib/clplumbing/cl_msg.c
@@ -0,0 +1,2581 @@
+/*
+ * Heartbeat messaging object.
+ *
+ * Copyright (C) 2000 Alan Robertson <alanr at 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 = (2*1024);
+
+static enum cl_msgfmt msgfmt = MSGFMT_NVPAIR;
+static	gboolean use_traditional_compression = TRUE;
+
+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	internal_type;
+	
+
+	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);
+	}
+	
+	internal_type = type;
+	
+	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;
+	char buf[MAXMSG];
+	
+	if(!msg || !name || !value){
+		cl_log(LOG_ERR, "cl_msg_list_add_string: input invalid");
+		return HA_FAIL;
+	}
+	
+	
+	strncpy(buf, value, MAXMSG);
+	list = g_list_append(list, buf);
+	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);
+	
+	if (len >= MAXMSG){
+		cl_log(LOG_ERR, "msg2string: msg is too large"
+		       "len =%d,MAX msg allowed=%d", len, MAXMSG);
+		return NULL;
+	}
+	
+	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;
+
+}
+
+
+static char*
+msg2wirefmt_ll(struct ha_msg*m, size_t* len, int flag)
+{
+	
+	int	wirefmtlen;
+	int	i;
+	char*	ret;
+	
+
+	if (msgfmt == MSGFMT_NETSTRING){
+		wirefmtlen = get_netstringlen(m);		
+	}else{
+		wirefmtlen =  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);
+			}
+		}
+	}
+	
+	
+	if (msgfmt == MSGFMT_NETSTRING || must_use_netstring(m)){
+		wirefmtlen = get_netstringlen(m);		
+		if (!(flag&MSG_NOSIZECHECK) && wirefmtlen >= MAXMSG){
+			cl_log(LOG_ERR, "%s: msg too big(%d)"
+			       "for netstring fmt",
+			       __FUNCTION__, wirefmtlen);
+			return NULL;
+		}
+		if (flag& MSG_NEEDAUTH){
+			return msg2netstring(m, len);
+		}else{
+			ret =  msg2netstring_noauth(m, len);
+			return ret;
+
+		}
+		
+		
+	}else{
+		char	*tmp;
+		
+		wirefmtlen =  get_stringlen(m);
+		if (wirefmtlen >= MAXMSG){
+			cl_log(LOG_ERR, "%s: msg too big(%d)"
+			       " for string fmt",
+			       __FUNCTION__, wirefmtlen);
+			return NULL;
+		}
+		
+		tmp = msg2string(m);
+		
+		if(tmp == NULL){
+			*len = 0;
+			return NULL;
+		}
+		
+		*len = strlen(tmp) + 1;
+		return(tmp);
+	}
+	
+
+}
+
+
+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){
+	
+	/* in this execution path the size check is not necessary;
+	 * still, the msg2wirefmt_ll is invoked more than once for
+	 * the same message (or parts of it) which is somewhat
+	 * strange, though perhaps it helps reduce the code
+	 * complexity
+	 */
+	return msg2wirefmt_ll(m, len, MSG_NOSIZECHECK);
+}
+
+
+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..6ff7227
--- /dev/null
+++ b/lib/clplumbing/cl_msg_types.c
@@ -0,0 +1,1755 @@
+/*
+ * Heartbeat message type functions
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi at 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 = NULL;
+	size_t total_length = 0;
+	
+	memcpy(&list, &_list, sizeof(GList*));
+	(void)list;
+
+	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;
+
+	memcpy(&list, &_list, sizeof(GList*));
+
+	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_cleanup:"
+			       "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;
+	struct ha_msg* childmsg;
+
+	if ( !msg || !name || !value
+	     || depth < 0){
+		cl_log(LOG_ERR, "add_struct_field:"
+		       " invalid input argument");
+		return HA_FAIL;
+	}
+	
+	childmsg = (struct ha_msg*)value; 
+	
+	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;
+	int stringlen_add;
+
+	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){
+		int listlen;
+		list = (GList*)value;
+
+		listlen = string_list_pack_length(list);
+
+		stringlen_add = list_stringlen(namelen,listlen , 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 oldlistlen = string_list_pack_length(oldlist);
+		int newlistlen;
+		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;
+		}
+		
+		newlistlen = string_list_pack_length(list);		
+		
+		stringlen_add = newlistlen - oldlistlen;
+
+		msg->values[j] = list;
+		msg->vlens[j] =  string_list_pack_length(list);
+		g_list_free((GList*)value); /*we don't free each element
+					      because they are used in new list*/
+		
+	} 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;
+	struct ha_msg* childmsg;
+
+	if ( !msg || !name || !value
+	     || depth < 0){
+		cl_log(LOG_ERR, "add_struct_field:"
+		       " invalid input argument");
+		return HA_FAIL;
+	}
+	
+	childmsg = (struct ha_msg*)value; 
+	
+	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;
+	int	stringlen_add = 0 ;
+
+	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 || (stringlen_add = 
+					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 at 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..419b655
--- /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 at 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[] =
+	{
+		{"HBcompress", &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..91732e2
--- /dev/null
+++ b/lib/clplumbing/cl_poll.c
@@ -0,0 +1,811 @@
+#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 at 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)
+{
+	short	nsig;
+	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;
+	}
+	nsig = monitorinfo[fd].nsig;
+
+	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..0956575
--- /dev/null
+++ b/lib/clplumbing/cl_random.c
@@ -0,0 +1,234 @@
+/*							
+ * Copyright (C) 2005 Guochun Shi <gshi at 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>
+
+#define	MAXRAND 100	/* How many do we try and keep around? */
+
+typedef int	rand_t;
+
+static int	first = 0;
+static int	last = 0;
+static rand_t	randomness[MAXRAND];
+
+/* How many random numbers do we currently have ? */
+#define NUMRNUMS	((first <= last) ? (last-first)	\
+			:	((MAXRAND-first)+(last+1)))
+
+#define IS_QUEUEFULL	(NUMRNUMS == MAXRAND)
+#define IS_QUEUEEMPTY	(first == last)
+
+
+
+static gboolean	randgen_scheduled = FALSE;
+static gboolean	inityet = FALSE;
+
+static void		cl_init_random(void);
+
+
+static rand_t
+gen_a_random(void)
+{
+	if (!inityet) {
+		cl_init_random();
+	}
+	return	rand();
+}
+
+
+static gboolean
+add_a_random(gpointer notused)
+{
+	if (IS_QUEUEFULL) {
+		return FALSE;
+	}
+	++last;
+	if (last >= MAXRAND) {
+		last = 0;
+	}
+	randomness[last] = gen_a_random();
+	return !IS_QUEUEFULL;
+}
+
+
+static void
+get_more_random(void)
+{
+	if (randgen_scheduled || IS_QUEUEFULL) {
+		return;
+	}
+	if (g_main_loop_is_running(NULL)) {
+		randgen_scheduled = TRUE;
+		Gmain_timeout_add_full(G_PRIORITY_LOW+1, 10, add_a_random, NULL, NULL);
+	}
+}
+
+int
+get_next_random(void)
+{
+	rand_t	ret;
+	if (IS_QUEUEEMPTY) {
+		ret = gen_a_random();
+	}else{
+		ret = randomness[first];
+		++first;
+		if (first >= MAXRAND) {
+			first = 0;
+		}
+	}
+	get_more_random();
+	return ret;
+}
+
+static void
+cl_init_random(void)
+{
+	if (inityet) {
+		return;
+	}
+	inityet=TRUE;
+	srand(cl_randseed());
+	first = 0;
+	last = 0;
+	get_more_random();
+}
+
+
+/* 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
+}
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 at 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..ed9366f
--- /dev/null
+++ b/lib/clplumbing/cl_syslog.c
@@ -0,0 +1,153 @@
+/*
+ * 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)
+{
+	struct _syslog_code *fnames;
+	int i;
+
+	if(fname == NULL || strcmp("none", fname) == 0) {
+		return 0;
+	}
+	
+	fnames = (struct _syslog_code *) facilitynames;
+	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)
+{
+	struct _syslog_code *fnames;
+	int i;
+
+	fnames = (struct _syslog_code *) facilitynames;
+	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 at 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 at 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..58794b0
--- /dev/null
+++ b/lib/clplumbing/ipcsocket.c
@@ -0,0 +1,2897 @@
+/*
+ * ipcsocket unix domain socket implementation of IPC abstraction.
+ *
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu at ncsa.uiuc.edu>
+ *
+ * Stream support (c) 2004,2006 David Lee <t.d.lee at 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 10
+
+# 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);
+
+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;
+	}
+	
+	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);
+	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) {
+			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);	       
+	}			
+	
+	return;	
+}
+
+
+
+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 < 0 || msg->msg_len > MAXMSG) {
+		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;
+	struct IPC_MESSAGE* ipcmsg;
+
+	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;
+	}
+	ipcmsg = *message = (struct IPC_MESSAGE *) (element->data);
+	
+	
+#ifdef IPC_TIME_DEBUG		
+	ipc_time_debug(ch, ipcmsg, 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");
+			exit(1);
+		}
+	}
+	
+	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");
+			exit(1);
+		}
+		
+		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) ;
+			
+			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 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;
+  }
+  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;
+  }
+  
+  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);
+
+	return;
+
+}
+
+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 ++;
+	
+	return;
+}
+
+
+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...
+ *
+ ***********************************************************************/
+
+
+/***********************************************************************
+ * 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. */
+	if (	auth_info->uid
+	&&	(g_hash_table_lookup(auth_info->uid
+		,	GUINT_TO_POINTER((guint)cred.uid)) != NULL)) {
+		ret = IPC_OK;
+	}else if (auth_info->gid
+	&&	(g_hash_table_lookup(auth_info->gid
+		,	GUINT_TO_POINTER((guint)cred.gid)) != NULL)) {
+		ret = IPC_OK;
+  	}
+	return ret;
+}
+
+/* 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;
+	if (ret == IPC_OK) {
+		return ret;
+	}
+
+	/* Check credentials against authorization information */
+
+	if (	auth_info->uid
+	&&	(g_hash_table_lookup(auth_info->uid
+		,	GUINT_TO_POINTER((guint)euid)) != NULL)) {
+		ret = IPC_OK;
+	}else if (auth_info->gid
+	&&	(g_hash_table_lookup(auth_info->gid
+		,	GUINT_TO_POINTER((guint)egid)) != NULL)) {
+		ret = IPC_OK;
+  	}
+	return ret;
+}
+
+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;
+  }
+
+  ret = IPC_OK;
+
+  if (	auth_info->uid
+  &&	g_hash_table_lookup(auth_info->uid, &(cred.crEuid)) == NULL) {
+		ret = IPC_FAIL;
+  }
+  if (	auth_info->gid
+  &&	g_hash_table_lookup(auth_info->gid, &(cred.crEgid)) == NULL) {
+		ret = IPC_FAIL;
+  }
+
+  return ret;
+}
+
+/*
+ * 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;
+	}
+
+	ret = IPC_OK;
+
+	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;
+	}
+	
+	if (auth_info->uid != NULL && g_hash_table_size(auth_info->uid) > 0
+	    && g_hash_table_lookup(
+		    auth_info->uid, GUINT_TO_POINTER(stat_buf.st_uid))==NULL) {
+		ret = IPC_FAIL;
+		
+	}
+	if (auth_info->gid != NULL && g_hash_table_size(auth_info->gid) > 0
+	    && g_hash_table_lookup(
+		    auth_info->gid, GUINT_TO_POINTER(stat_buf.st_gid))==NULL) {
+		ret = IPC_FAIL;
+	}
+
+	return ret;
+}
+
+
+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;
+
+	if (auth_info == NULL
+	  || (auth_info->uid == NULL && auth_info->gid == NULL)) {
+		return IPC_OK;	/* no restriction for authentication */
+	}
+
+	/* verify the credential information. */
+	if (	auth_info->uid
+	&&	(g_hash_table_lookup(auth_info->uid,
+		  GUINT_TO_POINTER((guint)conn_info->farside_uid)) != NULL)) {
+		return IPC_OK;
+	}else if (auth_info->gid
+	&&	(g_hash_table_lookup(auth_info->gid,
+		  GUINT_TO_POINTER((guint)conn_info->farside_gid)) != NULL)) {
+		return IPC_OK;
+	}
+	return IPC_FAIL;
+}
+
+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;
+	}
+
+	/* Check credentials against authorization information */
+
+	if (auth_info->uid
+	  && (g_hash_table_lookup(auth_info->uid,
+		  GUINT_TO_POINTER((guint)ucred_geteuid(ucred))) != NULL)) {
+		rc = IPC_OK;
+	}else if (auth_info->gid
+	  && (g_hash_table_lookup(auth_info->gid,
+		  GUINT_TO_POINTER((guint)ucred_getegid(ucred))) != NULL)) {
+		rc = IPC_OK;
+  	}
+
+	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..70f646a
--- /dev/null
+++ b/lib/clplumbing/ipctest.c
@@ -0,0 +1,1368 @@
+/*
+ * 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 = 10;
+
+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++;
+			}
+			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 10 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;
+	
+	str = malloc(data_size);
+	
+	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;
+	
+	str = malloc(data_size);
+
+	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;
+	GCHSource*	msgchan;
+	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);
+	msgchan = 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 at 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..398fb9c
--- /dev/null
+++ b/lib/clplumbing/ipctransientclient.c
@@ -0,0 +1,222 @@
+/* 
+ * Copyright (C) 2004 Andrew Beekhof <andrew at 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] Recieved someone else's message [%s] instead of [%s]", buffer, messages[lpc]);
+			}
+			else if(lpc >= MAX_MESSAGES)
+			{
+				cl_log(LOG_ERR, "[Client] Recieved an 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 at 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 at 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..6e2c985
--- /dev/null
+++ b/lib/clplumbing/longclock.c
@@ -0,0 +1,270 @@
+/*
+ * Longclock operations
+ *
+ * Copyright (c) 2002 International Business Machines
+ * Author:	Alan Robertson <alanr at 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 */
+	return (unsigned long)ret;
+}
+
+#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 MAXIMUMULONG	((unsigned long)~(0UL))
+#define MINJUMP		((MAXIMUMULONG/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	wrapcount	= 0L;
+	static	unsigned long	callcount	= 0L;
+	static	longclock_t	lc_wrapcount	= 0L;
+	unsigned long		timesval;
+
+	++callcount;
+
+	timesval = (unsigned long) cl_times();
+
+	if (calledbefore && timesval < lasttimes)  {
+		clock_t		jumpbackby = lasttimes - timesval;
+
+		if (jumpbackby < (clock_t)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;
+			++wrapcount;
+			lc_wrapcount = ((longclock_t)wrapcount) << WRAPSHIFT;
+
+			cl_log(LOG_INFO
+			,	"%s: clock_t wrapped around (uptime)."
+			,	__FUNCTION__);
+		}
+	}
+	else {
+		lasttimes = timesval;
+		calledbefore = TRUE;
+	}
+	return (lc_wrapcount | (longclock_t)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..46a0c1e
--- /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 at lysator.liu.se>
+ *	Sun Jiang Dong <sunjd at cn.ibm.com>
+ *	Pan Jia Ming <jmltc at 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 at 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..be71af8
--- /dev/null
+++ b/lib/clplumbing/ocf_ipc.c
@@ -0,0 +1,647 @@
+/*
+ *
+ * ocf_ipc.c: IPC abstraction implementation.
+ *
+ *
+ * Copyright (c) 2002 Xiaoxiang Liu <xiliu at 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_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_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){
+		cl_log(LOG_ERR, "pool is 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);
+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);
+	return;
+	       
+}
+
+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 + sizeof(struct ipc_bufpool) 
+		+ sizeof(struct SOCKET_MSG_HEAD) * 2 ;		
+	
+	if (totalsize < POOL_SIZE){
+		totalsize = POOL_SIZE;
+	}
+	
+	if (totalsize > MAXMSG){
+		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 ++ ;
+	return;
+}
+
+int
+ipc_bufpool_spaceleft(struct ipc_bufpool* pool)
+{
+
+	if( pool == NULL){
+		cl_log(LOG_ERR, "ipc_bufpool_spacelft:"
+		       "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);
+	      
+	return;
+	
+}
+
+/* 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, abort now, 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);
+			}
+			abort();
+		}
+
+		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);
+	}
+	
+	return;
+}
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 at 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 at 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 at 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 at 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 at 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/.cvsignore b/lib/lrm/.cvsignore
new file mode 100644
index 0000000..cab0d2e
--- /dev/null
+++ b/lib/lrm/.cvsignore
@@ -0,0 +1,10 @@
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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..f24469f
--- /dev/null
+++ b/lib/lrm/clientlib.c
@@ -0,0 +1,1608 @@
+/* 
+ * Client Library for Local Resource Manager  API.
+ *
+ * Author:  Huang Zhen <zhenh at 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;
+
+	/* 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 */
+	if (HA_OK != get_ret_from_ch(ch_cmd)) {
+		LOG_GOT_FAIL_RET(LOG_ERR, DELRSC);
+		return HA_FAIL;
+	}
+
+	return HA_OK;
+}
+
+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?HA_OK: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);
+
+	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: deamon dead";
+		case EXECRA_RA_DEAMON_DEAD2:
+			return "status: deamon dead";
+		case EXECRA_RA_DEAMON_STOPPED:
+			return "status: deamon 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 at 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..c50566a
--- /dev/null
+++ b/lib/lrm/racommon.c
@@ -0,0 +1,172 @@
+/*
+ * 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 at 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*/
+		g_strlcpy(pathname, type, RA_MAX_NAME_LENGTH);
+	}
+
+	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/.cvsignore b/lib/pils/.cvsignore
new file mode 100644
index 0000000..4a512a6
--- /dev/null
+++ b/lib/pils/.cvsignore
@@ -0,0 +1,11 @@
+main
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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..8f47ba4
--- /dev/null
+++ b/lib/pils/pils.c
@@ -0,0 +1,2151 @@
+/*
+ * Copyright (C) 2001 Alan Robertson <alanr at 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, ...);
+
+
+/*
+ *	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%x)", (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%x)", (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%x)"
+		,	(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%x)", (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%lx *******"
+			,	(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%x)"
+		,	(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] "
+		"%d %d %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 at 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 at 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 at 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..139d6f3
--- /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  
diff --git a/lib/plugins/lrm/.cvsignore b/lib/plugins/lrm/.cvsignore
new file mode 100644
index 0000000..3f39ebd
--- /dev/null
+++ b/lib/plugins/lrm/.cvsignore
@@ -0,0 +1,11 @@
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+*.loT
+*.beam
+parser-messages
+MISC_ERRORS
diff --git a/lib/plugins/lrm/Makefile.am b/lib/plugins/lrm/Makefile.am
new file mode 100644
index 0000000..cee3624
--- /dev/null
+++ b/lib/plugins/lrm/Makefile.am
@@ -0,0 +1,41 @@
+#
+# Author: Sun Jiang Dong <sunjd at 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
+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
+
+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
+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
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 at 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..5479709
--- /dev/null
+++ b/lib/plugins/lrm/raexeclsb.c
@@ -0,0 +1,605 @@
+/* 
+ * 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 at 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\" start-delay=\"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);
+		}
+#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;
+		}
+	}
+#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..7e8ef36
--- /dev/null
+++ b/lib/plugins/lrm/raexecocf.c
@@ -0,0 +1,492 @@
+/* 
+ * 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 at 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;
+		}
+		
+		stat(namelist[file_num]->d_name, &prop);
+		if (S_ISDIR(prop.st_mode)) {
+			free(namelist[file_num]);
+			continue;
+		}
+
+		snprintf(subdir,FILENAME_MAX,"%s/%s",
+			 RA_PATH, namelist[file_num]->d_name);
+			 
+		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/stonith/.cvsignore b/lib/plugins/stonith/.cvsignore
new file mode 100644
index 0000000..9734616
--- /dev/null
+++ b/lib/plugins/stonith/.cvsignore
@@ -0,0 +1,11 @@
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+*.beam
+parser-messages
+MISC_ERRORS
+ribcl.py
diff --git a/lib/plugins/stonith/Makefile.am b/lib/plugins/stonith/Makefile.am
new file mode 100644
index 0000000..0a051c4
--- /dev/null
+++ b/lib/plugins/stonith/Makefile.am
@@ -0,0 +1,214 @@
+#
+# 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	= $(GLIBLIB)
+
+apcmastersnmp_la_SOURCES= apcmastersnmp.c $(INCFILES)
+apcmastersnmp_la_LDFLAGS= -export-dynamic -module -avoid-version @SNMPLIB@ \
+			  @CRYPTOLIB@
+apcmastersnmp_la_LIBADD	= $(GLIBLIB)
+
+apcsmart_la_SOURCES	= apcsmart.c $(INCFILES)
+apcsmart_la_LDFLAGS	= -export-dynamic -module -avoid-version
+apcsmart_la_LIBADD	= $(GLIBLIB)
+
+baytech_la_SOURCES	= baytech.c $(INCFILES)
+baytech_la_LDFLAGS	= -export-dynamic -module -avoid-version
+baytech_la_LIBADD	= $(GLIBLIB)
+
+bladehpi_la_SOURCES	= bladehpi.c $(INCFILES)
+bladehpi_la_LDFLAGS	= -export-dynamic -module -avoid-version
+bladehpi_la_LIBADD	= $(GLIBLIB) -lopenhpi
+
+cyclades_la_SOURCES	= cyclades.c $(INCFILES)
+cyclades_la_LDFLAGS	= -export-dynamic -module -avoid-version
+cyclades_la_LIBADD	= $(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		= -lcurl -lxml2 $(GLIBLIB)
+
+external_la_SOURCES	= external.c	$(INCFILES)
+external_la_LDFLAGS	= -export-dynamic -module -avoid-version
+external_la_LIBADD	= $(top_builddir)/replace/libreplace.la
+
+rhcs_la_SOURCES	= rhcs.c	$(INCFILES)
+rhcs_la_LDFLAGS	= -export-dynamic -module -avoid-version
+rhcs_la_LIBADD	= $(top_builddir)/replace/libreplace.la
+
+ibmhmc_la_SOURCES	= ibmhmc.c	$(INCFILES)
+ibmhmc_la_LDFLAGS	= -export-dynamic -module -avoid-version
+ibmhmc_la_LIBADD	= $(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)/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)/replace/libreplace.la $(OPENIPMI_LIB) $(GLIBLIB)
+
+meatware_la_SOURCES	= meatware.c $(INCFILES)
+meatware_la_LDFLAGS	= -export-dynamic -module -avoid-version
+meatware_la_LIBADD	= $(GLIBLIB)
+
+null_la_SOURCES		= null.c $(INCFILES)
+null_la_LDFLAGS		= -export-dynamic -module -avoid-version
+null_la_LIBADD		= $(GLIBLIB)
+
+nw_rpc100s_la_SOURCES	= nw_rpc100s.c $(INCFILES)
+nw_rpc100s_la_LDFLAGS	= -export-dynamic -module -avoid-version
+nw_rpc100s_la_LIBADD	= $(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	= $(GLIBLIB)
+
+rps10_la_SOURCES	= rps10.c $(INCFILES)
+rps10_la_LDFLAGS	= -export-dynamic -module -avoid-version
+rps10_la_LIBADD		= $(GLIBLIB)
+
+ssh_la_SOURCES		= ssh.c $(INCFILES)
+ssh_la_LDFLAGS		= -export-dynamic -module -avoid-version
+
+vacm_la_SOURCES		= vacm.c $(INCFILES)
+vacm_la_LDFLAGS		= -export-dynamic -module -avoid-version
+vacm_la_LIBADD		= $(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)/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	= $(GLIBLIB)
+
+suicide_la_SOURCES	= suicide.c $(INCFILES)
+suicide_la_LDFLAGS	= -export-dynamic -module -avoid-version
+
+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..84e0818
--- /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 at mclinux.com>
+ *  author: Todd Wheeling <wheeling at mclinux.com>
+ *  mangled by Sun Jiang Dong, <sunjd at 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 **	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;
+			}
+			g_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 **
+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..00d89c5
--- /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 at gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd at 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 **	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","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") \
+	  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);
+		}
+		g_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 **
+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..3a0613e
--- /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 at 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 at exploits.org>
+ *   homepage: http://www.networkupstools.org/
+ *
+ *  Significantly mangled by Alan Robertson <alanr at 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**	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**
+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++) {
+		g_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 **)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..ad25974
--- /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 at 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 **	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;
+		}
+		g_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 **
+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..27bb975
--- /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 at 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 **	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") \
+	  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") \
+	  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;
+		}
+		g_strdown(ret[j]);
+	}
+
+done:
+	close_hpi_session(dev);
+	return ret;
+}
+
+
+static const char **     
+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..f8c0431
--- /dev/null
+++ b/lib/plugins/stonith/cyclades.c
@@ -0,0 +1,633 @@
+/*
+ * Stonith module for Cyclades AlterPath PM
+ * Bases off the SSH plugin
+ *
+ * Copyright (c) 2004 Cyclades corp.
+ *
+ * Author: Jon Taylor <jon.taylor at cyclades.com>
+ *
+ * Rewritten from scratch using baytech.c structure and code 
+ * and currently maintained by
+ *       Marcelo Tosatti  <marcelo.tosatti at 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 **	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"
+
+static const char *cycladesXML = 
+  XML_PARAMETERS_BEGIN
+    XML_IPADDR_PARM
+    XML_LOGIN_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;
+			}
+			g_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 **
+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..a984e4d
--- /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 at alfa21.com>
+ * Tiny bits Copyright 2005 International Business Machines
+ * Significantly Mangled by Sun Jiang Dong <sunjd at 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 ** 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") \
+	  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 **
+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 {
+		g_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 at 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 at 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 at 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 at 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..723205f
--- /dev/null
+++ b/lib/plugins/stonith/external.c
@@ -0,0 +1,845 @@
+/*
+ * 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 at suse.de>,
+ *                          Lars Marowsky-Bree <lmb at suse.de>
+ * Modified for external.c: Scott Kleihege <scott at tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo at tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb at suse.de>, so the circle
+ *   closes...
+ * Mangled by Zhaokai <zhaokai at cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke 
+ *   <debltc at 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**	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_CRIT, "%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++) {
+		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_INFO, "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**
+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 **)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			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");
+	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", path, GLUE_SHARED_DIR);
+	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);
+
+	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;
+	}
+
+	rc = pclose(file);
+	if (rc != 0) {
+		LOG(PIL_INFO, "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc);
+	}
+	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/.cvsignore b/lib/plugins/stonith/external/.cvsignore
new file mode 100644
index 0000000..25d4a27
--- /dev/null
+++ b/lib/plugins/stonith/external/.cvsignore
@@ -0,0 +1,5 @@
+Makefile
+Makefile.in
+ibmrsa
+riloe
+ssh
diff --git a/lib/plugins/stonith/external/Makefile.am b/lib/plugins/stonith/external/Makefile.am
new file mode 100644
index 0000000..969263d
--- /dev/null
+++ b/lib/plugins/stonith/external/Makefile.am
@@ -0,0 +1,32 @@
+# 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 xen0 \
+			xen0-ha-dom0-stonith-helper sbd kdumpcheck
+
+extdir		     = $(stonith_ext_plugindir)
+
+helperdir	     = $(stonith_plugindir)
+
+ext_SCRIPTS	     = drac5 dracmc-telnet ibmrsa ibmrsa-telnet ipmi riloe ssh vmware rackpdu xen0 hmchttp \
+			xen0-ha sbd kdumpcheck
+
+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 at novell.com>
+#
+# Based on ibmrsa-telnet external stonith plugin by Andreas Mock
+# (andreas.mock at 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 at 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/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 at 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..f7264a6
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Dejan Muhamedagic <dmuhamedagic at 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 '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
+}
+
+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
+	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..e140da6
--- /dev/null
+++ b/lib/plugins/stonith/external/ibmrsa-telnet
@@ -0,0 +1,319 @@
+#!/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 at 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 at 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):
+        self._history.append(self._get_timestamp() + ': WRITE: ' + 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)
+        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 at 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..10cafd7
--- /dev/null
+++ b/lib/plugins/stonith/external/ipmi
@@ -0,0 +1,215 @@
+#!/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 at 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, 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=`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() {
+	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
+
+	action="$*"
+
+	${IPMITOOL} -I ${interface} -H ${ipaddr} -U "${userid}" -P "${passwd}" ${action} 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
+}
+
+
+# Rewrite the hostname to accept "," as a delimeter for hostnames too.
+
+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="1">
+<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="1">
+<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="interface" unique="1">
+<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>
+
+</parameters>
+IPMIXML
+	exit 0
+	;;
+*)
+	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/rackpdu b/lib/plugins/stonith/external/rackpdu
new file mode 100644
index 0000000..a512a23
--- /dev/null
+++ b/lib/plugins/stonith/external/rackpdu
@@ -0,0 +1,270 @@
+#!/bin/sh
+#
+# External STONITH module for APC Switched Rack PDU
+#
+# Copyright (c) 2008 Sergey Maznichenko <msergeyb at gmail.com> <inbox at 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=`snmpwalk -v1 -c $community $pduip $names_oid 2>&1`
+
+	local 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=`snmpset -v1 -c $community $pduip $oid.$outlet i $command 2>&1`
+        local check_result=`echo "$set_result" | grep "Timeout"`	    
+
+        if [ ! -z "$check_result" ]; then
+    	    ha_log.sh err "Write SNMP value $oid.$outlet=$command. Result: $set_result"
+	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`
+	    snmp_check=`echo "$snmp_result" | grep "Timeout"`
+
+	    if [ ! -z "$snmp_check" ]; then
+		ha_log.sh err "Cannot read list of nodes from device. 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.it-consultant.su/rackpdu"
+	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..9a649a9
--- /dev/null
+++ b/lib/plugins/stonith/external/riloe
@@ -0,0 +1,486 @@
+#!/usr/bin/env python
+#
+# Stonith module for RILOE Stonith device
+#
+# Copyright (c) 2004 Alain St-Denis <alain.st-denis at ec.gc.ca>
+#
+# Modified by Alan Robertson <alanr at unix.sh> for STONITH external compatibility.
+#
+# Extended and merged by Tijl Van den broeck <subspawn at gmail.com>
+#  with ilo-v2 script from Guy Coates
+#
+# Cleanup by Andrew Beekhof <abeekhof at suse.de>
+#
+# Rewritten by Dejan Muhamedagic <dejan at suse.de>
+# Now, the plugin actually reads replies from iLO.
+#
+# 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
+from httplib import HTTPSConnection
+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)
+
+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>
+</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"]:
+        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:
+        return 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))
+
+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"%rihost)
+    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/sbd b/lib/plugins/stonith/external/sbd
new file mode 100644
index 0000000..2631292
--- /dev/null
+++ b/lib/plugins/stonith/external/sbd
@@ -0,0 +1,91 @@
+#!/bin/bash
+#
+# This STONITH script drives the shared-storage stonith plugin.
+#
+# Author:	Lars Marowsky-Bree
+# Copyright:	2008 Lars Marowsky-Bree
+# License:      GNU General Public License (GPL)
+#
+
+# Main code
+
+case $1 in
+gethosts)
+    echo `sbd -d $sbd_device list | cut -f2`
+    exit 0
+    ;;
+off|reset)
+    sbd -d $sbd_device message $2 $1
+    exit $?
+    ;;
+status)
+    if ! sbd -d $sbd_device list >/dev/null 2>&1 ; then
+    	ha_log err "sbd could not list nodes from $sbd_device"
+    	exit 1
+    fi
+    nodes="$(crm_node -l)"
+    if [ -z "$node" ]; then
+        # No active nodes; strange, but maybe not running pacemaker?
+	ha_log warn "no active nodes reported by the CRM"
+    	exit 0
+    fi
+    for N in $nodes ; do
+    	if ! sbd -d $sbd_device ping $N >/dev/null 2>&1 ; then
+	    ha_log err "node $N not accessible through $sbd_device"
+	    exit 1
+	fi
+    done
+    exit 0
+    ;;
+on)
+    exit 1
+    ;;
+getconfignames)
+    echo "sbd_device"
+    exit 0
+    ;;
+getinfo-devid)
+    echo "Shared storage STONITH device"
+    exit 0
+    ;;
+getinfo-devname)
+    echo "Shared storage STONITH device"
+    exit 0
+    ;;
+getinfo-devdescr)
+    cat << DESC
+sbd uses a shared storage device as a medium to communicate
+fencing requests. This allows clusters without network power
+switches; the downside is that access to the shared storage
+device becomes a Single Point of Failure. 
+
+It requires sbd to be configured.  Please read
+http://linux-ha.org/wiki/SBD_Fencing!
+
+DESC
+    exit 0
+    ;;
+getinfo-devurl)
+    echo "http://linux-ha.org/wiki/SBD_Fencing"
+    exit 0
+    ;;
+getinfo-xml)
+    cat << SSHXML
+<parameters>
+<parameter name="sbd_device" unique="1" required="1">
+<content type="string" />
+<shortdesc lang="en">
+SBD device
+</shortdesc>
+<longdesc lang="en">
+The block device used for the SBD partition.
+</longdesc>
+</parameter>
+</parameters>
+SSHXML
+    exit 0
+    ;;
+*)
+    exit 1
+    ;;
+esac
diff --git a/lib/plugins/stonith/external/ssh.in b/lib/plugins/stonith/external/ssh.in
new file mode 100644
index 0000000..dd786be
--- /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 at 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 Linux 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/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 at 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..97f4ec9
--- /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 at gmail.com)
+#               Inspired by Lars Marowsky-Bree's external/ssh agent.
+#
+# Copyright 2007 Serge Dubrouski <sergeyfd at 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 err "hostlist isn't set"
+        exit 1
+    fi
+}
+
+CheckDom0() {
+    if [ "x" = "x$dom0" ]
+    then
+        ha_log 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 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 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 Linux 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..2aa8422
--- /dev/null
+++ b/lib/plugins/stonith/ibmhmc.c
@@ -0,0 +1,1261 @@
+/*
+ * Stonith module for IBM Hardware Management Console (HMC)
+ *
+ * Author: Huang Zhen <zhenh at cn.ibm.com>
+ * Support for HMC V4+ added by Dave Blaschke <debltc at 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**	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") \
+	  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") \
+	  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;
+		}
+		g_strdown(ret[j]);
+	}
+	return ret;
+}
+
+
+static const char**     
+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 at 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..f4e2ea3
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan.c
@@ -0,0 +1,586 @@
+/*
+ * Stonith module for ipmi lan Stonith device
+ *
+ * Copyright (c) 2003 Intel Corp. 
+ *	Yixiong Zou <yixiong.zou at intel.com>
+ *
+ * Mangled by Sun Jiang Dong <sunjd at 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 **	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") \
+	  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") \
+	  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") \
+	  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") \
+	  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") \
+	  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;
+		}
+		g_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 **
+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);
+	}
+	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 at 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..8549805
--- /dev/null
+++ b/lib/plugins/stonith/ipmilan_command.c
@@ -0,0 +1,398 @@
+/*
+ * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
+ * 
+ * Copyright Intel Corp. 
+ * Yixiong.Zou at 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/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 at 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..fbc0742
--- /dev/null
+++ b/lib/plugins/stonith/meatware.c
@@ -0,0 +1,349 @@
+/*
+ * Stonith module for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder at sysfive.com>
+ *
+ *   This module is largely based on the "NULL Stonith device", written
+ *   by Alan Robertson <alanr at unix.sh>, using code by David C. Teigland
+ *   <teigland at sistina.com> originally appeared in the GFS stomith
+ *   meatware agent.
+ *
+ * Mangled by Zhaokai <zhaokai at 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**	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 **)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++) {
+		g_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;
+	}
+
+	memset(line, 0, 256);
+	rc = read(fd, line, 256);
+
+	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;
+	}
+	
+	g_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**
+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..09dccaa
--- /dev/null
+++ b/lib/plugins/stonith/null.c
@@ -0,0 +1,260 @@
+/*
+ * Stonith module for NULL Stonith device
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr at 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**	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**)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**
+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++) {
+		g_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..5b7b2df
--- /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 at unix.sh>
+ *
+ *      Modifications for NW RPC100S
+ *	Copyright (c) 2000 Computer Generation Incorporated
+ *               Eric Z. Ayers <eric.ayers at compgen.com>
+ *
+ *      Mangled by Zhaokai <zhaokai at 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**	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 {
+		g_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 **
+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..095bbe8
--- /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 at unix.sh>
+ *
+ * Copious borrowings from nw_rpc100s.c by
+ * Copyright (c) 2000 Computer Generation Incorporated
+ *          Eric Z. Ayers <eric.ayers at compgen.com>
+ *
+ *                and from apcsmart.c by
+ * Copyright (c) 2000 Andreas Piesk <a.piesk at gmx.net>
+ *
+ * Modifications for RC Delayed Serial Ciruit by 
+ * Copyright (c) 2002 John Sutton <john at scl.co.uk>
+ *
+ * Mangled by Zhaokai <zhaokai at 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 **	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") \
+	  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") \
+	  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 **)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;
+	g_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**
+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..7b9c6d5
--- /dev/null
+++ b/lib/plugins/stonith/rhcs.c
@@ -0,0 +1,1026 @@
+/*
+ * 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 at suse.de>,
+ *                          Lars Marowsky-Bree <lmb at suse.de>
+ * Modified for external.c: Scott Kleihege <scott at tummy.com>
+ * Reviewed, tested, and config parsing: Sean Reifschneider <jafo at tummy.com>
+ * And overhauled by Lars Marowsky-Bree <lmb at suse.de>, so the circle
+ *   closes...
+ * Mangled by Zhaokai <zhaokai at cn.ibm.com>, IBM, 2005
+ * Changed to allow full-featured external plugins by Dave Blaschke 
+ *   <debltc at us.ibm.com>
+ * Modified for rhcs.c: Dejan Muhamedagic <dejan at 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**	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, 
+		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, &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;
+
+	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;
+	char *k, *v;
+	
+	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;
+	}
+	
+	k = g_strdup("nodename");
+	v = g_strdup(host);
+	g_hash_table_insert(sd->cmd_opts, k, v);
+	rc = rhcs_run_cmd(sd, op, &output);
+	g_hash_table_remove(sd->cmd_opts, k);
+	g_free(k);
+	g_free(v);
+	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++) {
+		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, &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 **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**
+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 **)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
+rhcs_print_var(gpointer key, gpointer value, gpointer user_data)
+{
+	int fd = GPOINTER_TO_UINT(user_data);
+	char arg[MAXLINE];
+	int cnt;
+
+	cnt = snprintf(arg, MAXLINE, "%s=%s\n", (char *)key, (char *)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'", (char *)key, (char *)value);
+	}
+	if (write(fd, arg, cnt) < 0) {
+		LOG(PIL_CRIT, "%s: write: %m", __FUNCTION__);
+	}
+}
+
+/* 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, 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) {
+			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]);
+		if (execlp(cmd, cmd, "-o", op, NULL) < 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..e8e69c8
--- /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 at ec.gc.ca>
+ *
+ * Mangled by Zhaokai <zhaokai at 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 **	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 **)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++) {
+		g_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**
+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..ec47ce5
--- /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 at unix.sh>
+ *
+ *      Modifications for WTI RPS10
+ *	Copyright (c) 2000 Computer Generation Incorporated
+ *               Eric Z. Ayers <eric.ayers at compgen.com>
+ *
+ * 	Mangled by Zhaokai <zhaokai at 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**	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") \
+	  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);
+		g_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**
+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..8f8bf6d
--- /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 at suse.de>, Lars Marowsky-Br�e <lmb at 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**	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 at 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 **)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**
+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++) {
+			g_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 Linux 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..d36eb8a
--- /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 at us.ibm.com>
+ * Support: linux-ha at 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="1">?<content type="string" /></parameter> */
+#define XML_PARAMETER_BEGIN(name,type,req) \
+	"<parameter name=\"" name "\" unique=\"1\" 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") \
+	  XML_HOSTLIST_SHORTDESC \
+	  XML_HOSTLIST_LONGDESC \
+	XML_PARAMETER_END
+
+#define XML_IPADDR_PARM \
+	XML_PARAMETER_BEGIN(ST_IPADDR, "string", "1") \
+	  XML_IPADDR_SHORTDESC \
+	  XML_IPADDR_LONGDESC \
+	XML_PARAMETER_END
+
+#define XML_LOGIN_PARM \
+	XML_PARAMETER_BEGIN(ST_LOGIN, "string", "1") \
+	  XML_LOGIN_SHORTDESC \
+	  XML_LOGIN_LONGDESC \
+	XML_PARAMETER_END
+
+#define XML_PASSWD_PARM \
+	XML_PARAMETER_BEGIN(ST_PASSWD, "string", "1") \
+	  XML_PASSWD_SHORTDESC \
+	  XML_PASSWD_LONGDESC \
+	XML_PARAMETER_END
+
+#define XML_COMMUNITY_PARM \
+	XML_PARAMETER_BEGIN(ST_COMMUNITY, "string", "1") \
+	  XML_COMMUNITY_SHORTDESC \
+	  XML_COMMUNITY_LONGDESC \
+	XML_PARAMETER_END
+
+#define XML_TTYDEV_PARM \
+	XML_PARAMETER_BEGIN(ST_TTYDEV, "string", "1") \
+	  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 at 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 at 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 at 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..3b4ed4c
--- /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 at 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**	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;
+	}
+	g_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**
+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..af12f26
--- /dev/null
+++ b/lib/plugins/stonith/vacm.c
@@ -0,0 +1,485 @@
+
+/******************************************************************************
+*
+*    Copyright 2000 Sistina Software, Inc.
+*    Tiny bits Copyright 2000 Alan Robertson <alanr at 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 at 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 at 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 at 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 **	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") \
+	  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;
+	    }
+            g_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 **
+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..be1f227
--- /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 at gmx.net>
+ * Mangled by Sun Jiang Dong <sunjd at cn.ibm.com>, IBM, 2005
+ * 
+ * Modified for WTI MPC by Denis Chapligin <chollya at 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 **	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") \
+	  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") \
+	  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);
+		}
+		g_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 **
+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..4fb3821
--- /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 at mclinux.com>
+ *  author: Todd Wheeling <wheeling at mclinux.com>
+ *  Mangled by Zhaokai <zhaokai at cn.ibm.com>, IBM, 2005
+ *  Further hurt by Lon <lhh at 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**	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;
+			}
+			g_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**
+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/.cvsignore b/lib/stonith/.cvsignore
new file mode 100644
index 0000000..9e662d9
--- /dev/null
+++ b/lib/stonith/.cvsignore
@@ -0,0 +1,13 @@
+meatclient
+README
+stonith
+Makefile.in
+Makefile
+.*.swp
+.deps
+.libs
+*.lo
+*.la
+*.beam
+parser-messages
+MISC_ERRORS
diff --git a/lib/stonith/Makefile.am b/lib/stonith/Makefile.am
new file mode 100644
index 0000000..057086e
--- /dev/null
+++ b/lib/stonith/Makefile.am
@@ -0,0 +1,61 @@
+#
+# 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 
+if ON_LINUX
+sbin_PROGRAMS		+= sbd
+endif
+
+stonith_SOURCES		= main.c
+
+stonith_LDADD		= libstonith.la $(top_builddir)/lib/pils/libpils.la $(GLIBLIB)
+stonith_LDFLAGS		=  @LIBADD_DL@ @LIBLTDL@ -export-dynamic @DLOPEN_FORCE_FLAGS@ @LIBADD_INTL@ 
+
+meatclient_SOURCES	= meatclient.c 
+meatclient_LDADD	= $(GLIBLIB)
+
+sbd_SOURCES		= sbd.c
+sbd_CFLAGS		= -D_GNU_SOURCE
+sbd_LDADD		= $(GLIBLIB)					\
+			$(top_builddir)/lib/clplumbing/libplumb.la	\
+			$(top_builddir)/lib/clplumbing/libplumbgpl.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) sbd.h
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..53b8b17
--- /dev/null
+++ b/lib/stonith/expect.c
@@ -0,0 +1,539 @@
+/*
+ * Simple expect module for the STONITH library
+ *
+ *	Copyright (c) 2000 Alan Robertson <alanr at 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** hostlist)
+{
+	int hlleng = 1;
+	const char ** 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;
+		g_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..2050930
--- /dev/null
+++ b/lib/stonith/ha_log.sh
@@ -0,0 +1,104 @@
+#!/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: @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
+}
+
+ha_log() {
+	loglevel=$1
+	shift
+	prn_level=`level_pres $loglevel`
+	msg="$prn_level: $@"
+
+	# add parent pid to the logtag
+	if [ "$HA_LOGTAG" ]; then
+		HA_LOGTAG="$HA_LOGTAG[$PPID]"
+	fi
+
+	# if we're connected to a tty, then output to stderr
+	if tty >/dev/null; then
+		if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
+			return 0
+		fi
+		if [ "$HA_LOGTAG" ]; then
+			echo "$HA_LOGTAG: $msg"
+		else
+			echo "$msg"
+		fi >&2
+		return 0
+	fi
+
+	[ "x$HA_LOGD" = "xyes" ] &&
+		ha_logger -t "$HA_LOGTAG" "$msg" &&
+		return 0
+
+	if [ -n "$HA_LOGFACILITY" ]; 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..08ba408
--- /dev/null
+++ b/lib/stonith/main.c
@@ -0,0 +1,615 @@
+/*
+ * Stonith: simple test program for exercising the Stonith API code
+ *
+ * Copyright (C) 2000 Alan Robertson <alanr at 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 <glib.h>
+#include <libxml/entities.h>
+
+#define	OPTIONS	"c:F:p:t:T:snSlLmvhd"
+#define	EQUAL	'='
+
+extern char *	optarg;
+extern int	optind, opterr, optopt;
+
+static int	debug = 0;
+
+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=\"60\" />\n"
+"<action name=\"stop\"    timeout=\"15\" />\n"
+"<action name=\"status\"  timeout=\"60\" />\n"
+"<action name=\"monitor\" timeout=\"60\" 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 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);
+
+/*
+ * Note that we don't use the cl_log logging code because the STONITH
+ * command is intended to be shipped without the clplumbing libraries.
+ *
+ *	:-(
+ */
+
+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 | "
+		"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 | "
+		"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 at 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 **	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_DEVICENAME);
+	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
+
+int
+main(int argc, char** argv)
+{
+	char *		cmdname;
+	int		rc;
+	Stonith *	s;
+	const char *	SwitchType = NULL;
+	const char *	tmp;
+	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		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 '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;
+
+		default:	++errors;
+				break;
+		}
+	}
+
+	if (help && !errors) {
+		usage(cmdname, 0, SwitchType);
+	}
+	if (debug) {
+		PILpisysSetDebugLevel(debug);
+		setenv("HA_debug","2",0);
+	}
+	if (optfile && parameters) {
+		fprintf(stderr
+		,	"Cannot include both -F and -p 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)  {
+			fprintf(stderr
+			,	"Cannot include both -p and name=value "
+			"style arguments\n");
+			usage(cmdname, 1, NULL);
+		}
+		if (optfile)  {
+			fprintf(stderr
+			,	"Cannot include both -F and name=value "
+			"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) {
+		char **	typelist;
+
+		typelist = stonith_types();
+		if (typelist == NULL) {
+			syslog(LOG_ERR, "Could not list Stonith types.");
+		}else{
+			char **	this;
+
+			for(this=typelist; *this; ++this) {
+				printf("%s\n", *this);
+			}
+		}
+		exit(0);
+	}
+
+#ifndef LOG_PERROR
+#	define LOG_PERROR	0
+#endif
+	openlog(cmdname, (LOG_CONS|(silent ? 0 : LOG_PERROR)), LOG_USER);
+	if (SwitchType == NULL) {
+		fprintf(stderr,	"Must specify device type (-t option)\n");
+		usage(cmdname, 1, NULL);
+	}
+	s = stonith_new(SwitchType);
+	if (s == NULL) {
+		syslog(LOG_ERR, "Invalid device type: '%s'", SwitchType);
+		exit(S_OOPS);
+	}
+	if (debug) {
+		stonith_set_debug(s, debug);
+	}
+
+	if (!listparanames && !metadata && optfile == NULL && parameters == NULL && nvcount == 0) {
+		const char**	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 or "
+			"name=value style arguments\n");
+			if (s != NULL) {
+				stonith_delete(s); 
+			}
+			usage(cmdname, 1, NULL);
+		}
+	}
+
+	if (listparanames) {
+		const char**	names;
+		int		i;
+		names = stonith_get_confignames(s);
+
+		if (names != NULL) {
+			for (i=0; names[i]; ++i) {
+				printf("%s  ", names[i]);
+			}
+		}
+		printf("\n");
+		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) {
+			syslog(LOG_ERR
+			,	"Invalid config file for %s device."
+			,	SwitchType);
+#if 0
+			syslog(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 (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"
+			,	SwitchType);
+		}
+	}else{
+		/*
+		 *	Configure STONITH device using cmdline arguments...
+		 */
+		if ((rc = stonith_set_config(s, nvargs)) != S_OK) {
+			const char**	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 = stonith_get_status(s);
+
+		if ((tmp = stonith_get_info(s, ST_DEVICEID)) == NULL) {
+			SwitchType = tmp;
+		}
+
+		if (status && !silent) {
+			if (rc == S_OK) {
+				syslog(LOG_ERR, "%s device OK.", SwitchType);
+			}else{
+				/* Uh-Oh */
+				syslog(LOG_ERR, "%s device not accessible."
+				,	SwitchType);
+			}
+		}
+
+		if (listhosts) {
+			char **	hostlist;
+
+			hostlist = stonith_get_hostlist(s);
+			if (hostlist == NULL) {
+				syslog(LOG_ERR, "Could not list hosts for %s."
+				,	SwitchType);
+			}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]);
+			g_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..7674f39
--- /dev/null
+++ b/lib/stonith/meatclient.c
@@ -0,0 +1,152 @@
+/*
+ * Stonith client for Human Operator Stonith device
+ *
+ * Copyright (c) 2001 Gregor Binder <gbinder at sysfive.com>
+ *
+ *   This program is a rewrite of the "do_meatware" program by
+ *   David C. Teigland <teigland at 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;
+	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);
+	}
+	
+	g_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/sbd.c b/lib/stonith/sbd.c
new file mode 100644
index 0000000..825f3a9
--- /dev/null
+++ b/lib/stonith/sbd.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (C) 2008 Lars Marowsky-Bree <lmb at 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <clplumbing/cl_log.h>
+#include <clplumbing/coredumps.h>
+#include <clplumbing/realtime.h>
+#include <clplumbing/cl_reboot.h>
+#include <malloc.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include "sbd.h"
+
+/* These have to match the values in the header of the partition */
+static char		sbd_magic[8] = "SBD_SBD_";
+static char		sbd_version  = 0x02;
+
+/* Tunable defaults: */
+static unsigned long	timeout_watchdog 	= 5;
+static int		timeout_allocate 	= 2;
+static int		timeout_loop	    	= 1;
+static int		timeout_msgwait		= 10;
+
+static int	watchdog_use		= 0;
+static int	go_daemon		= 0;
+static const char *watchdogdev		= "/dev/watchdog";
+static char *	local_uname;
+
+/* Global, non-tunable variables: */
+static int	sector_size		= 0;
+static int	watchdogfd 		= -1;
+static int	devfd;
+static char	*devname;
+static char	*cmdname;
+
+static void
+usage(void)
+{
+	fprintf(stderr,
+"Shared storage fencing tool.\n"
+"Syntax:\n"
+"	%s <options> <command> <cmdarguments>\n"
+"Options:\n"
+"-d <devname>	Block device to use (mandatory)\n"
+"-h		Display this help.\n"
+"-n <node>	Set local node name; defaults to uname -n (optional)\n"
+"\n"
+"-W		Use watchdog (recommended) (watch only)\n"
+"-w <dev>	Specify watchdog device (optional) (watch only)\n"
+"-D		Run as background daemon (optional) (watch only)\n"
+"\n"
+"-1 <N>		Set watchdog timeout to N seconds (optional) (create only)\n"
+"-2 <N>		Set slot allocation timeout to N seconds (optional) (create only)\n"
+"-3 <N>		Set daemon loop timeout to N seconds (optional) (create only)\n"
+"-4 <N>		Set msgwait timeout to N seconds (optional) (create only)\n"
+"Commands:\n"
+"create		initialize N slots on <dev> - OVERWRITES DEVICE!\n"
+"list		List all allocated slots on device, and messages.\n"
+"dump		Dump meta-data header from device.\n"
+"watch		Loop forever, monitoring own slot\n"
+"allocate <node>\n"
+"		Allocate a slot for node (optional)\n"
+"message <node> (test|reset|off|clear|exit)\n"
+"		Writes the specified message to node's slot.\n"
+, cmdname);
+}
+
+static void
+watchdog_init_interval(void)
+{
+	if (watchdogfd < 0) {
+		return;
+	}
+
+	if (ioctl(watchdogfd, WDIOC_SETTIMEOUT, &timeout_watchdog) < 0) {
+		cl_perror( "WDIOC_SETTIMEOUT"
+		": Failed to set watchdog timer to %lu seconds.",
+		timeout_watchdog);
+	} else {
+		cl_log(LOG_INFO, "Set watchdog timeout to %lu seconds.",
+			timeout_watchdog);
+	}
+}
+
+static void
+watchdog_tickle(void)
+{
+	if (watchdogfd >= 0) {
+		if (write(watchdogfd, "", 1) != 1) {
+			cl_perror("Watchdog write failure: %s!",
+					watchdogdev);
+			/* TODO: Should we force the crash, or wait for
+			 * the watchdog to time us out? */
+		}
+	}
+}
+
+static void
+watchdog_init(void)
+{
+	if (watchdogfd < 0 && watchdogdev != NULL) {
+		watchdogfd = open(watchdogdev, O_WRONLY);
+		if (watchdogfd >= 0) {
+			if (fcntl(watchdogfd, F_SETFD, FD_CLOEXEC)) {
+				cl_perror("Error setting the "
+				"close-on-exec flag for watchdog");
+			}
+			cl_log(LOG_NOTICE, "Using watchdog device: %s",
+					watchdogdev);
+			watchdog_init_interval();
+			watchdog_tickle();
+		}else{
+			cl_perror("Cannot open watchdog device: %s",
+					watchdogdev);
+		}
+	}
+}
+
+static void
+watchdog_close(void)
+{
+	if (watchdogfd >= 0) {
+		if (write(watchdogfd, "V", 1) != 1) {
+			cl_perror(
+			"Watchdog write magic character failure: closing %s!",
+				watchdogdev);
+		}
+		if (close(watchdogfd) < 0) {
+			cl_perror("Watchdog close(2) failed.");
+		}
+		watchdogfd = -1;
+	}
+}
+
+static int
+open_device(const char* devname)
+{
+	if (!devname)
+		return -1;
+
+	devfd = open(devname, O_SYNC|O_RDWR|O_DIRECT);
+
+	if (devfd == -1) {
+		cl_perror("Opening device %s failed.", devname);
+		return -1;
+	}
+
+	ioctl(devfd, BLKSSZGET, &sector_size);
+
+	if (sector_size == 0) {
+		cl_perror("Get sector size failed.\n");
+		return -1;
+	}
+	return 0;
+}
+
+static signed char
+cmd2char(const char *cmd)
+{
+	if (strcmp("clear", cmd) == 0) {
+		return SBD_MSG_EMPTY;
+	} else if (strcmp("test", cmd) == 0) {
+		return SBD_MSG_TEST;
+	} else if (strcmp("reset", cmd) == 0) {
+		return SBD_MSG_RESET;
+	} else if (strcmp("off", cmd) == 0) {
+		return SBD_MSG_OFF;
+	} else if (strcmp("exit", cmd) == 0) {
+		return SBD_MSG_EXIT;
+	}
+	return -1;
+}
+
+static void *
+sector_alloc(void)
+{
+	void *x;
+
+	x = valloc(sector_size);
+	if (!x) {
+		exit(1);
+	}
+	memset(x, 0, sector_size);
+
+	return x;
+}
+
+static const char*
+char2cmd(const char cmd)
+{
+	switch (cmd) {
+		case SBD_MSG_EMPTY:
+			return "clear";
+			break;
+		case SBD_MSG_TEST:
+			return "test";
+			break;
+		case SBD_MSG_RESET:
+			return "reset";
+			break;
+		case SBD_MSG_OFF:
+			return "off";
+			break;
+		case SBD_MSG_EXIT:
+			return "exit";
+			break;
+		default:
+			return "undefined";
+			break;
+	}
+}
+
+static int
+sector_write(int sector, const void *data)
+{
+	if (lseek(devfd, sector_size*sector, 0) < 0) {
+		cl_perror("sector_write: lseek() failed");
+		return -1;
+	}
+
+	if (write(devfd, data, sector_size) <= 0) {
+		cl_perror("sector_write: write_sector() failed");
+		return -1;
+	}
+	return(0);
+}
+
+static int
+sector_read(int sector, void *data)
+{
+	if (lseek(devfd, sector_size*sector, 0) < 0) {
+		cl_perror("sector_read: lseek() failed");
+		return -1;
+	}
+
+	if (read(devfd, data, sector_size) < sector_size) {
+		cl_perror("sector_read: read() failed");
+		return -1;
+	}
+	return(0);
+}
+
+static int
+slot_read(int slot, struct sector_node_s *s_node)
+{
+	return sector_read(SLOT_TO_SECTOR(slot), s_node);
+}
+
+static int
+slot_write(int slot, const struct sector_node_s *s_node)
+{
+	return sector_write(SLOT_TO_SECTOR(slot), s_node);
+}
+
+static int
+mbox_write(int mbox, const struct sector_mbox_s *s_mbox)
+{
+	return sector_write(MBOX_TO_SECTOR(mbox), s_mbox);
+}
+
+static int
+mbox_read(int mbox, struct sector_mbox_s *s_mbox)
+{
+	return sector_read(MBOX_TO_SECTOR(mbox), s_mbox);
+}
+
+static int
+mbox_write_verify(int mbox, const struct sector_mbox_s *s_mbox)
+{
+	void *data;
+
+	if (sector_write(MBOX_TO_SECTOR(mbox), s_mbox) < 0)
+		return -1;
+
+	data = sector_alloc();
+	if (sector_read(MBOX_TO_SECTOR(mbox), data) < 0)
+		return -1;
+
+	if (memcmp(s_mbox, data, sector_size) != 0) {
+		cl_log(LOG_ERR, "Write verification failed!");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+header_write(struct sector_header_s *s_header)
+{
+	s_header->sector_size = htonl(s_header->sector_size);
+	s_header->timeout_watchdog = htonl(s_header->timeout_watchdog);
+	s_header->timeout_allocate = htonl(s_header->timeout_allocate);
+	s_header->timeout_loop = htonl(s_header->timeout_loop);
+	s_header->timeout_msgwait = htonl(s_header->timeout_msgwait);
+	return sector_write(0, s_header);
+}
+
+static int
+header_read(struct sector_header_s *s_header)
+{
+	if (sector_read(0, s_header) < 0)
+		return -1;
+	
+	s_header->sector_size = ntohl(s_header->sector_size);
+	s_header->timeout_watchdog = ntohl(s_header->timeout_watchdog);
+	s_header->timeout_allocate = ntohl(s_header->timeout_allocate);
+	s_header->timeout_loop = ntohl(s_header->timeout_loop);
+	s_header->timeout_msgwait = ntohl(s_header->timeout_msgwait);
+	/* This sets the global defaults: */
+	timeout_watchdog = s_header->timeout_watchdog;
+	timeout_allocate = s_header->timeout_allocate;
+	timeout_loop     = s_header->timeout_loop;
+	timeout_msgwait  = s_header->timeout_msgwait;
+
+	return 0;
+}
+
+static int
+valid_header(const struct sector_header_s *s_header)
+{
+	if (memcmp(s_header->magic, sbd_magic, sizeof(s_header->magic)) != 0) {
+		cl_log(LOG_ERR, "Header magic does not match.");
+		return -1;
+	}
+	if (s_header->version != sbd_version) {
+		cl_log(LOG_ERR, "Header version does not match.");
+		return -1;
+	}
+	if (s_header->sector_size != sector_size) {
+		cl_log(LOG_ERR, "Header sector size does not match.");
+		return -1;
+	}
+	return 0;
+}
+
+static struct sector_header_s *
+header_get(void)
+{
+	struct sector_header_s *s_header;
+	s_header = sector_alloc();
+	
+	if (header_read(s_header) < 0) {
+		cl_log(LOG_ERR, "Unable to read header from %s", devname);
+		return NULL;
+	}
+
+	if (valid_header(s_header) < 0) {
+		cl_log(LOG_ERR, "%s is not valid.", devname);
+		return NULL;
+	}
+	
+	/* cl_log(LOG_INFO, "Found version %d header with %d slots",
+			s_header->version, s_header->slots); */
+
+	return s_header;
+}
+
+static int
+init_device(void)
+{
+	struct sector_header_s	*s_header;
+	struct sector_node_s	*s_node;
+	struct sector_mbox_s	*s_mbox;
+	struct stat 		s;
+	int			i;	
+	int			rc = 0;
+
+	s_header = sector_alloc();
+	s_node = sector_alloc();
+	s_mbox = sector_alloc();
+	memcpy(s_header->magic, sbd_magic, sizeof(s_header->magic));
+	s_header->version = sbd_version;
+	s_header->slots = 255;
+	s_header->sector_size = sector_size;
+	s_header->timeout_watchdog = timeout_watchdog;
+	s_header->timeout_allocate = timeout_allocate;
+	s_header->timeout_loop = timeout_loop;
+	s_header->timeout_msgwait = timeout_msgwait;
+
+	fstat(devfd, &s);
+	/* printf("st_size = %ld, st_blksize = %ld, st_blocks = %ld\n",
+			s.st_size, s.st_blksize, s.st_blocks); */
+	
+	cl_log(LOG_INFO, "Creating version %d header on %s",
+			s_header->version,
+			devname);
+	if (header_write(s_header) < 0) {
+		rc = -1; goto out;
+	}
+	cl_log(LOG_INFO, "Initializing %d slots on %s",
+			s_header->slots,
+			devname);
+	for (i=0;i < s_header->slots;i++) {
+		if (slot_write(i, s_node) < 0) {
+			rc = -1; goto out;
+		}
+		if (mbox_write(i, s_mbox) < 0) {
+			rc = -1; goto out;
+		}
+	}
+
+out:	free(s_node);
+	free(s_header);
+	free(s_mbox);
+	return(rc);
+}
+
+/* Check if there already is a slot allocated to said name; returns the
+ * slot number. If not found, returns -1.
+ * This is necessary because slots might not be continuous. */
+static int
+slot_lookup(const struct sector_header_s *s_header, const char *name)
+{
+	struct sector_node_s	*s_node = NULL;
+	int 			i;
+	int			rc = -1;
+
+	if (!name) {
+		cl_log(LOG_ERR, "slot_lookup(): No name specified.\n");
+		goto out;
+	}
+
+	s_node = sector_alloc();
+
+	for (i=0; i < s_header->slots; i++) {
+		if (slot_read(i, s_node) < 0) {
+			rc = -1; goto out;
+		}
+		if (s_node->in_use != 0) {
+			if (strncasecmp(s_node->name, name, 
+						sizeof(s_node->name)) == 0) {
+				cl_log(LOG_INFO, "%s owns slot %d", name, i);
+				rc = i; goto out;
+			}
+		}
+	}
+
+out:	free(s_node);
+	return rc;
+}
+
+static int
+slot_unused(const struct sector_header_s *s_header)
+{
+	struct sector_node_s	*s_node;
+	int 			i;
+	int			rc = -1;
+
+	s_node = sector_alloc();
+
+	for (i=0; i < s_header->slots; i++) {
+		if (slot_read(i, s_node) < 0) {
+			rc = -1; goto out;
+		}
+		if (s_node->in_use == 0) {
+			rc = i; goto out;
+		}
+	}
+
+out:	free(s_node);
+	return rc;
+}
+
+
+static int
+slot_allocate(const char *name)
+{
+	struct sector_header_s	*s_header = NULL;
+	struct sector_node_s	*s_node = NULL;
+	struct sector_mbox_s	*s_mbox = NULL;
+	int			i;	
+	int			rc = 0;
+	
+	if (!name) {
+		cl_log(LOG_ERR, "slot_allocate(): No name specified.\n");
+		rc = -1; goto out;
+	}
+
+	s_header = header_get();
+	if (!s_header) {
+		rc = -1; goto out;
+	}
+
+	s_node = sector_alloc();
+	s_mbox = sector_alloc();
+
+	while (1) {
+		i = slot_lookup(s_header, name);
+		if (i >= 0) {
+			rc = i; goto out;
+		}
+
+		i = slot_unused(s_header);
+		if (i >= 0) {
+			cl_log(LOG_INFO, "slot %d is unused - trying to own", i);
+			memset(s_node, 0, sizeof(*s_node));
+			s_node->in_use = 1;
+			strncpy(s_node->name, name, sizeof(s_node->name));
+			if (slot_write(i, s_node) < 0) {
+				rc = -1; goto out;
+			}
+			sleep(timeout_allocate);
+		} else {
+			cl_log(LOG_ERR, "No more free slots.");
+			rc = -1; goto out;
+		}
+	}
+	
+out:	free(s_node);
+	free(s_header);
+	free(s_mbox);
+	return(rc);
+}
+
+static int
+slot_list(void)
+{
+	struct sector_header_s	*s_header = NULL;
+	struct sector_node_s	*s_node = NULL;
+	struct sector_mbox_s	*s_mbox = NULL;
+	int 			i;
+	int			rc = 0;
+
+	s_header = header_get();
+	if (!s_header) {
+		rc = -1; goto out;
+	}
+
+	s_node = sector_alloc();
+	s_mbox = sector_alloc();
+
+	for (i=0; i < s_header->slots; i++) {
+		if (slot_read(i, s_node) < 0) {
+			rc = -1; goto out;
+		}
+		if (s_node->in_use > 0) {
+			if (mbox_read(i, s_mbox) < 0) {
+				rc = -1; goto out;
+			}
+			printf("%d\t%s\t%s\t%s\n",
+				i, s_node->name, char2cmd(s_mbox->cmd),
+				s_mbox->from);
+		}
+	}
+
+out:	free(s_node);
+	free(s_header);
+	free(s_mbox);
+	return rc;
+}
+
+static int
+slot_msg(const char *name, const char *cmd)
+{
+	struct sector_header_s	*s_header = NULL;
+	struct sector_mbox_s	*s_mbox = NULL;
+	int			mbox;
+	int			rc = 0;
+
+	if (!name || !cmd) {
+		cl_log(LOG_ERR, "slot_msg(): No recipient / cmd specified.\n");
+		rc = -1; goto out;
+	}
+
+	s_header = header_get();
+	if (!s_header) {
+		rc = -1; goto out;
+	}
+
+	if (strcmp(name, "LOCAL") == 0) {
+		name = local_uname;
+	}
+
+	mbox = slot_lookup(s_header, name);
+	if (mbox < 0) {
+		cl_log(LOG_ERR, "slot_msg(): No slot found for %s.", name);
+		rc = -1; goto out;
+	}
+
+	s_mbox = sector_alloc();
+	
+	s_mbox->cmd = cmd2char(cmd);
+	if (s_mbox->cmd < 0) {
+		cl_log(LOG_ERR, "slot_msg(): Invalid command %s.", cmd);
+		rc = -1; goto out;
+	}
+
+	strncpy(s_mbox->from, local_uname, sizeof(s_mbox->from)-1);
+
+	cl_log(LOG_INFO, "Writing %s to node slot %s",
+			cmd, name);
+	if (mbox_write_verify(mbox, s_mbox) < -1) {
+		rc = -1; goto out;
+	}
+	if (strcasecmp(cmd, "exit") != 0) {
+		sleep(timeout_msgwait);
+	}
+	cl_log(LOG_INFO, "%s successfully delivered to %s",
+			cmd, name);
+
+out:	free(s_mbox);
+	free(s_header);
+	return rc;
+}
+
+static int
+slot_ping(const char *name)
+{
+	struct sector_header_s	*s_header = NULL;
+	struct sector_mbox_s	*s_mbox = NULL;
+	int			mbox;
+	int			waited = 0;
+	int			rc = 0;
+
+	if (!name) {
+		cl_log(LOG_ERR, "slot_ping(): No recipient specified.\n");
+		rc = -1; goto out;
+	}
+
+	s_header = header_get();
+	if (!s_header) {
+		rc = -1; goto out;
+	}
+
+	if (strcmp(name, "LOCAL") == 0) {
+		name = local_uname;
+	}
+
+	mbox = slot_lookup(s_header, name);
+	if (mbox < 0) {
+		cl_log(LOG_ERR, "slot_msg(): No slot found for %s.", name);
+		rc = -1; goto out;
+	}
+
+	s_mbox = sector_alloc();
+	s_mbox->cmd = SBD_MSG_TEST;
+
+	strncpy(s_mbox->from, local_uname, sizeof(s_mbox->from)-1);
+
+	cl_log(LOG_DEBUG, "Pinging node %s", name);
+	if (mbox_write(mbox, s_mbox) < -1) {
+		rc = -1; goto out;
+	}
+
+	rc = -1;
+	while (waited <= timeout_msgwait) {
+		if (mbox_read(mbox, s_mbox) < 0)
+			break;
+		if (s_mbox->cmd != SBD_MSG_TEST) {
+			rc = 0;
+			break;
+		}
+		sleep(1);
+		waited++;
+	}
+
+	if (rc == 0) {
+		cl_log(LOG_DEBUG, "%s successfully pinged.", name);
+	} else {
+		cl_log(LOG_ERR, "%s failed to ping.", name);
+	}
+
+out:	free(s_mbox);
+	free(s_header);
+	return rc;
+}
+
+static void
+sysrq_trigger(char t)
+{
+	FILE *procf;
+
+	procf = fopen("/proc/sysrq-trigger", "a");
+	if (!procf) {
+		cl_perror("Opening sysrq-trigger failed.");
+		return;
+	}
+	cl_log(LOG_INFO, "sysrq-trigger: %c\n", t);
+	fprintf(procf, "%c\n", t);
+	fclose(procf);
+	return;
+}
+
+static void
+do_reset(void)
+{
+	sysrq_trigger('b');
+	cl_reboot(5, "sbd is self-fencing (reset)");
+	sleep(timeout_watchdog * 2);
+	exit(1);
+}
+
+static void
+do_off(void)
+{
+	sysrq_trigger('o');
+	cl_reboot(5, "sbd is self-fencing (power-off)");
+	sleep(timeout_watchdog * 2);
+	exit(1);
+}
+
+static void
+make_daemon(void)
+{
+	long			pid;
+	const char *		devnull = "/dev/null";
+
+	if (go_daemon > 0) {
+		pid = fork();
+		if (pid < 0) {
+			cl_log(LOG_ERR, "%s: could not start daemon\n",
+					cmdname);
+			cl_perror("fork");
+			exit(1);
+		}else if (pid > 0) {
+			exit(0);
+		}
+	}
+
+	cl_log_enable_stderr(FALSE);
+
+	umask(022);
+	close(0);
+	(void)open(devnull, O_RDONLY);
+	close(1);
+	(void)open(devnull, O_WRONLY);
+	close(2);
+	(void)open(devnull, O_WRONLY);
+	cl_cdtocoredir();
+	cl_make_realtime(-1, -1, 128, 128);
+
+}
+
+
+static int
+daemonize(void)
+{
+	struct sector_mbox_s	*s_mbox = NULL;
+	int			mbox;
+	int			rc = 0;
+
+	mbox = slot_allocate(local_uname);
+	if (mbox < 0) {
+		cl_log(LOG_ERR, "No slot allocated, and automatic allocation failed.");
+		rc = -1; goto out;
+	}
+	cl_log(LOG_INFO, "Monitoring slot %d", mbox);
+
+	/* Clear mbox once on start */
+	s_mbox = sector_alloc();
+	if (mbox_write(mbox, s_mbox) < 0) {
+		rc = -1; goto out;
+	}
+
+	make_daemon();
+
+	if (watchdog_use != 0)
+		watchdog_init();
+	
+	while (1) {
+		if (mbox_read(mbox, s_mbox) < 0) {
+			cl_log(LOG_ERR, "mbox read failed.");
+			do_reset();
+		}
+
+		if (s_mbox->cmd > 0) {
+			cl_log(LOG_INFO, "Received command %s from %s",
+					char2cmd(s_mbox->cmd), s_mbox->from);
+
+			switch (s_mbox->cmd) {
+			case SBD_MSG_TEST:
+				memset(s_mbox, 0, sizeof(*s_mbox));
+				mbox_write(mbox, s_mbox);
+				break;
+			case SBD_MSG_RESET:
+				do_reset();
+				break;
+			case SBD_MSG_OFF:
+				do_off();
+				break;
+			case SBD_MSG_EXIT:
+				watchdog_close();
+				goto out;
+				break;
+			default:
+				/* TODO: Should we do something on
+				 * unknown messages? */
+				cl_log(LOG_ERR, "Unknown message; suicide!");
+				do_reset();
+				break;
+			}
+		}
+		watchdog_tickle();
+		sleep(timeout_loop);
+	}
+
+out:
+	free(s_mbox);
+	return rc;
+}
+
+static int
+header_dump(void)
+{
+	struct sector_header_s *s_header;
+	s_header = header_get();
+	if (s_header == NULL)
+		return -1;
+
+	printf("Header version     : %u\n", s_header->version);
+	printf("Number of slots    : %u\n", s_header->slots);
+	printf("Sector size        : %lu\n",
+			(unsigned long)s_header->sector_size);
+	printf("Timeout (watchdog) : %lu\n",
+			(unsigned long)s_header->timeout_watchdog);
+	printf("Timeout (allocate) : %lu\n",
+			(unsigned long)s_header->timeout_allocate);
+	printf("Timeout (loop)     : %lu\n",
+			(unsigned long)s_header->timeout_loop);
+	printf("Timeout (msgwait)  : %lu\n",
+			(unsigned long)s_header->timeout_msgwait);
+	return 0;
+}
+
+static void
+get_uname(void)
+{
+	struct utsname		uname_buf;
+	int i;
+
+	if (uname(&uname_buf) < 0) {
+		cl_perror("uname() failed?");
+		exit(1);
+	}
+	
+	local_uname = strdup(uname_buf.nodename);
+
+	for (i = 0; i < strlen(local_uname); i++)
+		local_uname[i] = tolower(local_uname[i]);
+}
+
+int
+main(int argc, char** argv)
+{
+	int		exit_status = 0;
+	int		c;
+
+	if ((cmdname = strrchr(argv[0], '/')) == NULL) {
+		cmdname = argv[0];
+	}else{
+		++cmdname;
+	}
+
+	cl_log_set_entity(cmdname);
+	cl_log_enable_stderr(1);
+	cl_log_set_facility(LOG_DAEMON);
+	
+	get_uname();
+
+	while ((c = getopt (argc, argv, "DWhw:d:n:1:2:3:4:")) != -1) {
+		switch (c) {
+		case 'D':
+			go_daemon = 1;
+			break;
+		case 'W':
+			watchdog_use = 1;
+			break;
+		case 'w':
+			watchdogdev = optarg;
+			break;
+		case 'd':
+			devname = optarg;
+			break;
+		case 'n':
+			local_uname = optarg;
+			break;
+		case '1':
+			timeout_watchdog = atoi(optarg);
+			break;
+		case '2':
+			timeout_allocate = atoi(optarg);
+			break;
+		case '3':
+			timeout_loop = atoi(optarg);
+			break;
+		case '4':
+			timeout_msgwait = atoi(optarg);
+			break;
+		case 'h':
+			usage();
+			return(0);
+		default:
+			exit_status = -1;
+			goto out;
+			break;
+		}
+	}
+	
+	/* There must at least be one command following the options: */
+	if ( (argc - optind) < 1) {
+		fprintf(stderr, "Not enough arguments.\n");
+		exit_status = -1;
+		goto out;
+	}
+
+	if (open_device(devname) < 0) {
+		exit_status = -1;
+		goto out;
+	}
+
+	if (strcmp(argv[optind],"create") == 0) {
+		exit_status = init_device();
+	} else if (strcmp(argv[optind],"dump") == 0) {
+		exit_status = header_dump();
+	} else if (strcmp(argv[optind],"allocate") == 0) {
+		exit_status = slot_allocate(argv[optind+1]);
+	} else if (strcmp(argv[optind],"list") == 0) {
+		exit_status = slot_list();
+	} else if (strcmp(argv[optind],"message") == 0) {
+		exit_status = slot_msg(argv[optind+1], argv[optind+2]);
+	} else if (strcmp(argv[optind],"ping") == 0) {
+		exit_status = slot_ping(argv[optind+1]);
+	} else if (strcmp(argv[optind],"watch") == 0) {
+		exit_status = daemonize();
+	} else {
+		exit_status = -1;
+	}
+
+out:
+	if (exit_status < 0) {
+		usage();
+		return(1);
+	}
+	return(0);
+}
diff --git a/lib/stonith/sbd.h b/lib/stonith/sbd.h
new file mode 100644
index 0000000..b671783
--- /dev/null
+++ b/lib/stonith/sbd.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Lars Marowsky-Bree <lmb at 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
+ */
+#include <arpa/inet.h>
+
+
+/* Sector data types */
+struct sector_header_s {
+	char	magic[8];
+	unsigned char	version;
+	unsigned char	slots;
+	/* Caveat: stored in network byte-order */
+	uint32_t	sector_size;
+	uint32_t	timeout_watchdog;
+	uint32_t	timeout_allocate;
+	uint32_t	timeout_loop;
+	uint32_t	timeout_msgwait;
+};
+
+struct sector_mbox_s {
+	signed char	cmd;
+	char		from[64];
+};
+
+struct sector_node_s {
+	/* slots will be created with in_use == 0 */
+	char	in_use;
+	char 	name[64];
+};
+
+#define SBD_MSG_EMPTY	0x00
+#define SBD_MSG_TEST	0x01
+#define SBD_MSG_RESET	0x02
+#define SBD_MSG_OFF	0x03
+#define SBD_MSG_EXIT	0x04
+			
+#define SLOT_TO_SECTOR(slot) (1+slot*2)
+#define MBOX_TO_SECTOR(mbox) (2+mbox*2)
+
+static void usage(void);
+static void watchdog_init_interval(void);
+static void watchdog_tickle(void);
+static void watchdog_init(void);
+static void watchdog_close(void);
+static int open_device(const char* devname);
+static signed char cmd2char(const char *cmd);
+static void * sector_alloc(void);
+static const char* char2cmd(const char cmd);
+static int sector_write(int sector, const void *data);
+static int sector_read(int sector, void *data);
+static int slot_read(int slot, struct sector_node_s *s_node);
+static int slot_write(int slot, const struct sector_node_s *s_node);
+static int mbox_write(int mbox, const struct sector_mbox_s *s_mbox);
+static int mbox_read(int mbox, struct sector_mbox_s *s_mbox);
+static int mbox_write_verify(int mbox, const struct sector_mbox_s *s_mbox);
+/* After a call to header_write(), certain data fields will have been
+ * converted to on-disk byte-order; the header should not be accessed
+ * afterwards anymore! */
+static int header_write(struct sector_header_s *s_header);
+static int header_read(struct sector_header_s *s_header);
+static int valid_header(const struct sector_header_s *s_header);
+static struct sector_header_s * header_get(void);
+static int init_device(void);
+static int slot_lookup(const struct sector_header_s *s_header, const char *name);
+static int slot_unused(const struct sector_header_s *s_header);
+static int slot_allocate(const char *name);
+static int slot_list(void);
+static int slot_ping(const char *name);
+static int slot_msg(const char *name, const char *cmd);
+static int header_dump(void);
+static void sysrq_trigger(char t);
+static void do_reset(void);
+static void do_off(void);
+static void make_daemon(void);
+static int daemonize(void);
+static void get_uname(void);
+
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..6975f0f
--- /dev/null
+++ b/lib/stonith/stonith.c
@@ -0,0 +1,581 @@
+/*
+ * Stonith API infrastructure.
+ *
+ * Copyright (c) 2000 Alan Robertson <alanr at 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 **extPI, **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) {
+		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++);
+
+	/* sort the external plugins */
+	qsort(extPI, numextPI, sizeof(char *), qsort_string_cmp);
+
+	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);
+	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 **
+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;
+}
+
+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;
+		}
+		g_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 **	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;
+}
+
+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/.cvsignore b/logd/.cvsignore
new file mode 100644
index 0000000..d43dc28
--- /dev/null
+++ b/logd/.cvsignore
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+ha_logd
+ha_logger
+.libs
+.deps
+logtest
diff --git a/logd/Makefile.am b/logd/Makefile.am
new file mode 100644
index 0000000..3005c7d
--- /dev/null
+++ b/logd/Makefile.am
@@ -0,0 +1,52 @@
+#
+# 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
+
+initd_SCRIPTS     = logd
+
diff --git a/logd/ha_logd.c b/logd/ha_logd.c
new file mode 100644
index 0000000..fb40c81
--- /dev/null
+++ b/logd/ha_logd.c
@@ -0,0 +1,1029 @@
+/*
+ * ha_logd.c logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi at 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[MAXLINE];
+	int		log_facility;
+	mode_t		logmode;
+	gboolean	syslogfmtmsgs;
+} logd_config =
+	{
+		"",
+		"",
+		"logd",
+		HA_LOG_FACILITY,
+		0644,
+		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_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},
+	{"sendqlen",	set_sendqlen},
+	{"recvqlen",	set_recvqlen},
+	{"logmode",	set_logmode},
+	{"syslogmsgfmt",set_syslogfmtmsgs}
+};
+
+struct _syslog_code {
+        const char    *c_name;
+        int     c_val;
+};
+
+static void
+logd_log( const char * fmt, ...)
+{
+	char		buf[MAXLINE];
+	va_list		ap;
+	int		nbytes;
+	
+	buf[MAXLINE-1] = EOS;
+	va_start(ap, fmt);
+	nbytes=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;
+	}
+	cl_log(LOG_INFO, "setting entity to %s", option);
+	strncpy(logd_config.entity, option, MAXLINE);
+	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);
+		fprintf(stderr, "%s: already running [pid %ld].\n",
+			cmdname, pid);
+		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_perror("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;
+}
+
+
+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_run(mainloop);
+	
+	return;
+}
+
+static gboolean
+direct_log(IPC_Channel* ch, gpointer user_data)
+{
+	
+	IPC_Message*		ipcmsg;
+	GMainLoop*		loop;
+	
+
+	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;
+#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);
+		
+
+			(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);
+			}
+		}
+		
+	}
+	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_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)) {
+		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_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*/
+		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;
+		
+		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 at ncsa.uiuc.edu>
+ Alan Robertson <alanr at unix.sh>
+ Lars Marowsky-Bree <lmb at suse.de>
+.fi
+
+
diff --git a/logd/ha_logger.c b/logd/ha_logger.c
new file mode 100644
index 0000000..f0c5126
--- /dev/null
+++ b/logd/ha_logger.c
@@ -0,0 +1,166 @@
+/*
+ * ha_logger.c utility to log a message to the logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi at 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;
+}
+int
+main(int argc, char** argv)
+{
+	int	priority; 
+	char*	entity = NULL;
+	int	c;
+	char	buf[1024];
+	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, *endp;
+		int len;
+		
+		for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
+			len = strlen(*argv);
+			if (p + len > endp && p > buf) {
+				if (LogToDaemon(priority,buf,
+						strnlen(buf, 1024),FALSE) ==HA_OK){
+					continue;
+				}else{
+					return EXIT_FAIL;
+				}
+				/* NOTREACHED */
+				/* p = buf; */
+			}
+			if (len > sizeof(buf) - 1) {
+				if (LogToDaemon(priority,*argv,
+						strnlen(*argv, 1024),FALSE) ==HA_OK){
+					argv++;
+					continue;
+				}else{
+					return EXIT_FAIL;
+				}
+				
+			} else {
+				if (p != buf){
+					*p++ = ' ';
+				}
+				memcpy(p, *argv++, len);
+				*(p += len) = '\0';
+			}
+		}
+		if (p != buf) {
+			if (LogToDaemon(priority,buf,
+					strnlen(buf, 1024),FALSE) ==HA_OK){
+				return EXIT_OK;
+			}else{
+				return EXIT_FAIL;
+			}
+		}
+		
+
+	}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..34ab807
--- /dev/null
+++ b/logd/logd.cf
@@ -0,0 +1,44 @@
+#	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
+
+#
+#
+#	Facility to use for syslog()/logger 
+#	Default: daemon
+#logfacility	daemon
+
+
+#	Entity to be shown at beginning of a message
+# 	for logging daemon
+# 	Default: "logd"
+#entity logd
+
+
+#	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..e1f9f0a
--- /dev/null
+++ b/logd/logd.in
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+#
+# logd     	    Start logd (non-blocking log service)
+#
+# Author:       Dejan Muhamedagic <dmuhamedagic at 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: 2 3 4 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
+	;;
+
+  *)
+	echo "Usage: $0 {start|stop|status|restart}"
+	exit 1
+esac
+
diff --git a/logd/logtest.c b/logd/logtest.c
new file mode 100644
index 0000000..93af0b8
--- /dev/null
+++ b/logd/logtest.c
@@ -0,0 +1,126 @@
+/*
+ * ha_logger.c utility to log a message to the logging daemon
+ *
+ * Copyright (C) 2004 Guochun Shi <gshi at 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 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");
+		return FALSE;
+
+	}
+	if (count >= maxcount){
+		cl_log(LOG_INFO, "total message dropped: %d", dropmsg);
+		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;
+	GMainLoop* loop;
+	
+	if (argc < 2){
+		usage(argv[0]);
+		return 1;
+	}
+	
+	maxcount = atoi(argv[1]);
+	
+	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/.cvsignore b/lrm/.cvsignore
new file mode 100644
index 0000000..f94fd1c
--- /dev/null
+++ b/lrm/.cvsignore
@@ -0,0 +1,10 @@
+Makefile.in
+Makefile
+.deps
+.libs
+*.la
+*.lo
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
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 at 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/.cvsignore b/lrm/admin/.cvsignore
new file mode 100644
index 0000000..a341d68
--- /dev/null
+++ b/lrm/admin/.cvsignore
@@ -0,0 +1,11 @@
+lrmadmin
+Makefile.in
+Makefile
+.deps
+.libs
+*.la
+*.lo
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
diff --git a/lrm/admin/Makefile.am b/lrm/admin/Makefile.am
new file mode 100644
index 0000000..c82f781
--- /dev/null
+++ b/lrm/admin/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Author: Sun Jiang Dong <sunjd at 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
+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
diff --git a/lrm/admin/lrmadmin.c b/lrm/admin/lrmadmin.c
new file mode 100644
index 0000000..f9c354d
--- /dev/null
+++ b/lrm/admin/lrmadmin.c
@@ -0,0 +1,1124 @@
+/* File: lrmadmin.c
+ * Description: A adminstration tool for Local Resource Manager
+ *
+ * Author: Sun Jiang Dong <sunjd at 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:h";
+
+#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'},
+	{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|--deamon}\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"
+"         {-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 '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..ac651e0
--- /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|--deamon}
+	 {-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 deamon options
+	 {-d|--deamon}
+#		-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/.cvsignore b/lrm/lrmd/.cvsignore
new file mode 100644
index 0000000..e32199d
--- /dev/null
+++ b/lrm/lrmd/.cvsignore
@@ -0,0 +1,11 @@
+lrmd
+Makefile.in
+Makefile
+.deps
+.libs
+*.la
+*.lo
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
diff --git a/lrm/lrmd/Makefile.am b/lrm/lrmd/Makefile.am
new file mode 100644
index 0000000..4578f9a
--- /dev/null
+++ b/lrm/lrmd/Makefile.am
@@ -0,0 +1,42 @@
+#
+#  Author: Sun Jiang Dong <sunjd at 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 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 at 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/lrmd.c b/lrm/lrmd/lrmd.c
new file mode 100644
index 0000000..077f58e
--- /dev/null
+++ b/lrm/lrmd/lrmd.c
@@ -0,0 +1,3937 @@
+/*
+ * Local Resource Manager Daemon
+ *
+ * Author: Huang Zhen <zhenhltc at cn.ibm.com>
+ * Partly contributed by Andrew Beekhof <andrew at 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;
+};
+
+/*
+ * 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)
+/* magic number, must be different from other return codes! */
+#define POSTPONED 32
+
+struct msg_map msg_maps[] = {
+	{REGISTER,	REPLY_NOW,	on_msg_register},
+	{GETRSCCLASSES,	NO_MSG,	on_msg_get_rsc_classes},
+	{GETRSCTYPES,	NO_MSG,	on_msg_get_rsc_types},
+	{GETPROVIDERS,	NO_MSG,	on_msg_get_rsc_providers},
+	{ADDRSC,	REPLY_NOW,	on_msg_add_rsc},
+	{GETRSC,	NO_MSG,	on_msg_get_rsc},
+	{GETLASTOP,	NO_MSG,	on_msg_get_last_op},
+	{GETALLRCSES,	NO_MSG,	on_msg_get_all},
+	{DELRSC,	REPLY_NOW,	on_msg_del_rsc},
+	{FAILRSC,	REPLY_NOW,	on_msg_fail_rsc},
+	{PERFORMOP,	REPLY_NOW,	on_msg_perform_op},
+	{FLUSHOPS,	REPLY_NOW,	on_msg_flush_all},
+	{CANCELOP,	REPLY_NOW,	on_msg_cancel_op},
+	{GETRSCSTATE,	NO_MSG,	on_msg_get_state},
+	{GETRSCMETA,	NO_MSG, 	on_msg_get_metadata},
+	{SETLRMDPARAM,	REPLY_NOW, 	on_msg_set_lrmd_param},
+	{GETLRMDPARAM,	NO_MSG, 	on_msg_get_lrmd_param},
+};
+#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 struct {
+	int	opcount;
+	int	clientcount;
+	int	rsccount;
+}lrm_objectstats;
+
+#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;
+	}
+	ha_msg_del(op->msg);
+	op->msg = NULL;
+	if( op->rsc_id ) {
+		free(op->rsc_id);
+		op->rsc_id = NULL;
+	}
+	if( op->app_name ) {
+		free(op->app_name);
+		op->app_name = 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);
+	}
+
+	lrmd_debug3(LOG_DEBUG, "%s: free the op whose address is %p"
+		  ,__FUNCTION__, op);
+	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->app_name = strdup(op->app_name);
+	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;
+	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"
+	,	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"));
+	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
+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;
+	}
+	/* 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));
+
+	/* Avoid infinite recursion loops... */
+	if (incall) {
+		return;
+	}
+	incall = TRUE;
+
+	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);
+	}
+	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;
+}
+
+/* main loop of the daemon*/
+int
+init_start ()
+{
+	DIR* dir = NULL;
+	PILPluginUniv * PluginLoadingSystem = NULL;
+	struct dirent* subdir;
+	struct passwd*	pw_entry;
+	char* dot = NULL;
+	char* ra_name = NULL;
+        int len;
+	IPC_Auth	* auth = NULL;
+	int		one = 1;
+	GHashTable*	uidlist;
+	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} };
+
+	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
+	 */
+
+	uidlist = g_hash_table_new(g_direct_hash, g_direct_equal);
+	/* Add root's uid */
+	g_hash_table_insert(uidlist, GUINT_TO_POINTER(0), &one); 
+
+	pw_entry = getpwnam(HA_CCMUSER);
+	if (pw_entry == NULL) {
+		lrmd_log(LOG_ERR, "Cannot get the uid of HACCMUSER");
+	} else {
+		g_hash_table_insert(uidlist, GUINT_TO_POINTER(pw_entry->pw_uid)
+				    , &one); 
+	}
+
+	if ( NULL == (auth = MALLOCT(struct IPC_AUTH)) ) {
+		lrmd_log(LOG_ERR, "init_start: MALLOCT (IPC_AUTH) failed.");
+	} else {
+		auth->uid = uidlist;
+		auth->gid = NULL;
+	}
+
+	/*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, auth, FALSE,
+				   on_connect_cmd, conn_cmd, NULL);
+
+	/*
+	 * 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, auth, 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;
+
+	g_hash_table_destroy(uidlist);
+	if ( NULL != auth ) {
+		free(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;
+
+	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_noauth(ch);
+	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);
+		return TRUE;
+	}
+	lrmd_debug2(LOG_DEBUG,"dumping request: %s",msg2string(msg));
+
+	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 {
+		int ret;
+
+		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 TRUE;
+}
+static void
+remove_repeat_op_from_client(gpointer key, gpointer value, gpointer user_data)
+{
+	lrmd_rsc_t* rsc = (lrmd_rsc_t*)value;
+	char *app_name = (char *)user_data;
+
+	(void)flush_all(&(rsc->repeat_op_list),app_name);
+	if( flush_all(&(rsc->op_list),app_name) ) {
+		set_rsc_flushing_ops(rsc); /* resource busy */
+	}
+}
+
+/* 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;
+	}
+	/* Remove from clients */
+	g_hash_table_remove(clients, (gpointer)&client->pid);
+
+	/* Search all resources for repeating ops this client owns */
+	g_hash_table_foreach(resources
+	,	remove_repeat_op_from_client, client->app_name);
+
+	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) {
+		Gmain_timeout_remove(op->repeat_timeout_tag);
+		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);
+	}
+
+	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),NULL);
+	if( flush_all(&(rsc->op_list),NULL) ) {
+		set_rsc_removal_pending(rsc);
+		LRMAUDIT();
+		return HA_OK; /* resource is busy, delay removal */
+	}
+	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 != POSTPONED && 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 == POSTPONED) {
+			op_cancelled = HA_OK;
+		}
+	}
+	if( op_cancelled == HA_FAIL ) {
+		lrmd_log(LOG_INFO, "%s: no operation with id %d",
+			__FUNCTION__, cancel_op_id);
+	}
+	LRMAUDIT();
+	return op_cancelled;
+}
+
+static gboolean
+flush_all(GList** listp, char *app_name)
+{
+	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 (app_name && strcmp(op->app_name,app_name)) {
+			node = g_list_next(node);
+			continue; /* not the client's operation */
+		}
+		if( flush_op(op) == POSTPONED ) {
+			rsc_busy = TRUE;
+			node = g_list_next(node);
+		} else if (!app_name || !strcmp(op->app_name,app_name)) {
+			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 */
+	}
+	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->app_name = strdup(client->app_name);
+	op->rsc_id = strdup(rsc->id);
+	op->interval = interval;
+	op->delay = delay;
+
+	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) {
+		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) {
+		g_hash_table_replace(client_last_op,op_hash_key,(gpointer)new_op);
+		lrmd_op_destroy(old_op);
+	} else {
+		g_hash_table_insert(client_last_op,op_hash_key,(gpointer)new_op);
+	}
+}
+
+static void
+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__);
+	}
+	rsc->last_op_done->repeat_timeout_tag = (guint)0;
+
+	client = lookup_client_by_name(op->app_name);
+	if (!client) {
+		lrmd_log(LOG_INFO, "%s: cannot record %s: the client is gone"
+		,	__FUNCTION__, small_op_info(op)); 
+		LRMAUDIT();
+		return;
+	}
+	/* insert (or replace) the new op in last_op_table for the client */
+	replace_last_op(client,rsc,op);
+	if (op->interval) /* copy op to the repeat list */
+		to_repeatlist(rsc,op);
+	LRMAUDIT();
+}
+
+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_by_name(op->app_name);
+	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;
+	}
+	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_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)) ) {
+		old_op = g_hash_table_lookup(client_last_op, op_id);
+		if (old_op) {
+			g_hash_table_remove(client_last_op,	op_id);
+			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 suspicously"
+		" 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_status != LRM_OP_CANCELLED) {
+		record_op_completion(rsc,op); /*record the outcome of the op */
+	} else {
+		remove_op_history(op);
+	}
+
+	if (op_status != LRM_OP_DONE
+		|| (op_rc == -1)
+		|| (op_rc == target_rc)
+		|| (target_rc == EVERYTIME)
+		|| ((target_rc == CHANGED) && rc_changed)
+	) {
+		notify_client(op);
+	}
+	lrmd_op_destroy(op);
+	if( !rsc->op_list ) {
+		if( rsc_removal_pending(rsc) ) {
+			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 {
+		lrmd_log(LOG_INFO, "%s: process for %s still "
+			"running, flush delayed"
+			,__FUNCTION__,small_op_info(op));
+		return POSTPONED;
+	}
+}
+
+/* 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) {
+		Gmain_timeout_remove(rsc->delay_timeout);
+		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)	{
+			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 (child_count >= max_child_count) {
+			if ((int)rsc->delay_timeout > 0) {
+				lrmd_log(LOG_INFO
+				,	"%s:%d: operations on resource %s already delayed"
+				, __FUNCTION__, __LINE__, 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);
+	if (!op->interval || is_logmsg_due(op)) { /* log non-repeating ops */
+		lrmd_log(LOG_INFO,"rsc:%s:%d: %s",rsc->id,op->call_id,probe_str(op,op_type));
+	} else {
+		lrmd_debug(LOG_DEBUG,"rsc:%s:%d: %s",rsc->id,op->call_id,op_type);
+	}
+	op_params = ha_msg_value_str_table(op->msg, F_LRM_PARAM);
+	params = merge_str_tables(rsc->params,op_params);
+	ha_msg_mod_str_table(op->msg, F_LRM_PARAM, params);
+	if (op_params) {
+		free_str_table(op_params);
+		op_params = NULL;
+	}
+	if (params) {
+		free_str_table(params);
+		params = NULL;
+	}
+	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++;
+			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)) {
+				op->t_lastlogmsg = time_longclock();
+			}
+			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());		
+			params = ha_msg_value_str_table(op->msg, F_LRM_PARAM);
+			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();
+	if (--child_count < 0) {
+		lrmd_log(LOG_ERR, "%s:%d: child count is less than zero: %d"
+			, __FUNCTION__, __LINE__, child_count);
+		child_count = 0;
+	}
+
+	CHECK_ALLOCATED(p, "ProcTrack p", );
+	op = proctrack_data(p);
+	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;
+	}
+
+	if (HA_OK == ha_msg_value_int(op->msg, F_LRM_OPSTATUS, &op_status)
+	&& (op_status_t)op_status == LRM_OP_CANCELLED ) {
+		lrmd_debug(LOG_DEBUG, "on_ra_proc_finished: "
+			"%s cancelled.", op_info(op));
+		on_op_done(rsc,op);
+		reset_proctrack_data(p);
+		if (debug_level >= 2) {	
+			dump_data_for_debug();
+		}
+		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"
+			, 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 (rc != EXECRA_OK || debug_level > 0) {
+			if (rc == exitcode) {
+				lrmd_debug2(rc == EXECRA_OK ? LOG_DEBUG : LOG_INFO
+				,	"%s: pid [%d] exited with"
+				" return code %d", op_info(op), proctrack_pid(p), rc);
+			}else{
+				lrmd_debug2(rc == EXECRA_OK ? LOG_DEBUG : LOG_INFO
+				,	"%s: pid [%d] exited with"
+				" return code %d (mapped from %d)"
+				,	op_info(op), proctrack_pid(p), rc, exitcode);
+			}
+			if (rc != EXECRA_OK || debug_level > 1) {
+				lrmd_debug2(LOG_INFO, "Resource Agent output: [%s]"
+				,	op->first_line_ra_stdout);
+			}
+		}
+		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 (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;
+		}
+		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_by_name(op->app_name);
+
+	if (client) {
+		/* send the result to client */
+		send_cbk_msg(op->msg, client);
+	} else {
+		lrmd_log(LOG_ERR
+		,	"%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{
+		snprintf(info, sizeof(info)
+		,"operation %s[%d] on %s::%s::%s for client %d"
+		,lrm_str(op_type), op->call_id
+		,lrm_str(rsc->class), lrm_str(rsc->type), 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;
+	CHECK_ALLOCATED(op, "op", );
+	t_stay_in_list = longclockto_ms(op->t_perform - op->t_addtolist);
+	if ( t_stay_in_list > WARNINGTIME_IN_LIST) 
+	{
+		lrmd_log(LOG_WARNING
+		,	"perform_ra_op: the operation %s stayed in operation "
+			"list for %lu ms (longer than %d ms)"
+		,	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..cd43106
--- /dev/null
+++ b/lrm/lrmd/lrmd.h
@@ -0,0 +1,265 @@
+#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.
+ *
+ *
+ */
+
+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;
+}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)
+
+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 */
+	GList*		requestors;	/* a list of client pids to send replies to */
+	int			state;  /* status of the resource */
+};
+
+struct lrmd_op
+{
+	char*			rsc_id;
+	gboolean		is_copy;
+	pid_t			client_id;
+	char*		app_name;
+	int			call_id;
+	int			exec_pid;
+	guint			repeat_timeout_tag;
+	int			interval;
+	int			delay;
+	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
diff --git a/lrm/lrmd/lrmd_fdecl.h b/lrm/lrmd/lrmd_fdecl.h
new file mode 100644
index 0000000..a4c8a4c
--- /dev/null
+++ b/lrm/lrmd/lrmd_fdecl.h
@@ -0,0 +1,110 @@
+/* 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 void 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, char *app_name);
+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);
+
+/*
+ * 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 at 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/.cvsignore b/lrm/test/.cvsignore
new file mode 100644
index 0000000..b92b57e
--- /dev/null
+++ b/lrm/test/.cvsignore
@@ -0,0 +1,19 @@
+LRMBasicSanityCheck
+apitest
+plugintest
+Makefile.in
+Makefile
+.deps
+.libs
+*.la
+*.lo
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
+callbacktest
+simple_ops
+lrmregtest
+lrmregtest-heartbeat
+lrmregtest-lsb
+regression.sh
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 at 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..4b1c4fd
--- /dev/null
+++ b/lrm/test/Makefile.am
@@ -0,0 +1,48 @@
+#
+# Author: Sun Jiang Dong <sunjd at 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-heartbeat 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..f944619
--- /dev/null
+++ b/lrm/test/README.regression
@@ -0,0 +1,161 @@
+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
+
+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..26367dd
--- /dev/null
+++ b/lrm/test/apitest.c
@@ -0,0 +1,318 @@
+
+/*
+ * Test program for Local Resource Manager  API.
+ *
+ * Copyright (C) 2004 Huang Zhen <zhenh at 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;
+	int call_id;
+	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;
+	call_id = 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;
+	call_id = 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;
+	call_id = 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..63792b9
--- /dev/null
+++ b/lrm/test/callbacktest.c
@@ -0,0 +1,205 @@
+
+/*
+ * Test program for the callback function of Local Resource Manager  API.
+ *
+ * Copyright (C) 2004 Huang Zhen <zhenh at 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;
+	int call_id;
+
+	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;
+	call_id = 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..014b7a3
--- /dev/null
+++ b/lrm/test/evaltest.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+ # Copyright (C) 2007 Dejan Muhamedagic <dejan at 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() {
+	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-heartbeat.in b/lrm/test/lrmregtest-heartbeat.in
new file mode 100644
index 0000000..cc6fdca
--- /dev/null
+++ b/lrm/test/lrmregtest-heartbeat.in
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# WARNING: This script is for LRM regressions tests only
+#
+
+. @HB_RA_DIR@/hto-mapfuncs
+
+case $# in
+  1)	op=$1;;
+  2)	export OCF_RESKEY_delay; OCF_RESKEY_delay=$1; op=$2;;
+esac
+
+OCF_TYPE=lrmregtest
+OCF_RESOURCE_INSTANCE=heartbeat
+export OCF_TYPE OCF_RESOURCE_INSTANCE
+
+ra_execocf $op
diff --git a/lrm/test/lrmregtest-lsb.in b/lrm/test/lrmregtest-lsb.in
new file mode 100644
index 0000000..1d47cd3
--- /dev/null
+++ b/lrm/test/lrmregtest-lsb.in
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# WARNING: This script is for LRM regressions tests only
+#
+
+. @HB_RA_DIR@/hto-mapfuncs
+
+OCF_TYPE=lrmregtest
+OCF_RESOURCE_INSTANCE=lsb
+export OCF_TYPE OCF_RESOURCE_INSTANCE
+
+ra_execocf $1
diff --git a/lrm/test/lrmregtest.in b/lrm/test/lrmregtest.in
new file mode 100644
index 0000000..b9a367b
--- /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}/resource.d/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 at 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..8233cee
--- /dev/null
+++ b/lrm/test/regression.sh.in
@@ -0,0 +1,262 @@
+#!/bin/sh
+
+ # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic at 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
+HB_RA=@HB_RA_DIR@/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
+. /usr/lib/ocf/resource.d/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() {
+	if [ ! -e $OCF_RA ]; then
+		cp -p lrmregtest $OCF_RA
+		lrmregtest_ocf=1
+	fi
+	chmod +x $OCF_RA
+	if [ ! -e $LSB_RA ]; then
+		cp -p lrmregtest-lsb $LSB_RA
+		lrmregtest_lsb=1
+	fi
+	chmod +x $LSB_RA
+	if [ ! -e $HB_RA ]; then
+		cp -p lrmregtest-heartbeat $HB_RA
+		lrmregtest_heartbeat=1
+	fi
+	chmod +x $HB_RA
+}
+rm_ra() {
+	[ "$lrmregtest_ocf" ] && rm -f $OCF_RA
+	[ "$lrmregtest_lsb" ] && rm -f $LSB_RA
+	[ "$lrmregtest_heartbeat" ] && rm -f $HB_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/.cvsignore b/lrm/test/testcases/.cvsignore
new file mode 100644
index 0000000..f94fd1c
--- /dev/null
+++ b/lrm/test/testcases/.cvsignore
@@ -0,0 +1,10 @@
+Makefile.in
+Makefile
+.deps
+.libs
+*.la
+*.lo
+.*.swp
+*.beam
+parser-messages
+MISC_ERRORS
diff --git a/lrm/test/testcases/BSC b/lrm/test/testcases/BSC
new file mode 100644
index 0000000..28e751e
--- /dev/null
+++ b/lrm/test/testcases/BSC
@@ -0,0 +1,3 @@
+rscmgmt
+metadata
+rscexec
diff --git a/lrm/test/testcases/Makefile.am b/lrm/test/testcases/Makefile.am
new file mode 100644
index 0000000..8c7bbae
--- /dev/null
+++ b/lrm/test/testcases/Makefile.am
@@ -0,0 +1,26 @@
+#
+# Author: Sun Jiang Dong <sunjd at 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
+#			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..8ca6081
--- /dev/null
+++ b/lrm/test/testcases/basicset
@@ -0,0 +1,5 @@
+rscmgmt
+metadata
+rscexec
+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..cb459a2
--- /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..02a1c74
--- /dev/null
+++ b/lrm/test/testcases/metadata.exp
@@ -0,0 +1,38 @@
+.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 ra-list.sh
+.TRY List types class=stonith
+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 many
+.TRY Meta-data class=stonith
+.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
+.EXTCHECK xmllint.sh
+.TRY Show meta-data class=stonith type=ssh 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..d2c7525
--- /dev/null
+++ b/lrm/test/testcases/rscexec
@@ -0,0 +1,62 @@
+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
+# heartbeat
+%setenv dflt_class=heartbeat dftl_rsc=rscexec_rsc_r1-heartbeat
+add args=0
+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
+# 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..c961361
--- /dev/null
+++ b/lrm/test/testcases/rscexec.exp
@@ -0,0 +1,229 @@
+.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=heartbeat dftl_rsc=rscexec_rsc_r1-heartbeat
+.TRY Add resource rscexec_rsc_r1 class=heartbeat type=lrmregtest provider=heartbeat args=0
+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: 1=0  
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+
+> monitor succeed (status=0,rc=0): INFO:  Running OK
+
+.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): INFO:  Running OK
+
+.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: 1=0  
+   operation 'monitor' [call_id=(removed)]:
+      start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+      rc=0 (ok), op_status=0 (succeed)
+      parameters: 1=0  
+   operation 'stop' [call_id=(removed)]:
+      start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+      rc=0 (ok), op_status=0 (succeed)
+      parameters: 1=0  
+.TRY Exec rscexec_rsc_r1 op=monitor timeout=1000 interval=0 target=EVERYTIME args=
+
+> monitor succeed (status=0,rc=7): INFO:  Resource is stopped
+
+.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): INFO:  Resource is stopped
+
+.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.
+.SETENV dflt_class=stonith dftl_rsc=rscexec_rsc_r1-stonith
+.TRY Add resource rscexec_rsc_r1 class=stonith type=null provider=heartbeat args=hostlist=node1
+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: hostlist=node1  
+.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: hostlist=node1  
+   operation 'monitor' [call_id=(removed)]:
+      start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+      rc=0 (ok), op_status=0 (succeed)
+      parameters: hostlist=node1  
+   operation 'stop' [call_id=(removed)]:
+      start_delay=0, interval=0, timeout=1000, app_name=lrmadmin
+      rc=0 (ok), op_status=0 (succeed)
+      parameters: hostlist=node1  
+.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..d4337d5
--- /dev/null
+++ b/lrm/test/testcases/rscmgmt
@@ -0,0 +1,34 @@
+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
+%setenv dflt_class=heartbeat 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=heartbeat 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..20e2774
--- /dev/null
+++ b/lrm/test/testcases/rscmgmt.exp
@@ -0,0 +1,87 @@
+.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.
+.SETENV dflt_class=heartbeat dflt_type=lrmregtest
+.TRY List resources
+Currently no resources are managed by LRM.
+.TRY Add resource r1 class=heartbeat type=lrmregtest provider=heartbeat args=
+Succeeded in adding this resource.
+.TRY List resources
+
+Resource ID:r1
+Resource agent class:heartbeat
+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=heartbeat 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:r1
+Resource agent class:lsb
+Resource agent type:lrmregtest
+Resource agent provider:heartbeat
+
+Resource ID:r2
+Resource agent class:ocf
+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/xmllint.sh b/lrm/test/testcases/xmllint.sh
new file mode 100755
index 0000000..5f34721
--- /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-z]+$/ {
+	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 at 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 at 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 at 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 at 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 at 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 at 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 at datapower.com>
+ *
+ *
+ *
+ *	-- Alan Robertson
+ *	   alanr at unix.sh
+ *
+ **************************************************************************
+ *
+ * Subject:	Re: Scandir replacement function
+ * Date:	18 May 2001 12:00:48 -0400
+ * From:	pinard at iro.umontreal.ca (Fran�ois Pinard)
+ * To:		Alan Robertson <alanr at 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 at datapower.com>
+ * To: Alan Robertson <alanr at unix.sh>
+ * Subject: Re: Verifying permissions/licenses/etc on some old code of yours -
+ *  scandir.c
+ * In-Reply-To: <433071CA.8000107 at unix.sh>
+ * Message-ID: <Pine.LNX.4.44L0.0509202151270.9198-100000 at 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 at 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 at hotmail.com>
+	 *			David Lee <t.d.lee at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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);
+	}
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-ha/cluster-glue.git



More information about the Debian-HA-Commits mailing list